From 0067c8bd27cad123897ab8424e8943212aeda7f2 Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Sun, 10 Sep 2023 10:09:10 -0700 Subject: [PATCH] improve: enfornce LF (unix-style) line endings --- .gitattributes | 1 + cmake/modules/BaseConfig.cmake | 298 +-- src/game/scheduling/dispatcher.cpp | 148 +- src/game/scheduling/dispatcher.hpp | 98 +- src/lib/di/soft_singleton.cpp | 78 +- src/lib/di/soft_singleton.hpp | 88 +- .../creatures/combat/spell_functions.cpp | 2276 ++++++++--------- .../creatures/player/party_functions.cpp | 466 ++-- src/lua/functions/events/action_functions.cpp | 434 ++-- 9 files changed, 1944 insertions(+), 1943 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..d01653a888d --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +text eol=lf diff --git a/cmake/modules/BaseConfig.cmake b/cmake/modules/BaseConfig.cmake index 4e9b01e6f8d..86b2890d11e 100644 --- a/cmake/modules/BaseConfig.cmake +++ b/cmake/modules/BaseConfig.cmake @@ -1,149 +1,149 @@ -# ***************************************************************************** -# CMake Features -# ***************************************************************************** -set(CMAKE_CXX_STANDARD 20) -set(GNUCXX_MINIMUM_VERSION 11) -set(MSVC_MINIMUM_VERSION "19.32") -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_POSITION_INDEPENDENT_CODE ON) -set(CMAKE_DISABLE_SOURCE_CHANGES ON) -set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) -set(Boost_NO_WARN_NEW_VERSIONS ON) - -# Make will print more details -set(CMAKE_VERBOSE_MAKEFILE OFF) - -# Generate compile_commands.json -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -# ***************************************************************************** -# Packages / Libs -# ***************************************************************************** -find_package(CURL CONFIG REQUIRED) -find_package(GMP REQUIRED) -find_package(LuaJIT REQUIRED) -find_package(MySQL REQUIRED) -find_package(Protobuf REQUIRED) -find_package(Threads REQUIRED) -find_package(ZLIB REQUIRED) -find_package(absl CONFIG REQUIRED) -find_package(asio CONFIG REQUIRED) -find_package(eventpp CONFIG REQUIRED) -find_package(jsoncpp CONFIG REQUIRED) -find_package(magic_enum CONFIG REQUIRED) -find_package(mio REQUIRED) -find_package(pugixml CONFIG REQUIRED) -find_package(spdlog REQUIRED) -find_package(unofficial-argon2 CONFIG REQUIRED) -find_package(unofficial-libmariadb CONFIG REQUIRED) - -find_path(BOOST_DI_INCLUDE_DIRS "boost/di.hpp") - -# ***************************************************************************** -# Sanity Checks -# ***************************************************************************** -# === GCC Minimum Version === -if (CMAKE_COMPILER_IS_GNUCXX) - message("-- Compiler: GCC - Version: ${CMAKE_CXX_COMPILER_VERSION}") - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS GNUCXX_MINIMUM_VERSION) - message(FATAL_ERROR "GCC version must be at least ${GNUCXX_MINIMUM_VERSION}!") - endif() -endif() - -# === Minimum required version for visual studio === -if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - message("-- Compiler: Visual Studio - Version: ${CMAKE_CXX_COMPILER_VERSION}") - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS MSVC_MINIMUM_VERSION) - message(FATAL_ERROR "Visual Studio version must be at least ${MSVC_MINIMUM_VERSION}") - endif() -endif() - -# ***************************************************************************** -# Sanity Checks -# ***************************************************************************** -option(TOGGLE_BIN_FOLDER "Use build/bin folder for generate compilation files" ON) -option(OPTIONS_ENABLE_OPENMP "Enable Open Multi-Processing support." ON) -option(DEBUG_LOG "Enable Debug Log" OFF) -option(ASAN_ENABLED "Build this target with AddressSanitizer" OFF) -option(BUILD_STATIC_LIBRARY "Build using static libraries" OFF) -option(SPEED_UP_BUILD_UNITY "Compile using build unity for speed up build" ON) - -# === ASAN === -if(ASAN_ENABLED) - log_option_enabled("asan") - if(MSVC) - add_compile_options(/fsanitize=address) - else() - add_compile_options(-fsanitize=address) - link_libraries(-fsanitize=address) - endif() -else() - log_option_disabled("asan") -endif() - -# Build static libs -if(BUILD_STATIC_LIBRARY) - log_option_enabled("STATIC_LIBRARY") - - if(MSVC) - set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib") - elseif(UNIX AND NOT APPLE) - set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") - elseif(APPLE) - set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".dylib") - endif() -else() - log_option_disabled("STATIC_LIBRARY") -endif() - -# === DEBUG LOG === -# cmake -DDEBUG_LOG=ON .. -if(DEBUG_LOG) - add_definitions(-DDEBUG_LOG=ON) - log_option_enabled("DEBUG LOG") -else() - log_option_disabled("DEBUG LOG") -endif(DEBUG_LOG) - -# ***************************************************************************** -# Compiler Options -# ***************************************************************************** -if (MSVC) - foreach(type RELEASE DEBUG RELWITHDEBINFO MINSIZEREL) - string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_${type} "${CMAKE_CXX_FLAGS_${type}}") - string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_${type} "${CMAKE_C_FLAGS_${type}}") - endforeach(type) - - add_compile_options(/MP /FS /Zf /EHsc) -endif (MSVC) - -## Link compilation files to build/bin folder, else link to the main dir -function(set_output_directory target_name) - if (TOGGLE_BIN_FOLDER) - set_target_properties(${target_name} - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" - ) - else() - set_target_properties(${target_name} - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/" - ) - endif() -endfunction() - -## Setup shared target basic configurations -function(setup_target TARGET_NAME) - if (MSVC AND BUILD_STATIC_LIBRARY) - set_property(TARGET ${TARGET_NAME} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - endif() -endfunction() - -# ***************************************************************************** -# DEBUG: Print cmake variables -# ***************************************************************************** -#get_cmake_property(_variableNames VARIABLES) -#list (SORT _variableNames) -#foreach (_variableName ${_variableNames}) -# message(STATUS "${_variableName}=${${_variableName}}") -#endforeach() +# ***************************************************************************** +# CMake Features +# ***************************************************************************** +set(CMAKE_CXX_STANDARD 20) +set(GNUCXX_MINIMUM_VERSION 11) +set(MSVC_MINIMUM_VERSION "19.32") +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_DISABLE_SOURCE_CHANGES ON) +set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) +set(Boost_NO_WARN_NEW_VERSIONS ON) + +# Make will print more details +set(CMAKE_VERBOSE_MAKEFILE OFF) + +# Generate compile_commands.json +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# ***************************************************************************** +# Packages / Libs +# ***************************************************************************** +find_package(CURL CONFIG REQUIRED) +find_package(GMP REQUIRED) +find_package(LuaJIT REQUIRED) +find_package(MySQL REQUIRED) +find_package(Protobuf REQUIRED) +find_package(Threads REQUIRED) +find_package(ZLIB REQUIRED) +find_package(absl CONFIG REQUIRED) +find_package(asio CONFIG REQUIRED) +find_package(eventpp CONFIG REQUIRED) +find_package(jsoncpp CONFIG REQUIRED) +find_package(magic_enum CONFIG REQUIRED) +find_package(mio REQUIRED) +find_package(pugixml CONFIG REQUIRED) +find_package(spdlog REQUIRED) +find_package(unofficial-argon2 CONFIG REQUIRED) +find_package(unofficial-libmariadb CONFIG REQUIRED) + +find_path(BOOST_DI_INCLUDE_DIRS "boost/di.hpp") + +# ***************************************************************************** +# Sanity Checks +# ***************************************************************************** +# === GCC Minimum Version === +if (CMAKE_COMPILER_IS_GNUCXX) + message("-- Compiler: GCC - Version: ${CMAKE_CXX_COMPILER_VERSION}") + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS GNUCXX_MINIMUM_VERSION) + message(FATAL_ERROR "GCC version must be at least ${GNUCXX_MINIMUM_VERSION}!") + endif() +endif() + +# === Minimum required version for visual studio === +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + message("-- Compiler: Visual Studio - Version: ${CMAKE_CXX_COMPILER_VERSION}") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS MSVC_MINIMUM_VERSION) + message(FATAL_ERROR "Visual Studio version must be at least ${MSVC_MINIMUM_VERSION}") + endif() +endif() + +# ***************************************************************************** +# Sanity Checks +# ***************************************************************************** +option(TOGGLE_BIN_FOLDER "Use build/bin folder for generate compilation files" ON) +option(OPTIONS_ENABLE_OPENMP "Enable Open Multi-Processing support." ON) +option(DEBUG_LOG "Enable Debug Log" OFF) +option(ASAN_ENABLED "Build this target with AddressSanitizer" OFF) +option(BUILD_STATIC_LIBRARY "Build using static libraries" OFF) +option(SPEED_UP_BUILD_UNITY "Compile using build unity for speed up build" ON) + +# === ASAN === +if(ASAN_ENABLED) + log_option_enabled("asan") + if(MSVC) + add_compile_options(/fsanitize=address) + else() + add_compile_options(-fsanitize=address) + link_libraries(-fsanitize=address) + endif() +else() + log_option_disabled("asan") +endif() + +# Build static libs +if(BUILD_STATIC_LIBRARY) + log_option_enabled("STATIC_LIBRARY") + + if(MSVC) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib") + elseif(UNIX AND NOT APPLE) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") + elseif(APPLE) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".dylib") + endif() +else() + log_option_disabled("STATIC_LIBRARY") +endif() + +# === DEBUG LOG === +# cmake -DDEBUG_LOG=ON .. +if(DEBUG_LOG) + add_definitions(-DDEBUG_LOG=ON) + log_option_enabled("DEBUG LOG") +else() + log_option_disabled("DEBUG LOG") +endif(DEBUG_LOG) + +# ***************************************************************************** +# Compiler Options +# ***************************************************************************** +if (MSVC) + foreach(type RELEASE DEBUG RELWITHDEBINFO MINSIZEREL) + string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_${type} "${CMAKE_CXX_FLAGS_${type}}") + string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_${type} "${CMAKE_C_FLAGS_${type}}") + endforeach(type) + + add_compile_options(/MP /FS /Zf /EHsc) +endif (MSVC) + +## Link compilation files to build/bin folder, else link to the main dir +function(set_output_directory target_name) + if (TOGGLE_BIN_FOLDER) + set_target_properties(${target_name} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" + ) + else() + set_target_properties(${target_name} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/" + ) + endif() +endfunction() + +## Setup shared target basic configurations +function(setup_target TARGET_NAME) + if (MSVC AND BUILD_STATIC_LIBRARY) + set_property(TARGET ${TARGET_NAME} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + endif() +endfunction() + +# ***************************************************************************** +# DEBUG: Print cmake variables +# ***************************************************************************** +#get_cmake_property(_variableNames VARIABLES) +#list (SORT _variableNames) +#foreach (_variableName ${_variableNames}) +# message(STATUS "${_variableName}=${${_variableName}}") +#endforeach() diff --git a/src/game/scheduling/dispatcher.cpp b/src/game/scheduling/dispatcher.cpp index 965b3a309bc..9f37393e36a 100644 --- a/src/game/scheduling/dispatcher.cpp +++ b/src/game/scheduling/dispatcher.cpp @@ -1,74 +1,74 @@ -/** - * Canary - A free and open-source MMORPG server emulator - * Copyright (©) 2019-2022 OpenTibiaBR - * Repository: https://github.com/opentibiabr/canary - * License: https://github.com/opentibiabr/canary/blob/main/LICENSE - * Contributors: https://github.com/opentibiabr/canary/graphs/contributors - * Website: https://docs.opentibiabr.com/ - */ - -#include "pch.hpp" - -#include "lib/di/container.hpp" -#include "lib/thread/thread_pool.hpp" -#include "game/scheduling/dispatcher.hpp" -#include "game/scheduling/task.hpp" - -Dispatcher::Dispatcher(ThreadPool &threadPool) : - threadPool(threadPool) { } - -Dispatcher &Dispatcher::getInstance() { - return inject(); -} - -void Dispatcher::addTask(std::function f, std::string context) { - addTask(std::make_shared(std::move(f), std::move(context))); -} - -void Dispatcher::addTask(std::function f, std::string context, uint32_t expiresAfterMs) { - addTask(std::make_shared(std::move(f), std::move(context)), expiresAfterMs); -} - -void Dispatcher::addTask(const std::shared_ptr task) { - addTask(task, 0); -} - -void Dispatcher::addTask(const std::shared_ptr task, uint32_t expiresAfterMs) { - auto executeTask = [this, task]() { - std::lock_guard lockClass(threadSafetyMutex); - - if (task->hasTraceableContext()) { - g_logger().trace("Executing task {}.", task->getContext()); - } else { - g_logger().debug("Executing task {}.", task->getContext()); - } - - ++dispatcherCycle; - (*task)(); - }; - - if (expiresAfterMs == 0) { - threadPool.addLoad(executeTask); - - return; - }; - - auto timer = std::make_shared(threadPool.getIoContext()); - timer->expires_after(std::chrono::milliseconds(expiresAfterMs)); - - timer->async_wait([task, expiresAfterMs](const std::error_code &error) { - if (error == asio::error::operation_aborted) { - return; - } - - g_logger().info("Task '{}' was not executed within {} ms, so it was cancelled.", task->getContext(), expiresAfterMs); - }); - - threadPool.addLoad([timer, executeTask]() { - if (timer->cancel() <= 0) { - return; - } - - executeTask(); - }); -} +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" + +#include "lib/di/container.hpp" +#include "lib/thread/thread_pool.hpp" +#include "game/scheduling/dispatcher.hpp" +#include "game/scheduling/task.hpp" + +Dispatcher::Dispatcher(ThreadPool &threadPool) : + threadPool(threadPool) { } + +Dispatcher &Dispatcher::getInstance() { + return inject(); +} + +void Dispatcher::addTask(std::function f, std::string context) { + addTask(std::make_shared(std::move(f), std::move(context))); +} + +void Dispatcher::addTask(std::function f, std::string context, uint32_t expiresAfterMs) { + addTask(std::make_shared(std::move(f), std::move(context)), expiresAfterMs); +} + +void Dispatcher::addTask(const std::shared_ptr task) { + addTask(task, 0); +} + +void Dispatcher::addTask(const std::shared_ptr task, uint32_t expiresAfterMs) { + auto executeTask = [this, task]() { + std::lock_guard lockClass(threadSafetyMutex); + + if (task->hasTraceableContext()) { + g_logger().trace("Executing task {}.", task->getContext()); + } else { + g_logger().debug("Executing task {}.", task->getContext()); + } + + ++dispatcherCycle; + (*task)(); + }; + + if (expiresAfterMs == 0) { + threadPool.addLoad(executeTask); + + return; + }; + + auto timer = std::make_shared(threadPool.getIoContext()); + timer->expires_after(std::chrono::milliseconds(expiresAfterMs)); + + timer->async_wait([task, expiresAfterMs](const std::error_code &error) { + if (error == asio::error::operation_aborted) { + return; + } + + g_logger().info("Task '{}' was not executed within {} ms, so it was cancelled.", task->getContext(), expiresAfterMs); + }); + + threadPool.addLoad([timer, executeTask]() { + if (timer->cancel() <= 0) { + return; + } + + executeTask(); + }); +} diff --git a/src/game/scheduling/dispatcher.hpp b/src/game/scheduling/dispatcher.hpp index 985bdf8e56b..2beda8b3401 100644 --- a/src/game/scheduling/dispatcher.hpp +++ b/src/game/scheduling/dispatcher.hpp @@ -1,49 +1,49 @@ -/** - * Canary - A free and open-source MMORPG server emulator - * Copyright (©) 2019-2022 OpenTibiaBR - * Repository: https://github.com/opentibiabr/canary - * License: https://github.com/opentibiabr/canary/blob/main/LICENSE - * Contributors: https://github.com/opentibiabr/canary/graphs/contributors - * Website: https://docs.opentibiabr.com/ - */ - -#pragma once - -#include "lib/thread/thread_pool.hpp" - -const int DISPATCHER_TASK_EXPIRATION = 2000; - -class Task; - -/** - * Dispatcher allow you to dispatch a task async to be executed - * in the dispatching thread. You can dispatch with an expiration - * time, after which the task will be ignored. - */ -class Dispatcher { -public: - explicit Dispatcher(ThreadPool &threadPool); - - // Ensures that we don't accidentally copy it - Dispatcher(const Dispatcher &) = delete; - Dispatcher operator=(const Dispatcher &) = delete; - - static Dispatcher &getInstance(); - - void addTask(std::function f, std::string context); - void addTask(std::function f, std::string context, uint32_t expiresAfterMs); - - void addTask(const std::shared_ptr task); - void addTask(const std::shared_ptr task, uint32_t expiresAfterMs); - - [[nodiscard]] uint64_t getDispatcherCycle() const { - return dispatcherCycle; - } - -private: - ThreadPool &threadPool; - uint64_t dispatcherCycle = 0; - std::mutex threadSafetyMutex; -}; - -constexpr auto g_dispatcher = Dispatcher::getInstance; +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#pragma once + +#include "lib/thread/thread_pool.hpp" + +const int DISPATCHER_TASK_EXPIRATION = 2000; + +class Task; + +/** + * Dispatcher allow you to dispatch a task async to be executed + * in the dispatching thread. You can dispatch with an expiration + * time, after which the task will be ignored. + */ +class Dispatcher { +public: + explicit Dispatcher(ThreadPool &threadPool); + + // Ensures that we don't accidentally copy it + Dispatcher(const Dispatcher &) = delete; + Dispatcher operator=(const Dispatcher &) = delete; + + static Dispatcher &getInstance(); + + void addTask(std::function f, std::string context); + void addTask(std::function f, std::string context, uint32_t expiresAfterMs); + + void addTask(const std::shared_ptr task); + void addTask(const std::shared_ptr task, uint32_t expiresAfterMs); + + [[nodiscard]] uint64_t getDispatcherCycle() const { + return dispatcherCycle; + } + +private: + ThreadPool &threadPool; + uint64_t dispatcherCycle = 0; + std::mutex threadSafetyMutex; +}; + +constexpr auto g_dispatcher = Dispatcher::getInstance; diff --git a/src/lib/di/soft_singleton.cpp b/src/lib/di/soft_singleton.cpp index 183ff009c0a..64ea514d140 100644 --- a/src/lib/di/soft_singleton.cpp +++ b/src/lib/di/soft_singleton.cpp @@ -1,39 +1,39 @@ -/** - * Canary - A free and open-source MMORPG server emulator - * Copyright (©) 2019-2023 OpenTibiaBR - * Repository: https://github.com/opentibiabr/canary - * License: https://github.com/opentibiabr/canary/blob/main/LICENSE - * Contributors: https://github.com/opentibiabr/canary/graphs/contributors - * Website: https://docs.opentibiabr.com/ - */ -#include "pch.hpp" -#include "lib/di/soft_singleton.hpp" -#include "utils/tools.hpp" - -SoftSingleton::SoftSingleton(std::string id) : - id(std::move(id)) { } - -void SoftSingleton::increment() { - instance_count++; - if (instance_count > 1) { - logger.warn( - "{} instances created for {}. This is a soft singleton, you probably want to use g_{} instead.", - instance_count, - id, - asLowerCaseString(id) - ); - } -} - -void SoftSingleton::decrement() { - instance_count--; -} - -SoftSingletonGuard::SoftSingletonGuard(SoftSingleton &t) : - tracker(t) { - tracker.increment(); -} - -SoftSingletonGuard::~SoftSingletonGuard() { - tracker.decrement(); -} +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2023 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ +#include "pch.hpp" +#include "lib/di/soft_singleton.hpp" +#include "utils/tools.hpp" + +SoftSingleton::SoftSingleton(std::string id) : + id(std::move(id)) { } + +void SoftSingleton::increment() { + instance_count++; + if (instance_count > 1) { + logger.warn( + "{} instances created for {}. This is a soft singleton, you probably want to use g_{} instead.", + instance_count, + id, + asLowerCaseString(id) + ); + } +} + +void SoftSingleton::decrement() { + instance_count--; +} + +SoftSingletonGuard::SoftSingletonGuard(SoftSingleton &t) : + tracker(t) { + tracker.increment(); +} + +SoftSingletonGuard::~SoftSingletonGuard() { + tracker.decrement(); +} diff --git a/src/lib/di/soft_singleton.hpp b/src/lib/di/soft_singleton.hpp index 2b138850559..12d90aa3bea 100644 --- a/src/lib/di/soft_singleton.hpp +++ b/src/lib/di/soft_singleton.hpp @@ -1,44 +1,44 @@ -/** - * Canary - A free and open-source MMORPG server emulator - * Copyright (©) 2019-2023 OpenTibiaBR - * Repository: https://github.com/opentibiabr/canary - * License: https://github.com/opentibiabr/canary/blob/main/LICENSE - * Contributors: https://github.com/opentibiabr/canary/graphs/contributors - * Website: https://docs.opentibiabr.com/ - */ -#pragma once - -#include -#include "lib/logging/log_with_spd_log.hpp" - -class SoftSingleton { -public: - explicit SoftSingleton(std::string id); - - // non-copyable - SoftSingleton(const SoftSingleton &) = delete; - void operator=(const SoftSingleton &) = delete; - - void increment(); - - void decrement(); - -private: - Logger &logger = g_logger(); - std::string id; - int instance_count = 0; -}; - -class SoftSingletonGuard { -public: - explicit SoftSingletonGuard(SoftSingleton &t); - - // non-copyable - SoftSingletonGuard(const SoftSingletonGuard &) = delete; - void operator=(const SoftSingletonGuard &) = delete; - - ~SoftSingletonGuard(); - -private: - SoftSingleton &tracker; -}; +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2023 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ +#pragma once + +#include +#include "lib/logging/log_with_spd_log.hpp" + +class SoftSingleton { +public: + explicit SoftSingleton(std::string id); + + // non-copyable + SoftSingleton(const SoftSingleton &) = delete; + void operator=(const SoftSingleton &) = delete; + + void increment(); + + void decrement(); + +private: + Logger &logger = g_logger(); + std::string id; + int instance_count = 0; +}; + +class SoftSingletonGuard { +public: + explicit SoftSingletonGuard(SoftSingleton &t); + + // non-copyable + SoftSingletonGuard(const SoftSingletonGuard &) = delete; + void operator=(const SoftSingletonGuard &) = delete; + + ~SoftSingletonGuard(); + +private: + SoftSingleton &tracker; +}; diff --git a/src/lua/functions/creatures/combat/spell_functions.cpp b/src/lua/functions/creatures/combat/spell_functions.cpp index a24412e255f..00bd785c616 100644 --- a/src/lua/functions/creatures/combat/spell_functions.cpp +++ b/src/lua/functions/creatures/combat/spell_functions.cpp @@ -1,1138 +1,1138 @@ -/** - * Canary - A free and open-source MMORPG server emulator - * Copyright (©) 2019-2022 OpenTibiaBR - * Repository: https://github.com/opentibiabr/canary - * License: https://github.com/opentibiabr/canary/blob/main/LICENSE - * Contributors: https://github.com/opentibiabr/canary/graphs/contributors - * Website: https://docs.opentibiabr.com/ - */ - -#include "pch.hpp" - -#include "creatures/combat/spells.hpp" -#include "creatures/players/vocations/vocation.hpp" -#include "lua/functions/creatures/combat/spell_functions.hpp" - -int SpellFunctions::luaSpellCreate(lua_State* L) { - // Spell(words, name or id) to get an existing spell - // Spell(type) ex: Spell(SPELL_INSTANT) or Spell(SPELL_RUNE) to create a new spell - if (lua_gettop(L) == 1) { - g_logger().error("[SpellFunctions::luaSpellCreate] - " - "There is no parameter set!"); - lua_pushnil(L); - return 1; - } - - SpellType_t spellType = SPELL_UNDEFINED; - - if (isNumber(L, 2)) { - uint16_t id = getNumber(L, 2); - std::shared_ptr rune = g_spells().getRuneSpell(id); - - if (rune) { - pushUserdata(L, rune); - setMetatable(L, -1, "Spell"); - return 1; - } - - spellType = static_cast(id); - } else if (isString(L, 2)) { - std::string arg = getString(L, 2); - std::shared_ptr instant = g_spells().getInstantSpellByName(arg); - if (instant) { - pushUserdata(L, instant); - setMetatable(L, -1, "Spell"); - return 1; - } - instant = g_spells().getInstantSpell(arg); - if (instant) { - pushUserdata(L, instant); - setMetatable(L, -1, "Spell"); - return 1; - } - std::shared_ptr rune = g_spells().getRuneSpellByName(arg); - if (rune) { - pushUserdata(L, rune); - setMetatable(L, -1, "Spell"); - return 1; - } - - std::string tmp = asLowerCaseString(arg); - if (tmp == "instant") { - spellType = SPELL_INSTANT; - } else if (tmp == "rune") { - spellType = SPELL_RUNE; - } - } - - if (spellType == SPELL_INSTANT) { - auto spell = std::make_shared(getScriptEnv()->getScriptInterface()); - pushUserdata(L, spell); - setMetatable(L, -1, "Spell"); - spell->spellType = SPELL_INSTANT; - return 1; - } else if (spellType == SPELL_RUNE) { - auto runeSpell = std::make_shared(getScriptEnv()->getScriptInterface()); - pushUserdata(L, runeSpell); - setMetatable(L, -1, "Spell"); - runeSpell->spellType = SPELL_RUNE; - return 1; - } - - lua_pushnil(L); - return 1; -} - -int SpellFunctions::luaSpellOnCastSpell(lua_State* L) { - // spell:onCastSpell(callback) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (spell->spellType == SPELL_INSTANT) { - const auto &spellBase = getUserdataShared(L, 1); - const auto &instant = std::static_pointer_cast(spellBase); - if (!instant->loadCallback()) { - pushBoolean(L, false); - return 1; - } - instant->setLoadedCallback(true); - pushBoolean(L, true); - } else if (spell->spellType == SPELL_RUNE) { - std::shared_ptr spellBase = getUserdataShared(L, 1); - std::shared_ptr rune = std::static_pointer_cast(spellBase); - if (!rune->loadCallback()) { - pushBoolean(L, false); - return 1; - } - rune->setLoadedCallback(true); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellRegister(lua_State* L) { - // spell:register() - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - if (spell->spellType == SPELL_INSTANT) { - const auto &spellBase = getUserdataShared(L, 1); - const auto &instant = std::static_pointer_cast(spellBase); - if (!instant->isLoadedCallback()) { - pushBoolean(L, false); - return 1; - } - pushBoolean(L, g_spells().registerInstantLuaEvent(instant)); - } else if (spell->spellType == SPELL_RUNE) { - const auto &spellBase = getUserdataShared(L, 1); - const auto &rune = std::static_pointer_cast(spellBase); - if (rune->getMagicLevel() != 0 || rune->getLevel() != 0) { - // Change information in the ItemType to get accurate description - ItemType &iType = Item::items.getItemType(rune->getRuneItemId()); - // If the item is not registered in items.xml then we will register it by rune name - if (iType.name.empty()) { - iType.name = rune->getName(); - } - iType.runeMagLevel = rune->getMagicLevel(); - iType.runeLevel = rune->getLevel(); - iType.charges = rune->getCharges(); - } - if (!rune->isLoadedCallback()) { - pushBoolean(L, false); - return 1; - } - pushBoolean(L, g_spells().registerRuneLuaEvent(rune)); - } - return 1; -} - -int SpellFunctions::luaSpellName(lua_State* L) { - - // spell:name(name) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushString(L, spell->getName()); - } else { - spell->setName(getString(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellId(lua_State* L) { - // spell:id(id) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (spell->spellType != SPELL_INSTANT && spell->spellType != SPELL_RUNE) { - reportErrorFunc("The method: 'spell:id(id)' is only for use of instant spells and rune spells"); - pushBoolean(L, false); - return 1; - } - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getId()); - } else { - spell->setId(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellGroup(lua_State* L) { - // spell:group(primaryGroup[, secondaryGroup]) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getGroup()); - lua_pushnumber(L, spell->getSecondaryGroup()); - return 2; - } else if (lua_gettop(L) == 2) { - SpellGroup_t group = getNumber(L, 2); - if (group) { - spell->setGroup(group); - pushBoolean(L, true); - } else if (isString(L, 2)) { - group = stringToSpellGroup(getString(L, 2)); - if (group != SPELLGROUP_NONE) { - spell->setGroup(group); - } else { - g_logger().warn("[SpellFunctions::luaSpellGroup] - " - "Unknown group: {}", - getString(L, 2)); - pushBoolean(L, false); - return 1; - } - pushBoolean(L, true); - } else { - g_logger().warn("[SpellFunctions::luaSpellGroup] - " - "Unknown group: {}", - getString(L, 2)); - pushBoolean(L, false); - return 1; - } - } else { - SpellGroup_t primaryGroup = getNumber(L, 2); - SpellGroup_t secondaryGroup = getNumber(L, 2); - if (primaryGroup && secondaryGroup) { - spell->setGroup(primaryGroup); - spell->setSecondaryGroup(secondaryGroup); - pushBoolean(L, true); - } else if (isString(L, 2) && isString(L, 3)) { - primaryGroup = stringToSpellGroup(getString(L, 2)); - if (primaryGroup != SPELLGROUP_NONE) { - spell->setGroup(primaryGroup); - } else { - g_logger().warn("[SpellFunctions::luaSpellGroup] - " - "Unknown primaryGroup: {}", - getString(L, 2)); - pushBoolean(L, false); - return 1; - } - secondaryGroup = stringToSpellGroup(getString(L, 3)); - if (secondaryGroup != SPELLGROUP_NONE) { - spell->setSecondaryGroup(secondaryGroup); - } else { - g_logger().warn("[SpellFunctions::luaSpellGroup] - " - "Unknown secondaryGroup: {}", - getString(L, 3)); - pushBoolean(L, false); - return 1; - } - pushBoolean(L, true); - } else { - g_logger().warn("[SpellFunctions::luaSpellGroup] - " - "Unknown primaryGroup: {} or secondaryGroup: {}", - getString(L, 2), getString(L, 3)); - pushBoolean(L, false); - return 1; - } - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellCastSound(lua_State* L) { - // get: spell:castSound() set: spell:castSound(effect) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, static_cast(spell->soundCastEffect)); - } else { - spell->soundCastEffect = static_cast(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellImpactSound(lua_State* L) { - // get: spell:impactSound() set: spell:impactSound(effect) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, static_cast(spell->soundImpactEffect)); - } else { - spell->soundImpactEffect = static_cast(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellCooldown(lua_State* L) { - // spell:cooldown(cooldown) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getCooldown()); - } else { - spell->setCooldown(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellGroupCooldown(lua_State* L) { - // spell:groupCooldown(primaryGroupCd[, secondaryGroupCd]) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getGroupCooldown()); - lua_pushnumber(L, spell->getSecondaryCooldown()); - return 2; - } else if (lua_gettop(L) == 2) { - spell->setGroupCooldown(getNumber(L, 2)); - pushBoolean(L, true); - } else { - spell->setGroupCooldown(getNumber(L, 2)); - spell->setSecondaryCooldown(getNumber(L, 3)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellLevel(lua_State* L) { - // spell:level(lvl) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getLevel()); - } else { - spell->setLevel(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellMagicLevel(lua_State* L) { - // spell:magicLevel(lvl) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getMagicLevel()); - } else { - spell->setMagicLevel(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellMana(lua_State* L) { - // spell:mana(mana) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getMana()); - } else { - spell->setMana(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellManaPercent(lua_State* L) { - // spell:manaPercent(percent) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getManaPercent()); - } else { - spell->setManaPercent(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellSoul(lua_State* L) { - // spell:soul(soul) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getSoulCost()); - } else { - spell->setSoulCost(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellRange(lua_State* L) { - // spell:range(range) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getRange()); - } else { - spell->setRange(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellPremium(lua_State* L) { - // spell:isPremium(bool) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->isPremium()); - } else { - spell->setPremium(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellEnabled(lua_State* L) { - // spell:isEnabled(bool) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->isEnabled()); - } else { - spell->setEnabled(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellNeedTarget(lua_State* L) { - // spell:needTarget(bool) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getNeedTarget()); - } else { - spell->setNeedTarget(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellNeedWeapon(lua_State* L) { - // spell:needWeapon(bool) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getNeedWeapon()); - } else { - spell->setNeedWeapon(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellNeedLearn(lua_State* L) { - // spell:needLearn(bool) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getNeedLearn()); - } else { - spell->setNeedLearn(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellSelfTarget(lua_State* L) { - // spell:isSelfTarget(bool) - if (const auto spell = getUserdataShared(L, 1)) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getSelfTarget()); - } else { - spell->setSelfTarget(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellBlocking(lua_State* L) { - // spell:isBlocking(blockingSolid, blockingCreature) - if (const auto spell = getUserdataShared(L, 1)) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getBlockingSolid()); - pushBoolean(L, spell->getBlockingCreature()); - return 2; - } else { - spell->setBlockingSolid(getBoolean(L, 2)); - spell->setBlockingCreature(getBoolean(L, 3)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellAggressive(lua_State* L) { - // spell:isAggressive(bool) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getAggressive()); - } else { - spell->setAggressive(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellAllowOnSelf(lua_State* L) { - // spell:allowOnSelf(bool) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getAllowOnSelf()); - } else { - spell->setAllowOnSelf(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellPzLocked(lua_State* L) { - // spell:isPzLocked(bool) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getLockedPZ()); - } else { - spell->setLockedPZ(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int SpellFunctions::luaSpellVocation(lua_State* L) { - // spell:vocation(vocation) - const auto spell = getUserdataShared(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_createtable(L, 0, 0); - auto it = 0; - for (auto voc : spell->getVocMap()) { - ++it; - std::string s = std::to_string(it); - const char* pchar = s.c_str(); - std::string name = g_vocations().getVocation(voc.first)->getVocName(); - setField(L, pchar, name); - } - setMetatable(L, -1, "Spell"); - } else { - int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc - for (int i = 0; i < parameters; ++i) { - if (getString(L, 2 + i).find(";") != std::string::npos) { - std::vector vocList = explodeString(getString(L, 2 + i), ";"); - int32_t vocationId = g_vocations().getVocationId(vocList[0]); - if (vocList.size() > 0) { - if (vocList[1] == "true") { - spell->addVocMap(vocationId, true); - } else { - spell->addVocMap(vocationId, false); - } - } - } else { - int32_t vocationId = g_vocations().getVocationId(getString(L, 2 + i)); - spell->addVocMap(vocationId, false); - } - } - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for InstantSpells -int SpellFunctions::luaSpellWords(lua_State* L) { - // spell:words(words[, separator = ""]) - const auto &spellBase = getUserdataShared(L, 1); - const auto spell = std::static_pointer_cast(spellBase); - if (spell) { - // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil - if (spell->spellType != SPELL_INSTANT) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushString(L, spell->getWords()); - pushString(L, spell->getSeparator()); - return 2; - } else { - std::string sep = ""; - if (lua_gettop(L) == 3) { - sep = getString(L, 3); - } - spell->setWords(getString(L, 2)); - spell->setSeparator(sep); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for InstantSpells -int SpellFunctions::luaSpellNeedDirection(lua_State* L) { - // spell:needDirection(bool) - const auto &spellBase = getUserdataShared(L, 1); - const auto spell = std::static_pointer_cast(spellBase); - if (spell) { - // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil - if (spell->spellType != SPELL_INSTANT) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getNeedDirection()); - } else { - spell->setNeedDirection(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for InstantSpells -int SpellFunctions::luaSpellHasParams(lua_State* L) { - // spell:hasParams(bool) - const auto &spellBase = getUserdataShared(L, 1); - const auto spell = std::static_pointer_cast(spellBase); - if (spell) { - // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil - if (spell->spellType != SPELL_INSTANT) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getHasParam()); - } else { - spell->setHasParam(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for InstantSpells -int SpellFunctions::luaSpellHasPlayerNameParam(lua_State* L) { - // spell:hasPlayerNameParam(bool) - const auto &spellBase = getUserdataShared(L, 1); - const auto spell = std::static_pointer_cast(spellBase); - if (spell) { - // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil - if (spell->spellType != SPELL_INSTANT) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getHasPlayerNameParam()); - } else { - spell->setHasPlayerNameParam(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for InstantSpells -int SpellFunctions::luaSpellNeedCasterTargetOrDirection(lua_State* L) { - // spell:needCasterTargetOrDirection(bool) - const auto &spellBase = getUserdataShared(L, 1); - const auto spell = std::static_pointer_cast(spellBase); - if (spell) { - // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil - if (spell->spellType != SPELL_INSTANT) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getNeedCasterTargetOrDirection()); - } else { - spell->setNeedCasterTargetOrDirection(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for InstantSpells -int SpellFunctions::luaSpellIsBlockingWalls(lua_State* L) { - // spell:blockWalls(bool) - const auto &spellBase = getUserdataShared(L, 1); - const auto spell = std::static_pointer_cast(spellBase); - if (spell) { - // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil - if (spell->spellType != SPELL_INSTANT) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getBlockWalls()); - } else { - spell->setBlockWalls(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for RuneSpells -int SpellFunctions::luaSpellRuneId(lua_State* L) { - // spell:runeId(id) - const auto &spellBase = getUserdataShared(L, 1); - const auto spell = std::static_pointer_cast(spellBase); - if (spell) { - // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil - if (spell->spellType != SPELL_RUNE) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getRuneItemId()); - } else { - spell->setRuneItemId(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for RuneSpells -int SpellFunctions::luaSpellCharges(lua_State* L) { - // spell:charges(charges) - const auto &spellBase = getUserdataShared(L, 1); - const auto spell = std::static_pointer_cast(spellBase); - if (spell) { - // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil - if (spell->spellType != SPELL_RUNE) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getCharges()); - } else { - spell->setCharges(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for RuneSpells -int SpellFunctions::luaSpellAllowFarUse(lua_State* L) { - // spell:allowFarUse(bool) - const auto &spellBase = getUserdataShared(L, 1); - const auto spell = std::static_pointer_cast(spellBase); - if (spell) { - // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil - if (spell->spellType != SPELL_RUNE) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getAllowFarUse()); - } else { - spell->setAllowFarUse(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for RuneSpells -int SpellFunctions::luaSpellBlockWalls(lua_State* L) { - // spell:blockWalls(bool) - const auto &spellBase = getUserdataShared(L, 1); - const auto spell = std::static_pointer_cast(spellBase); - if (spell) { - // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil - if (spell->spellType != SPELL_RUNE) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getCheckLineOfSight()); - } else { - spell->setCheckLineOfSight(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for RuneSpells -int SpellFunctions::luaSpellCheckFloor(lua_State* L) { - // spell:checkFloor(bool) - const auto &spellBase = getUserdataShared(L, 1); - const auto spell = std::static_pointer_cast(spellBase); - if (spell) { - // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil - if (spell->spellType != SPELL_RUNE) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getCheckFloor()); - } else { - spell->setCheckFloor(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// Wheel of destiny -int SpellFunctions::luaSpellManaWOD(lua_State* L) { - // spell:manaWOD(grade, mana) - const auto spell = getUserdataShared(L, 1); - WheelSpellGrade_t grade = getNumber(L, 2); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::MANA, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::MANA, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellCooldownWOD(lua_State* L) { - // spell:cooldownWOD(grade, time) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::COOLDOWN, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::COOLDOWN, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellGroupCooldownWOD(lua_State* L) { - // spell:groupCooldownWOD(grade, time) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::GROUP_COOLDOWN, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::GROUP_COOLDOWN, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellSecondaryGroupCooldownWOD(lua_State* L) { - // spell:secondaryGroupCooldownWOD(grade, time) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::SECONDARY_GROUP_COOLDOWN, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::SECONDARY_GROUP_COOLDOWN, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellIncreaseManaLeechWOD(lua_State* L) { - // spell:increaseManaLeechWOD(grade, value) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::MANA_LEECH, grade)); - } else { - int32_t value = getNumber(L, 3); - if (value > 0) { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::MANA_LEECH_CHANCE, grade, 100); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::MANA_LEECH_CHANCE, grade, 0); - } - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::MANA_LEECH, grade, value); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellIncreaselifeLeechWOD(lua_State* L) { - // spell:increaselifeLeechWOD(grade, value) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH, grade)); - } else { - int32_t value = getNumber(L, 3); - if (value > 0) { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH_CHANCE, grade, 100); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH_CHANCE, grade, 0); - } - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH, grade, value); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellIncreaseDamageWOD(lua_State* L) { - // spell:increaseDamageWOD(grade, value) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::DAMAGE, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::DAMAGE, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellIncreaseDamageReductionWOD(lua_State* L) { - // spell:increaseDamageReductionWOD(grade, value) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::DAMAGE_REDUCTION, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::DAMAGE_REDUCTION, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellIncreaseHealWOD(lua_State* L) { - // spell:increaseHealWOD(grade, value) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::HEAL, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::HEAL, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellIncreaseCriticalDamageWOD(lua_State* L) { - // spell:increaseCriticalDamageWOD(grade, value) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::CRITICAL_DAMAGE, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::CRITICAL_DAMAGE, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} - -int SpellFunctions::luaSpellIncreaseCriticalChanceWOD(lua_State* L) { - // spell:increaseCriticalChanceWOD(grade, value) - const auto spell = getUserdataShared(L, 1); - if (!spell) { - reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - WheelSpellGrade_t grade = getNumber(L, 2); - if (lua_gettop(L) == 2) { - lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::CRITICAL_CHANCE, grade)); - } else { - spell->setWheelOfDestinyBoost(WheelSpellBoost_t::CRITICAL_CHANCE, grade, getNumber(L, 3)); - spell->setWheelOfDestinyUpgraded(true); - pushBoolean(L, true); - } - return 1; -} +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" + +#include "creatures/combat/spells.hpp" +#include "creatures/players/vocations/vocation.hpp" +#include "lua/functions/creatures/combat/spell_functions.hpp" + +int SpellFunctions::luaSpellCreate(lua_State* L) { + // Spell(words, name or id) to get an existing spell + // Spell(type) ex: Spell(SPELL_INSTANT) or Spell(SPELL_RUNE) to create a new spell + if (lua_gettop(L) == 1) { + g_logger().error("[SpellFunctions::luaSpellCreate] - " + "There is no parameter set!"); + lua_pushnil(L); + return 1; + } + + SpellType_t spellType = SPELL_UNDEFINED; + + if (isNumber(L, 2)) { + uint16_t id = getNumber(L, 2); + std::shared_ptr rune = g_spells().getRuneSpell(id); + + if (rune) { + pushUserdata(L, rune); + setMetatable(L, -1, "Spell"); + return 1; + } + + spellType = static_cast(id); + } else if (isString(L, 2)) { + std::string arg = getString(L, 2); + std::shared_ptr instant = g_spells().getInstantSpellByName(arg); + if (instant) { + pushUserdata(L, instant); + setMetatable(L, -1, "Spell"); + return 1; + } + instant = g_spells().getInstantSpell(arg); + if (instant) { + pushUserdata(L, instant); + setMetatable(L, -1, "Spell"); + return 1; + } + std::shared_ptr rune = g_spells().getRuneSpellByName(arg); + if (rune) { + pushUserdata(L, rune); + setMetatable(L, -1, "Spell"); + return 1; + } + + std::string tmp = asLowerCaseString(arg); + if (tmp == "instant") { + spellType = SPELL_INSTANT; + } else if (tmp == "rune") { + spellType = SPELL_RUNE; + } + } + + if (spellType == SPELL_INSTANT) { + auto spell = std::make_shared(getScriptEnv()->getScriptInterface()); + pushUserdata(L, spell); + setMetatable(L, -1, "Spell"); + spell->spellType = SPELL_INSTANT; + return 1; + } else if (spellType == SPELL_RUNE) { + auto runeSpell = std::make_shared(getScriptEnv()->getScriptInterface()); + pushUserdata(L, runeSpell); + setMetatable(L, -1, "Spell"); + runeSpell->spellType = SPELL_RUNE; + return 1; + } + + lua_pushnil(L); + return 1; +} + +int SpellFunctions::luaSpellOnCastSpell(lua_State* L) { + // spell:onCastSpell(callback) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (spell->spellType == SPELL_INSTANT) { + const auto &spellBase = getUserdataShared(L, 1); + const auto &instant = std::static_pointer_cast(spellBase); + if (!instant->loadCallback()) { + pushBoolean(L, false); + return 1; + } + instant->setLoadedCallback(true); + pushBoolean(L, true); + } else if (spell->spellType == SPELL_RUNE) { + std::shared_ptr spellBase = getUserdataShared(L, 1); + std::shared_ptr rune = std::static_pointer_cast(spellBase); + if (!rune->loadCallback()) { + pushBoolean(L, false); + return 1; + } + rune->setLoadedCallback(true); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellRegister(lua_State* L) { + // spell:register() + const auto spell = getUserdataShared(L, 1); + if (!spell) { + reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + if (spell->spellType == SPELL_INSTANT) { + const auto &spellBase = getUserdataShared(L, 1); + const auto &instant = std::static_pointer_cast(spellBase); + if (!instant->isLoadedCallback()) { + pushBoolean(L, false); + return 1; + } + pushBoolean(L, g_spells().registerInstantLuaEvent(instant)); + } else if (spell->spellType == SPELL_RUNE) { + const auto &spellBase = getUserdataShared(L, 1); + const auto &rune = std::static_pointer_cast(spellBase); + if (rune->getMagicLevel() != 0 || rune->getLevel() != 0) { + // Change information in the ItemType to get accurate description + ItemType &iType = Item::items.getItemType(rune->getRuneItemId()); + // If the item is not registered in items.xml then we will register it by rune name + if (iType.name.empty()) { + iType.name = rune->getName(); + } + iType.runeMagLevel = rune->getMagicLevel(); + iType.runeLevel = rune->getLevel(); + iType.charges = rune->getCharges(); + } + if (!rune->isLoadedCallback()) { + pushBoolean(L, false); + return 1; + } + pushBoolean(L, g_spells().registerRuneLuaEvent(rune)); + } + return 1; +} + +int SpellFunctions::luaSpellName(lua_State* L) { + + // spell:name(name) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + pushString(L, spell->getName()); + } else { + spell->setName(getString(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellId(lua_State* L) { + // spell:id(id) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (spell->spellType != SPELL_INSTANT && spell->spellType != SPELL_RUNE) { + reportErrorFunc("The method: 'spell:id(id)' is only for use of instant spells and rune spells"); + pushBoolean(L, false); + return 1; + } + if (lua_gettop(L) == 1) { + lua_pushnumber(L, spell->getId()); + } else { + spell->setId(getNumber(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellGroup(lua_State* L) { + // spell:group(primaryGroup[, secondaryGroup]) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + lua_pushnumber(L, spell->getGroup()); + lua_pushnumber(L, spell->getSecondaryGroup()); + return 2; + } else if (lua_gettop(L) == 2) { + SpellGroup_t group = getNumber(L, 2); + if (group) { + spell->setGroup(group); + pushBoolean(L, true); + } else if (isString(L, 2)) { + group = stringToSpellGroup(getString(L, 2)); + if (group != SPELLGROUP_NONE) { + spell->setGroup(group); + } else { + g_logger().warn("[SpellFunctions::luaSpellGroup] - " + "Unknown group: {}", + getString(L, 2)); + pushBoolean(L, false); + return 1; + } + pushBoolean(L, true); + } else { + g_logger().warn("[SpellFunctions::luaSpellGroup] - " + "Unknown group: {}", + getString(L, 2)); + pushBoolean(L, false); + return 1; + } + } else { + SpellGroup_t primaryGroup = getNumber(L, 2); + SpellGroup_t secondaryGroup = getNumber(L, 2); + if (primaryGroup && secondaryGroup) { + spell->setGroup(primaryGroup); + spell->setSecondaryGroup(secondaryGroup); + pushBoolean(L, true); + } else if (isString(L, 2) && isString(L, 3)) { + primaryGroup = stringToSpellGroup(getString(L, 2)); + if (primaryGroup != SPELLGROUP_NONE) { + spell->setGroup(primaryGroup); + } else { + g_logger().warn("[SpellFunctions::luaSpellGroup] - " + "Unknown primaryGroup: {}", + getString(L, 2)); + pushBoolean(L, false); + return 1; + } + secondaryGroup = stringToSpellGroup(getString(L, 3)); + if (secondaryGroup != SPELLGROUP_NONE) { + spell->setSecondaryGroup(secondaryGroup); + } else { + g_logger().warn("[SpellFunctions::luaSpellGroup] - " + "Unknown secondaryGroup: {}", + getString(L, 3)); + pushBoolean(L, false); + return 1; + } + pushBoolean(L, true); + } else { + g_logger().warn("[SpellFunctions::luaSpellGroup] - " + "Unknown primaryGroup: {} or secondaryGroup: {}", + getString(L, 2), getString(L, 3)); + pushBoolean(L, false); + return 1; + } + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellCastSound(lua_State* L) { + // get: spell:castSound() set: spell:castSound(effect) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + lua_pushnumber(L, static_cast(spell->soundCastEffect)); + } else { + spell->soundCastEffect = static_cast(getNumber(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellImpactSound(lua_State* L) { + // get: spell:impactSound() set: spell:impactSound(effect) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + lua_pushnumber(L, static_cast(spell->soundImpactEffect)); + } else { + spell->soundImpactEffect = static_cast(getNumber(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellCooldown(lua_State* L) { + // spell:cooldown(cooldown) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + lua_pushnumber(L, spell->getCooldown()); + } else { + spell->setCooldown(getNumber(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellGroupCooldown(lua_State* L) { + // spell:groupCooldown(primaryGroupCd[, secondaryGroupCd]) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + lua_pushnumber(L, spell->getGroupCooldown()); + lua_pushnumber(L, spell->getSecondaryCooldown()); + return 2; + } else if (lua_gettop(L) == 2) { + spell->setGroupCooldown(getNumber(L, 2)); + pushBoolean(L, true); + } else { + spell->setGroupCooldown(getNumber(L, 2)); + spell->setSecondaryCooldown(getNumber(L, 3)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellLevel(lua_State* L) { + // spell:level(lvl) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + lua_pushnumber(L, spell->getLevel()); + } else { + spell->setLevel(getNumber(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellMagicLevel(lua_State* L) { + // spell:magicLevel(lvl) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + lua_pushnumber(L, spell->getMagicLevel()); + } else { + spell->setMagicLevel(getNumber(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellMana(lua_State* L) { + // spell:mana(mana) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + lua_pushnumber(L, spell->getMana()); + } else { + spell->setMana(getNumber(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellManaPercent(lua_State* L) { + // spell:manaPercent(percent) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + lua_pushnumber(L, spell->getManaPercent()); + } else { + spell->setManaPercent(getNumber(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellSoul(lua_State* L) { + // spell:soul(soul) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + lua_pushnumber(L, spell->getSoulCost()); + } else { + spell->setSoulCost(getNumber(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellRange(lua_State* L) { + // spell:range(range) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + lua_pushnumber(L, spell->getRange()); + } else { + spell->setRange(getNumber(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellPremium(lua_State* L) { + // spell:isPremium(bool) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->isPremium()); + } else { + spell->setPremium(getBoolean(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellEnabled(lua_State* L) { + // spell:isEnabled(bool) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->isEnabled()); + } else { + spell->setEnabled(getBoolean(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellNeedTarget(lua_State* L) { + // spell:needTarget(bool) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->getNeedTarget()); + } else { + spell->setNeedTarget(getBoolean(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellNeedWeapon(lua_State* L) { + // spell:needWeapon(bool) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->getNeedWeapon()); + } else { + spell->setNeedWeapon(getBoolean(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellNeedLearn(lua_State* L) { + // spell:needLearn(bool) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->getNeedLearn()); + } else { + spell->setNeedLearn(getBoolean(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellSelfTarget(lua_State* L) { + // spell:isSelfTarget(bool) + if (const auto spell = getUserdataShared(L, 1)) { + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->getSelfTarget()); + } else { + spell->setSelfTarget(getBoolean(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellBlocking(lua_State* L) { + // spell:isBlocking(blockingSolid, blockingCreature) + if (const auto spell = getUserdataShared(L, 1)) { + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->getBlockingSolid()); + pushBoolean(L, spell->getBlockingCreature()); + return 2; + } else { + spell->setBlockingSolid(getBoolean(L, 2)); + spell->setBlockingCreature(getBoolean(L, 3)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellAggressive(lua_State* L) { + // spell:isAggressive(bool) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->getAggressive()); + } else { + spell->setAggressive(getBoolean(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellAllowOnSelf(lua_State* L) { + // spell:allowOnSelf(bool) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->getAllowOnSelf()); + } else { + spell->setAllowOnSelf(getBoolean(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellPzLocked(lua_State* L) { + // spell:isPzLocked(bool) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->getLockedPZ()); + } else { + spell->setLockedPZ(getBoolean(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int SpellFunctions::luaSpellVocation(lua_State* L) { + // spell:vocation(vocation) + const auto spell = getUserdataShared(L, 1); + if (spell) { + if (lua_gettop(L) == 1) { + lua_createtable(L, 0, 0); + auto it = 0; + for (auto voc : spell->getVocMap()) { + ++it; + std::string s = std::to_string(it); + const char* pchar = s.c_str(); + std::string name = g_vocations().getVocation(voc.first)->getVocName(); + setField(L, pchar, name); + } + setMetatable(L, -1, "Spell"); + } else { + int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc + for (int i = 0; i < parameters; ++i) { + if (getString(L, 2 + i).find(";") != std::string::npos) { + std::vector vocList = explodeString(getString(L, 2 + i), ";"); + int32_t vocationId = g_vocations().getVocationId(vocList[0]); + if (vocList.size() > 0) { + if (vocList[1] == "true") { + spell->addVocMap(vocationId, true); + } else { + spell->addVocMap(vocationId, false); + } + } + } else { + int32_t vocationId = g_vocations().getVocationId(getString(L, 2 + i)); + spell->addVocMap(vocationId, false); + } + } + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +// only for InstantSpells +int SpellFunctions::luaSpellWords(lua_State* L) { + // spell:words(words[, separator = ""]) + const auto &spellBase = getUserdataShared(L, 1); + const auto spell = std::static_pointer_cast(spellBase); + if (spell) { + // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil + if (spell->spellType != SPELL_INSTANT) { + lua_pushnil(L); + return 1; + } + + if (lua_gettop(L) == 1) { + pushString(L, spell->getWords()); + pushString(L, spell->getSeparator()); + return 2; + } else { + std::string sep = ""; + if (lua_gettop(L) == 3) { + sep = getString(L, 3); + } + spell->setWords(getString(L, 2)); + spell->setSeparator(sep); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +// only for InstantSpells +int SpellFunctions::luaSpellNeedDirection(lua_State* L) { + // spell:needDirection(bool) + const auto &spellBase = getUserdataShared(L, 1); + const auto spell = std::static_pointer_cast(spellBase); + if (spell) { + // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil + if (spell->spellType != SPELL_INSTANT) { + lua_pushnil(L); + return 1; + } + + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->getNeedDirection()); + } else { + spell->setNeedDirection(getBoolean(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +// only for InstantSpells +int SpellFunctions::luaSpellHasParams(lua_State* L) { + // spell:hasParams(bool) + const auto &spellBase = getUserdataShared(L, 1); + const auto spell = std::static_pointer_cast(spellBase); + if (spell) { + // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil + if (spell->spellType != SPELL_INSTANT) { + lua_pushnil(L); + return 1; + } + + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->getHasParam()); + } else { + spell->setHasParam(getBoolean(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +// only for InstantSpells +int SpellFunctions::luaSpellHasPlayerNameParam(lua_State* L) { + // spell:hasPlayerNameParam(bool) + const auto &spellBase = getUserdataShared(L, 1); + const auto spell = std::static_pointer_cast(spellBase); + if (spell) { + // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil + if (spell->spellType != SPELL_INSTANT) { + lua_pushnil(L); + return 1; + } + + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->getHasPlayerNameParam()); + } else { + spell->setHasPlayerNameParam(getBoolean(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +// only for InstantSpells +int SpellFunctions::luaSpellNeedCasterTargetOrDirection(lua_State* L) { + // spell:needCasterTargetOrDirection(bool) + const auto &spellBase = getUserdataShared(L, 1); + const auto spell = std::static_pointer_cast(spellBase); + if (spell) { + // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil + if (spell->spellType != SPELL_INSTANT) { + lua_pushnil(L); + return 1; + } + + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->getNeedCasterTargetOrDirection()); + } else { + spell->setNeedCasterTargetOrDirection(getBoolean(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +// only for InstantSpells +int SpellFunctions::luaSpellIsBlockingWalls(lua_State* L) { + // spell:blockWalls(bool) + const auto &spellBase = getUserdataShared(L, 1); + const auto spell = std::static_pointer_cast(spellBase); + if (spell) { + // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil + if (spell->spellType != SPELL_INSTANT) { + lua_pushnil(L); + return 1; + } + + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->getBlockWalls()); + } else { + spell->setBlockWalls(getBoolean(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +// only for RuneSpells +int SpellFunctions::luaSpellRuneId(lua_State* L) { + // spell:runeId(id) + const auto &spellBase = getUserdataShared(L, 1); + const auto spell = std::static_pointer_cast(spellBase); + if (spell) { + // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil + if (spell->spellType != SPELL_RUNE) { + lua_pushnil(L); + return 1; + } + + if (lua_gettop(L) == 1) { + lua_pushnumber(L, spell->getRuneItemId()); + } else { + spell->setRuneItemId(getNumber(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +// only for RuneSpells +int SpellFunctions::luaSpellCharges(lua_State* L) { + // spell:charges(charges) + const auto &spellBase = getUserdataShared(L, 1); + const auto spell = std::static_pointer_cast(spellBase); + if (spell) { + // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil + if (spell->spellType != SPELL_RUNE) { + lua_pushnil(L); + return 1; + } + + if (lua_gettop(L) == 1) { + lua_pushnumber(L, spell->getCharges()); + } else { + spell->setCharges(getNumber(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +// only for RuneSpells +int SpellFunctions::luaSpellAllowFarUse(lua_State* L) { + // spell:allowFarUse(bool) + const auto &spellBase = getUserdataShared(L, 1); + const auto spell = std::static_pointer_cast(spellBase); + if (spell) { + // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil + if (spell->spellType != SPELL_RUNE) { + lua_pushnil(L); + return 1; + } + + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->getAllowFarUse()); + } else { + spell->setAllowFarUse(getBoolean(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +// only for RuneSpells +int SpellFunctions::luaSpellBlockWalls(lua_State* L) { + // spell:blockWalls(bool) + const auto &spellBase = getUserdataShared(L, 1); + const auto spell = std::static_pointer_cast(spellBase); + if (spell) { + // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil + if (spell->spellType != SPELL_RUNE) { + lua_pushnil(L); + return 1; + } + + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->getCheckLineOfSight()); + } else { + spell->setCheckLineOfSight(getBoolean(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +// only for RuneSpells +int SpellFunctions::luaSpellCheckFloor(lua_State* L) { + // spell:checkFloor(bool) + const auto &spellBase = getUserdataShared(L, 1); + const auto spell = std::static_pointer_cast(spellBase); + if (spell) { + // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil + if (spell->spellType != SPELL_RUNE) { + lua_pushnil(L); + return 1; + } + + if (lua_gettop(L) == 1) { + pushBoolean(L, spell->getCheckFloor()); + } else { + spell->setCheckFloor(getBoolean(L, 2)); + pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + +// Wheel of destiny +int SpellFunctions::luaSpellManaWOD(lua_State* L) { + // spell:manaWOD(grade, mana) + const auto spell = getUserdataShared(L, 1); + WheelSpellGrade_t grade = getNumber(L, 2); + if (!spell) { + reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + if (lua_gettop(L) == 2) { + lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::MANA, grade)); + } else { + spell->setWheelOfDestinyBoost(WheelSpellBoost_t::MANA, grade, getNumber(L, 3)); + spell->setWheelOfDestinyUpgraded(true); + pushBoolean(L, true); + } + return 1; +} + +int SpellFunctions::luaSpellCooldownWOD(lua_State* L) { + // spell:cooldownWOD(grade, time) + const auto spell = getUserdataShared(L, 1); + if (!spell) { + reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + WheelSpellGrade_t grade = getNumber(L, 2); + if (lua_gettop(L) == 2) { + lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::COOLDOWN, grade)); + } else { + spell->setWheelOfDestinyBoost(WheelSpellBoost_t::COOLDOWN, grade, getNumber(L, 3)); + spell->setWheelOfDestinyUpgraded(true); + pushBoolean(L, true); + } + return 1; +} + +int SpellFunctions::luaSpellGroupCooldownWOD(lua_State* L) { + // spell:groupCooldownWOD(grade, time) + const auto spell = getUserdataShared(L, 1); + if (!spell) { + reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + WheelSpellGrade_t grade = getNumber(L, 2); + if (lua_gettop(L) == 2) { + lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::GROUP_COOLDOWN, grade)); + } else { + spell->setWheelOfDestinyBoost(WheelSpellBoost_t::GROUP_COOLDOWN, grade, getNumber(L, 3)); + spell->setWheelOfDestinyUpgraded(true); + pushBoolean(L, true); + } + return 1; +} + +int SpellFunctions::luaSpellSecondaryGroupCooldownWOD(lua_State* L) { + // spell:secondaryGroupCooldownWOD(grade, time) + const auto spell = getUserdataShared(L, 1); + if (!spell) { + reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + WheelSpellGrade_t grade = getNumber(L, 2); + if (lua_gettop(L) == 2) { + lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::SECONDARY_GROUP_COOLDOWN, grade)); + } else { + spell->setWheelOfDestinyBoost(WheelSpellBoost_t::SECONDARY_GROUP_COOLDOWN, grade, getNumber(L, 3)); + spell->setWheelOfDestinyUpgraded(true); + pushBoolean(L, true); + } + return 1; +} + +int SpellFunctions::luaSpellIncreaseManaLeechWOD(lua_State* L) { + // spell:increaseManaLeechWOD(grade, value) + const auto spell = getUserdataShared(L, 1); + if (!spell) { + reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + WheelSpellGrade_t grade = getNumber(L, 2); + if (lua_gettop(L) == 2) { + lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::MANA_LEECH, grade)); + } else { + int32_t value = getNumber(L, 3); + if (value > 0) { + spell->setWheelOfDestinyBoost(WheelSpellBoost_t::MANA_LEECH_CHANCE, grade, 100); + } else { + spell->setWheelOfDestinyBoost(WheelSpellBoost_t::MANA_LEECH_CHANCE, grade, 0); + } + spell->setWheelOfDestinyBoost(WheelSpellBoost_t::MANA_LEECH, grade, value); + spell->setWheelOfDestinyUpgraded(true); + pushBoolean(L, true); + } + return 1; +} + +int SpellFunctions::luaSpellIncreaselifeLeechWOD(lua_State* L) { + // spell:increaselifeLeechWOD(grade, value) + const auto spell = getUserdataShared(L, 1); + if (!spell) { + reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + WheelSpellGrade_t grade = getNumber(L, 2); + if (lua_gettop(L) == 2) { + lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH, grade)); + } else { + int32_t value = getNumber(L, 3); + if (value > 0) { + spell->setWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH_CHANCE, grade, 100); + } else { + spell->setWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH_CHANCE, grade, 0); + } + spell->setWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH, grade, value); + spell->setWheelOfDestinyUpgraded(true); + pushBoolean(L, true); + } + return 1; +} + +int SpellFunctions::luaSpellIncreaseDamageWOD(lua_State* L) { + // spell:increaseDamageWOD(grade, value) + const auto spell = getUserdataShared(L, 1); + if (!spell) { + reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + WheelSpellGrade_t grade = getNumber(L, 2); + if (lua_gettop(L) == 2) { + lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::DAMAGE, grade)); + } else { + spell->setWheelOfDestinyBoost(WheelSpellBoost_t::DAMAGE, grade, getNumber(L, 3)); + spell->setWheelOfDestinyUpgraded(true); + pushBoolean(L, true); + } + return 1; +} + +int SpellFunctions::luaSpellIncreaseDamageReductionWOD(lua_State* L) { + // spell:increaseDamageReductionWOD(grade, value) + const auto spell = getUserdataShared(L, 1); + if (!spell) { + reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + WheelSpellGrade_t grade = getNumber(L, 2); + if (lua_gettop(L) == 2) { + lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::DAMAGE_REDUCTION, grade)); + } else { + spell->setWheelOfDestinyBoost(WheelSpellBoost_t::DAMAGE_REDUCTION, grade, getNumber(L, 3)); + spell->setWheelOfDestinyUpgraded(true); + pushBoolean(L, true); + } + return 1; +} + +int SpellFunctions::luaSpellIncreaseHealWOD(lua_State* L) { + // spell:increaseHealWOD(grade, value) + const auto spell = getUserdataShared(L, 1); + if (!spell) { + reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + WheelSpellGrade_t grade = getNumber(L, 2); + if (lua_gettop(L) == 2) { + lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::HEAL, grade)); + } else { + spell->setWheelOfDestinyBoost(WheelSpellBoost_t::HEAL, grade, getNumber(L, 3)); + spell->setWheelOfDestinyUpgraded(true); + pushBoolean(L, true); + } + return 1; +} + +int SpellFunctions::luaSpellIncreaseCriticalDamageWOD(lua_State* L) { + // spell:increaseCriticalDamageWOD(grade, value) + const auto spell = getUserdataShared(L, 1); + if (!spell) { + reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + WheelSpellGrade_t grade = getNumber(L, 2); + if (lua_gettop(L) == 2) { + lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::CRITICAL_DAMAGE, grade)); + } else { + spell->setWheelOfDestinyBoost(WheelSpellBoost_t::CRITICAL_DAMAGE, grade, getNumber(L, 3)); + spell->setWheelOfDestinyUpgraded(true); + pushBoolean(L, true); + } + return 1; +} + +int SpellFunctions::luaSpellIncreaseCriticalChanceWOD(lua_State* L) { + // spell:increaseCriticalChanceWOD(grade, value) + const auto spell = getUserdataShared(L, 1); + if (!spell) { + reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + WheelSpellGrade_t grade = getNumber(L, 2); + if (lua_gettop(L) == 2) { + lua_pushnumber(L, spell->getWheelOfDestinyBoost(WheelSpellBoost_t::CRITICAL_CHANCE, grade)); + } else { + spell->setWheelOfDestinyBoost(WheelSpellBoost_t::CRITICAL_CHANCE, grade, getNumber(L, 3)); + spell->setWheelOfDestinyUpgraded(true); + pushBoolean(L, true); + } + return 1; +} diff --git a/src/lua/functions/creatures/player/party_functions.cpp b/src/lua/functions/creatures/player/party_functions.cpp index 7c6ae7430b0..25feb5abb65 100644 --- a/src/lua/functions/creatures/player/party_functions.cpp +++ b/src/lua/functions/creatures/player/party_functions.cpp @@ -1,233 +1,233 @@ -/** - * Canary - A free and open-source MMORPG server emulator - * Copyright (©) 2019-2022 OpenTibiaBR - * Repository: https://github.com/opentibiabr/canary - * License: https://github.com/opentibiabr/canary/blob/main/LICENSE - * Contributors: https://github.com/opentibiabr/canary/graphs/contributors - * Website: https://docs.opentibiabr.com/ - */ - -#include "pch.hpp" - -#include "creatures/players/grouping/party.hpp" -#include "creatures/players/player.hpp" -#include "game/game.hpp" -#include "lua/functions/creatures/player/party_functions.hpp" - -int32_t PartyFunctions::luaPartyCreate(lua_State* L) { - // Party(userdata) - Player* player = getUserdata(L, 2); - if (!player) { - lua_pushnil(L); - return 1; - } - - Party* party = player->getParty(); - if (!party) { - party = new Party(player); - g_game().updatePlayerShield(player); - player->sendCreatureSkull(player); - pushUserdata(L, party); - setMetatable(L, -1, "Party"); - } else { - lua_pushnil(L); - } - return 1; -} - -int PartyFunctions::luaPartyDisband(lua_State* L) { - // party:disband() - Party** partyPtr = getRawUserdata(L, 1); - if (partyPtr && *partyPtr) { - Party*&party = *partyPtr; - party->disband(); - party = nullptr; - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int PartyFunctions::luaPartyGetLeader(lua_State* L) { - // party:getLeader() - Party* party = getUserdata(L, 1); - if (!party) { - lua_pushnil(L); - return 1; - } - - Player* leader = party->getLeader(); - if (leader) { - pushUserdata(L, leader); - setMetatable(L, -1, "Player"); - } else { - lua_pushnil(L); - } - return 1; -} - -int PartyFunctions::luaPartySetLeader(lua_State* L) { - // party:setLeader(player) - Player* player = getPlayer(L, 2); - Party* party = getUserdata(L, 1); - if (party && player) { - pushBoolean(L, party->passPartyLeadership(player)); - } else { - lua_pushnil(L); - } - return 1; -} - -int PartyFunctions::luaPartyGetMembers(lua_State* L) { - // party:getMembers() - Party* party = getUserdata(L, 1); - if (!party) { - lua_pushnil(L); - return 1; - } - - int index = 0; - lua_createtable(L, party->getMemberCount(), 0); - for (Player* player : party->getMembers()) { - pushUserdata(L, player); - setMetatable(L, -1, "Player"); - lua_rawseti(L, -2, ++index); - } - return 1; -} - -int PartyFunctions::luaPartyGetMemberCount(lua_State* L) { - // party:getMemberCount() - Party* party = getUserdata(L, 1); - if (party) { - lua_pushnumber(L, party->getMemberCount()); - } else { - lua_pushnil(L); - } - return 1; -} - -int PartyFunctions::luaPartyGetInvitees(lua_State* L) { - // party:getInvitees() - Party* party = getUserdata(L, 1); - if (party) { - lua_createtable(L, party->getInvitationCount(), 0); - - int index = 0; - for (Player* player : party->getInvitees()) { - pushUserdata(L, player); - setMetatable(L, -1, "Player"); - lua_rawseti(L, -2, ++index); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int PartyFunctions::luaPartyGetInviteeCount(lua_State* L) { - // party:getInviteeCount() - Party* party = getUserdata(L, 1); - if (party) { - lua_pushnumber(L, party->getInvitationCount()); - } else { - lua_pushnil(L); - } - return 1; -} - -int PartyFunctions::luaPartyAddInvite(lua_State* L) { - // party:addInvite(player) - Player* player = getPlayer(L, 2); - Party* party = getUserdata(L, 1); - if (party && player) { - pushBoolean(L, party->invitePlayer(*player)); - } else { - lua_pushnil(L); - } - return 1; -} - -int PartyFunctions::luaPartyRemoveInvite(lua_State* L) { - // party:removeInvite(player) - Player* player = getPlayer(L, 2); - Party* party = getUserdata(L, 1); - if (party && player) { - pushBoolean(L, party->removeInvite(*player)); - } else { - lua_pushnil(L); - } - return 1; -} - -int PartyFunctions::luaPartyAddMember(lua_State* L) { - // party:addMember(player) - Player* player = getPlayer(L, 2); - Party* party = getUserdata(L, 1); - if (party && player) { - pushBoolean(L, party->joinParty(*player)); - } else { - lua_pushnil(L); - } - return 1; -} - -int PartyFunctions::luaPartyRemoveMember(lua_State* L) { - // party:removeMember(player) - Player* player = getPlayer(L, 2); - Party* party = getUserdata(L, 1); - if (party && player) { - pushBoolean(L, party->leaveParty(player)); - } else { - lua_pushnil(L); - } - return 1; -} - -int PartyFunctions::luaPartyIsSharedExperienceActive(lua_State* L) { - // party:isSharedExperienceActive() - Party* party = getUserdata(L, 1); - if (party) { - pushBoolean(L, party->isSharedExperienceActive()); - } else { - lua_pushnil(L); - } - return 1; -} - -int PartyFunctions::luaPartyIsSharedExperienceEnabled(lua_State* L) { - // party:isSharedExperienceEnabled() - Party* party = getUserdata(L, 1); - if (party) { - pushBoolean(L, party->isSharedExperienceEnabled()); - } else { - lua_pushnil(L); - } - return 1; -} - -int PartyFunctions::luaPartyShareExperience(lua_State* L) { - // party:shareExperience(experience) - uint64_t experience = getNumber(L, 2); - Party* party = getUserdata(L, 1); - if (party) { - party->shareExperience(experience); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int PartyFunctions::luaPartySetSharedExperience(lua_State* L) { - // party:setSharedExperience(active) - bool active = getBoolean(L, 2); - Party* party = getUserdata(L, 1); - if (party) { - pushBoolean(L, party->setSharedExperience(party->getLeader(), active)); - } else { - lua_pushnil(L); - } - return 1; -} +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" + +#include "creatures/players/grouping/party.hpp" +#include "creatures/players/player.hpp" +#include "game/game.hpp" +#include "lua/functions/creatures/player/party_functions.hpp" + +int32_t PartyFunctions::luaPartyCreate(lua_State* L) { + // Party(userdata) + Player* player = getUserdata(L, 2); + if (!player) { + lua_pushnil(L); + return 1; + } + + Party* party = player->getParty(); + if (!party) { + party = new Party(player); + g_game().updatePlayerShield(player); + player->sendCreatureSkull(player); + pushUserdata(L, party); + setMetatable(L, -1, "Party"); + } else { + lua_pushnil(L); + } + return 1; +} + +int PartyFunctions::luaPartyDisband(lua_State* L) { + // party:disband() + Party** partyPtr = getRawUserdata(L, 1); + if (partyPtr && *partyPtr) { + Party*&party = *partyPtr; + party->disband(); + party = nullptr; + pushBoolean(L, true); + } else { + lua_pushnil(L); + } + return 1; +} + +int PartyFunctions::luaPartyGetLeader(lua_State* L) { + // party:getLeader() + Party* party = getUserdata(L, 1); + if (!party) { + lua_pushnil(L); + return 1; + } + + Player* leader = party->getLeader(); + if (leader) { + pushUserdata(L, leader); + setMetatable(L, -1, "Player"); + } else { + lua_pushnil(L); + } + return 1; +} + +int PartyFunctions::luaPartySetLeader(lua_State* L) { + // party:setLeader(player) + Player* player = getPlayer(L, 2); + Party* party = getUserdata(L, 1); + if (party && player) { + pushBoolean(L, party->passPartyLeadership(player)); + } else { + lua_pushnil(L); + } + return 1; +} + +int PartyFunctions::luaPartyGetMembers(lua_State* L) { + // party:getMembers() + Party* party = getUserdata(L, 1); + if (!party) { + lua_pushnil(L); + return 1; + } + + int index = 0; + lua_createtable(L, party->getMemberCount(), 0); + for (Player* player : party->getMembers()) { + pushUserdata(L, player); + setMetatable(L, -1, "Player"); + lua_rawseti(L, -2, ++index); + } + return 1; +} + +int PartyFunctions::luaPartyGetMemberCount(lua_State* L) { + // party:getMemberCount() + Party* party = getUserdata(L, 1); + if (party) { + lua_pushnumber(L, party->getMemberCount()); + } else { + lua_pushnil(L); + } + return 1; +} + +int PartyFunctions::luaPartyGetInvitees(lua_State* L) { + // party:getInvitees() + Party* party = getUserdata(L, 1); + if (party) { + lua_createtable(L, party->getInvitationCount(), 0); + + int index = 0; + for (Player* player : party->getInvitees()) { + pushUserdata(L, player); + setMetatable(L, -1, "Player"); + lua_rawseti(L, -2, ++index); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int PartyFunctions::luaPartyGetInviteeCount(lua_State* L) { + // party:getInviteeCount() + Party* party = getUserdata(L, 1); + if (party) { + lua_pushnumber(L, party->getInvitationCount()); + } else { + lua_pushnil(L); + } + return 1; +} + +int PartyFunctions::luaPartyAddInvite(lua_State* L) { + // party:addInvite(player) + Player* player = getPlayer(L, 2); + Party* party = getUserdata(L, 1); + if (party && player) { + pushBoolean(L, party->invitePlayer(*player)); + } else { + lua_pushnil(L); + } + return 1; +} + +int PartyFunctions::luaPartyRemoveInvite(lua_State* L) { + // party:removeInvite(player) + Player* player = getPlayer(L, 2); + Party* party = getUserdata(L, 1); + if (party && player) { + pushBoolean(L, party->removeInvite(*player)); + } else { + lua_pushnil(L); + } + return 1; +} + +int PartyFunctions::luaPartyAddMember(lua_State* L) { + // party:addMember(player) + Player* player = getPlayer(L, 2); + Party* party = getUserdata(L, 1); + if (party && player) { + pushBoolean(L, party->joinParty(*player)); + } else { + lua_pushnil(L); + } + return 1; +} + +int PartyFunctions::luaPartyRemoveMember(lua_State* L) { + // party:removeMember(player) + Player* player = getPlayer(L, 2); + Party* party = getUserdata(L, 1); + if (party && player) { + pushBoolean(L, party->leaveParty(player)); + } else { + lua_pushnil(L); + } + return 1; +} + +int PartyFunctions::luaPartyIsSharedExperienceActive(lua_State* L) { + // party:isSharedExperienceActive() + Party* party = getUserdata(L, 1); + if (party) { + pushBoolean(L, party->isSharedExperienceActive()); + } else { + lua_pushnil(L); + } + return 1; +} + +int PartyFunctions::luaPartyIsSharedExperienceEnabled(lua_State* L) { + // party:isSharedExperienceEnabled() + Party* party = getUserdata(L, 1); + if (party) { + pushBoolean(L, party->isSharedExperienceEnabled()); + } else { + lua_pushnil(L); + } + return 1; +} + +int PartyFunctions::luaPartyShareExperience(lua_State* L) { + // party:shareExperience(experience) + uint64_t experience = getNumber(L, 2); + Party* party = getUserdata(L, 1); + if (party) { + party->shareExperience(experience); + pushBoolean(L, true); + } else { + lua_pushnil(L); + } + return 1; +} + +int PartyFunctions::luaPartySetSharedExperience(lua_State* L) { + // party:setSharedExperience(active) + bool active = getBoolean(L, 2); + Party* party = getUserdata(L, 1); + if (party) { + pushBoolean(L, party->setSharedExperience(party->getLeader(), active)); + } else { + lua_pushnil(L); + } + return 1; +} diff --git a/src/lua/functions/events/action_functions.cpp b/src/lua/functions/events/action_functions.cpp index 61750d15bf7..f31b74618e9 100644 --- a/src/lua/functions/events/action_functions.cpp +++ b/src/lua/functions/events/action_functions.cpp @@ -1,217 +1,217 @@ -/** - * Canary - A free and open-source MMORPG server emulator - * Copyright (©) 2019-2022 OpenTibiaBR - * Repository: https://github.com/opentibiabr/canary - * License: https://github.com/opentibiabr/canary/blob/main/LICENSE - * Contributors: https://github.com/opentibiabr/canary/graphs/contributors - * Website: https://docs.opentibiabr.com/ - */ - -#include "pch.hpp" - -#include "lua/creature/actions.hpp" -#include "lua/functions/events/action_functions.hpp" -#include "game/game.hpp" -#include "items/item.hpp" - -int ActionFunctions::luaCreateAction(lua_State* L) { - // Action() - auto action = std::make_shared(getScriptEnv()->getScriptInterface()); - pushUserdata(L, action); - setMetatable(L, -1, "Action"); - return 1; -} - -int ActionFunctions::luaActionOnUse(lua_State* L) { - // action:onUse(callback) - const auto action = getUserdataShared(L, 1); - if (action) { - if (!action->loadCallback()) { - pushBoolean(L, false); - return 1; - } - action->setLoadedCallback(true); - pushBoolean(L, true); - } else { - reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); - pushBoolean(L, false); - } - return 1; -} - -int ActionFunctions::luaActionRegister(lua_State* L) { - // action:register() - const auto action = getUserdataShared(L, 1); - if (action) { - if (!action->isLoadedCallback()) { - pushBoolean(L, false); - return 1; - } - pushBoolean(L, g_actions().registerLuaEvent(action)); - pushBoolean(L, true); - } else { - reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); - pushBoolean(L, false); - } - return 1; -} - -int ActionFunctions::luaActionItemId(lua_State* L) { - // action:id(ids) - const auto action = getUserdataShared(L, 1); - if (action) { - int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc - if (parameters > 1) { - for (int i = 0; i < parameters; ++i) { - action->setItemIdsVector(getNumber(L, 2 + i)); - } - } else { - action->setItemIdsVector(getNumber(L, 2)); - } - pushBoolean(L, true); - } else { - reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); - pushBoolean(L, false); - } - return 1; -} - -int ActionFunctions::luaActionActionId(lua_State* L) { - // action:aid(aids) - const auto action = getUserdataShared(L, 1); - if (action) { - int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc - if (parameters > 1) { - for (int i = 0; i < parameters; ++i) { - action->setActionIdsVector(getNumber(L, 2 + i)); - } - } else { - action->setActionIdsVector(getNumber(L, 2)); - } - pushBoolean(L, true); - } else { - reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); - pushBoolean(L, false); - } - return 1; -} - -int ActionFunctions::luaActionUniqueId(lua_State* L) { - // action:uid(uids) - const auto action = getUserdataShared(L, 1); - if (action) { - int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc - if (parameters > 1) { - for (int i = 0; i < parameters; ++i) { - action->setUniqueIdsVector(getNumber(L, 2 + i)); - } - } else { - action->setUniqueIdsVector(getNumber(L, 2)); - } - pushBoolean(L, true); - } else { - reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); - pushBoolean(L, false); - } - return 1; -} - -int ActionFunctions::luaActionPosition(lua_State* L) { - /** @brief Create action position - * @param positions = position or table of positions to set a action script - * @param itemId or @param itemName = if item id or string name is set, the item is created on position (if not exists), this variable is nil by default - * action:position(positions, itemId or name) - */ - const auto action = getUserdataShared(L, 1); - if (!action) { - reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - Position position = getPosition(L, 2); - // The parameter "- 1" because self is a parameter aswell, which we want to skip L 1 (UserData) - // isNumber(L, 2) is for skip the itemId - if (int parameters = lua_gettop(L) - 1; - parameters > 1 && isNumber(L, 2)) { - for (int i = 0; i < parameters; ++i) { - action->setPositionsVector(getPosition(L, 2 + i)); - } - } else { - action->setPositionsVector(position); - } - - uint16_t itemId; - bool createItem = false; - if (isNumber(L, 3)) { - itemId = getNumber(L, 3); - createItem = true; - } else if (isString(L, 3)) { - itemId = Item::items.getItemIdByName(getString(L, 3)); - if (itemId == 0) { - reportErrorFunc("Not found item with name: " + getString(L, 3)); - pushBoolean(L, false); - return 1; - } - - createItem = true; - } - - if (createItem) { - if (!Item::items.hasItemType(itemId)) { - reportErrorFunc("Not found item with id: " + itemId); - pushBoolean(L, false); - return 1; - } - - // If it is an item that can be removed, then it will be set as non-movable. - ItemType &itemType = Item::items.getItemType(itemId); - if (itemType.moveable == true) { - itemType.moveable = false; - } - - g_game().setCreateLuaItems(position, itemId); - } - - pushBoolean(L, true); - return 1; -} - -int ActionFunctions::luaActionAllowFarUse(lua_State* L) { - // action:allowFarUse(bool) - const auto action = getUserdataShared(L, 1); - if (action) { - action->setAllowFarUse(getBoolean(L, 2)); - pushBoolean(L, true); - } else { - reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); - pushBoolean(L, false); - } - return 1; -} - -int ActionFunctions::luaActionBlockWalls(lua_State* L) { - // action:blockWalls(bool) - const auto action = getUserdataShared(L, 1); - if (action) { - action->setCheckLineOfSight(getBoolean(L, 2)); - pushBoolean(L, true); - } else { - reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); - pushBoolean(L, false); - } - return 1; -} - -int ActionFunctions::luaActionCheckFloor(lua_State* L) { - // action:checkFloor(bool) - const auto action = getUserdataShared(L, 1); - if (action) { - action->setCheckFloor(getBoolean(L, 2)); - pushBoolean(L, true); - } else { - reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); - pushBoolean(L, false); - } - return 1; -} +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "pch.hpp" + +#include "lua/creature/actions.hpp" +#include "lua/functions/events/action_functions.hpp" +#include "game/game.hpp" +#include "items/item.hpp" + +int ActionFunctions::luaCreateAction(lua_State* L) { + // Action() + auto action = std::make_shared(getScriptEnv()->getScriptInterface()); + pushUserdata(L, action); + setMetatable(L, -1, "Action"); + return 1; +} + +int ActionFunctions::luaActionOnUse(lua_State* L) { + // action:onUse(callback) + const auto action = getUserdataShared(L, 1); + if (action) { + if (!action->loadCallback()) { + pushBoolean(L, false); + return 1; + } + action->setLoadedCallback(true); + pushBoolean(L, true); + } else { + reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); + pushBoolean(L, false); + } + return 1; +} + +int ActionFunctions::luaActionRegister(lua_State* L) { + // action:register() + const auto action = getUserdataShared(L, 1); + if (action) { + if (!action->isLoadedCallback()) { + pushBoolean(L, false); + return 1; + } + pushBoolean(L, g_actions().registerLuaEvent(action)); + pushBoolean(L, true); + } else { + reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); + pushBoolean(L, false); + } + return 1; +} + +int ActionFunctions::luaActionItemId(lua_State* L) { + // action:id(ids) + const auto action = getUserdataShared(L, 1); + if (action) { + int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc + if (parameters > 1) { + for (int i = 0; i < parameters; ++i) { + action->setItemIdsVector(getNumber(L, 2 + i)); + } + } else { + action->setItemIdsVector(getNumber(L, 2)); + } + pushBoolean(L, true); + } else { + reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); + pushBoolean(L, false); + } + return 1; +} + +int ActionFunctions::luaActionActionId(lua_State* L) { + // action:aid(aids) + const auto action = getUserdataShared(L, 1); + if (action) { + int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc + if (parameters > 1) { + for (int i = 0; i < parameters; ++i) { + action->setActionIdsVector(getNumber(L, 2 + i)); + } + } else { + action->setActionIdsVector(getNumber(L, 2)); + } + pushBoolean(L, true); + } else { + reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); + pushBoolean(L, false); + } + return 1; +} + +int ActionFunctions::luaActionUniqueId(lua_State* L) { + // action:uid(uids) + const auto action = getUserdataShared(L, 1); + if (action) { + int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc + if (parameters > 1) { + for (int i = 0; i < parameters; ++i) { + action->setUniqueIdsVector(getNumber(L, 2 + i)); + } + } else { + action->setUniqueIdsVector(getNumber(L, 2)); + } + pushBoolean(L, true); + } else { + reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); + pushBoolean(L, false); + } + return 1; +} + +int ActionFunctions::luaActionPosition(lua_State* L) { + /** @brief Create action position + * @param positions = position or table of positions to set a action script + * @param itemId or @param itemName = if item id or string name is set, the item is created on position (if not exists), this variable is nil by default + * action:position(positions, itemId or name) + */ + const auto action = getUserdataShared(L, 1); + if (!action) { + reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + Position position = getPosition(L, 2); + // The parameter "- 1" because self is a parameter aswell, which we want to skip L 1 (UserData) + // isNumber(L, 2) is for skip the itemId + if (int parameters = lua_gettop(L) - 1; + parameters > 1 && isNumber(L, 2)) { + for (int i = 0; i < parameters; ++i) { + action->setPositionsVector(getPosition(L, 2 + i)); + } + } else { + action->setPositionsVector(position); + } + + uint16_t itemId; + bool createItem = false; + if (isNumber(L, 3)) { + itemId = getNumber(L, 3); + createItem = true; + } else if (isString(L, 3)) { + itemId = Item::items.getItemIdByName(getString(L, 3)); + if (itemId == 0) { + reportErrorFunc("Not found item with name: " + getString(L, 3)); + pushBoolean(L, false); + return 1; + } + + createItem = true; + } + + if (createItem) { + if (!Item::items.hasItemType(itemId)) { + reportErrorFunc("Not found item with id: " + itemId); + pushBoolean(L, false); + return 1; + } + + // If it is an item that can be removed, then it will be set as non-movable. + ItemType &itemType = Item::items.getItemType(itemId); + if (itemType.moveable == true) { + itemType.moveable = false; + } + + g_game().setCreateLuaItems(position, itemId); + } + + pushBoolean(L, true); + return 1; +} + +int ActionFunctions::luaActionAllowFarUse(lua_State* L) { + // action:allowFarUse(bool) + const auto action = getUserdataShared(L, 1); + if (action) { + action->setAllowFarUse(getBoolean(L, 2)); + pushBoolean(L, true); + } else { + reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); + pushBoolean(L, false); + } + return 1; +} + +int ActionFunctions::luaActionBlockWalls(lua_State* L) { + // action:blockWalls(bool) + const auto action = getUserdataShared(L, 1); + if (action) { + action->setCheckLineOfSight(getBoolean(L, 2)); + pushBoolean(L, true); + } else { + reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); + pushBoolean(L, false); + } + return 1; +} + +int ActionFunctions::luaActionCheckFloor(lua_State* L) { + // action:checkFloor(bool) + const auto action = getUserdataShared(L, 1); + if (action) { + action->setCheckFloor(getBoolean(L, 2)); + pushBoolean(L, true); + } else { + reportErrorFunc(getErrorDesc(LUA_ERROR_ACTION_NOT_FOUND)); + pushBoolean(L, false); + } + return 1; +}