diff --git a/data/sql/updates/db_world/2024_02_06_00.sql b/data/sql/updates/db_world/2024_02_06_00.sql new file mode 100644 index 000000000..eceb8cd38 --- /dev/null +++ b/data/sql/updates/db_world/2024_02_06_00.sql @@ -0,0 +1,5 @@ +-- DB update 2024_02_05_04 -> 2024_02_06_00 +-- +UPDATE `creature_template` SET `AIName` = '', `ScriptName` = 'npc_tainted_elemental' WHERE `entry` = 22009; + +DELETE FROM `smart_scripts` WHERE `entryorguid` = 22009 AND `source_type` = 0; diff --git a/data/sql/updates/db_world/2024_02_08_00.sql b/data/sql/updates/db_world/2024_02_08_00.sql new file mode 100644 index 000000000..7e810ffad --- /dev/null +++ b/data/sql/updates/db_world/2024_02_08_00.sql @@ -0,0 +1,5 @@ +-- DB update 2024_02_06_00 -> 2024_02_08_00 +-- +DELETE FROM `creature_template_movement` WHERE `CreatureId` = 24224; +INSERT INTO `creature_template_movement` (`CreatureId`, `Flight`, `Rooted`) VALUES +(24224, 1, 1); diff --git a/data/sql/updates/db_world/2024_02_08_01.sql b/data/sql/updates/db_world/2024_02_08_01.sql new file mode 100644 index 000000000..c2f3d42c2 --- /dev/null +++ b/data/sql/updates/db_world/2024_02_08_01.sql @@ -0,0 +1,3 @@ +-- DB update 2024_02_08_00 -> 2024_02_08_01 +-- +UPDATE `creature_template` SET `spell_school_immune_mask` = `spell_school_immune_mask`|4 WHERE `entry` IN (19514, 19551); diff --git a/data/sql/updates/db_world/2024_02_08_02.sql b/data/sql/updates/db_world/2024_02_08_02.sql new file mode 100644 index 000000000..c50f508dc --- /dev/null +++ b/data/sql/updates/db_world/2024_02_08_02.sql @@ -0,0 +1,4 @@ +-- DB update 2024_02_08_01 -> 2024_02_08_02 +DELETE FROM `gossip_menu_option` WHERE `MenuID`=21893; +INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES +(21893, 0, 0, 'Hand over the Southfury moonstone and I\'ll let you go.', 20723, 1, 1, 0, 0, 0, 0, '', 0, 0); diff --git a/deps/boost/CMakeLists.txt b/deps/boost/CMakeLists.txt index 3cf5d466b..11e4b0bac 100644 --- a/deps/boost/CMakeLists.txt +++ b/deps/boost/CMakeLists.txt @@ -12,10 +12,10 @@ if(WIN32) set(BOOST_DEBUG ON) - if(DEFINED ENV{BOOST_ROOT}) - set(BOOST_ROOT $ENV{BOOST_ROOT}) + if(DEFINED ENV{Boost_ROOT}) + set(Boost_ROOT $ENV{Boost_ROOT}) list(APPEND BOOST_LIBRARYDIR - ${BOOST_ROOT}/lib${PLATFORM}-msvc-14.2) + ${Boost_ROOT}/lib${PLATFORM}-msvc-14.2) endif() set(Boost_USE_STATIC_LIBS ON) @@ -35,8 +35,8 @@ endif() find_package(Boost ${BOOST_REQUIRED_VERSION} REQUIRED system filesystem program_options iostreams regex thread) if(NOT Boost_FOUND) - if(NOT DEFINED ENV{BOOST_ROOT} AND NOT DEFINED Boost_DIR AND NOT DEFINED BOOST_ROOT AND NOT DEFINED BOOSTROOT) - message(FATAL_ERROR "No BOOST_ROOT environment variable could be found! Please make sure it is set and the points to your Boost installation.") + if(NOT DEFINED ENV{Boost_ROOT} AND NOT DEFINED Boost_DIR AND NOT DEFINED BOOST_ROOT AND NOT DEFINED BOOSTROOT) + message(FATAL_ERROR "No Boost_ROOT environment variable could be found! Please make sure it is set and the points to your Boost installation.") endif() endif() diff --git a/src/cmake/macros/FindOpenSSL.cmake b/src/cmake/macros/FindOpenSSL.cmake index fb1651e6d..6eeafdf9b 100644 --- a/src/cmake/macros/FindOpenSSL.cmake +++ b/src/cmake/macros/FindOpenSSL.cmake @@ -7,15 +7,29 @@ FindOpenSSL Find the OpenSSL encryption library. +This module finds an installed OpenSSL library and determines its version. + +.. versionadded:: 3.19 + When a version is requested, it can be specified as a simple value or as a + range. For a detailed description of version range usage and capabilities, + refer to the :command:`find_package` command. + +.. versionadded:: 3.18 + Support for OpenSSL 3.0. + Optional COMPONENTS ^^^^^^^^^^^^^^^^^^^ +.. versionadded:: 3.12 + This module supports two optional COMPONENTS: ``Crypto`` and ``SSL``. Both components have associated imported targets, as described below. Imported Targets ^^^^^^^^^^^^^^^^ +.. versionadded:: 3.4 + This module defines the following :prop_tgt:`IMPORTED` targets: ``OpenSSL::SSL`` @@ -23,6 +37,8 @@ This module defines the following :prop_tgt:`IMPORTED` targets: ``OpenSSL::Crypto`` The OpenSSL ``crypto`` library, if found. ``OpenSSL::applink`` + .. versionadded:: 3.18 + The OpenSSL ``applink`` components that might be need to be compiled into projects under MSVC. This target is available only if found OpenSSL version is not less than 0.9.8. By linking this target the above OpenSSL targets can @@ -74,36 +90,115 @@ This module will set the following variables in your project: Hints ^^^^^ -Set ``OPENSSL_ROOT_DIR`` to the root directory of an OpenSSL installation. -Set ``OPENSSL_USE_STATIC_LIBS`` to ``TRUE`` to look for static libraries. -Set ``OPENSSL_MSVC_STATIC_RT`` set ``TRUE`` to choose the MT version of the lib. +The following variables may be set to control search behavior: + +``OPENSSL_ROOT_DIR`` + Set to the root directory of an OpenSSL installation. + +``OPENSSL_USE_STATIC_LIBS`` + .. versionadded:: 3.4 + + Set to ``TRUE`` to look for static libraries. + +``OPENSSL_MSVC_STATIC_RT`` + .. versionadded:: 3.5 + + Set to ``TRUE`` to choose the MT version of the lib. + +``ENV{PKG_CONFIG_PATH}`` + On UNIX-like systems, ``pkg-config`` is used to locate the system OpenSSL. + Set the ``PKG_CONFIG_PATH`` environment variable to look in alternate + locations. Useful on multi-lib systems. #]=======================================================================] -set(OPENSSL_EXPECTED_VERSION "1.0") - macro(_OpenSSL_test_and_find_dependencies ssl_library crypto_library) - if((CMAKE_SYSTEM_NAME STREQUAL "Linux") AND + unset(_OpenSSL_extra_static_deps) + if(UNIX AND (("${ssl_library}" MATCHES "\\${CMAKE_STATIC_LIBRARY_SUFFIX}$") OR ("${crypto_library}" MATCHES "\\${CMAKE_STATIC_LIBRARY_SUFFIX}$"))) set(_OpenSSL_has_dependencies TRUE) - find_package(Threads) + unset(_OpenSSL_has_dependency_zlib) + if(OPENSSL_USE_STATIC_LIBS) + set(_OpenSSL_libs "${_OPENSSL_STATIC_LIBRARIES}") + set(_OpenSSL_ldflags_other "${_OPENSSL_STATIC_LDFLAGS_OTHER}") + else() + set(_OpenSSL_libs "${_OPENSSL_LIBRARIES}") + set(_OpenSSL_ldflags_other "${_OPENSSL_LDFLAGS_OTHER}") + endif() + if(_OpenSSL_libs) + unset(_OpenSSL_has_dependency_dl) + foreach(_OPENSSL_DEP_LIB IN LISTS _OpenSSL_libs) + if (_OPENSSL_DEP_LIB STREQUAL "ssl" OR _OPENSSL_DEP_LIB STREQUAL "crypto") + # ignoring: these are the targets + elseif(_OPENSSL_DEP_LIB STREQUAL CMAKE_DL_LIBS) + set(_OpenSSL_has_dependency_dl TRUE) + elseif(_OPENSSL_DEP_LIB STREQUAL "z") + find_package(ZLIB) + set(_OpenSSL_has_dependency_zlib TRUE) + else() + list(APPEND _OpenSSL_extra_static_deps "${_OPENSSL_DEP_LIB}") + endif() + endforeach() + unset(_OPENSSL_DEP_LIB) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(_OpenSSL_has_dependency_dl TRUE) + endif() + if(_OpenSSL_ldflags_other) + unset(_OpenSSL_has_dependency_threads) + foreach(_OPENSSL_DEP_LDFLAG IN LISTS _OpenSSL_ldflags_other) + if (_OPENSSL_DEP_LDFLAG STREQUAL "-pthread") + set(_OpenSSL_has_dependency_threads TRUE) + find_package(Threads) + endif() + endforeach() + unset(_OPENSSL_DEP_LDFLAG) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(_OpenSSL_has_dependency_threads TRUE) + find_package(Threads) + endif() + unset(_OpenSSL_libs) + unset(_OpenSSL_ldflags_other) else() set(_OpenSSL_has_dependencies FALSE) endif() endmacro() function(_OpenSSL_add_dependencies libraries_var) - if(CMAKE_THREAD_LIBS_INIT) + if(_OpenSSL_has_dependency_zlib) + list(APPEND ${libraries_var} ${ZLIB_LIBRARY}) + endif() + if(_OpenSSL_has_dependency_threads) list(APPEND ${libraries_var} ${CMAKE_THREAD_LIBS_INIT}) endif() - list(APPEND ${libraries_var} ${CMAKE_DL_LIBS}) + if(_OpenSSL_has_dependency_dl) + list(APPEND ${libraries_var} ${CMAKE_DL_LIBS}) + endif() + list(APPEND ${libraries_var} ${_OpenSSL_extra_static_deps}) set(${libraries_var} ${${libraries_var}} PARENT_SCOPE) endfunction() function(_OpenSSL_target_add_dependencies target) if(_OpenSSL_has_dependencies) - set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Threads::Threads ) - set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS} ) + if(_OpenSSL_has_dependency_zlib) + set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ZLIB::ZLIB ) + endif() + if(_OpenSSL_has_dependency_threads) + set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Threads::Threads) + endif() + if(_OpenSSL_has_dependency_dl) + set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS} ) + endif() + if(_OpenSSL_extra_static_deps) + set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${_OpenSSL_extra_static_deps}) + endif() + endif() + if(WIN32 AND OPENSSL_USE_STATIC_LIBS) + if(WINCE) + set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ws2 ) + else() + set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ws2_32 ) + endif() + set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES crypt32 ) endif() endfunction() @@ -115,62 +210,87 @@ endif () # Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES if(OPENSSL_USE_STATIC_LIBS) set(_openssl_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) - if(WIN32) + if(MSVC) set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) else() set(CMAKE_FIND_LIBRARY_SUFFIXES .a ) endif() endif() -if (WIN32) +if(CMAKE_SYSTEM_NAME STREQUAL "QNX" AND + CMAKE_SYSTEM_VERSION VERSION_GREATER_EQUAL "7.0" AND CMAKE_SYSTEM_VERSION VERSION_LESS "7.1" AND + OpenSSL_FIND_VERSION VERSION_GREATER_EQUAL "1.1" AND OpenSSL_FIND_VERSION VERSION_LESS "1.2") + # QNX 7.0.x provides openssl 1.0.2 and 1.1.1 in parallel: + # * openssl 1.0.2: libcrypto.so.2 and libssl.so.2, headers under usr/include/openssl + # * openssl 1.1.1: libcrypto1_1.so.2.1 and libssl1_1.so.2.1, header under usr/include/openssl1_1 + # See http://www.qnx.com/developers/articles/rel_6726_0.html + set(_OPENSSL_FIND_PATH_SUFFIX "openssl1_1") + set(_OPENSSL_NAME_POSTFIX "1_1") +else() + set(_OPENSSL_FIND_PATH_SUFFIX "include") +endif() + +if (OPENSSL_ROOT_DIR OR NOT "$ENV{OPENSSL_ROOT_DIR}" STREQUAL "") + set(_OPENSSL_ROOT_HINTS HINTS ${OPENSSL_ROOT_DIR} ENV OPENSSL_ROOT_DIR) + set(_OPENSSL_ROOT_PATHS NO_DEFAULT_PATH) +elseif (MSVC) # http://www.slproweb.com/products/Win32OpenSSL.html - set(_OPENSSL_MSI_INSTALL_GUID "") - if(PLATFORM EQUAL 64) - set(_OPENSSL_MSI_INSTALL_GUID "117551DB-A110-4BBD-BB05-CFE0BCB3ED31") - set(_OPENSSL_ROOT_HINTS - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]" - ${OPENSSL_ROOT_DIR} - ENV OPENSSL_ROOT_DIR - ) + set(_OPENSSL_MSI_INSTALL_GUIDS "") + + if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") + if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64") + set(_arch "Win64-ARM") + set(_OPENSSL_MSI_INSTALL_GUIDS "99C28AFA-6419-40B1-B88D-32B810BB4234") + else() + set(_arch "Win64") + set(_OPENSSL_MSI_INSTALL_GUIDS "117551DB-A110-4BBD-BB05-CFE0BCB3ED31" "50A9FBE2-0F8C-4D5D-97A4-A63A71C4EA1E") + endif() file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) - set(_OPENSSL_ROOT_PATHS - "${_programfiles}/OpenSSL" - "${_programfiles}/OpenSSL-Win64" - "C:/OpenSSL/" - "C:/OpenSSL-Win64/" - ) + set(_OPENSSL_ROOT_HINTS HINTS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]") else() - set(_OPENSSL_MSI_INSTALL_GUID "A1EEC576-43B9-4E75-9E02-03DA542D2A38") - set(_OPENSSL_ROOT_HINTS - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]" - ${OPENSSL_ROOT_DIR} - ENV OPENSSL_ROOT_DIR - ) - file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) - set(_OPENSSL_ROOT_PATHS - "${_programfiles}/OpenSSL" - "${_programfiles}/OpenSSL-Win32" - "C:/OpenSSL/" - "C:/OpenSSL-Win32/" - ) + set(_arch "Win32") + set(_progfiles_x86 "ProgramFiles(x86)") + if(NOT "$ENV{${_progfiles_x86}}" STREQUAL "") + # under windows 64 bit machine + file(TO_CMAKE_PATH "$ENV{${_progfiles_x86}}" _programfiles) + else() + # under windows 32 bit machine + file(TO_CMAKE_PATH "$ENV{ProgramFiles}" _programfiles) + endif() + set(_OPENSSL_ROOT_HINTS HINTS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]") + set(_OPENSSL_MSI_INSTALL_GUIDS "A1EEC576-43B9-4E75-9E02-03DA542D2A38" "31D2408A-9CAE-4988-9EC3-F40FDE7D6AE5") endif() - unset(_programfiles) + # If OpenSSL was installed using .msi package instead of .exe, Inno Setup registry values are not written to Uninstall\OpenSSL # but because it is only a shim around Inno Setup it does write the location of uninstaller which we can use to determine path - get_filename_component(_OPENSSL_MSI_INSTALL_PATH "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Inno Setup MSIs\\${_OPENSSL_MSI_INSTALL_GUID};]" DIRECTORY) - if(NOT _OPENSSL_MSI_INSTALL_PATH STREQUAL "/") - list(INSERT _OPENSSL_ROOT_HINTS 0 ${_OPENSSL_MSI_INSTALL_PATH}) - endif() -else () - set(_OPENSSL_ROOT_HINTS - ${OPENSSL_ROOT_DIR} - ENV OPENSSL_ROOT_DIR + foreach(_OPENSSL_MSI_INSTALL_GUID IN LISTS _OPENSSL_MSI_INSTALL_GUIDS) + get_filename_component(_OPENSSL_MSI_INSTALL_PATH "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Inno Setup MSIs\\${_OPENSSL_MSI_INSTALL_GUID};]" DIRECTORY) + if(NOT _OPENSSL_MSI_INSTALL_PATH STREQUAL "/") + list(INSERT _OPENSSL_ROOT_HINTS 2 ${_OPENSSL_MSI_INSTALL_PATH}) + endif() + endforeach() + unset(_OPENSSL_MSI_INSTALL_GUIDS) + + set(_OPENSSL_ROOT_PATHS + PATHS + "${_programfiles}/OpenSSL" + "${_programfiles}/OpenSSL-${_arch}" + "C:/OpenSSL/" + "C:/OpenSSL-${_arch}/" ) + unset(_programfiles) + unset(_arch) endif () +if(HOMEBREW_PREFIX) + list(APPEND _OPENSSL_ROOT_HINTS + "${HOMEBREW_PREFIX}/opt/openssl@1.1" + "${HOMEBREW_PREFIX}/opt/openssl@3") +endif() + set(_OPENSSL_ROOT_HINTS_AND_PATHS - HINTS ${_OPENSSL_ROOT_HINTS} - PATHS ${_OPENSSL_ROOT_PATHS} + ${_OPENSSL_ROOT_HINTS} + ${_OPENSSL_ROOT_PATHS} ) find_path(OPENSSL_INCLUDE_DIR @@ -181,7 +301,7 @@ find_path(OPENSSL_INCLUDE_DIR ${_OPENSSL_INCLUDEDIR} ${_OPENSSL_INCLUDE_DIRS} PATH_SUFFIXES - include + ${_OPENSSL_FIND_PATH_SUFFIX} ) if(WIN32 AND NOT CYGWIN) @@ -210,9 +330,15 @@ if(WIN32 AND NOT CYGWIN) # Since OpenSSL 1.1, lib names are like libcrypto32MTd.lib and libssl32MTd.lib if( "${CMAKE_SIZEOF_VOID_P}" STREQUAL "8" ) - set(_OPENSSL_MSVC_ARCH_SUFFIX "64") + set(_OPENSSL_MSVC_ARCH_SUFFIX "64") + if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64") + set(_OPENSSL_MSVC_ARCH_DIRECTORY "arm64") + else() + set(_OPENSSL_MSVC_ARCH_DIRECTORY "x64") + endif() else() - set(_OPENSSL_MSVC_ARCH_SUFFIX "32") + set(_OPENSSL_MSVC_ARCH_SUFFIX "32") + set(_OPENSSL_MSVC_ARCH_DIRECTORY "x86") endif() if(OPENSSL_USE_STATIC_LIBS) @@ -237,29 +363,40 @@ if(WIN32 AND NOT CYGWIN) find_library(LIB_EAY_DEBUG NAMES - # When OpenSSL is built with default options, the static library name is suffixed with "_static". - # Looking the "libcrypto_static.lib" with a higher priority than "libcrypto.lib" which is the - # import library of "libcrypto.dll". - libcrypto${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - libcrypto${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - libcrypto${_OPENSSL_STATIC_SUFFIX}d - libeay32${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - libeay32${_OPENSSL_STATIC_SUFFIX}d - crypto${_OPENSSL_STATIC_SUFFIX}d - # When OpenSSL is built with the "-static" option, only the static build is produced, - # and it is not suffixed with "_static". - libcrypto${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - libcrypto${_OPENSSL_MSVC_RT_MODE}d - libcryptod - libeay32${_OPENSSL_MSVC_RT_MODE}d - libeay32d - cryptod + libcrypto${_OPENSSL_STATIC_SUFFIX} NAMES_PER_DIR ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES - ${_OPENSSL_PATH_SUFFIXES} + "lib/VC/${_OPENSSL_MSVC_ARCH_DIRECTORY}/${_OPENSSL_MSVC_RT_MODE}d" ) + if(NOT LIB_EAY_DEBUG) + find_library(LIB_EAY_DEBUG + NAMES + # When OpenSSL is built with default options, the static library name is suffixed with "_static". + # Looking the "libcrypto_static.lib" with a higher priority than "libcrypto.lib" which is the + # import library of "libcrypto.dll". + libcrypto${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d + libcrypto${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d + libcrypto${_OPENSSL_STATIC_SUFFIX}d + libeay32${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d + libeay32${_OPENSSL_STATIC_SUFFIX}d + crypto${_OPENSSL_STATIC_SUFFIX}d + # When OpenSSL is built with the "-static" option, only the static build is produced, + # and it is not suffixed with "_static". + libcrypto${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d + libcrypto${_OPENSSL_MSVC_RT_MODE}d + libcryptod + libeay32${_OPENSSL_MSVC_RT_MODE}d + libeay32d + cryptod + NAMES_PER_DIR + ${_OPENSSL_ROOT_HINTS_AND_PATHS} + PATH_SUFFIXES + ${_OPENSSL_PATH_SUFFIXES} + ) + endif() + find_library(LIB_EAY_RELEASE NAMES # When OpenSSL is built with default options, the static library name is suffixed with "_static". @@ -283,33 +420,45 @@ if(WIN32 AND NOT CYGWIN) ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES ${_OPENSSL_PATH_SUFFIXES} + "lib/VC/${_OPENSSL_MSVC_ARCH_DIRECTORY}/${_OPENSSL_MSVC_RT_MODE}" ) find_library(SSL_EAY_DEBUG NAMES - # When OpenSSL is built with default options, the static library name is suffixed with "_static". - # Looking the "libssl_static.lib" with a higher priority than "libssl.lib" which is the - # import library of "libssl.dll". - libssl${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - libssl${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - libssl${_OPENSSL_STATIC_SUFFIX}d - ssleay32${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - ssleay32${_OPENSSL_STATIC_SUFFIX}d - ssl${_OPENSSL_STATIC_SUFFIX}d - # When OpenSSL is built with the "-static" option, only the static build is produced, - # and it is not suffixed with "_static". - libssl${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - libssl${_OPENSSL_MSVC_RT_MODE}d - libssld - ssleay32${_OPENSSL_MSVC_RT_MODE}d - ssleay32d - ssld + libssl${_OPENSSL_STATIC_SUFFIX} NAMES_PER_DIR ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES - ${_OPENSSL_PATH_SUFFIXES} + "lib/VC/${_OPENSSL_MSVC_ARCH_DIRECTORY}/${_OPENSSL_MSVC_RT_MODE}d" ) + if(NOT SSL_EAY_DEBUG) + find_library(SSL_EAY_DEBUG + NAMES + # When OpenSSL is built with default options, the static library name is suffixed with "_static". + # Looking the "libssl_static.lib" with a higher priority than "libssl.lib" which is the + # import library of "libssl.dll". + libssl${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d + libssl${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d + libssl${_OPENSSL_STATIC_SUFFIX}d + ssleay32${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d + ssleay32${_OPENSSL_STATIC_SUFFIX}d + ssl${_OPENSSL_STATIC_SUFFIX}d + # When OpenSSL is built with the "-static" option, only the static build is produced, + # and it is not suffixed with "_static". + libssl${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d + libssl${_OPENSSL_MSVC_RT_MODE}d + libssld + ssleay32${_OPENSSL_MSVC_RT_MODE}d + ssleay32d + ssld + NAMES_PER_DIR + ${_OPENSSL_ROOT_HINTS_AND_PATHS} + PATH_SUFFIXES + ${_OPENSSL_PATH_SUFFIXES} + ) + endif() + find_library(SSL_EAY_RELEASE NAMES # When OpenSSL is built with default options, the static library name is suffixed with "_static". @@ -333,6 +482,7 @@ if(WIN32 AND NOT CYGWIN) ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES ${_OPENSSL_PATH_SUFFIXES} + "lib/VC/${_OPENSSL_MSVC_ARCH_DIRECTORY}/${_OPENSSL_MSVC_RT_MODE}" ) set(LIB_EAY_LIBRARY_DEBUG "${LIB_EAY_DEBUG}") @@ -360,6 +510,7 @@ if(WIN32 AND NOT CYGWIN) PATH_SUFFIXES "lib/MinGW" "lib" + "lib64" ) find_library(SSL_EAY @@ -370,6 +521,7 @@ if(WIN32 AND NOT CYGWIN) PATH_SUFFIXES "lib/MinGW" "lib" + "lib64" ) mark_as_advanced(SSL_EAY LIB_EAY) @@ -411,7 +563,7 @@ else() find_library(OPENSSL_SSL_LIBRARY NAMES - ssl + ssl${_OPENSSL_NAME_POSTFIX} ssleay32 ssleay32MD NAMES_PER_DIR @@ -420,19 +572,19 @@ else() ${_OPENSSL_LIBDIR} ${_OPENSSL_LIBRARY_DIRS} PATH_SUFFIXES - lib + lib lib64 ) find_library(OPENSSL_CRYPTO_LIBRARY NAMES - crypto + crypto${_OPENSSL_NAME_POSTFIX} NAMES_PER_DIR ${_OPENSSL_ROOT_HINTS_AND_PATHS} HINTS ${_OPENSSL_LIBDIR} ${_OPENSSL_LIBRARY_DIRS} PATH_SUFFIXES - lib + lib lib64 ) mark_as_advanced(OPENSSL_CRYPTO_LIBRARY OPENSSL_SSL_LIBRARY) @@ -570,14 +722,6 @@ find_package_handle_standard_args(OpenSSL mark_as_advanced(OPENSSL_INCLUDE_DIR) if(OPENSSL_FOUND) - message(STATUS "Found OpenSSL library: ${OPENSSL_LIBRARIES}") - message(STATUS "Found OpenSSL headers: ${OPENSSL_INCLUDE_DIR}") - include(EnsureVersion) - ENSURE_VERSION("${OPENSSL_EXPECTED_VERSION}" "${OPENSSL_VERSION}" OPENSSL_VERSION_OK) - if(NOT OPENSSL_VERSION_OK) - 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 (EXISTS "${OPENSSL_CRYPTO_LIBRARY}" OR EXISTS "${LIB_EAY_LIBRARY_DEBUG}" OR @@ -642,7 +786,7 @@ if(OPENSSL_FOUND) _OpenSSL_target_add_dependencies(OpenSSL::SSL) endif() - if("${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_FIX}" VERSION_GREATER_EQUAL "0.9.8") + if("${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_MINOR}.${OPENSSL_VERSION_FIX}" VERSION_GREATER_EQUAL "0.9.8") if(MSVC) if(EXISTS "${OPENSSL_INCLUDE_DIR}") set(_OPENSSL_applink_paths PATHS ${OPENSSL_INCLUDE_DIR}) @@ -669,3 +813,10 @@ endif() if(OPENSSL_USE_STATIC_LIBS) set(CMAKE_FIND_LIBRARY_SUFFIXES ${_openssl_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) endif() + +unset(_OPENSSL_FIND_PATH_SUFFIX) +unset(_OPENSSL_NAME_POSTFIX) +unset(_OpenSSL_extra_static_deps) +unset(_OpenSSL_has_dependency_dl) +unset(_OpenSSL_has_dependency_threads) +unset(_OpenSSL_has_dependency_zlib) diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index c0ac0a690..1896ad3b6 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -3069,9 +3069,9 @@ Rate.RepairCost = 1 # Description: Should the player leave their group when they log out? # (It does not affect raids or dungeon finder groups) # -# Default: 1 - (Enabled) +# Default: 0 - (Disabled) -LeaveGroupOnLogout.Enabled = 1 +LeaveGroupOnLogout.Enabled = 0 # # Group.Raid.LevelRestriction diff --git a/src/server/database/Database/MySQLConnection.cpp b/src/server/database/Database/MySQLConnection.cpp index 3e776059a..a77f78d07 100644 --- a/src/server/database/Database/MySQLConnection.cpp +++ b/src/server/database/Database/MySQLConnection.cpp @@ -227,7 +227,11 @@ bool MySQLConnection::Execute(PreparedStatementBase* stmt) uint32 _s = getMSTime(); +#if !defined(MARIADB_VERSION_ID) && (MYSQL_VERSION_ID >= 80300) + if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->GetParameterCount(), nullptr)) +#else if (mysql_stmt_bind_param(msql_STMT, msql_BIND)) +#endif { uint32 lErrno = mysql_errno(m_Mysql); LOG_ERROR("sql.sql", "SQL(p): {}\n [ERROR]: [{}] {}", m_mStmt->getQueryString(), lErrno, mysql_stmt_error(msql_STMT)); @@ -275,7 +279,11 @@ bool MySQLConnection::_Query(PreparedStatementBase* stmt, MySQLPreparedStatement uint32 _s = getMSTime(); +#if !defined(MARIADB_VERSION_ID) && (MYSQL_VERSION_ID >= 80300) + if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->GetParameterCount(), nullptr)) +#else if (mysql_stmt_bind_param(msql_STMT, msql_BIND)) +#endif { uint32 lErrno = mysql_errno(m_Mysql); LOG_ERROR("sql.sql", "SQL(p): {}\n [ERROR]: [{}] {}", m_mStmt->getQueryString(), lErrno, mysql_stmt_error(msql_STMT)); diff --git a/src/server/game/ArenaSpectator/ArenaSpectator.cpp b/src/server/game/ArenaSpectator/ArenaSpectator.cpp index 6048b7636..0f5e11c35 100644 --- a/src/server/game/ArenaSpectator/ArenaSpectator.cpp +++ b/src/server/game/ArenaSpectator/ArenaSpectator.cpp @@ -42,7 +42,7 @@ bool ArenaSpectator::HandleSpectatorSpectateCommand(ChatHandler* handler, std::s return true; } - if (player->getClass() == CLASS_DEATH_KNIGHT && player->GetMapId() == 609) + if (player->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_TELEPORT) && player->GetMapId() == 609) { handler->PSendSysMessage("Death Knights can't spectate before finishing questline."); return true; diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index 3de529c90..d5a275812 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -1055,7 +1055,7 @@ namespace lfg baseAP = p->GetTotalAttackPowerValue(BASE_ATTACK); rangedAP = p->GetTotalAttackPowerValue(RANGED_ATTACK); maxPower = 0; - if (p->getClass() == CLASS_DRUID) + if (p->IsClass(CLASS_DRUID)) maxPower = p->GetMaxPower(POWER_MANA); else maxPower = (p->getPowerType() == POWER_RAGE || p->getPowerType() == POWER_RUNIC_POWER) ? p->GetMaxPower(p->getPowerType()) / 10 : p->GetMaxPower(p->getPowerType()); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index ab8f2f831..1d71b3775 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1196,8 +1196,8 @@ bool Creature::isCanInteractWithBattleMaster(Player* player, bool msg) const bool Creature::isCanTrainingAndResetTalentsOf(Player* player) const { return player->GetLevel() >= 10 - && GetCreatureTemplate()->trainer_type == TRAINER_TYPE_CLASS - && player->getClass() == GetCreatureTemplate()->trainer_class; + && GetCreatureTemplate()->trainer_type == TRAINER_TYPE_CLASS + && player->IsClass((Classes)GetCreatureTemplate()->trainer_class, CLASS_CONTEXT_CLASS_TRAINER); } bool Creature::IsValidTrainerForPlayer(Player* player, uint32* npcFlags /*= nullptr*/) const @@ -1211,7 +1211,7 @@ bool Creature::IsValidTrainerForPlayer(Player* player, uint32* npcFlags /*= null { case TRAINER_TYPE_CLASS: case TRAINER_TYPE_PETS: - if (m_creatureInfo->trainer_class && m_creatureInfo->trainer_class != player->getClass()) + if (m_creatureInfo->trainer_class && !player->IsClass((Classes)m_creatureInfo->trainer_class, CLASS_CONTEXT_CLASS_TRAINER)) { if (npcFlags) *npcFlags &= ~UNIT_NPC_FLAG_TRAINER_CLASS; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index f58797ac4..416bb2d0b 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -107,7 +107,7 @@ void Pet::AddToWorld() { if (Player* owner = GetOwner()) { - if (getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK) + if (getPetType() == SUMMON_PET && owner->IsClass(CLASS_WARLOCK, CLASS_CONTEXT_PET)) { owner->SetLastPetSpell(GetUInt32Value(UNIT_CREATED_BY_SPELL)); } @@ -238,7 +238,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c bool forceLoadFromDB = false; sScriptMgr->OnBeforeLoadPetFromDB(owner, petEntry, petnumber, current, forceLoadFromDB); - if (!forceLoadFromDB && (owner->getClass() == CLASS_DEATH_KNIGHT && !owner->CanSeeDKPet())) // DK Pet exception + if (!forceLoadFromDB && (owner->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_PET) && !owner->CanSeeDKPet())) // DK Pet exception return false; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(petInfo->CreatedBySpellId); @@ -1043,12 +1043,12 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) if (petType == MAX_PET_TYPE) { // The petType was not overwritten by the hook, continue with default initialization - if (owner->getClass() == CLASS_WARLOCK || - owner->getClass() == CLASS_SHAMAN || // Fire Elemental - owner->getClass() == CLASS_DEATH_KNIGHT || // Risen Ghoul - owner->getClass() == CLASS_MAGE) // Water Elemental with glyph + if (owner->IsClass(CLASS_WARLOCK, CLASS_CONTEXT_PET) || + owner->IsClass(CLASS_SHAMAN, CLASS_CONTEXT_PET) || // Fire Elemental + owner->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_PET) || // Risen Ghoul + owner->IsClass(CLASS_MAGE, CLASS_CONTEXT_PET)) // Water Elemental with glyph petType = SUMMON_PET; - else if (owner->getClass() == CLASS_HUNTER) + else if (owner->IsClass(CLASS_HUNTER, CLASS_CONTEXT_PET)) { petType = HUNTER_PET; } @@ -1080,7 +1080,7 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(petlevel * 50)); uint32 attackTime = BASE_ATTACK_TIME; - if (owner->getClass() != CLASS_HUNTER && cinfo->BaseAttackTime >= 1000) + if (!owner->IsClass(CLASS_HUNTER, CLASS_CONTEXT_PET) && cinfo->BaseAttackTime >= 1000) attackTime = cinfo->BaseAttackTime; SetAttackTime(BASE_ATTACK, attackTime); @@ -2299,17 +2299,14 @@ bool Pet::IsPermanentPetFor(Player* owner) const switch (getPetType()) { case SUMMON_PET: - switch (owner->getClass()) - { - case CLASS_WARLOCK: - return GetCreatureTemplate()->type == CREATURE_TYPE_DEMON; - case CLASS_DEATH_KNIGHT: - return GetCreatureTemplate()->type == CREATURE_TYPE_UNDEAD; - case CLASS_MAGE: - return GetEntry() == 37994; - default: - return false; - } + if (owner->IsClass(CLASS_WARLOCK, CLASS_CONTEXT_PET)) + return GetCreatureTemplate()->type == CREATURE_TYPE_DEMON; + else if (owner->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_PET)) + return GetCreatureTemplate()->type == CREATURE_TYPE_UNDEAD; + else if (owner->IsClass(CLASS_MAGE, CLASS_CONTEXT_PET)) + return GetEntry() == 37994; + else + return false; case HUNTER_PET: return true; default: diff --git a/src/server/game/Entities/Player/KillRewarder.cpp b/src/server/game/Entities/Player/KillRewarder.cpp index 50d83a692..04ba9ca1d 100644 --- a/src/server/game/Entities/Player/KillRewarder.cpp +++ b/src/server/game/Entities/Player/KillRewarder.cpp @@ -204,12 +204,13 @@ void KillRewarder::_RewardPlayer(Player* player, bool isDungeon) if (_victim->GetTypeId() == TYPEID_PLAYER) player->KilledPlayerCredit(); } + // Give XP only in PvE or in battlegrounds. // Give reputation and kill credit only in PvE. if (!_isPvP || _isBattleGround) { float xpRate = _group ? _groupRate * float(player->GetLevel()) / _aliveSumLevel : /*Personal rate is 100%.*/ 1.0f; // Group rate depends on the sum of levels. - sScriptMgr->OnRewardKillRewarder(player, isDungeon, xpRate); // Personal rate is 100%. + sScriptMgr->OnRewardKillRewarder(player, this, isDungeon, xpRate); // Personal rate is 100%. if (_xp) { @@ -290,3 +291,13 @@ void KillRewarder::Reward() if (Map* map = _victim->FindMap()) map->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, _victim->GetEntry(), _victim); } + +Unit* KillRewarder::GetVictim() +{ + return _victim; +} + +Player* KillRewarder::GetKiller() +{ + return _killer; +} diff --git a/src/server/game/Entities/Player/KillRewarder.h b/src/server/game/Entities/Player/KillRewarder.h index 6f1fda257..3efc3cc62 100644 --- a/src/server/game/Entities/Player/KillRewarder.h +++ b/src/server/game/Entities/Player/KillRewarder.h @@ -30,6 +30,8 @@ public: KillRewarder(Player* killer, Unit* victim, bool isBattleGround); void Reward(); + Unit* GetVictim(); + Player* GetKiller(); private: void _InitXP(Player* player); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index de51f8cc0..3e0704f83 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -553,7 +553,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0); // set starting level - uint32 start_level = getClass() != CLASS_DEATH_KNIGHT + uint32 start_level = !IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_INIT) ? sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL) : sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL); @@ -568,7 +568,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo InitRunes(); - SetUInt32Value(PLAYER_FIELD_COINAGE, getClass() != CLASS_DEATH_KNIGHT + SetUInt32Value(PLAYER_FIELD_COINAGE, !IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_INIT) ? sWorld->getIntConfig(CONFIG_START_PLAYER_MONEY) : sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_MONEY)); SetHonorPoints(sWorld->getIntConfig(CONFIG_START_HONOR_POINTS)); @@ -587,13 +587,13 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo InitPrimaryProfessions(); // to max set before any spell added // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods() - if (getPowerType() == POWER_MANA) + if (HasActivePowerType(POWER_MANA)) { UpdateMaxPower(POWER_MANA); // Update max Mana (for add bonus from intellect) SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); } - if (getPowerType() == POWER_RUNIC_POWER) + if (HasActivePowerType(POWER_RUNIC_POWER)) { SetPower(POWER_RUNE, 8); SetMaxPower(POWER_RUNE, 8); @@ -633,7 +633,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo switch (iProto->Spells[0].SpellCategory) { case SPELL_CATEGORY_FOOD: // food - count = getClass() == CLASS_DEATH_KNIGHT ? 10 : 4; + count = IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_INIT) ? 10 : 4; break; case SPELL_CATEGORY_DRINK: // drink count = 2; @@ -1269,6 +1269,15 @@ bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data) return true; } +bool Player::IsClass(Classes unitClass, ClassContext context) const +{ + Optional scriptResult = sScriptMgr->OnPlayerIsClass(this, unitClass, context); + if (scriptResult != std::nullopt) + return *scriptResult; + else + return (getClass() == unitClass); +} + void Player::ToggleAFK() { ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK); @@ -1460,7 +1469,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati } else { - if (getClass() == CLASS_DEATH_KNIGHT && GetMapId() == 609 && !IsGameMaster() && !HasSpell(50977)) + if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_TELEPORT) && GetMapId() == 609 && !IsGameMaster() && !HasSpell(50977)) { SendTransferAborted(mapid, TRANSFER_ABORT_UNIQUE_MESSAGE, 1); return false; @@ -1745,7 +1754,7 @@ void Player::RegenerateAll() Regenerate(POWER_MANA); // Runes act as cooldowns, and they don't need to send any data - if (getClass() == CLASS_DEATH_KNIGHT) + if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) for (uint8 i = 0; i < MAX_RUNES; ++i) { // xinef: implement grace @@ -1775,7 +1784,7 @@ void Player::RegenerateAll() } Regenerate(POWER_RAGE); - if (getClass() == CLASS_DEATH_KNIGHT) + if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) Regenerate(POWER_RUNIC_POWER); m_regenTimerCount -= 2000; @@ -2019,22 +2028,21 @@ void Player::RegenerateHealth() void Player::ResetAllPowers() { SetHealth(GetMaxHealth()); - switch (getPowerType()) + if (HasActivePowerType(POWER_MANA)) { - case POWER_MANA: - SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); - break; - case POWER_RAGE: - SetPower(POWER_RAGE, 0); - break; - case POWER_ENERGY: - SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY)); - break; - case POWER_RUNIC_POWER: - SetPower(POWER_RUNIC_POWER, 0); - break; - default: - break; + SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); + } + if (HasActivePowerType(POWER_RAGE)) + { + SetPower(POWER_RAGE, 0); + } + if (HasActivePowerType(POWER_ENERGY)) + { + SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY)); + } + if (HasActivePowerType(POWER_RUNIC_POWER)) + { + SetPower(POWER_RUNIC_POWER, 0); } } @@ -2107,7 +2115,7 @@ Creature* Player::GetNPCIfCanInteractWith(ObjectGuid guid, uint32 npcflagmask) // pussywizard: many npcs have missing conditions for class training and rogue trainer can for eg. train dual wield to a shaman :/ too many to change in sql and watch in the future // pussywizard: this function is not used when talking, but when already taking action (buy spell, reset talents, show spell list) - if (npcflagmask & (UNIT_NPC_FLAG_TRAINER | UNIT_NPC_FLAG_TRAINER_CLASS) && creature->GetCreatureTemplate()->trainer_type == TRAINER_TYPE_CLASS && getClass() != creature->GetCreatureTemplate()->trainer_class) + if (npcflagmask & (UNIT_NPC_FLAG_TRAINER | UNIT_NPC_FLAG_TRAINER_CLASS) && creature->GetCreatureTemplate()->trainer_type == TRAINER_TYPE_CLASS && !IsClass((Classes)creature->GetCreatureTemplate()->trainer_class, CLASS_CONTEXT_CLASS_TRAINER)) return nullptr; return creature; @@ -2714,6 +2722,14 @@ void Player::InitStatsForLevel(bool reapplyMods) pet->SynchronizeLevelWithOwner(); } +bool Player::HasActivePowerType(Powers power) +{ + if (sScriptMgr->OnPlayerHasActivePowerType(this, power)) + return true; + else + return (getPowerType() == power); +} + void Player::SendInitialSpells() { uint32 curTime = GameTime::GetGameTimeMS().count(); @@ -6388,7 +6404,7 @@ void Player::DuelComplete(DuelCompleteType type) opponent->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL, 1); // Credit for quest Death's Challenge - if (getClass() == CLASS_DEATH_KNIGHT && opponent->GetQuestStatus(12733) == QUEST_STATUS_INCOMPLETE) + if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_QUEST) && opponent->GetQuestStatus(12733) == QUEST_STATUS_INCOMPLETE) { opponent->CastSpell(opponent, 52994, true); } @@ -6784,7 +6800,7 @@ void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply } // Druids get feral AP bonus from weapon dps (also use DPS from ScalingStatValue) - if (getClass() == CLASS_DRUID) + if (IsClass(CLASS_DRUID, CLASS_CONTEXT_STATS)) { int32 dpsMod = 0; int32 feral_bonus = 0; @@ -9841,7 +9857,7 @@ void Player::AddSpellMod(SpellModifier* mod, bool apply) if (apply) { m_spellMods[mod->op].push_back(mod); - if (getClass() == CLASS_MAGE) + if (IsClass(CLASS_MAGE, CLASS_CONTEXT_ABILITY)) m_spellMods[mod->op].sort(MageSpellModPred()); else m_spellMods[mod->op].sort(SpellModPred()); @@ -10275,7 +10291,7 @@ bool Player::ActivateTaxiPathTo(std::vector const& nodes, Creature* npc // only one mount ID for both sides. Probably not good to use 315 in case DBC nodes // change but I couldn't find a suitable alternative. OK to use class because only DK // can use this taxi. - uint32 mount_display_id = sObjectMgr->GetTaxiMountDisplayId(sourcenode, GetTeamId(true), npc == nullptr || (sourcenode == 315 && getClass() == CLASS_DEATH_KNIGHT)); + uint32 mount_display_id = sObjectMgr->GetTaxiMountDisplayId(sourcenode, GetTeamId(true), npc == nullptr || (sourcenode == 315 && IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_TAXI))); // in spell case allow 0 model if ((mount_display_id == 0 && spellid == 0) || sourcepath == 0) @@ -11324,7 +11340,7 @@ WorldLocation Player::GetStartPosition() const { PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(true), getClass()); uint32 mapId = info->mapId; - if (getClass() == CLASS_DEATH_KNIGHT && HasSpell(50977)) + if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_INIT) && HasSpell(50977)) return WorldLocation(0, 2352.0f, -5709.0f, 154.5f, 0.0f); return WorldLocation(mapId, info->positionX, info->positionY, info->positionZ, 0); } @@ -11818,7 +11834,7 @@ void Player::LearnDefaultSkill(uint32 skillId, uint16 rank) { skillValue = maxValue; } - else if (getClass() == CLASS_DEATH_KNIGHT) + else if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_SKILL)) { skillValue = std::min(std::max({ 1, uint16((GetLevel() - 1) * 5) }), maxValue); } @@ -11851,7 +11867,7 @@ void Player::LearnDefaultSkill(uint32 skillId, uint16 rank) { skillValue = maxValue; } - else if (getClass() == CLASS_DEATH_KNIGHT) + else if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_SKILL)) { skillValue = std::min(std::max({ uint16(1), uint16((GetLevel() - 1) * 5) }), maxValue); } @@ -13376,7 +13392,7 @@ static RuneType runeSlotTypes[MAX_RUNES] = void Player::InitRunes() { - if (getClass() != CLASS_DEATH_KNIGHT) + if (!IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) return; m_runes = new Runes; @@ -13542,7 +13558,7 @@ uint32 Player::CalculateTalentsPoints() const uint32 base_talent = GetLevel() < 10 ? 0 : GetLevel() - 9; uint32 talentPointsForLevel = 0; - if (getClass() != CLASS_DEATH_KNIGHT || GetMapId() != 609) + if (!IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_TALENT_POINT_CALC) || GetMapId() != 609) { talentPointsForLevel = base_talent; } @@ -14174,19 +14190,21 @@ void Player::ResummonPetTemporaryUnSummonedIfAny() bool Player::CanResummonPet(uint32 spellid) { - if (getClass() == CLASS_DEATH_KNIGHT) + if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_PET)) { if (CanSeeDKPet()) return true; else if (spellid == 52150) // Raise Dead return false; } - else if (getClass() == CLASS_MAGE) + + if (IsClass(CLASS_MAGE, CLASS_CONTEXT_PET)) { if (HasSpell(31687) && HasAura(70937)) //Has [Summon Water Elemental] spell and [Glyph of Eternal Water]. return true; } - else if (getClass() == CLASS_HUNTER) + + if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_PET)) { return true; } @@ -15171,7 +15189,7 @@ void Player::ActivateSpec(uint8 spec) AutoUnequipOffhandIfNeed(); // Xinef: Patch 3.2.0: Switching spec removes paladins spell Righteous Fury (25780) - if (getClass() == CLASS_PALADIN) + if (IsClass(CLASS_PALADIN, CLASS_CONTEXT_ABILITY)) RemoveAurasDueToSpell(25780); // Xinef: Remove talented single target auras at other targets diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 3805c4e48..dcc960805 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1112,6 +1112,8 @@ public: static bool BuildEnumData(PreparedQueryResult result, WorldPacket* data); + [[nodiscard]] bool IsClass(Classes playerClass, ClassContext context = CLASS_CONTEXT_NONE) const override; + void SetInWater(bool apply); [[nodiscard]] bool IsInWater() const override { return m_isInWater; } @@ -1170,6 +1172,8 @@ public: void InitStatsForLevel(bool reapplyMods = false); + [[nodiscard]] bool HasActivePowerType(Powers power) override; + // .cheat command related [[nodiscard]] bool GetCommandStatus(uint32 command) const { return _activeCheats & command; } void SetCommandStatusOn(uint32 command) { _activeCheats |= command; } diff --git a/src/server/game/Entities/Player/PlayerGossip.cpp b/src/server/game/Entities/Player/PlayerGossip.cpp index 77bc1901a..953adca83 100644 --- a/src/server/game/Entities/Player/PlayerGossip.cpp +++ b/src/server/game/Entities/Player/PlayerGossip.cpp @@ -109,7 +109,7 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool canTalk = false; break; case GOSSIP_OPTION_STABLEPET: - if (getClass() != CLASS_HUNTER) + if (!IsClass(CLASS_HUNTER, CLASS_CONTEXT_PET)) canTalk = false; break; case GOSSIP_OPTION_QUESTGIVER: diff --git a/src/server/game/Entities/Player/PlayerStorage.cpp b/src/server/game/Entities/Player/PlayerStorage.cpp index 1aacad7b7..da9c0e338 100644 --- a/src/server/game/Entities/Player/PlayerStorage.cpp +++ b/src/server/game/Entities/Player/PlayerStorage.cpp @@ -133,8 +133,6 @@ void Player::SetSheath(SheathState sheathed) uint8 Player::FindEquipSlot(ItemTemplate const* proto, uint32 slot, bool swap) const { - uint8 playerClass = getClass(); - uint8 slots[4]; slots[0] = NULL_SLOT; slots[1] = NULL_SLOT; @@ -246,23 +244,23 @@ uint8 Player::FindEquipSlot(ItemTemplate const* proto, uint32 slot, bool swap) c switch (proto->SubClass) { case ITEM_SUBCLASS_ARMOR_LIBRAM: - if (playerClass == CLASS_PALADIN) + if (IsClass(CLASS_PALADIN, CLASS_CONTEXT_EQUIP_RELIC)) slots[0] = EQUIPMENT_SLOT_RANGED; break; case ITEM_SUBCLASS_ARMOR_IDOL: - if (playerClass == CLASS_DRUID) + if (IsClass(CLASS_DRUID, CLASS_CONTEXT_EQUIP_RELIC)) slots[0] = EQUIPMENT_SLOT_RANGED; break; case ITEM_SUBCLASS_ARMOR_TOTEM: - if (playerClass == CLASS_SHAMAN) + if (IsClass(CLASS_SHAMAN, CLASS_CONTEXT_EQUIP_RELIC)) slots[0] = EQUIPMENT_SLOT_RANGED; break; case ITEM_SUBCLASS_ARMOR_MISC: - if (playerClass == CLASS_WARLOCK) + if (IsClass(CLASS_WARLOCK, CLASS_CONTEXT_EQUIP_RELIC)) slots[0] = EQUIPMENT_SLOT_RANGED; break; case ITEM_SUBCLASS_ARMOR_SIGIL: - if (playerClass == CLASS_DEATH_KNIGHT) + if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_EQUIP_RELIC)) slots[0] = EQUIPMENT_SLOT_RANGED; break; } @@ -2270,16 +2268,13 @@ InventoryResult Player::CanUseItem(Item* pItem, bool not_loading) const // In fact it's a visual bug, everything works properly... I need sniffs of operations with // binded to account items from off server. - switch (getClass()) + if (IsClass(CLASS_PALADIN, CLASS_CONTEXT_EQUIP_ARMOR_CLASS) || IsClass(CLASS_WARRIOR, CLASS_CONTEXT_EQUIP_ARMOR_CLASS)) { - case CLASS_HUNTER: - case CLASS_SHAMAN: - allowEquip = (itemSkill == SKILL_MAIL); - break; - case CLASS_PALADIN: - case CLASS_WARRIOR: - allowEquip = (itemSkill == SKILL_PLATE_MAIL); - break; + allowEquip = (itemSkill == SKILL_PLATE_MAIL); + } + else if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_EQUIP_ARMOR_CLASS) || IsClass(CLASS_SHAMAN, CLASS_CONTEXT_EQUIP_ARMOR_CLASS)) + { + allowEquip = (itemSkill == SKILL_MAIL); } } if (!allowEquip && GetSkillValue(itemSkill) == 0) @@ -2394,39 +2389,40 @@ InventoryResult Player::CanRollForItemInLFG(ItemTemplate const* proto, WorldObje return EQUIP_ERR_CANT_EQUIP_SKILL; } - uint8 _class = getClass(); - if (proto->Class == ITEM_CLASS_WEAPON && GetSkillValue(item_weapon_skills[proto->SubClass]) == 0) return EQUIP_ERR_NO_REQUIRED_PROFICIENCY; if (proto->Class == ITEM_CLASS_ARMOR) { // Check for shields - if (proto->SubClass == ITEM_SUBCLASS_ARMOR_SHIELD && !(_class == CLASS_PALADIN || _class == CLASS_WARRIOR || _class == CLASS_SHAMAN)) + if (proto->SubClass == ITEM_SUBCLASS_ARMOR_SHIELD && !( + IsClass(CLASS_PALADIN, CLASS_CONTEXT_EQUIP_SHIELDS) + || IsClass(CLASS_WARRIOR, CLASS_CONTEXT_EQUIP_SHIELDS) + || IsClass(CLASS_SHAMAN, CLASS_CONTEXT_EQUIP_SHIELDS))) { return EQUIP_ERR_NO_REQUIRED_PROFICIENCY; } // Check for librams. - if (proto->SubClass == ITEM_SUBCLASS_ARMOR_LIBRAM && _class != CLASS_PALADIN) + if (proto->SubClass == ITEM_SUBCLASS_ARMOR_LIBRAM && !IsClass(CLASS_PALADIN, CLASS_CONTEXT_EQUIP_RELIC)) { return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM; } // CHeck for idols. - if (proto->SubClass == ITEM_SUBCLASS_ARMOR_IDOL && _class != CLASS_DRUID) + if (proto->SubClass == ITEM_SUBCLASS_ARMOR_IDOL && !IsClass(CLASS_DRUID, CLASS_CONTEXT_EQUIP_RELIC)) { return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM; } // Check for totems. - if (proto->SubClass == ITEM_SUBCLASS_ARMOR_TOTEM && _class != CLASS_SHAMAN) + if (proto->SubClass == ITEM_SUBCLASS_ARMOR_TOTEM && !IsClass(CLASS_SHAMAN, CLASS_CONTEXT_EQUIP_RELIC)) { return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM; } // Check for sigils. - if (proto->SubClass == ITEM_SUBCLASS_ARMOR_SIGIL && _class != CLASS_DEATH_KNIGHT) + if (proto->SubClass == ITEM_SUBCLASS_ARMOR_SIGIL && !IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_EQUIP_RELIC)) { return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM; } @@ -2436,33 +2432,33 @@ InventoryResult Player::CanRollForItemInLFG(ItemTemplate const* proto, WorldObje proto->InventoryType != INVTYPE_CLOAK) { uint32 subclassToCompare = ITEM_SUBCLASS_ARMOR_CLOTH; - switch (_class) + if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_EQUIP_ARMOR_CLASS) || IsClass(CLASS_PALADIN, CLASS_CONTEXT_EQUIP_ARMOR_CLASS)) { - case CLASS_WARRIOR: - if (proto->HasStat(ITEM_MOD_SPELL_POWER) || proto->HasSpellPowerStat()) - { - return EQUIP_ERR_CANT_DO_RIGHT_NOW; - } - [[fallthrough]]; - case CLASS_DEATH_KNIGHT: - case CLASS_PALADIN: - subclassToCompare = ITEM_SUBCLASS_ARMOR_PLATE; - break; - case CLASS_HUNTER: - case CLASS_SHAMAN: - subclassToCompare = ITEM_SUBCLASS_ARMOR_MAIL; - break; - case CLASS_ROGUE: - if (proto->HasStat(ITEM_MOD_SPELL_POWER) || proto->HasSpellPowerStat()) - { - return EQUIP_ERR_CANT_DO_RIGHT_NOW; - } - [[fallthrough]]; - case CLASS_DRUID: - subclassToCompare = ITEM_SUBCLASS_ARMOR_LEATHER; - break; - default: - break; + subclassToCompare = ITEM_SUBCLASS_ARMOR_PLATE; + } + else if (IsClass(CLASS_WARRIOR, CLASS_CONTEXT_EQUIP_ARMOR_CLASS)) + { + if ((proto->HasStat(ITEM_MOD_SPELL_POWER) || proto->HasSpellPowerStat())) + { + return EQUIP_ERR_CANT_DO_RIGHT_NOW; + } + subclassToCompare = ITEM_SUBCLASS_ARMOR_PLATE; + } + else if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_EQUIP_ARMOR_CLASS) || IsClass(CLASS_SHAMAN, CLASS_CONTEXT_EQUIP_ARMOR_CLASS)) + { + subclassToCompare = ITEM_SUBCLASS_ARMOR_MAIL; + } + else if (IsClass(CLASS_DRUID, CLASS_CONTEXT_EQUIP_ARMOR_CLASS)) + { + subclassToCompare = ITEM_SUBCLASS_ARMOR_LEATHER; + } + else if (IsClass(CLASS_ROGUE, CLASS_CONTEXT_EQUIP_ARMOR_CLASS)) + { + if (proto->HasStat(ITEM_MOD_SPELL_POWER) || proto->HasSpellPowerStat()) + { + return EQUIP_ERR_CANT_DO_RIGHT_NOW; + } + subclassToCompare = ITEM_SUBCLASS_ARMOR_LEATHER; } if (proto->SubClass > subclassToCompare) @@ -2772,7 +2768,7 @@ Item* Player::EquipItem(uint16 pos, Item* pItem, bool update) if (pProto && IsInCombat() && (pProto->Class == ITEM_CLASS_WEAPON || pProto->InventoryType == INVTYPE_RELIC) && m_weaponChangeTimer == 0) { - uint32 cooldownSpell = getClass() == CLASS_ROGUE ? 6123 : 6119; + uint32 cooldownSpell = IsClass(CLASS_ROGUE, CLASS_CONTEXT_WEAPON_SWAP) ? 6123 : 6119; SpellInfo const* spellProto = sSpellMgr->GetSpellInfo(cooldownSpell); if (!spellProto) @@ -4645,7 +4641,7 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool } case ITEM_ENCHANTMENT_TYPE_TOTEM: // Shaman Rockbiter Weapon { - if (getClass() == CLASS_SHAMAN) + if (IsClass(CLASS_SHAMAN, CLASS_CONTEXT_ABILITY)) { float addValue = 0.0f; if (item->GetSlot() == EQUIPMENT_SLOT_MAINHAND) diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index 5e23e2142..f2191b39e 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -346,134 +346,127 @@ void Player::UpdateAttackPowerAndDamage(bool ranged) index_mod = UNIT_FIELD_RANGED_ATTACK_POWER_MODS; index_mult = UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER; - switch (getClass()) + if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_STATS)) { - case CLASS_HUNTER: - val2 = level * 2.0f + GetStat(STAT_AGILITY) - 10.0f; - break; - case CLASS_ROGUE: - case CLASS_WARRIOR: - val2 = level + GetStat(STAT_AGILITY) - 10.0f; - break; - case CLASS_DRUID: - switch (GetShapeshiftForm()) - { - case FORM_CAT: - case FORM_BEAR: - case FORM_DIREBEAR: - val2 = 0.0f; - break; - default: - val2 = GetStat(STAT_AGILITY) - 10.0f; - break; - } + val2 = level * 2.0f + GetStat(STAT_AGILITY) - 10.0f; + } + else if (IsClass(CLASS_ROGUE, CLASS_CONTEXT_STATS) || IsClass(CLASS_WARRIOR, CLASS_CONTEXT_STATS)) + { + val2 = level + GetStat(STAT_AGILITY) - 10.0f; + } + else if (IsClass(CLASS_DRUID, CLASS_CONTEXT_STATS)) + { + switch (GetShapeshiftForm()) + { + case FORM_CAT: + case FORM_BEAR: + case FORM_DIREBEAR: + val2 = 0.0f; break; default: val2 = GetStat(STAT_AGILITY) - 10.0f; break; + } + } + else + { + val2 = GetStat(STAT_AGILITY) - 10.0f; } } else { - switch (getClass()) + if (IsClass(CLASS_PALADIN, CLASS_CONTEXT_STATS) || IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_STATS) || IsClass(CLASS_WARRIOR, CLASS_CONTEXT_STATS)) { - case CLASS_PALADIN: - case CLASS_DEATH_KNIGHT: - case CLASS_WARRIOR: - val2 = level * 3.0f + GetStat(STAT_STRENGTH) * 2.0f - 20.0f; - break; - case CLASS_HUNTER: - case CLASS_SHAMAN: - case CLASS_ROGUE: - val2 = level * 2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; - break; - case CLASS_DRUID: + val2 = level * 3.0f + GetStat(STAT_STRENGTH) * 2.0f - 20.0f; + } + else if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_STATS) || IsClass(CLASS_SHAMAN, CLASS_CONTEXT_STATS) || IsClass(CLASS_ROGUE, CLASS_CONTEXT_STATS)) + { + val2 = level * 2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; + } + else if (IsClass(CLASS_DRUID, CLASS_CONTEXT_STATS)) + { + // Check if Predatory Strikes is skilled + float mLevelMult = 0.0f; + float weapon_bonus = 0.0f; + if (IsInFeralForm()) + { + Unit::AuraEffectList const& mDummy = GetAuraEffectsByType(SPELL_AURA_DUMMY); + for (Unit::AuraEffectList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr) { - // Check if Predatory Strikes is skilled - float mLevelMult = 0.0f; - float weapon_bonus = 0.0f; - if (IsInFeralForm()) + AuraEffect* aurEff = *itr; + if (aurEff->GetSpellInfo()->SpellIconID == 1563) { - Unit::AuraEffectList const& mDummy = GetAuraEffectsByType(SPELL_AURA_DUMMY); - for (Unit::AuraEffectList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr) + switch (aurEff->GetEffIndex()) { - AuraEffect* aurEff = *itr; - if (aurEff->GetSpellInfo()->SpellIconID == 1563) + case 0: // Predatory Strikes (effect 0) + mLevelMult = CalculatePct(1.0f, aurEff->GetAmount()); + break; + case 1: // Predatory Strikes (effect 1) + if (Item* mainHand = m_items[EQUIPMENT_SLOT_MAINHAND]) { - switch (aurEff->GetEffIndex()) + // also gains % attack power from equipped weapon + ItemTemplate const* proto = mainHand->GetTemplate(); + if (!proto) + continue; + + uint32 ap = proto->getFeralBonus(); + // Get AP Bonuses from weapon + for (uint8 i = 0; i < MAX_ITEM_PROTO_STATS; ++i) { - case 0: // Predatory Strikes (effect 0) - mLevelMult = CalculatePct(1.0f, aurEff->GetAmount()); + if (i >= proto->StatsCount) break; - case 1: // Predatory Strikes (effect 1) - if (Item* mainHand = m_items[EQUIPMENT_SLOT_MAINHAND]) - { - // also gains % attack power from equipped weapon - ItemTemplate const* proto = mainHand->GetTemplate(); - if (!proto) - continue; - uint32 ap = proto->getFeralBonus(); - // Get AP Bonuses from weapon - for (uint8 i = 0; i < MAX_ITEM_PROTO_STATS; ++i) - { - if (i >= proto->StatsCount) - break; - - if (proto->ItemStat[i].ItemStatType == ITEM_MOD_ATTACK_POWER) - ap += proto->ItemStat[i].ItemStatValue; - } - - // Get AP Bonuses from weapon spells - for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) - { - // no spell - if (!proto->Spells[i].SpellId || proto->Spells[i].SpellTrigger != ITEM_SPELLTRIGGER_ON_EQUIP) - continue; - - // check if it is valid spell - SpellInfo const* spellproto = sSpellMgr->GetSpellInfo(proto->Spells[i].SpellId); - if (!spellproto) - continue; - - for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j) - if (spellproto->Effects[j].ApplyAuraName == SPELL_AURA_MOD_ATTACK_POWER) - ap += spellproto->Effects[j].CalcValue(); - } - - weapon_bonus = CalculatePct(float(ap), aurEff->GetAmount()); - } - break; - default: - break; + if (proto->ItemStat[i].ItemStatType == ITEM_MOD_ATTACK_POWER) + ap += proto->ItemStat[i].ItemStatValue; } - } - } - } - switch (GetShapeshiftForm()) - { - case FORM_CAT: - val2 = (GetLevel() * mLevelMult) + GetStat(STAT_STRENGTH) * 2.0f + GetStat(STAT_AGILITY) - 20.0f + weapon_bonus + m_baseFeralAP; - break; - case FORM_BEAR: - case FORM_DIREBEAR: - val2 = (GetLevel() * mLevelMult) + GetStat(STAT_STRENGTH) * 2.0f - 20.0f + weapon_bonus + m_baseFeralAP; - break; - case FORM_MOONKIN: - val2 = (GetLevel() * mLevelMult) + GetStat(STAT_STRENGTH) * 2.0f - 20.0f + m_baseFeralAP; + // Get AP Bonuses from weapon spells + for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + // no spell + if (!proto->Spells[i].SpellId || proto->Spells[i].SpellTrigger != ITEM_SPELLTRIGGER_ON_EQUIP) + continue; + + // check if it is valid spell + SpellInfo const* spellproto = sSpellMgr->GetSpellInfo(proto->Spells[i].SpellId); + if (!spellproto) + continue; + + for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j) + if (spellproto->Effects[j].ApplyAuraName == SPELL_AURA_MOD_ATTACK_POWER) + ap += spellproto->Effects[j].CalcValue(); + } + + weapon_bonus = CalculatePct(float(ap), aurEff->GetAmount()); + } break; default: - val2 = GetStat(STAT_STRENGTH) * 2.0f - 20.0f; break; + } } - break; } - case CLASS_MAGE: - case CLASS_PRIEST: - case CLASS_WARLOCK: - val2 = GetStat(STAT_STRENGTH) - 10.0f; + } + + switch (GetShapeshiftForm()) + { + case FORM_CAT: + val2 = (GetLevel() * mLevelMult) + GetStat(STAT_STRENGTH) * 2.0f + GetStat(STAT_AGILITY) - 20.0f + weapon_bonus + m_baseFeralAP; break; + case FORM_BEAR: + case FORM_DIREBEAR: + val2 = (GetLevel() * mLevelMult) + GetStat(STAT_STRENGTH) * 2.0f - 20.0f + weapon_bonus + m_baseFeralAP; + break; + case FORM_MOONKIN: + val2 = (GetLevel() * mLevelMult) + GetStat(STAT_STRENGTH) * 2.0f - 20.0f + m_baseFeralAP; + break; + default: + val2 = GetStat(STAT_STRENGTH) * 2.0f - 20.0f; + break; + } + } + else if (IsClass(CLASS_MAGE, CLASS_CONTEXT_STATS) || IsClass(CLASS_PRIEST, CLASS_CONTEXT_STATS) || IsClass(CLASS_WARLOCK, CLASS_CONTEXT_STATS)) + { + val2 = GetStat(STAT_STRENGTH) - 10.0f; } } @@ -521,7 +514,7 @@ void Player::UpdateAttackPowerAndDamage(bool ranged) UpdateDamagePhysical(BASE_ATTACK); if (CanDualWield() && haveOffhandWeapon()) //allow update offhand damage only if player knows DualWield Spec and has equipped offhand weapon UpdateDamagePhysical(OFF_ATTACK); - if (getClass() == CLASS_SHAMAN || getClass() == CLASS_PALADIN) // mental quickness + if (IsClass(CLASS_SHAMAN, CLASS_CONTEXT_STATS) || IsClass(CLASS_PALADIN, CLASS_CONTEXT_STATS)) // mental quickness UpdateSpellDamageAndHealingBonus(); } } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 35d658ac5..f607cf381 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -929,7 +929,7 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage } // Rage from Damage made (only from direct weapon damage) - if (attacker && cleanDamage && damagetype == DIRECT_DAMAGE && attacker != victim && attacker->getPowerType() == POWER_RAGE) + if (attacker && cleanDamage && damagetype == DIRECT_DAMAGE && attacker != victim && attacker->HasActivePowerType(POWER_RAGE)) { uint32 weaponSpeedHitFactor; @@ -957,10 +957,10 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage // Rage from absorbed damage if (cleanDamage && cleanDamage->absorbed_damage) { - if (victim->getPowerType() == POWER_RAGE) + if (victim->HasActivePowerType(POWER_RAGE)) victim->RewardRage(cleanDamage->absorbed_damage, 0, false); - if (attacker && attacker->getPowerType() == POWER_RAGE ) + if (attacker && attacker->HasActivePowerType(POWER_RAGE)) attacker->RewardRage(cleanDamage->absorbed_damage, 0, true); } @@ -1083,7 +1083,7 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage } // Rage from damage received - if (attacker != victim && victim->getPowerType() == POWER_RAGE) + if (attacker != victim && victim->HasActivePowerType(POWER_RAGE)) { uint32 rageDamage = damage + (cleanDamage ? cleanDamage->absorbed_damage : 0); victim->RewardRage(rageDamage, 0, false); @@ -6996,7 +6996,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere // Magic Absorption if (dummySpell->SpellIconID == 459) // only this spell has SpellIconID == 459 and dummy aura { - if (getPowerType() != POWER_MANA) + if (!HasActivePowerType(POWER_MANA)) return false; // mana reward @@ -7775,7 +7775,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere // Judgement of Wisdom case 20186: { - if (!victim || !victim->IsAlive() || victim->getPowerType() != POWER_MANA || victim->HasSpellCooldown(20268)) + if (!victim || !victim->IsAlive() || !victim->HasActivePowerType(POWER_MANA) || victim->HasSpellCooldown(20268)) return false; // 2% of base mana @@ -8834,7 +8834,7 @@ bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, Sp // Convert recently used Blood Rune to Death Rune if (Player* player = ToPlayer()) { - if (player->getClass() != CLASS_DEATH_KNIGHT) + if (!player->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) return false; // xinef: not true @@ -9565,7 +9565,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg // Item - Death Knight T10 Melee 4P Bonus if (auraSpellInfo->Id == 70656) { - if (GetTypeId() != TYPEID_PLAYER || getClass() != CLASS_DEATH_KNIGHT) + if (GetTypeId() != TYPEID_PLAYER || !IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) return false; for (uint8 i = 0; i < MAX_RUNES; ++i) @@ -9576,7 +9576,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg else if (auraSpellInfo->SpellIconID == 85) { Player* plr = ToPlayer(); - if (!plr || plr->getClass() != CLASS_DEATH_KNIGHT || !procSpell) + if (!plr || !plr->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY) || !procSpell) return false; if (!plr->IsBaseRuneSlotsOnCooldown(RUNE_BLOOD)) @@ -13840,7 +13840,7 @@ void Unit::ClearInCombat() else if (Player* player = ToPlayer()) { player->UpdatePotionCooldown(); - if (player->getClass() == CLASS_DEATH_KNIGHT) + if (player->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) for (uint8 i = 0; i < MAX_RUNES; ++i) player->SetGracePeriod(i, 0); } @@ -16378,7 +16378,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u if (procExtra & PROC_EX_DODGE) { // Update AURA_STATE on dodge - if (getClass() != CLASS_ROGUE) // skip Rogue Riposte + if (!IsClass(CLASS_ROGUE, CLASS_CONTEXT_ABILITY_REACTIVE)) // skip Rogue Riposte { ModifyAuraState(AURA_STATE_DEFENSE, true); StartReactiveTimer(REACTIVE_DEFENSE); @@ -16388,7 +16388,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u if (procExtra & PROC_EX_PARRY) { // For Hunters only Counterattack (skip Mongoose bite) - if (getClass() == CLASS_HUNTER) + if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_ABILITY_REACTIVE)) { ModifyAuraState(AURA_STATE_HUNTER_PARRY, true); StartReactiveTimer(REACTIVE_HUNTER_PARRY); @@ -16411,7 +16411,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u // Overpower on victim dodge if (procExtra & PROC_EX_DODGE) { - if (getClass() == CLASS_WARRIOR) + if (IsClass(CLASS_WARRIOR, CLASS_CONTEXT_ABILITY_REACTIVE)) { AddComboPoints(target, 1); StartReactiveTimer(REACTIVE_OVERPOWER); @@ -17218,9 +17218,9 @@ void Unit::ClearAllReactives() if (HasAuraState(AURA_STATE_DEFENSE)) ModifyAuraState(AURA_STATE_DEFENSE, false); - if (getClass() == CLASS_HUNTER && HasAuraState(AURA_STATE_HUNTER_PARRY)) + if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_ABILITY_REACTIVE) && HasAuraState(AURA_STATE_HUNTER_PARRY)) ModifyAuraState(AURA_STATE_HUNTER_PARRY, false); - if (getClass() == CLASS_WARRIOR && GetTypeId() == TYPEID_PLAYER) + if (IsClass(CLASS_WARRIOR, CLASS_CONTEXT_ABILITY_REACTIVE) && GetTypeId() == TYPEID_PLAYER) ClearComboPoints(); } @@ -17244,11 +17244,11 @@ void Unit::UpdateReactives(uint32 p_time) ModifyAuraState(AURA_STATE_DEFENSE, false); break; case REACTIVE_HUNTER_PARRY: - if (getClass() == CLASS_HUNTER && HasAuraState(AURA_STATE_HUNTER_PARRY)) + if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_ABILITY_REACTIVE) && HasAuraState(AURA_STATE_HUNTER_PARRY)) ModifyAuraState(AURA_STATE_HUNTER_PARRY, false); break; case REACTIVE_OVERPOWER: - if (getClass() == CLASS_WARRIOR) + if (IsClass(CLASS_WARRIOR, CLASS_CONTEXT_ABILITY_REACTIVE)) { ClearComboPoints(); } @@ -18113,7 +18113,7 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp // Spirit of Redemption // if talent known but not triggered (check priest class for speedup check) bool spiritOfRedemption = false; - if (victim->GetTypeId() == TYPEID_PLAYER && victim->getClass() == CLASS_PRIEST && !victim->ToPlayer()->HasPlayerFlag(PLAYER_FLAGS_IS_OUT_OF_BOUNDS)) + if (victim->GetTypeId() == TYPEID_PLAYER && victim->IsClass(CLASS_PRIEST, CLASS_CONTEXT_ABILITY) && !victim->ToPlayer()->HasPlayerFlag(PLAYER_FLAGS_IS_OUT_OF_BOUNDS)) { if (AuraEffect* aurEff = victim->GetAuraEffectDummy(20711)) { @@ -18723,7 +18723,7 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au GetMotionMaster()->MoveIdle(); StopMoving(); - if (charmer->GetTypeId() == TYPEID_PLAYER && charmer->getClass() == CLASS_WARLOCK && ToCreature()->GetCreatureTemplate()->type == CREATURE_TYPE_DEMON) + if (charmer->GetTypeId() == TYPEID_PLAYER && charmer->IsClass(CLASS_WARLOCK, CLASS_CONTEXT_PET_CHARM) && ToCreature()->GetCreatureTemplate()->type == CREATURE_TYPE_DEMON) { // Disable CreatureAI/SmartAI and switch to CharmAI when charmed by warlock Creature* charmed = ToCreature(); @@ -18789,7 +18789,7 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au playerCharmer->PossessSpellInitialize(); break; case CHARM_TYPE_CHARM: - if (GetTypeId() == TYPEID_UNIT && charmer->getClass() == CLASS_WARLOCK) + if (GetTypeId() == TYPEID_UNIT && charmer->IsClass(CLASS_WARLOCK, CLASS_CONTEXT_PET_CHARM)) { CreatureTemplate const* cinfo = ToCreature()->GetCreatureTemplate(); if (cinfo && cinfo->type == CREATURE_TYPE_DEMON) @@ -18911,7 +18911,7 @@ void Unit::RemoveCharmedBy(Unit* charmer) ClearUnitState(UNIT_STATE_NO_ENVIRONMENT_UPD); break; case CHARM_TYPE_CHARM: - if (GetTypeId() == TYPEID_UNIT && charmer->getClass() == CLASS_WARLOCK) + if (GetTypeId() == TYPEID_UNIT && charmer->IsClass(CLASS_WARLOCK, CLASS_CONTEXT_PET_CHARM)) { CreatureTemplate const* cinfo = ToCreature()->GetCreatureTemplate(); if (cinfo && cinfo->type == CREATURE_TYPE_DEMON) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index ab4fc5e3a..a424ae465 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -440,6 +440,29 @@ enum DamageEffectType : uint8 SELF_DAMAGE = 5 }; +// Used for IsClass hook +enum ClassContext : uint8 +{ + CLASS_CONTEXT_NONE = 0, // Default + CLASS_CONTEXT_INIT = 1, + CLASS_CONTEXT_TELEPORT = 2, + CLASS_CONTEXT_QUEST = 3, + CLASS_CONTEXT_STATS = 4, + CLASS_CONTEXT_TAXI = 5, + CLASS_CONTEXT_SKILL = 6, + CLASS_CONTEXT_TALENT_POINT_CALC = 7, + CLASS_CONTEXT_ABILITY = 8, + CLASS_CONTEXT_ABILITY_REACTIVE = 9, + CLASS_CONTEXT_PET = 10, + CLASS_CONTEXT_PET_CHARM = 11, + CLASS_CONTEXT_EQUIP_RELIC = 12, + CLASS_CONTEXT_EQUIP_SHIELDS = 13, + CLASS_CONTEXT_EQUIP_ARMOR_CLASS = 14, + CLASS_CONTEXT_WEAPON_SWAP = 15, + CLASS_CONTEXT_GRAVEYARD = 16, + CLASS_CONTEXT_CLASS_TRAINER = 17 +}; + // Value masks for UNIT_FIELD_FLAGS // EnumUtils: DESCRIBE THIS enum UnitFlags : uint32 @@ -1436,6 +1459,7 @@ public: void setRace(uint8 race); [[nodiscard]] uint32 getRaceMask() const { return 1 << (getRace(true) - 1); } [[nodiscard]] uint8 getClass() const { return GetByteValue(UNIT_FIELD_BYTES_0, 1); } + [[nodiscard]] virtual bool IsClass(Classes unitClass, [[maybe_unused]] ClassContext context = CLASS_CONTEXT_NONE) const { return (getClass() == unitClass); } [[nodiscard]] uint32 getClassMask() const { return 1 << (getClass() - 1); } [[nodiscard]] uint8 getGender() const { return GetByteValue(UNIT_FIELD_BYTES_0, 2); } [[nodiscard]] DisplayRace GetDisplayRaceFromModelId(uint32 modelId) const; @@ -1472,6 +1496,7 @@ public: [[nodiscard]] Powers getPowerType() const { return Powers(GetByteValue(UNIT_FIELD_BYTES_0, 3)); } void setPowerType(Powers power); + [[nodiscard]] virtual bool HasActivePowerType(Powers power) { return getPowerType() == power; } [[nodiscard]] uint32 GetPower(Powers power) const { return GetUInt32Value(static_cast(UNIT_FIELD_POWER1) + power); } [[nodiscard]] uint32 GetMaxPower(Powers power) const { return GetUInt32Value(static_cast(UNIT_FIELD_MAXPOWER1) + power); } void SetPower(Powers power, uint32 val, bool withPowerUpdate = true, bool fromRegenerate = false); diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index b33502da2..1e7ecbe8b 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -72,7 +72,7 @@ void Vehicle::Install() { if (PowerDisplayEntry const* powerDisplay = sPowerDisplayStore.LookupEntry(_vehicleInfo->m_powerDisplayId)) _me->setPowerType(Powers(powerDisplay->PowerType)); - else if (_me->getClass() == CLASS_ROGUE) + else if (_me->IsClass(CLASS_ROGUE, CLASS_CONTEXT_ABILITY)) _me->setPowerType(POWER_ENERGY); } diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index e2208fa84..f6563042e 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -1983,7 +1983,7 @@ GroupJoinBattlegroundResult Group::CanJoinBattlegroundQueue(Battleground const* return ERR_IN_NON_RANDOM_BG; // don't let Death Knights join BG queues when they are not allowed to be teleported yet - if (member->getClass() == CLASS_DEATH_KNIGHT && member->GetMapId() == 609 && !member->IsGameMaster() && !member->HasSpell(50977)) + if (member->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_TELEPORT) && member->GetMapId() == 609 && !member->IsGameMaster() && !member->HasSpell(50977)) return ERR_GROUP_JOIN_BATTLEGROUND_FAIL; if (!member->GetBGAccessByLevel(bgTemplate->GetBgTypeID())) diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp index 7bb348caf..dacd0b5c7 100644 --- a/src/server/game/Handlers/BattleGroundHandler.cpp +++ b/src/server/game/Handlers/BattleGroundHandler.cpp @@ -184,7 +184,7 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) err = ERR_BATTLEGROUND_QUEUED_FOR_RATED; } // don't let Death Knights join BG queues when they are not allowed to be teleported yet - else if (_player->getClass() == CLASS_DEATH_KNIGHT && _player->GetMapId() == 609 && !_player->IsGameMaster() && !_player->HasSpell(50977)) + else if (_player->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_TELEPORT) && _player->GetMapId() == 609 && !_player->IsGameMaster() && !_player->HasSpell(50977)) { err = ERR_BATTLEGROUND_NONE; } diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index e4a1b3c3c..70a2e0513 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -83,7 +83,7 @@ void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket& recvData) { Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid); - bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING); + bool lootAllowed = creature && creature->IsAlive() == (player->IsClass(CLASS_ROGUE, CLASS_CONTEXT_ABILITY) && creature->loot.loot_type == LOOT_PICKPOCKETING); if (!lootAllowed || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) { player->SendLootError(lguid, lootAllowed ? LOOT_ERROR_TOO_FAR : LOOT_ERROR_DIDNT_KILL); @@ -162,7 +162,7 @@ void WorldSession::HandleLootMoneyOpcode(WorldPacket& /*recvData*/) case HighGuid::Vehicle: { Creature* creature = player->GetMap()->GetCreature(guid); - bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING); + bool lootAllowed = creature && creature->IsAlive() == (player->IsClass(CLASS_ROGUE, CLASS_CONTEXT_ABILITY) && creature->loot.loot_type == LOOT_PICKPOCKETING); if (lootAllowed && creature->IsWithinDistInMap(player, INTERACTION_DISTANCE)) { loot = &creature->loot; @@ -382,7 +382,7 @@ void WorldSession::DoLootRelease(ObjectGuid lguid) { Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid); - bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING); + bool lootAllowed = creature && creature->IsAlive() == (player->IsClass(CLASS_ROGUE, CLASS_CONTEXT_ABILITY) && creature->loot.loot_type == LOOT_PICKPOCKETING); if (!lootAllowed || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) return; diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp index 31954970d..2c6e51752 100644 --- a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp @@ -1000,11 +1000,11 @@ void ScriptMgr::PetitionShowList(Player* player, Creature* creature, uint32& Cha }); } -void ScriptMgr::OnRewardKillRewarder(Player* player, bool isDungeon, float& rate) +void ScriptMgr::OnRewardKillRewarder(Player* player, KillRewarder* rewarder, bool isDungeon, float& rate) { ExecuteScript([&](PlayerScript* script) { - script->OnRewardKillRewarder(player, isDungeon, rate); + script->OnRewardKillRewarder(player, rewarder, isDungeon, rate); }); } @@ -1046,6 +1046,19 @@ bool ScriptMgr::CanRepopAtGraveyard(Player* player) return true; } +Optional ScriptMgr::OnPlayerIsClass(Player const* player, Classes unitClass, ClassContext context) +{ + if (ScriptRegistry::ScriptPointerList.empty()) + return {}; + for (auto const& [scriptID, script] : ScriptRegistry::ScriptPointerList) + { + Optional scriptResult = script->OnPlayerIsClass(player, unitClass, context); + if (scriptResult) + return scriptResult; + } + return {}; +} + void ScriptMgr::OnGetMaxSkillValue(Player* player, uint32 skill, int32& result, bool IsPure) { ExecuteScript([&](PlayerScript* script) @@ -1054,6 +1067,21 @@ void ScriptMgr::OnGetMaxSkillValue(Player* player, uint32 skill, int32& result, }); } +bool ScriptMgr::OnPlayerHasActivePowerType(Player const* player, Powers power) +{ + auto ret = IsValidBoolScript([&](PlayerScript* script) + { + return script->OnPlayerHasActivePowerType(player, power); + }); + + if (ret && *ret) + { + return true; + } + + return false; +} + void ScriptMgr::OnUpdateGatheringSkill(Player *player, uint32 skillId, uint32 currentLevel, uint32 gray, uint32 green, uint32 yellow, uint32 &gain) { ExecuteScript([&](PlayerScript* script) { diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.h b/src/server/game/Scripting/ScriptDefines/PlayerScript.h index 301de1ac3..aa699ee0e 100644 --- a/src/server/game/Scripting/ScriptDefines/PlayerScript.h +++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.h @@ -22,6 +22,7 @@ // TODO to remove #include "AchievementMgr.h" +#include "KillRewarder.h" class PlayerScript : public ScriptObject { @@ -315,7 +316,7 @@ public: virtual void PetitionShowList(Player* /*player*/, Creature* /*creature*/, uint32& /*CharterEntry*/, uint32& /*CharterDispayID*/, uint32& /*CharterCost*/) { } - virtual void OnRewardKillRewarder(Player* /*player*/, bool /*isDungeon*/, float& /*rate*/) { } + virtual void OnRewardKillRewarder(Player* /*player*/, KillRewarder* /*rewarder*/, bool /*isDungeon*/, float& /*rate*/) { } [[nodiscard]] virtual bool CanGiveMailRewardAtGiveLevel(Player* /*player*/, uint8 /*level*/) { return true; } @@ -323,8 +324,12 @@ public: [[nodiscard]] virtual bool CanRepopAtGraveyard(Player* /*player*/) { return true; } + [[nodiscard]] virtual Optional OnPlayerIsClass(Player const* /*player*/, Classes /*playerClass*/, ClassContext /*context*/) { return std::nullopt; } + virtual void OnGetMaxSkillValue(Player* /*player*/, uint32 /*skill*/, int32& /*result*/, bool /*IsPure*/) { } + [[nodiscard]] virtual bool OnPlayerHasActivePowerType(Player const* /*player*/, Powers /*power*/) { return false; } + /** * @brief This hook called before gathering skill gain is applied to the character. * diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 2b3e0b99d..1ef20c2db 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -430,11 +430,13 @@ public: /* PlayerScript */ bool CanSendMail(Player* player, ObjectGuid receiverGuid, ObjectGuid mailbox, std::string& subject, std::string& body, uint32 money, uint32 COD, Item* item); void PetitionBuy(Player* player, Creature* creature, uint32& charterid, uint32& cost, uint32& type); void PetitionShowList(Player* player, Creature* creature, uint32& CharterEntry, uint32& CharterDispayID, uint32& CharterCost); - void OnRewardKillRewarder(Player* player, bool isDungeon, float& rate); + void OnRewardKillRewarder(Player* player, KillRewarder* rewarder, bool isDungeon, float& rate); bool CanGiveMailRewardAtGiveLevel(Player* player, uint8 level); void OnDeleteFromDB(CharacterDatabaseTransaction trans, uint32 guid); bool CanRepopAtGraveyard(Player* player); + std::optional OnPlayerIsClass(Player const* player, Classes playerClass, ClassContext context); void OnGetMaxSkillValue(Player* player, uint32 skill, int32& result, bool IsPure); + bool OnPlayerHasActivePowerType(Player const* player, Powers power); void OnUpdateGatheringSkill(Player* player, uint32 skillId, uint32 currentLevel, uint32 gray, uint32 green, uint32 yellow, uint32& gain); void OnUpdateCraftingSkill(Player* player, SkillLineAbilityEntry const* skill, uint32 currentLevel, uint32& gain); bool OnUpdateFishingSkill(Player* player, int32 skill, int32 zone_skill, int32 chance, int32 roll); diff --git a/src/server/game/Scripting/ScriptObjectFwd.h b/src/server/game/Scripting/ScriptObjectFwd.h index 67c5e8ca7..a7a899e02 100644 --- a/src/server/game/Scripting/ScriptObjectFwd.h +++ b/src/server/game/Scripting/ScriptObjectFwd.h @@ -79,6 +79,7 @@ class WorldSocket; enum ArenaTeamInfoType : uint8; enum AuraRemoveMode : uint8; enum BattlegroundDesertionType : uint8; +enum ClassContext : uint8; enum ContentLevels : uint8; enum DamageEffectType : uint8; enum EnchantmentSlot : uint8; diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index f8c8117af..e333b98ee 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -2027,7 +2027,7 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo if (!target->HasAuraType(SPELL_AURA_MOD_SHAPESHIFT)) { target->SetShapeshiftForm(FORM_NONE); - if (target->getClass() == CLASS_DRUID) + if (target->IsClass(CLASS_DRUID, CLASS_CONTEXT_ABILITY)) { target->setPowerType(POWER_MANA); // Remove movement impairing effects also when shifting out @@ -2106,7 +2106,7 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo if (target->GetTypeId() == TYPEID_PLAYER) target->ToPlayer()->InitDataForForm(); - if (target->getClass() == CLASS_DRUID) + if (target->IsClass(CLASS_DRUID, CLASS_CONTEXT_ABILITY)) { // Dash if (AuraEffect* aurEff = target->GetAuraEffect(SPELL_AURA_MOD_INCREASE_SPEED, SPELLFAMILY_DRUID, 0, 0, 0x8)) @@ -6038,7 +6038,7 @@ void AuraEffect::HandleAuraConvertRune(AuraApplication const* aurApp, uint8 mode Player* player = target->ToPlayer(); - if (player->getClass() != CLASS_DEATH_KNIGHT) + if (!player->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) return; uint32 runes = m_amount; @@ -6244,7 +6244,7 @@ void AuraEffect::HandlePeriodicDummyAuraTick(Unit* target, Unit* caster) const { // Converts up to 10 rage per second into health for $d. Each point of rage is converted into ${$m2/10}.1% of max health. // Should be manauser - if (target->getPowerType() != POWER_RAGE) + if (!target->HasActivePowerType(POWER_RAGE)) break; uint32 rage = target->GetPower(POWER_RAGE); // Nothing todo @@ -6310,7 +6310,7 @@ void AuraEffect::HandlePeriodicDummyAuraTick(Unit* target, Unit* caster) const { if (target->GetTypeId() != TYPEID_PLAYER) return; - if (target->ToPlayer()->getClass() != CLASS_DEATH_KNIGHT) + if (!target->ToPlayer()->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) return; // timer expired - remove death runes @@ -7082,7 +7082,7 @@ void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) con { Powers PowerType = Powers(GetMiscValue()); - if (!caster || !caster->IsAlive() || !target->IsAlive() || target->getPowerType() != PowerType) + if (!caster || !caster->IsAlive() || !target->IsAlive() || !target->HasActivePowerType(PowerType)) return; if (target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsImmunedToDamageOrSchool(GetSpellInfo())) @@ -7189,7 +7189,7 @@ void AuraEffect::HandlePeriodicEnergizeAuraTick(Unit* target, Unit* caster) cons { Powers PowerType = Powers(GetMiscValue()); - if (target->GetTypeId() == TYPEID_PLAYER && target->getPowerType() != PowerType && !m_spellInfo->HasAttribute(SPELL_ATTR7_ONLY_IN_SPELLBOOK_UNTIL_LEARNED)) + if (target->GetTypeId() == TYPEID_PLAYER && !target->HasActivePowerType(PowerType) && !m_spellInfo->HasAttribute(SPELL_ATTR7_ONLY_IN_SPELLBOOK_UNTIL_LEARNED)) return; if (!target->IsAlive() || !target->GetMaxPower(PowerType)) @@ -7223,7 +7223,7 @@ void AuraEffect::HandlePeriodicPowerBurnAuraTick(Unit* target, Unit* caster) con { Powers PowerType = Powers(GetMiscValue()); - if (!caster || !target->IsAlive() || target->getPowerType() != PowerType) + if (!caster || !target->IsAlive() || !target->HasActivePowerType(PowerType)) return; if (target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsImmunedToDamageOrSchool(GetSpellInfo())) diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 093823a1f..5e4e67f51 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -1876,7 +1876,7 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b break; if (target->GetTypeId() != TYPEID_PLAYER) break; - if (target->ToPlayer()->getClass() != CLASS_DEATH_KNIGHT) + if (!target->ToPlayer()->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) break; // aura removed - remove death runes diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 7f86c1842..177e7b392 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -4807,7 +4807,7 @@ void Spell::SendSpellGo() } if ((m_caster->GetTypeId() == TYPEID_PLAYER) - && (m_caster->getClass() == CLASS_DEATH_KNIGHT) + && (m_caster->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) && m_spellInfo->RuneCostID && m_spellInfo->PowerType == POWER_RUNE) { @@ -5396,7 +5396,7 @@ SpellCastResult Spell::CheckRuneCost(uint32 RuneCostID) return SPELL_CAST_OK; } - if (player->getClass() != CLASS_DEATH_KNIGHT) + if (!player->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) return SPELL_CAST_OK; SpellRuneCostEntry const* src = sSpellRuneCostStore.LookupEntry(RuneCostID); @@ -5437,7 +5437,7 @@ SpellCastResult Spell::CheckRuneCost(uint32 RuneCostID) void Spell::TakeRunePower(bool didHit) { - if (m_caster->GetTypeId() != TYPEID_PLAYER || m_caster->getClass() != CLASS_DEATH_KNIGHT) + if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_caster->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) return; SpellRuneCostEntry const* runeCostData = sSpellRuneCostStore.LookupEntry(m_spellInfo->RuneCostID); @@ -6173,7 +6173,7 @@ SpellCastResult Spell::CheckCast(bool strict) // Can be area effect, Check only for players and not check if target - caster (spell can have multiply drain/burn effects) if (m_caster->GetTypeId() == TYPEID_PLAYER) if (Unit* target = m_targets.GetUnitTarget()) - if (target != m_caster && target->getPowerType() != Powers(m_spellInfo->Effects[i].MiscValue)) + if (target != m_caster && !target->HasActivePowerType(Powers(m_spellInfo->Effects[i].MiscValue))) return SPELL_FAILED_BAD_TARGETS; break; } @@ -6408,7 +6408,7 @@ SpellCastResult Spell::CheckCast(bool strict) return SPELL_FAILED_ALREADY_HAVE_CHARM; } - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->getClass() == CLASS_WARLOCK && strict) + if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->IsClass(CLASS_WARLOCK, CLASS_CONTEXT_PET) && strict) if (Pet* pet = m_caster->ToPlayer()->GetPet()) pet->CastSpell(pet, 32752, true, nullptr, nullptr, pet->GetGUID()); //starting cast, trigger pet stun (cast by pet so it doesn't attack player) @@ -6702,7 +6702,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (!m_targets.GetUnitTarget()) return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - if (m_targets.GetUnitTarget()->getPowerType() != POWER_MANA) + if (!m_targets.GetUnitTarget()->HasActivePowerType(POWER_MANA)) return SPELL_FAILED_BAD_TARGETS; break; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 98cd74e6c..97cbbd4d2 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1345,7 +1345,7 @@ void Spell::EffectPowerDrain(SpellEffIndex effIndex) Powers PowerType = Powers(m_spellInfo->Effects[effIndex].MiscValue); - if (!unitTarget || !unitTarget->IsAlive() || unitTarget->getPowerType() != PowerType || damage < 0) + if (!unitTarget || !unitTarget->IsAlive() || !unitTarget->HasActivePowerType(PowerType) || damage < 0) return; // add spell damage bonus @@ -1424,7 +1424,7 @@ void Spell::EffectPowerBurn(SpellEffIndex effIndex) Powers PowerType = Powers(m_spellInfo->Effects[effIndex].MiscValue); - if (!unitTarget || !unitTarget->IsAlive() || unitTarget->getPowerType() != PowerType || damage < 0) + if (!unitTarget || !unitTarget->IsAlive() || !unitTarget->HasActivePowerType(PowerType) || damage < 0) return; // burn x% of target's mana, up to maximum of 2x% of caster's mana (Mana Burn) @@ -1877,7 +1877,7 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) Powers power = Powers(m_spellInfo->Effects[effIndex].MiscValue); - if (unitTarget->GetTypeId() == TYPEID_PLAYER && unitTarget->getPowerType() != power && m_spellInfo->SpellFamilyName != SPELLFAMILY_POTION + if (unitTarget->GetTypeId() == TYPEID_PLAYER && !unitTarget->HasActivePowerType(power) && m_spellInfo->SpellFamilyName != SPELLFAMILY_POTION && !m_spellInfo->HasAttribute(SPELL_ATTR7_ONLY_IN_SPELLBOOK_UNTIL_LEARNED)) return; @@ -1982,7 +1982,7 @@ void Spell::EffectEnergizePct(SpellEffIndex effIndex) Powers power = Powers(m_spellInfo->Effects[effIndex].MiscValue); - if (unitTarget->GetTypeId() == TYPEID_PLAYER && unitTarget->getPowerType() != power && !m_spellInfo->HasAttribute(SPELL_ATTR7_ONLY_IN_SPELLBOOK_UNTIL_LEARNED)) + if (unitTarget->GetTypeId() == TYPEID_PLAYER && !unitTarget->HasActivePowerType(power) && !m_spellInfo->HasAttribute(SPELL_ATTR7_ONLY_IN_SPELLBOOK_UNTIL_LEARNED)) return; uint32 maxPower = unitTarget->GetMaxPower(power); @@ -3077,7 +3077,7 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/) if (creatureTarget->IsPet()) return; - if (m_caster->getClass() != CLASS_HUNTER) + if (!m_caster->IsClass(CLASS_HUNTER, CLASS_CONTEXT_PET)) return; // cast finish successfully @@ -3209,7 +3209,7 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex) pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); // Reset cooldowns - if (owner->getClass() != CLASS_HUNTER) + if (!owner->IsClass(CLASS_HUNTER, CLASS_CONTEXT_PET)) { pet->m_CreatureSpellCooldowns.clear(); owner->PetSpellInitialize(); @@ -5746,7 +5746,7 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex) Player* player = m_caster->ToPlayer(); - if (player->getClass() != CLASS_DEATH_KNIGHT) + if (!player->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) return; // needed later @@ -5814,7 +5814,7 @@ void Spell::EffectCreateTamedPet(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || unitTarget->GetPetGUID() || unitTarget->getClass() != CLASS_HUNTER) + if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || unitTarget->GetPetGUID() || !unitTarget->IsClass(CLASS_HUNTER, CLASS_CONTEXT_PET)) return; uint32 creatureEntry = m_spellInfo->Effects[effIndex].MiscValue; diff --git a/src/server/scripts/Commands/cs_reset.cpp b/src/server/scripts/Commands/cs_reset.cpp index 3a2ba01ac..81c4e742a 100644 --- a/src/server/scripts/Commands/cs_reset.cpp +++ b/src/server/scripts/Commands/cs_reset.cpp @@ -140,7 +140,7 @@ public: uint8 oldLevel = playerTarget->GetLevel(); // set starting level - uint32 startLevel = playerTarget->getClass() != CLASS_DEATH_KNIGHT + uint32 startLevel = !playerTarget->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_INIT) ? sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL) : sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp index 7f73b5b37..bbc8a0f48 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp @@ -1029,7 +1029,7 @@ class spell_class_call_handler : public SpellScript targets.remove_if([spellInfo](WorldObject const* target) -> bool { Player const* player = target->ToPlayer(); - if (!player || player->getClass() == CLASS_DEATH_KNIGHT) // ignore all death knights from whatever spell, for some reason the condition below is not working x.x + if (!player || player->IsClass(CLASS_DEATH_KNIGHT)) // ignore all death knights from whatever spell, for some reason the condition below is not working x.x { return true; } diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp index 6dd0b436c..53d2c3d2d 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp @@ -37,9 +37,13 @@ enum Spells SPELL_SHRED_ARMOR = 43243 // Used by Spirit Lynx }; +enum UniqueEvents +{ + EVENT_BERSERK = 0 +}; + enum Hal_CreatureIds { - NPC_SPIRIT_LYNX = 24143, NPC_TOTEM = 24224 }; @@ -63,326 +67,274 @@ enum Yells SAY_DEATH = 5 }; -class boss_halazzi : public CreatureScript +enum Groups { -public: - boss_halazzi() : CreatureScript("boss_halazzi") { } + GROUP_LYNX = 0, + GROUP_HUMAN = 1, + GROUP_MERGE = 2 +}; - struct boss_halazziAI : public ScriptedAI +struct boss_halazzi : public BossAI +{ + boss_halazzi(Creature* creature) : BossAI(creature, DATA_HALAZZIEVENT) { - boss_halazziAI(Creature* creature) : ScriptedAI(creature), summons(me) + scheduler.SetValidator([this] { - instance = creature->GetInstanceScript(); + return !me->HasUnitState(UNIT_STATE_CASTING); + }); + } + + void Reset() override + { + BossAI::Reset(); + _transformCount = 0; + _healthCheckPercentage = 0; + _phase = PHASE_NONE; + _lynxFormHealth = me->GetMaxHealth(); + _healthPortion = _lynxFormHealth/4; + _humanFormHealth = (me->GetMaxHealth())/0.66666666; + EnterPhase(PHASE_LYNX); + DoCastSelf(SPELL_DUAL_WIELD, true); + } + + void JustSummoned(Creature* summon) override + { + BossAI::JustSummoned(summon); + summon->Attack(me->GetVictim(), false); + summon->SetInCombatWithZone(); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + Talk(SAY_AGGRO); + ScheduleUniqueTimedEvent(10min, [&] + { + DoCastSelf(SPELL_BERSERK, true); + }, EVENT_BERSERK); + EnterPhase(PHASE_LYNX); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override + { + if (damage >= me->GetHealth() && _phase != PHASE_ENRAGE) + { + damage = 0; } - - InstanceScript* instance; - SummonList summons; - PhaseHalazzi Phase; - - uint32 FrenzyTimer; - uint32 SaberlashTimer; - uint32 ShockTimer; - uint32 TotemTimer; - uint32 CheckTimer; - uint32 BerserkTimer; - uint32 TransformCount; - - ObjectGuid LynxGUID; - - void Reset() override + else { - instance->SetData(DATA_HALAZZIEVENT, NOT_STARTED); - summons.DespawnAll(); - - LynxGUID.Clear(); - TransformCount = 0; - BerserkTimer = 600000; - CheckTimer = 1000; - - DoCast(me, SPELL_DUAL_WIELD, true); - - Phase = PHASE_NONE; - EnterPhase(PHASE_LYNX); - } - - void JustEngagedWith(Unit* /*who*/) override - { - instance->SetData(DATA_HALAZZIEVENT, IN_PROGRESS); - Talk(SAY_AGGRO); - EnterPhase(PHASE_LYNX); - } - - void JustSummoned(Creature* summon) override - { - summon->AI()->AttackStart(me->GetVictim()); - if (summon->GetEntry() == NPC_SPIRIT_LYNX) - LynxGUID = summon->GetGUID(); - summons.Summon(summon); - } - - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (damage >= me->GetHealth() && Phase != PHASE_ENRAGE) - damage = 0; - } - - void SpellHit(Unit*, SpellInfo const* spell) override - { - if (spell->Id == SPELL_TRANSFORM_SPLIT2) - EnterPhase(PHASE_HUMAN); - } - - void AttackStart(Unit* who) override - { - if (Phase != PHASE_MERGE) - ScriptedAI::AttackStart(who); - } - - void EnterPhase(PhaseHalazzi NextPhase) - { - switch (NextPhase) + if (_phase == PHASE_LYNX || _phase == PHASE_ENRAGE) { - case PHASE_LYNX: - case PHASE_ENRAGE: - if (Phase == PHASE_MERGE) - { - DoCast(me, SPELL_TRANSFORM_MERGE, true); - me->Attack(me->GetVictim(), true); - me->GetMotionMaster()->MoveChase(me->GetVictim()); - } - if (Creature* Lynx = ObjectAccessor::GetCreature(*me, LynxGUID)) - Lynx->DisappearAndDie(); - me->SetMaxHealth(600000); - me->SetHealth(600000 - 150000 * TransformCount); - FrenzyTimer = 16000; - SaberlashTimer = 20000; - ShockTimer = 10000; - TotemTimer = 12000; - break; - case PHASE_SPLIT: - Talk(SAY_SPLIT); - DoCast(me, SPELL_TRANSFORM_SPLIT, true); - break; - case PHASE_HUMAN: - //DoCast(me, SPELL_SUMMON_LYNX, true); - DoSpawnCreature(NPC_SPIRIT_LYNX, 5, 5, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); - me->SetMaxHealth(400000); - me->SetHealth(400000); - ShockTimer = 10000; - TotemTimer = 12000; - break; - case PHASE_MERGE: - if (Unit* pLynx = ObjectAccessor::GetUnit(*me, LynxGUID)) - { - Talk(SAY_MERGE); - pLynx->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - pLynx->GetMotionMaster()->Clear(); - pLynx->GetMotionMaster()->MoveFollow(me, 0, 0); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveFollow(pLynx, 0, 0); - ++TransformCount; - } - break; - default: - break; + _healthCheckPercentage = 25 * (3 - _transformCount); + if (!HealthAbovePct(_healthCheckPercentage)) + { + EnterPhase(PHASE_SPLIT); + } } - Phase = NextPhase; - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (BerserkTimer <= diff) + else if (_phase == PHASE_HUMAN) { - DoCast(me, SPELL_BERSERK, true); - BerserkTimer = 60000; + if (Creature* lynx = instance->GetCreature(DATA_SPIRIT_LYNX)) + { + if (!HealthAbovePct(20) || !lynx->HealthAbovePct(20)) + { + EnterPhase(PHASE_MERGE); + } + } + else + { + //should not really happen + EnterEvadeMode(); + } } - else BerserkTimer -= diff; + } + } - if (Phase == PHASE_LYNX || Phase == PHASE_ENRAGE) - { - if (SaberlashTimer <= diff) + void SpellHit(Unit*, SpellInfo const* spell) override + { + if (spell->Id == SPELL_TRANSFORM_SPLIT2) + { + EnterPhase(PHASE_HUMAN); + } + } + + void AttackStart(Unit* who) override + { + if (_phase != PHASE_MERGE) + { + BossAI::AttackStart(who); + } + } + + void EnterPhase(PhaseHalazzi nextPhase) + { + switch (nextPhase) + { + case PHASE_LYNX: + case PHASE_ENRAGE: + if (_phase == PHASE_MERGE) + { + DoCastSelf(SPELL_TRANSFORM_MERGE, true); + me->RemoveAurasDueToSpell(SPELL_TRANSFORM_SPLIT2); + me->GetMotionMaster()->MoveChase(me->GetVictim()); + } + summons.DespawnAll(); + me->SetMaxHealth(_lynxFormHealth); + me->SetHealth(_lynxFormHealth - _healthPortion * _transformCount); + scheduler.CancelGroup(GROUP_MERGE); + scheduler.Schedule(16s, GROUP_LYNX, [this](TaskContext context) + { + DoCastSelf(SPELL_FRENZY); + context.Repeat(10s, 15s); + }).Schedule(20s, GROUP_LYNX, [this](TaskContext context) { Talk(SAY_SABER); - // A tank with more than 490 defense skills should receive no critical hit - //DoCast(me, 41296, true); DoCastVictim(SPELL_SABER_LASH, true); - //me->RemoveAurasDueToSpell(41296); - SaberlashTimer = 30000; - } - else SaberlashTimer -= diff; - - if (FrenzyTimer <= diff) - { - DoCast(me, SPELL_FRENZY); - FrenzyTimer = urand(10000, 15000); - } - else FrenzyTimer -= diff; - - if (Phase == PHASE_LYNX) - { - if (CheckTimer <= diff) - { - if (HealthBelowPct(25 * (3 - TransformCount))) - EnterPhase(PHASE_SPLIT); - CheckTimer = 1000; - } - else CheckTimer -= diff; - } - } - - if (Phase == PHASE_HUMAN || Phase == PHASE_ENRAGE) - { - if (TotemTimer <= diff) - { - DoCast(me, SPELL_SUMMON_TOTEM); - TotemTimer = 20000; - } - else TotemTimer -= diff; - - if (ShockTimer <= diff) + context.Repeat(30s); + }); + break; + case PHASE_SPLIT: + Talk(SAY_SPLIT); + DoCastSelf(SPELL_TRANSFORM_SPLIT, true); + break; + case PHASE_HUMAN: + scheduler.CancelGroup(GROUP_MERGE); + DoCastSelf(SPELL_SUMMON_LYNX, true); + me->SetMaxHealth(_humanFormHealth); + me->SetHealth(_humanFormHealth); + scheduler.CancelGroup(GROUP_LYNX); + scheduler.Schedule(10s, GROUP_HUMAN, [this](TaskContext context) { if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) { if (target->IsNonMeleeSpellCast(false)) + { DoCast(target, SPELL_EARTHSHOCK); + } else + { DoCast(target, SPELL_FLAMESHOCK); - ShockTimer = urand(10000, 15000); - } - } - else ShockTimer -= diff; - - if (Phase == PHASE_HUMAN) - { - if (CheckTimer <= diff) - { - if (!HealthAbovePct(20) /*HealthBelowPct(10)*/) - EnterPhase(PHASE_MERGE); - else - { - Unit* Lynx = ObjectAccessor::GetUnit(*me, LynxGUID); - if (Lynx && !Lynx->HealthAbovePct(20) /*Lynx->HealthBelowPct(10)*/) - EnterPhase(PHASE_MERGE); - } - CheckTimer = 1000; - } - else CheckTimer -= diff; - } - } - - if (Phase == PHASE_MERGE) - { - if (CheckTimer <= diff) - { - Unit* Lynx = ObjectAccessor::GetUnit(*me, LynxGUID); - if (Lynx) - { - Lynx->GetMotionMaster()->MoveFollow(me, 0, 0); - me->GetMotionMaster()->MoveFollow(Lynx, 0, 0); - if (me->IsWithinDistInMap(Lynx, 6.0f)) - { - if (TransformCount < 3) - EnterPhase(PHASE_LYNX); - else - EnterPhase(PHASE_ENRAGE); } } - CheckTimer = 1000; + context.Repeat(10s, 15s); + }).Schedule(12s, GROUP_HUMAN, [this](TaskContext context) + { + DoCastSelf(SPELL_SUMMON_TOTEM); + context.Repeat(20s); + }); + break; + case PHASE_MERGE: + if (Creature* lynx = instance->GetCreature(DATA_SPIRIT_LYNX)) + { + Talk(SAY_MERGE); + scheduler.CancelGroup(GROUP_HUMAN); + lynx->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + lynx->GetMotionMaster()->Clear(); + lynx->GetMotionMaster()->MoveFollow(me, 0, 0); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveFollow(lynx, 0, 0); + ++_transformCount; + scheduler.Schedule(2s, GROUP_MERGE, [this](TaskContext context) + { + if (Creature* lynx = instance->GetCreature(DATA_SPIRIT_LYNX)) + { + if (me->IsWithinDistInMap(lynx, 6.0f)) + { + if (_transformCount < 3) + { + EnterPhase(PHASE_LYNX); + } + else + { + EnterPhase(PHASE_ENRAGE); + } + } + } + context.Repeat(2s); + }); } - else CheckTimer -= diff; - } - - DoMeleeAttackIfReady(); + break; + default: + break; } + _phase = nextPhase; + } - void KilledUnit(Unit* victim) override + void KilledUnit(Unit* victim) override + { + BossAI::KilledUnit(victim); + if (victim->IsPlayer()) { - if (victim->GetTypeId() != TYPEID_PLAYER) - return; - Talk(SAY_KILL); } - - void JustDied(Unit* /*killer*/) override - { - instance->SetData(DATA_HALAZZIEVENT, DONE); - Talk(SAY_DEATH); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetZulAmanAI(creature); } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + Talk(SAY_DEATH); + } +private: + uint32 _lynxFormHealth; + uint32 _humanFormHealth; + uint32 _healthPortion; + uint8 _transformCount; + uint32 _healthCheckPercentage; + PhaseHalazzi _phase; }; - // Spirits Lynx AI -class npc_halazzi_lynx : public CreatureScript +struct npc_halazzi_lynx : public ScriptedAI { -public: - npc_halazzi_lynx() : CreatureScript("npc_halazzi_lynx") { } + npc_halazzi_lynx(Creature* creature) : ScriptedAI(creature) { } - struct npc_halazzi_lynxAI : public ScriptedAI + void Reset() override { - npc_halazzi_lynxAI(Creature* creature) : ScriptedAI(creature) { } + scheduler.CancelAll(); + } - uint32 FrenzyTimer; - uint32 shredder_timer; - - void Reset() override - { - FrenzyTimer = urand(30000, 50000); //frenzy every 30-50 seconds - shredder_timer = 4000; - } - - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (damage >= me->GetHealth()) - damage = 0; - } - - void AttackStart(Unit* who) override - { - if (!me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) - ScriptedAI::AttackStart(who); - } - - void JustEngagedWith(Unit* /*who*/) override {/*DoZoneInCombat();*/ } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (FrenzyTimer <= diff) - { - DoCast(me, SPELL_LYNX_FRENZY); - FrenzyTimer = urand(30000, 50000); //frenzy every 30-50 seconds - } - else FrenzyTimer -= diff; - - if (shredder_timer <= diff) - { - DoCastVictim(SPELL_SHRED_ARMOR); - shredder_timer = 4000; - } - else shredder_timer -= diff; - - DoMeleeAttackIfReady(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override { - return GetZulAmanAI(creature); + if (damage >= me->GetHealth()) + { + damage = 0; + } + } + + void AttackStart(Unit* who) override + { + if (!me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) + { + ScriptedAI::AttackStart(who); + } + } + + void JustEngagedWith(Unit* who) override + { + ScriptedAI::JustEngagedWith(who); + + ScheduleTimedEvent(30s, 50s, [&] + { + DoCastSelf(SPELL_LYNX_FRENZY); + }, 30s, 50s); + ScheduleTimedEvent(4s, [&]{ + DoCastVictim(SPELL_SHRED_ARMOR); + }, 4s); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + { + return; + } + + scheduler.Update(diff); + + DoMeleeAttackIfReady(); } }; void AddSC_boss_halazzi() { - new boss_halazzi(); - new npc_halazzi_lynx(); + RegisterZulAmanCreatureAI(boss_halazzi); + RegisterZulAmanCreatureAI(npc_halazzi_lynx); } diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp index 8d357de1e..8f6f20e62 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp @@ -447,9 +447,9 @@ public: PlayerAbility_Timer = urand(8000, 10000); PlayerClass = target->getClass() - 1; - if (PlayerClass == CLASS_DRUID - 1) + if (target->IsClass(CLASS_DRUID)) PlayerClass = CLASS_DRUID; - else if (PlayerClass == CLASS_PRIEST - 1 && target->HasSpell(15473)) + else if (target->IsClass(CLASS_PRIEST) && target->HasSpell(15473)) PlayerClass = CLASS_PRIEST; // shadow priest SiphonSoul_Timer = 99999; // buff lasts 30 sec diff --git a/src/server/scripts/EasternKingdoms/ZulAman/instance_zulaman.cpp b/src/server/scripts/EasternKingdoms/ZulAman/instance_zulaman.cpp index ea7198184..baa83f557 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/instance_zulaman.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/instance_zulaman.cpp @@ -55,6 +55,17 @@ static SHostageInfo HostageInfo[] = Position const HarrisonJonesLoc = {120.687f, 1674.0f, 42.0217f, 1.59044f}; +ObjectData const creatureData[] = +{ + { NPC_SPIRIT_LYNX, DATA_SPIRIT_LYNX }, + { 0, 0 } +}; + +ObjectData const gameObjectData[] = +{ + { 0, 0 } +}; + class instance_zulaman : public InstanceMapScript { public: @@ -92,6 +103,7 @@ public: void Initialize() override { SetHeaders(DataHeader); + LoadObjectData(creatureData, gameObjectData); memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); QuestTimer = 0; @@ -135,6 +147,7 @@ public: default: break; } + InstanceScript::OnCreatureCreate(creature); } void OnGameObjectCreate(GameObject* go) override diff --git a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h index d8a5fdd1c..4ca81145b 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h +++ b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h @@ -32,9 +32,10 @@ enum DataTypes DATA_HALAZZIEVENT = 4, DATA_HEXLORDEVENT = 5, DATA_ZULJINEVENT = 6, - DATA_CHESTLOOTED = 7, - TYPE_RAND_VENDOR_1 = 8, - TYPE_RAND_VENDOR_2 = 9 + DATA_SPIRIT_LYNX = 7, + DATA_CHESTLOOTED = 8, + TYPE_RAND_VENDOR_1 = 9, + TYPE_RAND_VENDOR_2 = 10 }; enum CreatureIds @@ -44,7 +45,8 @@ enum CreatureIds NPC_ZULJIN = 23863, NPC_HEXLORD = 24239, NPC_HALAZZI = 23577, - NPC_NALORAKK = 23576 + NPC_NALORAKK = 23576, + NPC_SPIRIT_LYNX = 24143 }; enum GameobjectIds @@ -68,4 +70,6 @@ inline AI* GetZulAmanAI(T* obj) return GetInstanceAI(obj, ZulAmanScriptName); } +#define RegisterZulAmanCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetZulAmanAI) + #endif diff --git a/src/server/scripts/Kalimdor/zone_azshara.cpp b/src/server/scripts/Kalimdor/zone_azshara.cpp index e225cff10..8fbb8f62a 100644 --- a/src/server/scripts/Kalimdor/zone_azshara.cpp +++ b/src/server/scripts/Kalimdor/zone_azshara.cpp @@ -53,10 +53,10 @@ enum RizzleSprysprocketData SAY_RIZZLE_START = 0, SAY_RIZZLE_GRENADE = 1, SAY_RIZZLE_FINAL = 2, - MSG_ESCAPE_NOTICE = 3 -}; + MSG_ESCAPE_NOTICE = 3, + GOSSIP_GET_MOONSTONE = 21893 -#define GOSSIP_GET_MOONSTONE "Hand over the Southfury moonstone and I'll let you go." +}; Position const WPs[58] = { @@ -295,7 +295,7 @@ public: if (player->GetQuestStatus(QUEST_CHASING_THE_MOONSTONE) != QUEST_STATUS_INCOMPLETE) return true; - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_GET_MOONSTONE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + AddGossipItemFor(player, GOSSIP_GET_MOONSTONE, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); SendGossipMenuFor(player, 10811, creature->GetGUID()); return true; diff --git a/src/server/scripts/Kalimdor/zone_moonglade.cpp b/src/server/scripts/Kalimdor/zone_moonglade.cpp index c574da421..df9757da8 100644 --- a/src/server/scripts/Kalimdor/zone_moonglade.cpp +++ b/src/server/scripts/Kalimdor/zone_moonglade.cpp @@ -65,7 +65,7 @@ public: { case GOSSIP_ACTION_INFO_DEF + 1: CloseGossipMenuFor(player); - if (player->getClass() == CLASS_DRUID && player->GetTeamId() == TEAM_HORDE) + if (player->IsClass(CLASS_DRUID, CLASS_CONTEXT_TAXI) && player->GetTeamId() == TEAM_HORDE) player->ActivateTaxiPathTo(TAXI_PATH_ID_HORDE); break; case GOSSIP_ACTION_INFO_DEF + 2: @@ -80,29 +80,42 @@ public: bool OnGossipHello(Player* player, Creature* creature) override { - if (player->getClass() != CLASS_DRUID) + if (player->GetTeamId() != TEAM_HORDE) { - SendGossipMenuFor(player, 4916, creature->GetGUID()); - } - else if (player->GetTeamId() != TEAM_HORDE) - { - if (player->GetQuestStatus(QUEST_SEA_LION_ALLY) == QUEST_STATUS_INCOMPLETE) + if (player->IsClass(CLASS_DRUID, CLASS_CONTEXT_QUEST) && player->GetQuestStatus(QUEST_SEA_LION_ALLY) == QUEST_STATUS_INCOMPLETE) { AddGossipItemFor(player, 4042, 2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); } - SendGossipMenuFor(player, 4917, creature->GetGUID()); + if (player->IsClass(CLASS_DRUID)) + { + SendGossipMenuFor(player, 4917, creature->GetGUID()); + } + else + { + SendGossipMenuFor(player, 4916, creature->GetGUID()); + } } - else if (player->getClass() == CLASS_DRUID && player->GetTeamId() == TEAM_HORDE) + else if (player->GetTeamId() == TEAM_HORDE) { - AddGossipItemFor(player, 4042, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + if (player->IsClass(CLASS_DRUID, CLASS_CONTEXT_TAXI)) + { + AddGossipItemFor(player, 4042, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } - if (player->GetQuestStatus(QUEST_SEA_LION_HORDE) == QUEST_STATUS_INCOMPLETE) + if (player->IsClass(CLASS_DRUID, CLASS_CONTEXT_QUEST) && player->GetQuestStatus(QUEST_SEA_LION_HORDE) == QUEST_STATUS_INCOMPLETE) { AddGossipItemFor(player, 4042, 1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); } - SendGossipMenuFor(player, 4918, creature->GetGUID()); + if (player->IsClass(CLASS_DRUID)) + { + SendGossipMenuFor(player, 4918, creature->GetGUID()); + } + else + { + SendGossipMenuFor(player, 4916, creature->GetGUID()); + } } return true; } @@ -176,7 +189,7 @@ public: { case GOSSIP_ACTION_INFO_DEF + 1: CloseGossipMenuFor(player); - if (player->getClass() == CLASS_DRUID && player->GetTeamId() == TEAM_ALLIANCE) + if (player->IsClass(CLASS_DRUID, CLASS_CONTEXT_TAXI) && player->GetTeamId() == TEAM_ALLIANCE) player->ActivateTaxiPathTo(TAXI_PATH_ID_ALLY); break; case GOSSIP_ACTION_INFO_DEF + 2: @@ -191,29 +204,41 @@ public: bool OnGossipHello(Player* player, Creature* creature) override { - if (player->getClass() != CLASS_DRUID) + if (player->GetTeamId() != TEAM_ALLIANCE) { - SendGossipMenuFor(player, 4913, creature->GetGUID()); - } - else if (player->GetTeamId() != TEAM_ALLIANCE) - { - if (player->GetQuestStatus(QUEST_SEA_LION_HORDE) == QUEST_STATUS_INCOMPLETE) + if (player->IsClass(CLASS_DRUID, CLASS_CONTEXT_QUEST) && player->GetQuestStatus(QUEST_SEA_LION_HORDE) == QUEST_STATUS_INCOMPLETE) { AddGossipItemFor(player, 4041, 2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); } - - SendGossipMenuFor(player, 4915, creature->GetGUID()); + if (player->IsClass(CLASS_DRUID)) + { + SendGossipMenuFor(player, 4915, creature->GetGUID()); + } + else + { + SendGossipMenuFor(player, 4913, creature->GetGUID()); + } } - else if (player->getClass() == CLASS_DRUID && player->GetTeamId() == TEAM_ALLIANCE) + else if (player->GetTeamId() == TEAM_ALLIANCE) { - AddGossipItemFor(player, 4041, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + if (player->IsClass(CLASS_DRUID, CLASS_CONTEXT_TAXI)) + { + AddGossipItemFor(player, 4041, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } - if (player->GetQuestStatus(QUEST_SEA_LION_ALLY) == QUEST_STATUS_INCOMPLETE) + if (player->IsClass(CLASS_DRUID, CLASS_CONTEXT_QUEST) && player->GetQuestStatus(QUEST_SEA_LION_ALLY) == QUEST_STATUS_INCOMPLETE) { AddGossipItemFor(player, 4041, 1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); } - SendGossipMenuFor(player, 4914, creature->GetGUID()); + if (player->IsClass(CLASS_DRUID)) + { + SendGossipMenuFor(player, 4914, creature->GetGUID()); + } + else + { + SendGossipMenuFor(player, 4913, creature->GetGUID()); + } } return true; } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp index b1ab5028e..d21c9935d 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp @@ -853,12 +853,12 @@ public: { if (p->getPowerType() != POWER_MANA) return true; - if (p->getClass() == CLASS_HUNTER) + if (p->IsClass(CLASS_HUNTER)) return true; uint8 maxIndex = p->GetMostPointsTalentTree(); - if ((p->getClass() == CLASS_PALADIN && maxIndex >= 1) || (p->getClass() == CLASS_SHAMAN && maxIndex == 1) || (p->getClass() == CLASS_DRUID && maxIndex == 1)) + if ((p->IsClass(CLASS_PALADIN) && maxIndex >= 1) || (p->IsClass(CLASS_SHAMAN) && maxIndex == 1) || (p->IsClass(CLASS_DRUID) && maxIndex == 1)) return true; - if (_removeHealers == ((p->getClass() == CLASS_DRUID && maxIndex == 2) || (p->getClass() == CLASS_PALADIN && maxIndex == 0) || (p->getClass() == CLASS_PRIEST && maxIndex <= 1) || (p->getClass() == CLASS_SHAMAN && maxIndex == 2))) + if (_removeHealers == ((p->IsClass(CLASS_DRUID) && maxIndex == 2) || (p->IsClass(CLASS_PALADIN) && maxIndex == 0) || (p->IsClass(CLASS_PRIEST) && maxIndex <= 1) || (p->IsClass(CLASS_SHAMAN) && maxIndex == 2))) return true; return false; diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp index cc9516d3a..a8e4c3ce8 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp @@ -2856,8 +2856,7 @@ public: { c->AI()->AttackStart(target); DoZoneInCombat(c); - uint8 Class = target->getClass(); - if (Class != CLASS_DRUID) + if (!target->IsClass(CLASS_DRUID)) if (Player* p = target->ToPlayer()) { if (Item* i = p->GetWeaponForAttack(BASE_ATTACK)) @@ -2869,7 +2868,7 @@ public: target->CastSpell(c, 60352, true); // Mirror Image, clone visual appearance } - c->AI()->DoAction(Class); + c->AI()->DoAction(target->getClass()); } } } diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp index fb0061340..2f48c1338 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp @@ -57,7 +57,9 @@ enum Spells SPELL_SUMMON_SPOREBAT2 = 38490, SPELL_SUMMON_SPOREBAT3 = 38492, SPELL_SUMMON_SPOREBAT4 = 38493, - SPELL_TOXIC_SPORES = 38574 + SPELL_TOXIC_SPORES = 38574, + + SPELL_POISON_BOLT = 38253 }; enum Misc @@ -91,6 +93,8 @@ struct boss_lady_vashj : public BossAI ScheduleHealthCheckEvent(70, [&]{ Talk(SAY_PHASE2); + scheduler.CancelAll(); + me->CastStop(); me->SetReactState(REACT_PASSIVE); me->GetMotionMaster()->MovePoint(POINT_HOME, me->GetHomePosition().GetPositionX(), me->GetHomePosition().GetPositionY(), me->GetHomePosition().GetPositionZ(), true, true); }); @@ -178,7 +182,6 @@ struct boss_lady_vashj : public BossAI me->AddUnitState(UNIT_STATE_ROOT); me->SetFacingTo(me->GetHomePosition().GetOrientation()); instance->SetData(DATA_ACTIVATE_SHIELD, 0); - scheduler.CancelAll(); scheduler.Schedule(2400ms, [this](TaskContext context) { if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) @@ -271,6 +274,42 @@ private: std::chrono::seconds _batTimer; }; +struct npc_tainted_elemental : public ScriptedAI +{ + npc_tainted_elemental(Creature* creature) : ScriptedAI(creature) { } + + void Reset() override + { + scheduler.CancelAll(); + me->SetInCombatWithZone(); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + { + me->AddThreat(target, 1000.0f); + } + } + + void JustEngagedWith(Unit* /*who*/) override + { + scheduler.Schedule(100ms, 500ms, [this](TaskContext context) + { + DoCastVictim(SPELL_POISON_BOLT); + context.Repeat(2350ms, 2650ms); + }).Schedule(15s, [this](TaskContext) + { + me->DespawnOrUnsummon(); + }); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + scheduler.Update(diff); + } + +}; + class spell_lady_vashj_magic_barrier : public AuraScript { PrepareAuraScript(spell_lady_vashj_magic_barrier); @@ -410,6 +449,7 @@ class spell_lady_vashj_summons : public SpellScript void AddSC_boss_lady_vashj() { RegisterSerpentShrineAI(boss_lady_vashj); + RegisterSerpentShrineAI(npc_tainted_elemental); RegisterSpellScript(spell_lady_vashj_magic_barrier); RegisterSpellScript(spell_lady_vashj_remove_tainted_cores); RegisterSpellScript(spell_lady_vashj_summon_sporebat); diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp index 7264a07ae..e8e82b443 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp @@ -28,6 +28,7 @@ enum Spells SPELL_FLAME_QUILLS = 34229, SPELL_QUILL_MISSILE_1 = 34269, // 21 SPELL_QUILL_MISSILE_2 = 34314, // 3 + SPELL_CLEAR_ALL_DEBUFFS = 34098, SPELL_FLAME_BUFFET = 34121, SPELL_EMBER_BLAST = 34341, SPELL_REBIRTH_PHASE2 = 34342, @@ -38,15 +39,18 @@ enum Spells SPELL_DIVE_BOMB = 35181 }; -const Position alarPoints[7] = +// @todo: Alar doesnt seem to move to waypoints but instead to the triggers in p1 +const Position alarPoints[9] = { - {340.15f, 58.65f, 17.71f, 4.60f}, - {388.09f, 31.54f, 20.18f, 1.61f}, - {388.18f, -32.85f, 20.18f, 0.52f}, - {340.29f, -60.19f, 17.72f, 5.71f}, - {332.0f, 0.01f, 43.0f, 0.0f}, - {331.0f, 0.01f, -2.38f, 0.0f}, - {332.0f, 0.01f, 43.0f, 0.0f} + {335.638f, 59.4879f, 17.9319f, 4.60f}, //first platform + {388.751007f, 31.731199f, 20.263599f, 1.61f}, + {388.790985f, -33.105900f, 20.263599f, 0.52f}, + {332.722992f, -61.159f, 17.979099f, 5.71f}, + {258.959015f, -38.687099f, 20.262899f, 5.21f}, + {259.2277997, 35.879002f, 20.263f, 4.81f}, //sixth platform + {332.0f, 0.01f, 43.0f, 0.0f}, //quill + {331.0f, 0.01f, -2.38f, 0.0f}, //middle (p2) + {332.0f, 0.01f, 43.0f, 0.0f} // dive }; enum Misc @@ -56,26 +60,28 @@ enum Misc NPC_FLAME_PATCH = 20602, POINT_PLATFORM = 0, - POINT_QUILL = 4, - POINT_MIDDLE = 5, - POINT_DIVE = 6, + POINT_QUILL = 6, + POINT_MIDDLE = 7, + POINT_DIVE = 8, - EVENT_SWITCH_PLATFORM = 1, - EVENT_START_QUILLS = 2, - EVENT_RELOCATE_MIDDLE = 3, - EVENT_REBIRTH = 4, - EVENT_SPELL_MELT_ARMOR = 5, - EVENT_SPELL_FLAME_PATCH = 6, - EVENT_SPELL_CHARGE = 7, - EVENT_SPELL_DIVE_BOMB = 8, - EVENT_START_DIVE = 9, - EVENT_CAST_DIVE_BOMB = 10, - EVENT_SUMMON_DIVE_PHOENIX = 11, - EVENT_REBIRTH_DIVE = 12, - EVENT_SPELL_BERSERK = 13, + EVENT_RELOCATE_MIDDLE = 1, + EVENT_REBIRTH = 2, + EVENT_SPELL_BERSERK = 3, - EVENT_MOVE_TO_PHASE_2 = 20, - EVENT_FINISH_DIVE = 21 + EVENT_MOVE_TO_PHASE_2 = 4, + EVENT_FINISH_DIVE = 5 +}; + +enum PlatformMoveDirections +{ + DIRECTION_ANTI_CLOCKWISE = 0, + DIRECTION_CLOCKWISE = 1, + DIRECTION_ACROSS = 2 +}; + +enum GroupAlar +{ + GROUP_FLAME_BUFFET = 1 }; // Xinef: Ruse of the Ashtongue (10946) @@ -85,279 +91,284 @@ enum qruseoftheAshtongue QUEST_RUSE_OF_THE_ASHTONGUE = 10946, }; -class boss_alar : public CreatureScript +struct boss_alar : public BossAI { -public: - boss_alar() : CreatureScript("boss_alar") { } - struct boss_alarAI : public BossAI + boss_alar(Creature* creature) : BossAI(creature, DATA_ALAR) { - boss_alarAI(Creature* creature) : BossAI(creature, DATA_ALAR) - { - startPath = true; - SetCombatMovement(false); - } - - uint8 platform; - uint8 noQuillTimes; - bool startPath; - - void JustReachedHome() override - { - BossAI::JustReachedHome(); - startPath = true; - } - - void Reset() override - { - BossAI::Reset(); - platform = 0; - noQuillTimes = 0; - me->SetModelVisible(true); - me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_FIRE, true); - me->SetReactState(REACT_AGGRESSIVE); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - events.ScheduleEvent(EVENT_SWITCH_PLATFORM, 0); - } - - void JustDied(Unit* killer) override - { - me->SetModelVisible(true); - BossAI::JustDied(killer); - - // Xinef: Ruse of the Ashtongue (10946) - Map::PlayerList const& pl = me->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) - { - Player* player = itr->GetSource(); - if (player->GetQuestStatus(QUEST_RUSE_OF_THE_ASHTONGUE) == QUEST_STATUS_INCOMPLETE) - if (player->HasAura(SPELL_ASHTONGUE_RUSE)) - player->AreaExploredOrEventHappens(QUEST_RUSE_OF_THE_ASHTONGUE); - } - } - - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - if (summon->GetEntry() == NPC_EMBER_OF_ALAR) - summon->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_FIRE, true); - } - - void MoveInLineOfSight(Unit* /*who*/) override { } - - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (damage >= me->GetHealth() && platform < POINT_MIDDLE) - { - damage = 0; - if (events.GetNextEventTime(EVENT_REBIRTH) == 0) - { - me->InterruptNonMeleeSpells(false); - me->SetHealth(me->GetMaxHealth()); - me->SetReactState(REACT_PASSIVE); - me->CastSpell(me, SPELL_EMBER_BLAST, true); - - me->setAttackTimer(BASE_ATTACK, 16000); - events.Reset(); - events.ScheduleEvent(EVENT_RELOCATE_MIDDLE, 8000); - events.ScheduleEvent(EVENT_MOVE_TO_PHASE_2, 12000); - events.ScheduleEvent(EVENT_REBIRTH, 16001); - } - } - } - - void MovementInform(uint32 type, uint32 id) override - { - if (type != POINT_MOTION_TYPE) - { - if (type == ESCORT_MOTION_TYPE && me->movespline->Finalized() && !me->IsInCombat()) - startPath = true; - return; - } - - if (id == POINT_PLATFORM) - me->setAttackTimer(BASE_ATTACK, 1000); - else if (id == POINT_QUILL) - events.ScheduleEvent(EVENT_START_QUILLS, 1000); - else if (id == POINT_DIVE) - { - events.ScheduleEvent(EVENT_START_DIVE, 1000); - events.ScheduleEvent(EVENT_CAST_DIVE_BOMB, 5000); - } - } - - void UpdateAI(uint32 diff) override - { - if (startPath) - { - me->StopMoving(); - startPath = false; - if (WaypointPath const* i_path = sWaypointMgr->GetPath(me->GetWaypointPath())) - { - Movement::PointsArray pathPoints; - pathPoints.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); - for (uint8 i = 0; i < i_path->size(); ++i) - { - WaypointData const* node = i_path->at(i); - pathPoints.push_back(G3D::Vector3(node->x, node->y, node->z)); - } - me->GetMotionMaster()->MoveSplinePath(&pathPoints); - } - } - - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_SWITCH_PLATFORM: - if (roll_chance_i(20 * noQuillTimes)) - { - noQuillTimes = 0; - platform = RAND(0, 3); - me->GetMotionMaster()->MovePoint(POINT_QUILL, alarPoints[POINT_QUILL], false, true); - events.ScheduleEvent(EVENT_SWITCH_PLATFORM, 16000); - } - else - { - if (noQuillTimes++ > 0) - { - me->SetOrientation(alarPoints[platform].GetOrientation()); - me->SummonCreature(NPC_EMBER_OF_ALAR, *me, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000); - } - me->GetMotionMaster()->MovePoint(POINT_PLATFORM, alarPoints[platform], false, true); - platform = (platform + 1) % 4; - events.ScheduleEvent(EVENT_SWITCH_PLATFORM, 30000); - } - me->setAttackTimer(BASE_ATTACK, 20000); - break; - case EVENT_START_QUILLS: - me->CastSpell(me, SPELL_FLAME_QUILLS, false); - break; - case EVENT_RELOCATE_MIDDLE: - me->SetPosition(alarPoints[POINT_MIDDLE]); - break; - case EVENT_MOVE_TO_PHASE_2: - me->RemoveAurasDueToSpell(SPELL_EMBER_BLAST); - me->CastSpell(me, SPELL_REBIRTH_PHASE2, false); - break; - case EVENT_REBIRTH: - me->SetReactState(REACT_AGGRESSIVE); - platform = POINT_MIDDLE; - me->GetMotionMaster()->MoveChase(me->GetVictim()); - - events.ScheduleEvent(EVENT_SPELL_MELT_ARMOR, 67000); - events.ScheduleEvent(EVENT_SPELL_CHARGE, 10000); - events.ScheduleEvent(EVENT_SPELL_FLAME_PATCH, 20000); - events.ScheduleEvent(EVENT_SPELL_DIVE_BOMB, 30000); - break; - case EVENT_SPELL_MELT_ARMOR: - me->CastSpell(me->GetVictim(), SPELL_MELT_ARMOR, false); - events.ScheduleEvent(EVENT_SPELL_MELT_ARMOR, 60000); - break; - case EVENT_SPELL_CHARGE: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true)) - me->CastSpell(target, SPELL_CHARGE, false); - events.ScheduleEvent(EVENT_SPELL_CHARGE, 30000); - break; - case EVENT_SPELL_FLAME_PATCH: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true)) - me->SummonCreature(NPC_FLAME_PATCH, *target, TEMPSUMMON_TIMED_DESPAWN, 2 * MINUTE * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_SPELL_FLAME_PATCH, 30000); - break; - case EVENT_SPELL_DIVE_BOMB: - me->GetMotionMaster()->MovePoint(POINT_DIVE, alarPoints[POINT_DIVE], false, true); - events.ScheduleEvent(EVENT_SPELL_DIVE_BOMB, 30000); - events.DelayEvents(15000); - me->setAttackTimer(BASE_ATTACK, 20000); - break; - case EVENT_START_DIVE: - me->CastSpell(me, SPELL_DIVE_BOMB_VISUAL, false); - break; - case EVENT_CAST_DIVE_BOMB: - events.ScheduleEvent(EVENT_SUMMON_DIVE_PHOENIX, 2000); - events.ScheduleEvent(EVENT_REBIRTH_DIVE, 6000); - events.ScheduleEvent(EVENT_FINISH_DIVE, 10000); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 90.0f, true)) - { - me->CastSpell(target, SPELL_DIVE_BOMB, false); - me->SetPosition(*target); - me->StopMovingOnCurrentPos(); - } - - me->RemoveAurasDueToSpell(SPELL_DIVE_BOMB_VISUAL); - break; - case EVENT_SUMMON_DIVE_PHOENIX: - { - Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 10.0f, true); - me->SummonCreature(NPC_EMBER_OF_ALAR, target ? *target : *me, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000); - me->SummonCreature(NPC_EMBER_OF_ALAR, target ? *target : *me, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000); - break; - } - case EVENT_REBIRTH_DIVE: - me->SetModelVisible(true); - me->CastSpell(me, SPELL_REBIRTH_DIVE, false); - break; - case EVENT_FINISH_DIVE: - me->GetMotionMaster()->MoveChase(me->GetVictim()); - break; - case EVENT_SPELL_BERSERK: - me->CastSpell(me, SPELL_BERSERK, true); - break; - } - - if (me->isAttackReady()) - { - if (me->IsWithinMeleeRange(me->GetVictim())) - { - me->AttackerStateUpdate(me->GetVictim()); - me->resetAttackTimer(); - } - else - { - me->resetAttackTimer(); - ThreatContainer::StorageType const& threatList = me->GetThreatMgr().GetThreatList(); - for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr) - if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid())) - if (me->IsWithinMeleeRange(unit)) - { - me->AttackerStateUpdate(unit); - return; - } - - me->CastSpell(me, SPELL_FLAME_BUFFET, false); - } - } - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetTheEyeAI(creature); + SetCombatMovement(false); } + + void JustReachedHome() override + { + BossAI::JustReachedHome(); + if (me->IsEngaged()) + { + ConstructWaypointsAndMove(); + } + } + + void Reset() override + { + BossAI::Reset(); + _canAttackCooldown = true; + _baseAttackOverride = false; + _spawnPhoenixes = false; + _platform = 0; + _platformRoll = 0; + _noQuillTimes = 0; + _platformMoveRepeatTimer = 16s; + me->SetModelVisible(true); + me->SetReactState(REACT_AGGRESSIVE); + ConstructWaypointsAndMove(); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + ScheduleTimedEvent(0s, [&] + { + if (roll_chance_i(20 * _noQuillTimes)) + { + _noQuillTimes = 0; + _platform = RAND(0, 5); + me->GetMotionMaster()->MovePoint(POINT_QUILL, alarPoints[POINT_QUILL], false, true); + _platformMoveRepeatTimer = 16s; + } + else + { + if (_noQuillTimes++ > 0) + { + me->SetOrientation(alarPoints[_platform].GetOrientation()); + if (_spawnPhoenixes) + { + SpawnPhoenixes(3, me); + } + } + me->GetMotionMaster()->MovePoint(POINT_PLATFORM, alarPoints[_platform], false, true); + _platformRoll = RAND(0, 2); + switch(_platformRoll) + { + case DIRECTION_ANTI_CLOCKWISE: + _platform = (_platform+5)%6; + _spawnPhoenixes = false; + break; + case DIRECTION_CLOCKWISE: + _platform = (_platform+1)%6; + _spawnPhoenixes = false; + break; + case DIRECTION_ACROSS: + _platform = (_platform+3)%6; + _spawnPhoenixes = true; + break; + } + _platformMoveRepeatTimer = 30s; + } + }, _platformMoveRepeatTimer); + ScheduleMainSpellAttack(0s); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + me->SetModelVisible(true); + + if (Map* map = me->GetMap()) + { + map->DoForAllPlayers([&](Player* player) + { + if (player->GetQuestStatus(QUEST_RUSE_OF_THE_ASHTONGUE) == QUEST_STATUS_INCOMPLETE) + { + if (player->HasAura(SPELL_ASHTONGUE_RUSE)) + { + player->AreaExploredOrEventHappens(QUEST_RUSE_OF_THE_ASHTONGUE); + } + } + }); + } + } + + void MoveInLineOfSight(Unit* /*who*/) override { } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override + { + if (damage >= me->GetHealth() && _platform < POINT_MIDDLE) + { + damage = 0; + me->InterruptNonMeleeSpells(false); + me->SetHealth(me->GetMaxHealth()); + me->SetReactState(REACT_PASSIVE); + DoCastSelf(SPELL_CLEAR_ALL_DEBUFFS); + DoCastSelf(SPELL_EMBER_BLAST, true); + scheduler.CancelAll(); + ScheduleUniqueTimedEvent(8s, [&]{ + me->SetPosition(alarPoints[POINT_MIDDLE]); + }, EVENT_RELOCATE_MIDDLE); + ScheduleUniqueTimedEvent(12s, [&] + { + me->RemoveAurasDueToSpell(SPELL_EMBER_BLAST); + DoCastSelf(SPELL_REBIRTH_PHASE2); + }, EVENT_MOVE_TO_PHASE_2); + ScheduleUniqueTimedEvent(16001ms, [&]{ + me->SetReactState(REACT_AGGRESSIVE); + _platform = POINT_MIDDLE; + me->GetMotionMaster()->MoveChase(me->GetVictim()); + ScheduleAbilities(); + }, EVENT_REBIRTH); + + } + } + + void ScheduleAbilities() + { + ScheduleTimedEvent(57s, [&] + { + DoCastVictim(SPELL_MELT_ARMOR); + }, 60s); + ScheduleTimedEvent(10s, [&] + { + DoCastRandomTarget(SPELL_CHARGE, 0, 50.0f); + }, 30s); + ScheduleTimedEvent(20s, [&] + { + // find spell from sniffs? + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true)) + { + me->SummonCreature(NPC_FLAME_PATCH, *target, TEMPSUMMON_TIMED_DESPAWN, 2 * MINUTE * IN_MILLISECONDS); + } + }, 30s); + ScheduleTimedEvent(30s, [&] + { + me->GetMotionMaster()->MovePoint(POINT_DIVE, alarPoints[POINT_DIVE], false, true); + scheduler.DelayAll(15s); + }, 30s); + ScheduleUniqueTimedEvent(10min, [&] + { + DoCastSelf(SPELL_BERSERK); + }, EVENT_SPELL_BERSERK); + ScheduleMainSpellAttack(0s); + } + + void SpawnPhoenixes(uint8 count, Unit* targetToSpawnAt) + { + if (targetToSpawnAt) + { + for (uint8 i = 0; i < count; ++i) + { + me->SummonCreature(NPC_EMBER_OF_ALAR, *targetToSpawnAt, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000); + } + } + } + + void DoDiveBomb() + { + scheduler.Schedule(2s, [this](TaskContext) + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 10.0f, true)) + { + SpawnPhoenixes(2, target); + } + }).Schedule(6s, [this](TaskContext) + { + me->SetModelVisible(true); + DoCastSelf(SPELL_REBIRTH_DIVE); + }).Schedule(10s, [this](TaskContext) + { + me->GetMotionMaster()->MoveChase(me->GetVictim()); + }); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 90.0f, true)) + { + DoCast(target, SPELL_DIVE_BOMB); + me->SetPosition(*target); + me->StopMovingOnCurrentPos(); + } + me->RemoveAurasDueToSpell(SPELL_DIVE_BOMB_VISUAL); + + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type != POINT_MOTION_TYPE) + { + if (type == ESCORT_MOTION_TYPE && me->movespline->Finalized() && !me->IsInCombat()) + { + ConstructWaypointsAndMove(); + } + return; + } + + switch(id) + { + case POINT_QUILL: + scheduler.CancelGroup(GROUP_FLAME_BUFFET); + scheduler.Schedule(1s, [this](TaskContext) + { + DoCastSelf(SPELL_FLAME_QUILLS); + }); + ScheduleMainSpellAttack(13s); + break; + case POINT_DIVE: + scheduler.Schedule(1s, [this](TaskContext) + { + DoCastSelf(SPELL_DIVE_BOMB_VISUAL); + }).Schedule(5s, [this](TaskContext) + { + DoDiveBomb(); + }); + break; + default: + return; + } + } + + void ScheduleMainSpellAttack(std::chrono::seconds timer) + { + scheduler.Schedule(timer, GROUP_FLAME_BUFFET, [this](TaskContext context) + { + if (!me->IsWithinMeleeRange(me->GetVictim()) && !me->isMoving()) + { + DoCastVictim(SPELL_FLAME_BUFFET); + } + context.Repeat(2s); + }); + } + + void ConstructWaypointsAndMove() + { + me->StopMoving(); + if (WaypointPath const* i_path = sWaypointMgr->GetPath(me->GetWaypointPath())) + { + Movement::PointsArray pathPoints; + pathPoints.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); + for (uint8 i = 0; i < i_path->size(); ++i) + { + WaypointData const* node = i_path->at(i); + pathPoints.push_back(G3D::Vector3(node->x, node->y, node->z)); + } + me->GetMotionMaster()->MoveSplinePath(&pathPoints); + } + } + +private: + bool _canAttackCooldown; + bool _baseAttackOverride; + bool _spawnPhoenixes; + uint8 _platform; + uint8 _platformRoll; + uint8 _noQuillTimes; + std::chrono::seconds _platformMoveRepeatTimer; }; class CastQuill : public BasicEvent { public: - CastQuill(Unit* caster, uint32 spellId) : _caster(caster), _spellId(spellId) - { - } + CastQuill(Unit* caster, uint32 spellId) : _caster(caster), _spellId(spellId){ } bool Execute(uint64 /*execTime*/, uint32 /*diff*/) override { _caster->CastSpell(_caster, _spellId, true); return true; } - private: Unit* _caster; uint32 _spellId; @@ -496,7 +507,7 @@ public: void AddSC_boss_alar() { - new boss_alar(); + RegisterTheEyeAI(boss_alar); new spell_alar_flame_quills(); new spell_alar_ember_blast(); new spell_alar_ember_blast_death(); diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp index bbceb7b97..d04b9055b 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp @@ -68,6 +68,7 @@ struct boss_high_astromancer_solarian : public BossAI ScheduleHealthCheckEvent(20, [&]{ scheduler.CancelAll(); + me->ResumeChasingVictim(); scheduler.Schedule(3s, [this](TaskContext context) { DoCastVictim(SPELL_VOID_BOLT); @@ -109,10 +110,21 @@ struct boss_high_astromancer_solarian : public BossAI Talk(SAY_AGGRO); BossAI::JustEngagedWith(who); me->CallForHelp(105.0f); + me->GetMotionMaster()->Clear(); scheduler.Schedule(3650ms, [this](TaskContext context) { - DoCastRandomTarget(SPELL_ARCANE_MISSILES, 0, 40.0f); + me->GetMotionMaster()->Clear(); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40.0f, true)) + { + DoCast(target, SPELL_ARCANE_MISSILES); + } + else + { + //no targets in required range + me->GetMotionMaster()->MoveChase(me->GetVictim(), 30.0f); + me->CastStop(); + } context.Repeat(800ms, 7300ms); }).Schedule(21800ms, [this](TaskContext context) { diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp index 4971d5c70..4fa9f1a27 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp @@ -55,7 +55,7 @@ enum Yells enum Spells { - // Phase 2 spells + // _phase 2 spells SPELL_SUMMON_WEAPONS = 36976, SPELL_SUMMON_WEAPONA = 36958, SPELL_SUMMON_WEAPONB = 36959, @@ -65,10 +65,10 @@ enum Spells SPELL_SUMMON_WEAPONF = 36963, SPELL_SUMMON_WEAPONG = 36964, - // Phase 3 spells + // _phase 3 spells SPELL_RESURRECTION = 36450, - // Phase 4 spells + // _phase 4 spells SPELL_FIREBALL = 36805, SPELL_ARCANE_DISRUPTION = 36834, SPELL_PHOENIX = 36723, @@ -94,12 +94,17 @@ enum Spells SPELL_KEAL_STUNNED = 36185, SPELL_KAEL_FULL_POWER = 36187, SPELL_FLOATING_DROWNED = 36550, + SPELL_DARK_BANISH_STATE = 52241, // wrong visual apparently + SPELL_ARCANE_EXPLOSION_VISUAL = 34807, SPELL_PURE_NETHER_BEAM1 = 36196, SPELL_PURE_NETHER_BEAM2 = 36197, SPELL_PURE_NETHER_BEAM3 = 36198, + SPELL_PURE_NETHER_BEAM4 = 36201, + SPELL_PURE_NETHER_BEAM5 = 36290, + SPELL_PURE_NETHER_BEAM6 = 36291, - // Phase 5 spells + // _phase 5 spells SPELL_GRAVITY_LAPSE = 35941, SPELL_GRAVITY_LAPSE_TELEPORT1 = 35966, SPELL_GRAVITY_LAPSE_KNOCKBACK = 34480, @@ -197,6 +202,21 @@ enum Misc EVENT_SCENE_16 = 65 }; +enum KaelActions +{ + ACTION_START_SANGUINAR = 0, + ACTION_START_CAPERNIAN = 1, + ACTION_START_TELONICUS = 2, + ACTION_START_WEAPONS = 3 +}; + +enum SpellGroups +{ + GROUP_PYROBLAST = 0, + GROUP_SHOCK_BARRIER = 1, + GROUP_NETHER_BEAM = 2 +}; + const Position triggersPos[6] = { {799.11f, -38.95f, 85.0f, 0.0f}, @@ -207,549 +227,531 @@ const Position triggersPos[6] = {843.35f, 6.35f, 67.14f, 0.0f} }; -class boss_kaelthas : public CreatureScript +struct boss_kaelthas : public BossAI { -public: - boss_kaelthas() : CreatureScript("boss_kaelthas") { } + boss_kaelthas(Creature* creature) : BossAI(creature, DATA_KAELTHAS) { } - struct boss_kaelthasAI : public BossAI + void PrepareAdvisors() { - boss_kaelthasAI(Creature* creature) : BossAI(creature, DATA_KAELTHAS) { } - - uint8 phase; - EventMap events2; - - void PrepareAdvisors() + for (uint8 advisorData = DATA_THALADRED; advisorData <= DATA_TELONICUS; ++advisorData) { - for (uint8 i = DATA_KAEL_ADVISOR1; i <= DATA_KAEL_ADVISOR4; ++i) - if (Creature* advisor = ObjectAccessor::GetCreature(*me, instance->GetGuidData(i))) - { - advisor->Respawn(true); - advisor->StopMovingOnCurrentPos(); - advisor->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - advisor->SetReactState(REACT_PASSIVE); - summons.Summon(advisor); - } - } - - void SetData(uint32 type, uint32 data) override - { - if (type == DATA_RESURRECT_CAST && data == DATA_RESURRECT_CAST) + if (Creature* advisor = instance->GetCreature(advisorData)) { - for (SummonList::const_iterator i = summons.begin(); i != summons.end(); ++i) - if (Creature* summon = ObjectAccessor::GetCreature(*me, *i)) - if (summon->GetSpawnId()) - { - summon->SetReactState(REACT_PASSIVE); - summon->setDeathState(DeathState::JustRespawned); - summon->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - } + advisor->Respawn(true); + advisor->StopMovingOnCurrentPos(); + advisor->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + advisor->SetReactState(REACT_PASSIVE); + summons.Summon(advisor); } } + } - void SetRoomState(GOState state) + void SetData(uint32 type, uint32 data) override + { + if (type == DATA_RESURRECT_CAST && data == DATA_RESURRECT_CAST) { - if (GameObject* window = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(GO_BRIDGE_WINDOW))) - window->SetGoState(state); - if (GameObject* window = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(GO_KAEL_STATUE_RIGHT))) - window->SetGoState(state); - if (GameObject* window = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(GO_KAEL_STATUE_LEFT))) - window->SetGoState(state); + summons.DoForAllSummons([&](WorldObject* summon){ + if (Creature* summonedCreature = summon->ToCreature()) + { + summonedCreature->SetReactState(REACT_PASSIVE); + summonedCreature->setDeathState(DeathState::JustRespawned); + summonedCreature->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + } + }); } + } - void Reset() override + void SetRoomState(GOState state) + { + //TODO: handle door closing + if (GameObject* window = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(GO_BRIDGE_WINDOW))) + window->SetGoState(state); + if (GameObject* window = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(GO_KAEL_STATUE_RIGHT))) + window->SetGoState(state); + if (GameObject* window = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(GO_KAEL_STATUE_LEFT))) + window->SetGoState(state); + } + + void Reset() override + { + BossAI::Reset(); + scheduler.Schedule(1s, [this](TaskContext) { - BossAI::Reset(); - events2.Reset(); - events2.ScheduleEvent(EVENT_GATHER_ADVISORS, 1000); - phase = PHASE_NONE; + PrepareAdvisors(); + }); + _phase = PHASE_NONE; - me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_HOVER, true); - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); - SetRoomState(GO_STATE_READY); + me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_HOVER, true); + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + SetRoomState(GO_STATE_READY); + me->SetDisableGravity(false); + me->SetWalk(false); + ScheduleHealthCheckEvent(50, [&]{ + scheduler.CancelAll(); + me->CastStop(); + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + me->SetReactState(REACT_PASSIVE); + me->GetMotionMaster()->MovePoint(POINT_MIDDLE, me->GetHomePosition(), true, true); + me->ClearUnitState(UNIT_STATE_MELEE_ATTACKING); + me->SendMeleeAttackStop(); + }); + } + + void AttackStart(Unit* who) override + { + if (_phase == PHASE_FINAL /* check is scheduled&& events.GetNextEventTime(EVENT_GRAVITY_LAPSE_END) == 0*/) + BossAI::AttackStart(who); + } + + void MoveInLineOfSight(Unit* who) override + { + if (_phase == PHASE_NONE && who->GetTypeId() == TYPEID_PLAYER && me->IsValidAttackTarget(who)) + { + _phase = PHASE_SINGLE_ADVISOR; + me->SetInCombatWithZone(); + Talk(SAY_INTRO); + ScheduleUniqueTimedEvent(23s, [&] + { + Talk(SAY_INTRO_THALADRED); + }, EVENT_PREFIGHT_PHASE11); + ScheduleUniqueTimedEvent(30s, [&] + { + if (Creature* thaladred = summons.GetCreatureWithEntry(NPC_THALADRED)) + { + thaladred->SetReactState(REACT_AGGRESSIVE); + thaladred->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + thaladred->AI()->AttackStart(target); + thaladred->SetInCombatWithZone(); + } + }, EVENT_PREFIGHT_PHASE12); + } + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + } + + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + if (summon->GetEntry() == NPC_NETHER_VAPOR) + summon->GetMotionMaster()->MoveRandom(20.0f); + } + + void DoAction(int32 action) override + { + switch(action) + { + case ACTION_START_SANGUINAR: + IntroduceNewAdvisor(SAY_INTRO_SANGUINAR, ACTION_START_SANGUINAR); + break; + case ACTION_START_CAPERNIAN: + IntroduceNewAdvisor(SAY_INTRO_CAPERNIAN, ACTION_START_CAPERNIAN); + break; + case ACTION_START_TELONICUS: + IntroduceNewAdvisor(SAY_INTRO_TELONICUS, ACTION_START_TELONICUS); + break; + case ACTION_START_WEAPONS: + ScheduleUniqueTimedEvent(3s, [&]{ + Talk(SAY_PHASE2_WEAPON); + DoCastSelf(SPELL_SUMMON_WEAPONS); + _phase = PHASE_WEAPONS; + }, EVENT_PREFIGHT_PHASE51); + ScheduleUniqueTimedEvent(9s, [&]{ + summons.DoForAllSummons([&](WorldObject* summon) + { + if (Creature* summonedCreature = summon->ToCreature()) + { + if (!summonedCreature->GetSpawnId()) + { + summonedCreature->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + summonedCreature->SetInCombatWithZone(); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + { + summonedCreature->AI()->AttackStart(target); + } + } + } + }); + ScheduleUniqueTimedEvent(2min, [&]{ + PhaseAllAdvisorsExecute(); + }, EVENT_PREFIGHT_PHASE61); + }, EVENT_PREFIGHT_PHASE52); + break; + default: + break; + } + } + + void MovementInform(uint32 type, uint32 point) override + { + if (type != POINT_MOTION_TYPE) + return; + + if (point == POINT_MIDDLE) + { + ExecuteMiddleEvent(); + } + else if (point == POINT_START_LAST_PHASE) + { me->SetDisableGravity(false); me->SetWalk(false); - } - - void AttackStart(Unit* who) override - { - if (phase == PHASE_FINAL && events.GetNextEventTime(EVENT_GRAVITY_LAPSE_END) == 0) - BossAI::AttackStart(who); - } - - void MoveInLineOfSight(Unit* who) override - { - if (phase == PHASE_NONE && who->GetTypeId() == TYPEID_PLAYER && me->IsValidAttackTarget(who)) + me->RemoveAurasDueToSpell(SPELL_KAEL_FULL_POWER); + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + //re-set validator + scheduler.SetValidator([this]{ + return !me->HasUnitState(UNIT_STATE_CASTING); + }); + ScheduleTimedEvent(0ms, [&] { - phase = PHASE_SINGLE_ADVISOR; - me->SetInCombatWithZone(); - Talk(SAY_INTRO); - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE11, 23000); - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE12, 30000); - } - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - } - - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_SLAY); - } - - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - if (summon->GetEntry() == NPC_NETHER_VAPOR) - summon->GetMotionMaster()->MoveRandom(20.0f); - } - - void SummonedCreatureDies(Creature* summon, Unit*) override - { - if (phase == PHASE_FINAL) - return; - - if (summon->GetSpawnId() && phase == PHASE_ALL_ADVISORS) + DoCastVictim(SPELL_FIREBALL); + }, 2400ms, 7500ms); + ScheduleTimedEvent(10000ms, [&] { - for (SummonList::const_iterator i = summons.begin(); i != summons.end(); ++i) - if (Creature* summon = ObjectAccessor::GetCreature(*me, *i)) - if (summon->GetSpawnId() && summon->IsAlive()) - return; - - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE71, 2000); - return; - } - - if (summon->GetEntry() == NPC_THALADRED) + DoCastRandomTarget(SPELL_FLAME_STRIKE, 0, 100.0f); + }, 30250ms, 50650ms); + ScheduleTimedEvent(20000ms, [&] { - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE21, 2000); - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE22, 14500); - } - else if (summon->GetEntry() == NPC_LORD_SANGUINAR) + Talk(SAY_SUMMON_PHOENIX); + DoCastSelf(SPELL_PHOENIX); + }, 31450ms, 66550ms); + ScheduleTimedEvent(5s, [&] { - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE31, 2000); - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE32, 9000); - } - else if (summon->GetEntry() == NPC_CAPERNIAN) - { - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE41, 2000); - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE42, 10400); - } - else if (summon->GetEntry() == NPC_TELONICUS) - { - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE51, 3000); - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE52, 9000); - } - } - - void JustDied(Unit* killer) override - { - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - - Talk(SAY_DEATH); - BossAI::JustDied(killer); - } - - void MovementInform(uint32 type, uint32 point) override - { - if (type != POINT_MOTION_TYPE) - return; - - if (point == POINT_MIDDLE) - { - events2.ScheduleEvent(EVENT_SCENE_1, 0); - events2.ScheduleEvent(EVENT_SCENE_2, 2500); - events2.ScheduleEvent(EVENT_SCENE_3, 4000); - events2.ScheduleEvent(EVENT_SCENE_4, 7000); - events2.ScheduleEvent(EVENT_SCENE_5, 10000); - events2.ScheduleEvent(EVENT_SCENE_6, 14000); - events2.ScheduleEvent(EVENT_SCENE_7, 17500); - events2.ScheduleEvent(EVENT_SCENE_8, 19000); - events2.ScheduleEvent(EVENT_SCENE_9, 22000); // two first lightnings + aura - events2.ScheduleEvent(EVENT_SCENE_10, 22800); // two - events2.ScheduleEvent(EVENT_SCENE_11, 23600); // two - events2.ScheduleEvent(EVENT_SCENE_12, 24500); // two - events2.ScheduleEvent(EVENT_SCENE_13, 24800); // two - events2.ScheduleEvent(EVENT_SCENE_14, 25300); // two - events2.ScheduleEvent(EVENT_SCENE_15, 32000); // full power - events2.ScheduleEvent(EVENT_SCENE_16, 36000); // remove lightnings + aura, move down - } - else if (point == POINT_START_LAST_PHASE) - { - me->SetDisableGravity(false); - me->SetWalk(false); - me->RemoveAurasDueToSpell(SPELL_KAEL_FULL_POWER); - me->SetReactState(REACT_AGGRESSIVE); - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); - events.SetTimer(60000); - events.ScheduleEvent(EVENT_SPELL_FIREBALL, 0); - events.ScheduleEvent(EVENT_SPELL_FLAMESTRIKE, 10000); - events.ScheduleEvent(EVENT_SPELL_SUMMON_PHOENIX, 20000); - events.ScheduleEvent(EVENT_SPELL_GRAVITY_LAPSE, 5000); - if (me->GetVictim()) + scheduler.DelayAll(30s); + me->setAttackTimer(BASE_ATTACK, 30000); + DoCastSelf(SPELL_GRAVITY_LAPSE); + DoCastSelf(SPELL_SUMMON_NETHER_VAPOR); + scheduler.Schedule(4s, GROUP_NETHER_BEAM, [this](TaskContext context) + { + DoCastSelf(SPELL_NETHER_BEAM); + context.Repeat(4s); + }).Schedule(0s, GROUP_SHOCK_BARRIER, [this](TaskContext context) + { + DoCastSelf(SPELL_SHOCK_BARRIER); + context.Repeat(10s); + }).Schedule(20500ms, GROUP_SHOCK_BARRIER, [this](TaskContext) + { + scheduler.CancelGroup(GROUP_SHOCK_BARRIER); + }).Schedule(32s, [this](TaskContext) { - me->SetTarget(me->GetVictim()->GetGUID()); - AttackStart(me->GetVictim()); - } - } - } - - void UpdateAI(uint32 diff) override - { - events2.Update(diff); - switch (events2.ExecuteEvent()) - { - case EVENT_GATHER_ADVISORS: - PrepareAdvisors(); - break; - case EVENT_PREFIGHT_PHASE11: - Talk(SAY_INTRO_THALADRED); - break; - case EVENT_PREFIGHT_PHASE12: - if (Creature* advisor = summons.GetCreatureWithEntry(NPC_THALADRED)) - { - advisor->SetReactState(REACT_AGGRESSIVE); - advisor->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - advisor->AI()->AttackStart(target); - advisor->SetInCombatWithZone(); - advisor->AI()->Talk(SAY_THALADRED_AGGRO); - } - break; - case EVENT_PREFIGHT_PHASE21: - Talk(SAY_INTRO_SANGUINAR); - break; - case EVENT_PREFIGHT_PHASE22: - if (Creature* advisor = summons.GetCreatureWithEntry(NPC_LORD_SANGUINAR)) - { - advisor->SetReactState(REACT_AGGRESSIVE); - advisor->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - advisor->AI()->AttackStart(target); - advisor->SetInCombatWithZone(); - advisor->AI()->Talk(SAY_SANGUINAR_AGGRO); - } - break; - case EVENT_PREFIGHT_PHASE31: - Talk(SAY_INTRO_CAPERNIAN); - break; - case EVENT_PREFIGHT_PHASE32: - if (Creature* advisor = summons.GetCreatureWithEntry(NPC_CAPERNIAN)) - { - advisor->SetReactState(REACT_AGGRESSIVE); - advisor->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - advisor->AI()->AttackStart(target); - advisor->SetInCombatWithZone(); - advisor->AI()->Talk(SAY_CAPERNIAN_AGGRO); - } - break; - case EVENT_PREFIGHT_PHASE41: - Talk(SAY_INTRO_TELONICUS); - break; - case EVENT_PREFIGHT_PHASE42: - if (Creature* advisor = summons.GetCreatureWithEntry(NPC_TELONICUS)) - { - advisor->SetReactState(REACT_AGGRESSIVE); - advisor->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - advisor->AI()->AttackStart(target); - advisor->SetInCombatWithZone(); - advisor->AI()->Talk(SAY_TELONICUS_AGGRO); - } - break; - case EVENT_PREFIGHT_PHASE51: - Talk(SAY_PHASE2_WEAPON); - me->CastSpell(me, SPELL_SUMMON_WEAPONS, false); - phase = PHASE_WEAPONS; - break; - case EVENT_PREFIGHT_PHASE52: - for (SummonList::const_iterator i = summons.begin(); i != summons.end(); ++i) - { - if (Creature* summon = ObjectAccessor::GetCreature(*me, *i)) - if (!summon->GetSpawnId()) - { - summon->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); - summon->SetInCombatWithZone(); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - summon->AI()->AttackStart(target); - } - } - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE61, 2 * MINUTE * IN_MILLISECONDS); - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE62, 2 * MINUTE * IN_MILLISECONDS + 6000); - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE63, 2 * MINUTE * IN_MILLISECONDS + 12000); - break; - case EVENT_PREFIGHT_PHASE61: - phase = PHASE_ALL_ADVISORS; - Talk(SAY_PHASE3_ADVANCE); - break; - case EVENT_PREFIGHT_PHASE62: - me->CastSpell(me, SPELL_RESURRECTION, false); - break; - case EVENT_PREFIGHT_PHASE63: - for (SummonList::const_iterator i = summons.begin(); i != summons.end(); ++i) - if (Creature* summon = ObjectAccessor::GetCreature(*me, *i)) - if (summon->GetSpawnId()) - { - summon->SetReactState(REACT_AGGRESSIVE); - summon->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - summon->SetInCombatWithZone(); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - summon->AI()->AttackStart(target); - } - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE71, 3 * MINUTE * IN_MILLISECONDS); - break; - case EVENT_PREFIGHT_PHASE71: - events2.CancelEvent(EVENT_PREFIGHT_PHASE71); - Talk(SAY_PHASE4_INTRO2); - phase = PHASE_FINAL; - DoResetThreatList(); - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - AttackStart(target); - - events2.Reset(); - events.Reset(); - events.ScheduleEvent(EVENT_SPELL_FIREBALL, 1000); - events.ScheduleEvent(EVENT_SPELL_FLAMESTRIKE, 15000); - events.ScheduleEvent(EVENT_SPELL_SUMMON_PHOENIX, 30000); - events.ScheduleEvent(EVENT_SPELL_SEQ_1, 20000); - events.ScheduleEvent(EVENT_SPELL_SEQ_2, 40000); - events.ScheduleEvent(EVENT_SPELL_SEQ_3, 60000); - events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000); - break; - case EVENT_SCENE_1: - me->SetTarget(); - me->SetFacingTo(M_PI); - me->SetWalk(true); - Talk(SAY_PHASE5_NUTS); - break; - case EVENT_SCENE_2: - me->SetTarget(); - me->CastSpell(me, SPELL_KAEL_EXPLODES1, true); - me->CastSpell(me, SPELL_KAEL_GAINING_POWER, false); - me->SetDisableGravity(true); - break; - case EVENT_SCENE_3: - me->SetTarget(); - for (uint8 i = 0; i < 2; ++i) - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, triggersPos[i], TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_NETHERBEAM1 + i, false); - me->GetMotionMaster()->MovePoint(POINT_AIR, me->GetPositionX(), me->GetPositionY(), 76.0f, false, true); - me->CastSpell(me, SPELL_GROW, true); - break; - case EVENT_SCENE_4: - me->SetTarget(); - me->CastSpell(me, SPELL_GROW, true); - me->CastSpell(me, SPELL_KAEL_EXPLODES2, true); - me->CastSpell(me, SPELL_NETHERBEAM_AURA1, true); - for (uint8 i = 0; i < 2; ++i) - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, triggersPos[i + 2], TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_NETHERBEAM1 + i, false); - break; - case EVENT_SCENE_5: - me->SetTarget(); - me->CastSpell(me, SPELL_GROW, true); - me->CastSpell(me, SPELL_KAEL_EXPLODES3, true); - me->CastSpell(me, SPELL_NETHERBEAM_AURA2, true); - for (uint8 i = 0; i < 2; ++i) - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, triggersPos[i + 4], TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_NETHERBEAM1 + i, false); - break; - case EVENT_SCENE_6: - me->CastSpell(me, SPELL_GROW, true); - me->CastSpell(me, SPELL_KAEL_EXPLODES4, true); - me->CastSpell(me, SPELL_NETHERBEAM_AURA3, true); - break; - case EVENT_SCENE_7: - SetRoomState(GO_STATE_ACTIVE); - me->SetUnitMovementFlags(MOVEMENTFLAG_HOVER | MOVEMENTFLAG_WALKING | MOVEMENTFLAG_DISABLE_GRAVITY); - me->SendMovementFlagUpdate(); - break; - case EVENT_SCENE_8: - summons.DespawnEntry(WORLD_TRIGGER); - me->RemoveAurasDueToSpell(SPELL_NETHERBEAM_AURA1); - me->RemoveAurasDueToSpell(SPELL_NETHERBEAM_AURA2); - me->RemoveAurasDueToSpell(SPELL_NETHERBEAM_AURA3); - me->CastSpell(me, SPELL_KAEL_EXPLODES5, true); - me->CastSpell(me, SPELL_FLOATING_DROWNED, false); - //me->CastSpell(me, SPELL_KEAL_STUNNED, true); - break; - case EVENT_SCENE_9: - me->CastSpell(me, 52241, true); // WRONG VISUAL - me->CastSpell(me, 34807, true); - me->SummonCreature(NPC_WORLD_TRIGGER, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000); - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX() + 5, me->GetPositionY(), me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM1, true); - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX() - 5, me->GetPositionY(), me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM2, true); - break; - case EVENT_SCENE_10: - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX() - 5, me->GetPositionY() - 5, me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM3, true); - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX() + 5, me->GetPositionY() + 5, me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM1, true); - break; - case EVENT_SCENE_11: - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX(), me->GetPositionY() + 5, me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM2, true); - break; - case EVENT_SCENE_12: - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX(), me->GetPositionY() - 5, me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM3, true); - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX() + 5, me->GetPositionY() - 5, me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM1, true); - break; - case EVENT_SCENE_13: - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX() - 5, me->GetPositionY() + 5, me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM2, true); - break; - case EVENT_SCENE_14: - //if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX()-5, me->GetPositionY()+5, me->GetPositionZ()+15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) - // trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM3, true); - break; - case EVENT_SCENE_15: - me->RemoveAurasDueToSpell(SPELL_FLOATING_DROWNED); - me->RemoveAurasDueToSpell(SPELL_KEAL_STUNNED); - me->CastSpell(me, SPELL_KAEL_FULL_POWER, false); - me->CastSpell(me, 36709, true); - me->CastSpell(me, 36201, true); - me->CastSpell(me, 36290, true); - me->CastSpell(me, 36291, true); - me->SetUnitMovementFlags(MOVEMENTFLAG_DISABLE_GRAVITY | MOVEMENTFLAG_WALKING); - me->SendMovementFlagUpdate(); - break; - case EVENT_SCENE_16: - summons.DespawnEntry(WORLD_TRIGGER); - me->RemoveAurasDueToSpell(52241); // WRONG VISUAL - me->GetMotionMaster()->MovePoint(POINT_START_LAST_PHASE, me->GetHomePosition(), false, true); - break; - } - - if (!events2.Empty()) - return; - - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_SPELL_SEQ_1: - events.ScheduleEvent(EVENT_SPELL_MIND_CONTROL, 0); - events.ScheduleEvent(EVENT_SPELL_ARCANE_DISRUPTION, 3000); - events.ScheduleEvent(EVENT_SPELL_SEQ_1, 50000); - break; - case EVENT_SPELL_SEQ_2: - events.ScheduleEvent(EVENT_SPELL_MIND_CONTROL, 3000); - events.ScheduleEvent(EVENT_SPELL_ARCANE_DISRUPTION, 6000); - events.ScheduleEvent(EVENT_SPELL_SEQ_2, 50000); - break; - case EVENT_SPELL_SEQ_3: - Talk(SAY_PYROBLAST); - me->CastSpell(me, SPELL_SHOCK_BARRIER, false); - events.ScheduleEvent(EVENT_SPELL_SEQ_3, 50000); - events.DelayEvents(10000); - events.ScheduleEvent(EVENT_SPELL_PYROBLAST, 0); - events.ScheduleEvent(EVENT_SPELL_PYROBLAST, 4000); - events.ScheduleEvent(EVENT_SPELL_PYROBLAST, 8000); - break; - case EVENT_SPELL_SHOCK_BARRIER: - me->CastSpell(me, SPELL_SHOCK_BARRIER, false); - break; - case EVENT_SPELL_FIREBALL: - me->CastSpell(me->GetVictim(), SPELL_FIREBALL, false); - events.ScheduleEvent(EVENT_SPELL_FIREBALL, roll_chance_i(70) ? 2000 : 4000); - break; - case EVENT_SPELL_PYROBLAST: - me->CastSpell(me->GetVictim(), SPELL_PYROBLAST, false); - break; - case EVENT_SPELL_FLAMESTRIKE: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true)) - me->CastSpell(target, SPELL_FLAME_STRIKE, false); - events.ScheduleEvent(EVENT_SPELL_FLAMESTRIKE, 20000); - break; - case EVENT_SPELL_ARCANE_DISRUPTION: - me->CastSpell(me, SPELL_ARCANE_DISRUPTION, false); - break; - case EVENT_SPELL_MIND_CONTROL: - if (roll_chance_i(50)) - Talk(SAY_MINDCONTROL); - me->CastCustomSpell(SPELL_MIND_CONTROL, SPELLVALUE_MAX_TARGETS, 3, me, false); - break; - case EVENT_SPELL_SUMMON_PHOENIX: - Talk(SAY_SUMMON_PHOENIX); - me->CastSpell(me, SPELL_PHOENIX, false); - events.ScheduleEvent(EVENT_SPELL_SUMMON_PHOENIX, 40000); - break; - case EVENT_CHECK_HEALTH: - if (me->HealthBelowPct(51)) - { - events.Reset(); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - me->SetReactState(REACT_PASSIVE); - me->GetMotionMaster()->MovePoint(POINT_MIDDLE, me->GetHomePosition(), true, true); - me->ClearUnitState(UNIT_STATE_MELEE_ATTACKING); - me->SendMeleeAttackStop(); - break; - } - events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000); - break; - case EVENT_SPELL_GRAVITY_LAPSE: - events.DelayEvents(30000); - me->setAttackTimer(BASE_ATTACK, 30000); - events.ScheduleEvent(EVENT_SPELL_GRAVITY_LAPSE, 90000); - events.ScheduleEvent(EVENT_GRAVITY_LAPSE_END, 32000); - events.ScheduleEvent(EVENT_SPELL_SHOCK_BARRIER, 20000); - events.ScheduleEvent(EVENT_SPELL_SHOCK_BARRIER, 10000); - events.ScheduleEvent(EVENT_SPELL_NETHER_BEAM, 4000); - events.ScheduleEvent(EVENT_SPELL_NETHER_VAPOR, 0); - me->CastSpell(me, SPELL_SHOCK_BARRIER, false); - me->CastSpell(me, SPELL_GRAVITY_LAPSE, false); - me->SetTarget(); - me->GetMotionMaster()->Clear(); - me->StopMoving(); - Talk(SAY_GRAVITYLAPSE); - break; - case EVENT_SPELL_NETHER_VAPOR: - me->CastSpell(me, SPELL_SUMMON_NETHER_VAPOR, false); - break; - case EVENT_SPELL_NETHER_BEAM: - me->CastSpell(me, SPELL_NETHER_BEAM, false); - events.ScheduleEvent(EVENT_SPELL_NETHER_BEAM, 4000); - break; - case EVENT_GRAVITY_LAPSE_END: summons.DespawnEntry(NPC_NETHER_VAPOR); - events.CancelEvent(EVENT_SPELL_NETHER_BEAM); + scheduler.CancelGroup(GROUP_NETHER_BEAM); me->SetTarget(me->GetVictim()->GetGUID()); me->GetMotionMaster()->MoveChase(me->GetVictim()); - break; + }); + me->SetTarget(); + me->GetMotionMaster()->Clear(); + me->StopMoving(); + Talk(SAY_GRAVITYLAPSE); + }, 90s); + if (me->GetVictim()) + { + me->SetTarget(me->GetVictim()->GetGUID()); + AttackStart(me->GetVictim()); } - - DoMeleeAttackIfReady(); } - - bool CheckEvadeIfOutOfCombatArea() const override - { - return me->GetHomePosition().GetExactDist2d(me) > 165.0f || !SelectTargetFromPlayerList(165.0f); - } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetTheEyeAI(creature); } -}; + void ExecuteMiddleEvent() + { + scheduler.ClearValidator(); + me->SetTarget(); + me->SetFacingTo(M_PI); + me->SetWalk(true); + Talk(SAY_PHASE5_NUTS); + ScheduleUniqueTimedEvent(2500ms, [&] + { + me->SetTarget(); + DoCastSelf(SPELL_KAEL_EXPLODES1, true); + DoCastSelf(SPELL_KAEL_GAINING_POWER); + me->SetDisableGravity(true); + }, EVENT_SCENE_2); + ScheduleUniqueTimedEvent(4000ms, [&] + { + me->SetTarget(); + for (uint8 i = 0; i < 2; ++i) + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, triggersPos[i], TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_NETHERBEAM1 + i, false); + me->GetMotionMaster()->MovePoint(POINT_AIR, me->GetPositionX(), me->GetPositionY(), 76.0f, false, true); + DoCastSelf(SPELL_GROW, true); + }, EVENT_SCENE_3); + ScheduleUniqueTimedEvent(7000ms, [&] + { + me->SetTarget(); + DoCastSelf(SPELL_GROW, true); + DoCastSelf(SPELL_KAEL_EXPLODES2, true); + DoCastSelf(SPELL_NETHERBEAM_AURA1, true); + for (uint8 i = 0; i < 2; ++i) + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, triggersPos[i + 2], TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_NETHERBEAM1 + i, false); + }, EVENT_SCENE_4); + ScheduleUniqueTimedEvent(10000ms, [&] + { + me->SetTarget(); + DoCastSelf(SPELL_GROW, true); + DoCastSelf(SPELL_KAEL_EXPLODES3, true); + DoCastSelf(SPELL_NETHERBEAM_AURA2, true); + for (uint8 i = 0; i < 2; ++i) + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, triggersPos[i + 4], TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_NETHERBEAM1 + i, false); + }, EVENT_SCENE_5); + ScheduleUniqueTimedEvent(14000ms, [&] + { + DoCastSelf(SPELL_GROW, true); + DoCastSelf(SPELL_KAEL_EXPLODES4, true); + DoCastSelf(SPELL_NETHERBEAM_AURA3, true); + }, EVENT_SCENE_6); + ScheduleUniqueTimedEvent(17500ms, [&] + { + SetRoomState(GO_STATE_ACTIVE); + me->SetUnitMovementFlags(MOVEMENTFLAG_HOVER | MOVEMENTFLAG_WALKING | MOVEMENTFLAG_DISABLE_GRAVITY); + me->SendMovementFlagUpdate(); + }, EVENT_SCENE_7); + ScheduleUniqueTimedEvent(19000ms, [&] + { + summons.DespawnEntry(WORLD_TRIGGER); + me->RemoveAurasDueToSpell(SPELL_NETHERBEAM_AURA1); + me->RemoveAurasDueToSpell(SPELL_NETHERBEAM_AURA2); + me->RemoveAurasDueToSpell(SPELL_NETHERBEAM_AURA3); + DoCastSelf(SPELL_KAEL_EXPLODES5, true); + DoCastSelf(SPELL_FLOATING_DROWNED); + //me->CastSpell(me, SPELL_KEAL_STUNNED, true); + }, EVENT_SCENE_8); + ScheduleUniqueTimedEvent(22000ms, [&] + { + DoCastSelf(SPELL_DARK_BANISH_STATE, true); + DoCastSelf(SPELL_ARCANE_EXPLOSION_VISUAL, true); + me->SummonCreature(NPC_WORLD_TRIGGER, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000); + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX() + 5, me->GetPositionY(), me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM1, true); + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX() - 5, me->GetPositionY(), me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM2, true); + }, EVENT_SCENE_9); + ScheduleUniqueTimedEvent(22800ms, [&] + { + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX() - 5, me->GetPositionY() - 5, me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM3, true); + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX() + 5, me->GetPositionY() + 5, me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM1, true); + }, EVENT_SCENE_10); + ScheduleUniqueTimedEvent(23600ms, [&] + { + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX(), me->GetPositionY() + 5, me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM2, true); + }, EVENT_SCENE_11); + ScheduleUniqueTimedEvent(24500ms, [&] + { + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX(), me->GetPositionY() - 5, me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM3, true); + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX() + 5, me->GetPositionY() - 5, me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM1, true); + }, EVENT_SCENE_12); + ScheduleUniqueTimedEvent(24800ms, [&] + { + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX() - 5, me->GetPositionY() + 5, me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM2, true); + }, EVENT_SCENE_13); + ScheduleUniqueTimedEvent(25300ms, [&] + { + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX()-5, me->GetPositionY()+5, me->GetPositionZ()+15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM3, true); + }, EVENT_SCENE_14); + ScheduleUniqueTimedEvent(32000ms, [&] + { + me->RemoveAurasDueToSpell(SPELL_FLOATING_DROWNED); + me->RemoveAurasDueToSpell(SPELL_KEAL_STUNNED); + DoCastSelf(SPELL_KAEL_FULL_POWER); + DoCastSelf(SPELL_KAEL_PHASE_TWO, true); + DoCastSelf(SPELL_PURE_NETHER_BEAM4, true); + DoCastSelf(SPELL_PURE_NETHER_BEAM5, true); + DoCastSelf(SPELL_PURE_NETHER_BEAM6, true); + me->SetUnitMovementFlags(MOVEMENTFLAG_DISABLE_GRAVITY | MOVEMENTFLAG_WALKING); + me->SendMovementFlagUpdate(); + }, EVENT_SCENE_15); + ScheduleUniqueTimedEvent(36000ms, [&] + { + summons.DespawnEntry(WORLD_TRIGGER); + me->CastStop(); + me->GetMotionMaster()->Clear(); + me->RemoveAurasDueToSpell(SPELL_DARK_BANISH_STATE); // WRONG VISUAL + me->GetMotionMaster()->MovePoint(POINT_START_LAST_PHASE, me->GetHomePosition(), false, true); + }, EVENT_SCENE_16); + } + + void IntroduceNewAdvisor(Yells talkIntroduction, KaelActions kaelAction) + { + std::chrono::milliseconds attackStartTimer = 0ms; + EyeNPCs advisorNPCId = NPC_THALADRED; + scheduler.Schedule(2s, [this, talkIntroduction](TaskContext) + { + Talk(talkIntroduction); + }); + //switch because talk times are different + switch(kaelAction) + { + case ACTION_START_SANGUINAR: + attackStartTimer = 14500ms; + advisorNPCId = NPC_LORD_SANGUINAR; + break; + case ACTION_START_CAPERNIAN: + attackStartTimer = 9000ms; + advisorNPCId = NPC_CAPERNIAN; + break; + case ACTION_START_TELONICUS: + attackStartTimer = 10400ms; + advisorNPCId = NPC_TELONICUS; + break; + default: + break; + } + scheduler.Schedule(attackStartTimer, [this, advisorNPCId](TaskContext) + { + if (Creature* advisor = summons.GetCreatureWithEntry(advisorNPCId)) + { + advisor->SetReactState(REACT_AGGRESSIVE); + advisor->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + advisor->AI()->AttackStart(target); + advisor->SetInCombatWithZone(); + } + }); + } + + void PhaseAllAdvisorsExecute() + { + //remove all weapons so they don't get revived + summons.DoForAllSummons([&](WorldObject* summon) + { + if (Creature* summonedCreature = summon->ToCreature()) + { + if (summonedCreature->GetEntry() >= 21268 && summonedCreature->GetEntry() <= 21274) + { + summonedCreature->DespawnOrUnsummon(); + } + } + }); + _phase = PHASE_ALL_ADVISORS; + Talk(SAY_PHASE3_ADVANCE); + ScheduleUniqueTimedEvent(6s, [&]{ + DoCastSelf(SPELL_RESURRECTION); + }, EVENT_PREFIGHT_PHASE62); + ScheduleUniqueTimedEvent(12s, [&]{ + summons.DoForAllSummons([&](WorldObject* summon) + { + if (Creature* summonedCreature = summon->ToCreature()) + { + if (summonedCreature->GetSpawnId()) + { + summonedCreature->SetReactState(REACT_AGGRESSIVE); + summonedCreature->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + summonedCreature->SetInCombatWithZone(); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + { + summonedCreature->AI()->AttackStart(target); + } + } + } + }); + ScheduleUniqueTimedEvent(3min, [&]{ + PhaseKaelExecute(); + }, EVENT_PREFIGHT_PHASE71); + }, EVENT_PREFIGHT_PHASE63); + } + + void PhaseKaelExecute() + { + scheduler.CancelAll(); + Talk(SAY_PHASE4_INTRO2); + _phase = PHASE_FINAL; + DoResetThreatList(); + me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + { + AttackStart(target); + } + ScheduleTimedEvent(1000ms, [&] + { + DoCastVictim(SPELL_FIREBALL); + }, 2400ms, 7500ms); + ScheduleTimedEvent(15000ms, [&] + { + DoCastRandomTarget(SPELL_FLAME_STRIKE, 0, 100.0f); + }, 30250ms, 50650ms); + ScheduleTimedEvent(30000ms, [&] + { + Talk(SAY_SUMMON_PHOENIX); + DoCastSelf(SPELL_PHOENIX); + }, 31450ms, 66550ms); + //sequence + ScheduleTimedEvent(20s, [&] + { + if (roll_chance_i(50)) + Talk(SAY_MINDCONTROL); + me->CastCustomSpell(SPELL_MIND_CONTROL, SPELLVALUE_MAX_TARGETS, 3, me, false); + scheduler.Schedule(3s, [this](TaskContext) + { + DoCastSelf(SPELL_ARCANE_DISRUPTION); + }); + }, 50s); + ScheduleTimedEvent(40s, [&] + { + scheduler.Schedule(3s, [this](TaskContext) + { + if (roll_chance_i(50)) + Talk(SAY_MINDCONTROL); + me->CastCustomSpell(SPELL_MIND_CONTROL, SPELLVALUE_MAX_TARGETS, 3, me, false); + }).Schedule(6s, [this](TaskContext) + { + DoCastSelf(SPELL_ARCANE_DISRUPTION); + }); + }, 50s); + ScheduleTimedEvent(60s, [&] + { + Talk(SAY_PYROBLAST); + DoCastSelf(SPELL_SHOCK_BARRIER); + scheduler.DelayAll(10s); + scheduler.Schedule(0s, GROUP_PYROBLAST, [this](TaskContext context) + { + DoCastVictim(SPELL_PYROBLAST); + context.Repeat(4s); + }).Schedule(8500ms, GROUP_PYROBLAST, [this](TaskContext) + { + scheduler.CancelGroup(GROUP_PYROBLAST); + }); + }, 50s); + } + + void UpdateAI(uint32 diff) override + { + scheduler.Update(diff); + + if (!UpdateVictim()) + return; + + DoMeleeAttackIfReady(); + } + + bool CheckEvadeIfOutOfCombatArea() const override + { + return me->GetHomePosition().GetExactDist2d(me) > 165.0f || !SelectTargetFromPlayerList(165.0f); + } +private: + uint32 _phase; +}; struct npc_lord_sanguinar : public ScriptedAI { npc_lord_sanguinar(Creature* creature) : ScriptedAI(creature) { + _instance = creature->GetInstanceScript(); scheduler.SetValidator([this] { return !me->HasUnitState(UNIT_STATE_CASTING); @@ -759,10 +761,16 @@ struct npc_lord_sanguinar : public ScriptedAI void Reset() override { scheduler.CancelAll(); + _hasDied = false; + me->SetReactState(REACT_PASSIVE); } void JustEngagedWith(Unit* /*who*/) override { + if (!_hasDied) + { + Talk(SAY_SANGUINAR_AGGRO); + } ScheduleTimedEvent(0s, [&]{ DoCastSelf(SPELL_BELLOWING_ROAR); }, 15s); @@ -770,8 +778,16 @@ struct npc_lord_sanguinar : public ScriptedAI void JustDied(Unit* /*killer*/) override { - Talk(SAY_SANGUINAR_DEATH); - DoCastSelf(SPELL_KAEL_PHASE_TWO, true); + if (!_hasDied) + { + Talk(SAY_SANGUINAR_DEATH); + DoCastSelf(SPELL_KAEL_PHASE_TWO, true); + if (Creature* kael = _instance->GetCreature(DATA_KAELTHAS)) + { + kael->AI()->DoAction(ACTION_START_CAPERNIAN); + } + _hasDied = true; + } } void UpdateAI(uint32 diff) override @@ -786,11 +802,15 @@ struct npc_lord_sanguinar : public ScriptedAI DoMeleeAttackIfReady(); } +private: + InstanceScript* _instance; + bool _hasDied; }; struct npc_capernian : public ScriptedAI { npc_capernian(Creature* creature) : ScriptedAI(creature) { + _instance = creature->GetInstanceScript(); scheduler.SetValidator([this] { return !me->HasUnitState(UNIT_STATE_CASTING); @@ -800,10 +820,16 @@ struct npc_capernian : public ScriptedAI void Reset() override { scheduler.CancelAll(); + _hasDied = false; + me->SetReactState(REACT_PASSIVE); } void JustEngagedWith(Unit* /*who*/) override { + if (!_hasDied) + { + Talk(SAY_CAPERNIAN_AGGRO); + } ScheduleTimedEvent(0ms, [&]{ DoCastVictim(SPELL_CAPERNIAN_FIREBALL); }, 2500ms); @@ -817,8 +843,16 @@ struct npc_capernian : public ScriptedAI void JustDied(Unit* /*killer*/) override { - Talk(SAY_CAPERNIAN_DEATH); - DoCastSelf(SPELL_KAEL_PHASE_TWO, true); + if (!_hasDied) + { + Talk(SAY_CAPERNIAN_DEATH); + DoCastSelf(SPELL_KAEL_PHASE_TWO, true); + if (Creature* kael = _instance->GetCreature(DATA_KAELTHAS)) + { + kael->AI()->DoAction(ACTION_START_TELONICUS); + } + _hasDied = true; + } } void UpdateAI(uint32 diff) override @@ -833,11 +867,15 @@ struct npc_capernian : public ScriptedAI DoMeleeAttackIfReady(); } +private: + InstanceScript* _instance; + bool _hasDied; }; struct npc_telonicus : public ScriptedAI { npc_telonicus(Creature* creature) : ScriptedAI(creature) { + _instance = creature->GetInstanceScript(); scheduler.SetValidator([this] { return !me->HasUnitState(UNIT_STATE_CASTING); @@ -847,10 +885,16 @@ struct npc_telonicus : public ScriptedAI void Reset() override { scheduler.CancelAll(); + _hasDied = false; + me->SetReactState(REACT_PASSIVE); } void JustEngagedWith(Unit* /*who*/) override { + if (!_hasDied) + { + Talk(SAY_TELONICUS_AGGRO); + } ScheduleTimedEvent(0ms, [&]{ DoCastVictim(SPELL_BOMB); }, 3600ms, 7100ms); @@ -861,8 +905,16 @@ struct npc_telonicus : public ScriptedAI void JustDied(Unit* /*killer*/) override { - Talk(SAY_TELONICUS_DEATH); - DoCastSelf(SPELL_KAEL_PHASE_TWO, true); + if (!_hasDied) + { + Talk(SAY_TELONICUS_DEATH); + DoCastSelf(SPELL_KAEL_PHASE_TWO, true); + if (Creature* kael = _instance->GetCreature(DATA_KAELTHAS)) + { + kael->AI()->DoAction(ACTION_START_WEAPONS); + } + _hasDied = true; + } } void UpdateAI(uint32 diff) override @@ -877,11 +929,15 @@ struct npc_telonicus : public ScriptedAI DoMeleeAttackIfReady(); } +private: + InstanceScript* _instance; + bool _hasDied; }; struct npc_thaladred : public ScriptedAI { npc_thaladred(Creature* creature) : ScriptedAI(creature) { + _instance = creature->GetInstanceScript(); scheduler.SetValidator([this] { return !me->HasUnitState(UNIT_STATE_CASTING); @@ -891,11 +947,17 @@ struct npc_thaladred : public ScriptedAI void Reset() override { scheduler.CancelAll(); + me->SetReactState(REACT_PASSIVE); + _hasDied = false; me->SetWalk(true); } void JustEngagedWith(Unit* /*who*/) override { + if (!_hasDied) + { + Talk(SAY_THALADRED_AGGRO); + } ScheduleTimedEvent(100ms, [&] { DoResetThreatList(); @@ -927,8 +989,16 @@ struct npc_thaladred : public ScriptedAI void JustDied(Unit* /*killer*/) override { - Talk(SAY_THALADRED_DEATH); - DoCastSelf(SPELL_KAEL_PHASE_TWO, true); + if (!_hasDied) + { + Talk(SAY_THALADRED_DEATH); + DoCastSelf(SPELL_KAEL_PHASE_TWO, true); + if (Creature* kael = _instance->GetCreature(DATA_KAELTHAS)) + { + kael->AI()->DoAction(ACTION_START_SANGUINAR); + } + _hasDied = true; + } } void UpdateAI(uint32 diff) override @@ -943,6 +1013,9 @@ struct npc_thaladred : public ScriptedAI DoMeleeAttackIfReady(); } +private: + InstanceScript* _instance; + bool _hasDied; }; class spell_kaelthas_kael_phase_two : public SpellScriptLoader @@ -1275,7 +1348,7 @@ public: void AddSC_boss_kaelthas() { - new boss_kaelthas(); + RegisterTheEyeAI(boss_kaelthas); RegisterTheEyeAI(npc_lord_sanguinar); RegisterTheEyeAI(npc_capernian); RegisterTheEyeAI(npc_telonicus); diff --git a/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp b/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp index 8b1ce688b..c02375e37 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp @@ -20,6 +20,21 @@ #include "SpellScriptLoader.h" #include "the_eye.h" +ObjectData const creatureData[] = +{ + { NPC_KAELTHAS, DATA_KAELTHAS }, + { NPC_THALADRED, DATA_THALADRED }, + { NPC_LORD_SANGUINAR, DATA_LORD_SANGUINAR }, + { NPC_CAPERNIAN, DATA_CAPERNIAN }, + { NPC_TELONICUS, DATA_TELONICUS }, + { 0, 0 } +}; + +ObjectData const gameObjectData[] = +{ + { 0, 0 } +}; + class instance_the_eye : public InstanceMapScript { public: @@ -30,6 +45,7 @@ public: instance_the_eye_InstanceMapScript(Map* map) : InstanceScript(map) { SetHeaders(DataHeader); + LoadObjectData(creatureData, gameObjectData); SetBossNumber(MAX_ENCOUNTER); } @@ -66,6 +82,7 @@ public: LordSanguinarGUID = creature->GetGUID(); break; } + InstanceScript::OnCreatureCreate(creature); } void OnGameObjectCreate(GameObject* gobject) override @@ -98,14 +115,6 @@ public: return AlarGUID; case NPC_KAELTHAS: return KaelthasGUID; - case DATA_KAEL_ADVISOR1: - return ThaladredTheDarkenerGUID; - case DATA_KAEL_ADVISOR2: - return LordSanguinarGUID; - case DATA_KAEL_ADVISOR3: - return GrandAstromancerCapernianGUID; - case DATA_KAEL_ADVISOR4: - return MasterEngineerTelonicusGUID; } return ObjectGuid::Empty; diff --git a/src/server/scripts/Outland/TempestKeep/Eye/the_eye.h b/src/server/scripts/Outland/TempestKeep/Eye/the_eye.h index 5961fcc2d..f66fceb71 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/the_eye.h +++ b/src/server/scripts/Outland/TempestKeep/Eye/the_eye.h @@ -36,10 +36,10 @@ enum EyeData DATA_KAELTHAS = 3, MAX_ENCOUNTER = 4, - DATA_KAEL_ADVISOR1 = 10, - DATA_KAEL_ADVISOR2 = 11, - DATA_KAEL_ADVISOR3 = 12, - DATA_KAEL_ADVISOR4 = 13 + DATA_THALADRED = 10, + DATA_LORD_SANGUINAR = 11, + DATA_CAPERNIAN = 12, + DATA_TELONICUS = 13 }; enum EyeNPCs diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index 88a938c40..b8f95822c 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -1014,7 +1014,7 @@ class spell_dk_blood_boil : public SpellScript bool Load() override { _executed = false; - return GetCaster()->GetTypeId() == TYPEID_PLAYER && GetCaster()->getClass() == CLASS_DEATH_KNIGHT; + return GetCaster()->GetTypeId() == TYPEID_PLAYER && GetCaster()->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY); } void HandleAfterHit() @@ -1258,7 +1258,7 @@ class spell_dk_death_gate : public SpellScript SpellCastResult CheckClass() { - if (GetCaster()->getClass() != CLASS_DEATH_KNIGHT) + if (!GetCaster()->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) { SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_MUST_BE_DEATH_KNIGHT); return SPELL_FAILED_CUSTOM_ERROR; diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 6688c5e31..e8cf44042 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -446,7 +446,7 @@ class spell_pet_hit_expertise_scalling : public AuraScript { if (Player* modOwner = GetUnitOwner()->GetSpellModOwner()) { - if (modOwner->getClass() == CLASS_HUNTER) + if (modOwner->IsClass(CLASS_HUNTER, CLASS_CONTEXT_STATS)) amount = CalculatePercent(modOwner->m_modRangedHitChance, 8.0f, 8.0f); else if (modOwner->getPowerType() == POWER_MANA) amount = CalculatePercent(modOwner->m_modSpellHitChance, 17.0f, 8.0f); @@ -459,7 +459,7 @@ class spell_pet_hit_expertise_scalling : public AuraScript { if (Player* modOwner = GetUnitOwner()->GetSpellModOwner()) { - if (modOwner->getClass() == CLASS_HUNTER) + if (modOwner->IsClass(CLASS_HUNTER, CLASS_CONTEXT_STATS)) amount = CalculatePercent(modOwner->m_modRangedHitChance, 8.0f, 17.0f); else if (modOwner->getPowerType() == POWER_MANA) amount = CalculatePercent(modOwner->m_modSpellHitChance, 17.0f, 17.0f); @@ -472,7 +472,7 @@ class spell_pet_hit_expertise_scalling : public AuraScript { if (Player* modOwner = GetUnitOwner()->GetSpellModOwner()) { - if (modOwner->getClass() == CLASS_HUNTER) + if (modOwner->IsClass(CLASS_HUNTER, CLASS_CONTEXT_STATS)) amount = CalculatePercent(modOwner->m_modRangedHitChance, 8.0f, 26.0f); else if (modOwner->getPowerType() == POWER_MANA) amount = CalculatePercent(modOwner->m_modSpellHitChance, 17.0f, 26.0f); @@ -1673,7 +1673,7 @@ class spell_gen_pet_summoned : public SpellScript Player* player = GetCaster()->ToPlayer(); if (player->GetLastPetNumber() && player->CanResummonPet(player->GetLastPetSpell())) { - PetType newPetType = (player->getClass() == CLASS_HUNTER) ? HUNTER_PET : SUMMON_PET; + PetType newPetType = (player->IsClass(CLASS_HUNTER, CLASS_CONTEXT_PET)) ? HUNTER_PET : SUMMON_PET; Pet* newPet = new Pet(player, newPetType); if (newPet->LoadPetFromDB(player, 0, player->GetLastPetNumber(), true, 100)) { @@ -3545,11 +3545,11 @@ class spell_gen_on_tournament_mount : public AuraScript case NPC_ARGENT_WARHORSE: { if (player->HasAchieved(ACHIEVEMENT_CHAMPION_ALLIANCE) || player->HasAchieved(ACHIEVEMENT_CHAMPION_HORDE)) - return player->getClass() == CLASS_DEATH_KNIGHT ? SPELL_PENNANT_EBON_BLADE_CHAMPION : SPELL_PENNANT_ARGENT_CRUSADE_CHAMPION; + return player->IsClass(CLASS_DEATH_KNIGHT) ? SPELL_PENNANT_EBON_BLADE_CHAMPION : SPELL_PENNANT_ARGENT_CRUSADE_CHAMPION; else if (player->HasAchieved(ACHIEVEMENT_ARGENT_VALOR)) - return player->getClass() == CLASS_DEATH_KNIGHT ? SPELL_PENNANT_EBON_BLADE_VALIANT : SPELL_PENNANT_ARGENT_CRUSADE_VALIANT; + return player->IsClass(CLASS_DEATH_KNIGHT) ? SPELL_PENNANT_EBON_BLADE_VALIANT : SPELL_PENNANT_ARGENT_CRUSADE_VALIANT; else - return player->getClass() == CLASS_DEATH_KNIGHT ? SPELL_PENNANT_EBON_BLADE_ASPIRANT : SPELL_PENNANT_ARGENT_CRUSADE_ASPIRANT; + return player->IsClass(CLASS_DEATH_KNIGHT) ? SPELL_PENNANT_EBON_BLADE_ASPIRANT : SPELL_PENNANT_ARGENT_CRUSADE_ASPIRANT; } default: return 0; diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index 6feb5f6c3..d0c6880b5 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -576,7 +576,7 @@ class spell_item_skull_of_impeding_doom : public AuraScript void CalculateManaLeechAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) { - if (!GetCaster() || GetCaster()->getPowerType() != POWER_MANA) + if (!GetCaster() || !GetCaster()->HasActivePowerType(POWER_MANA)) return; amount = GetCaster()->GetMaxPower(POWER_MANA) * 0.12f; // 5 ticks which reduce health by 60% @@ -3469,7 +3469,7 @@ class spell_item_refocus : public SpellScript { Player* caster = GetCaster()->ToPlayer(); - if (!caster || caster->getClass() != CLASS_HUNTER) + if (!caster || !caster->IsClass(CLASS_HUNTER, CLASS_CONTEXT_ABILITY)) return; caster->RemoveCategoryCooldown(SPELL_CATEGORY_AIMED_MULTI); diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp index 0cd83b638..4793489fe 100644 --- a/src/server/scripts/Spells/spell_paladin.cpp +++ b/src/server/scripts/Spells/spell_paladin.cpp @@ -483,7 +483,7 @@ class spell_pal_blessing_of_sanctuary : public AuraScript bool CheckProc(ProcEventInfo& /*eventInfo*/) { - return GetTarget()->getPowerType() == POWER_MANA; + return GetTarget()->HasActivePowerType(POWER_MANA); } void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) @@ -969,7 +969,7 @@ class spell_pal_lay_on_hands : public SpellScript // Xinef: Glyph of Divinity if (Unit* target = GetExplTargetUnit()) - if (target->getPowerType() == POWER_MANA) + if (target->HasActivePowerType(POWER_MANA)) _manaAmount = target->GetPower(POWER_MANA); return SPELL_CAST_OK; diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index 9fd557adb..5ca5f32b2 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -992,7 +992,7 @@ class spell_sha_mana_spring_totem : public SpellScript int32 damage = GetEffectValue(); if (Unit* target = GetHitUnit()) if (Unit* caster = GetCaster()) - if (target->getPowerType() == POWER_MANA) + if (target->HasActivePowerType(POWER_MANA)) caster->CastCustomSpell(target, SPELL_SHAMAN_MANA_SPRING_TOTEM_ENERGIZE, &damage, 0, 0, true, 0, 0, GetOriginalCaster()->GetGUID()); } @@ -1017,7 +1017,7 @@ class spell_sha_mana_tide_totem : public SpellScript if (Unit* caster = GetCaster()) if (Unit* unitTarget = GetHitUnit()) { - if (unitTarget->getPowerType() == POWER_MANA) + if (unitTarget->HasActivePowerType(POWER_MANA)) { int32 effValue = GetEffectValue(); // Glyph of Mana Tide