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/data-otservbr-global/monster/quests/cults_of_tibia/bosses/summons/liquor_spirit.lua b/data-otservbr-global/monster/quests/cults_of_tibia/bosses/summons/liquor_spirit.lua index c5d603cc688..f3bbf3dcff8 100644 --- a/data-otservbr-global/monster/quests/cults_of_tibia/bosses/summons/liquor_spirit.lua +++ b/data-otservbr-global/monster/quests/cults_of_tibia/bosses/summons/liquor_spirit.lua @@ -56,14 +56,6 @@ monster.flags = { canWalkOnPoison = true, } -monster.events = { - "Evaporate", -} - -monster.events = { - "Evaporate", -} - monster.light = { level = 0, color = 0, diff --git a/data-otservbr-global/monster/quests/cults_of_tibia/bosses/wine_cask.lua b/data-otservbr-global/monster/quests/cults_of_tibia/bosses/wine_cask.lua index 1d0ad0c3955..1f8e6024792 100644 --- a/data-otservbr-global/monster/quests/cults_of_tibia/bosses/wine_cask.lua +++ b/data-otservbr-global/monster/quests/cults_of_tibia/bosses/wine_cask.lua @@ -50,14 +50,6 @@ monster.flags = { canWalkOnPoison = true, } -monster.events = { - "Splash", -} - -monster.events = { - "Splash", -} - monster.light = { level = 0, color = 0, diff --git a/data-otservbr-global/monster/quests/forgotten_knowledge/cosmic_energy_prism_d.lua b/data-otservbr-global/monster/quests/forgotten_knowledge/cosmic_energy_prism_d.lua index 876f47ddcff..cf53e32a384 100644 --- a/data-otservbr-global/monster/quests/forgotten_knowledge/cosmic_energy_prism_d.lua +++ b/data-otservbr-global/monster/quests/forgotten_knowledge/cosmic_energy_prism_d.lua @@ -52,10 +52,6 @@ monster.light = { color = 0, } -monster.events = { - "EnergyPrism", -} - monster.voices = { interval = 5000, chance = 10, diff --git a/data-otservbr-global/monster/quests/heart_of_destruction/anomaly.lua b/data-otservbr-global/monster/quests/heart_of_destruction/anomaly.lua index 4d41d6d46e9..79a7f3b9e4a 100644 --- a/data-otservbr-global/monster/quests/heart_of_destruction/anomaly.lua +++ b/data-otservbr-global/monster/quests/heart_of_destruction/anomaly.lua @@ -63,16 +63,6 @@ monster.flags = { canWalkOnPoison = true, } -monster.events = { - "AnomalyTransform", - "HeartBossDeath", -} - -monster.events = { - "AnomalyTransform", - "HeartBossDeath", -} - monster.light = { level = 0, color = 0, diff --git a/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_chaplain_gaunder.lua b/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_chaplain_gaunder.lua index 955b688d645..bd22561f584 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_chaplain_gaunder.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_chaplain_gaunder.lua @@ -58,10 +58,6 @@ monster.flags = { canWalkOnPoison = true, } -monster.events = { - "GrandChaplainGaunderDeath", -} - monster.light = { level = 0, color = 0, diff --git a/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_commander_soeren.lua b/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_commander_soeren.lua index e38e21c1fc0..9e8a232f9df 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_commander_soeren.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/bosses/grand_commander_soeren.lua @@ -58,10 +58,6 @@ monster.flags = { canWalkOnPoison = true, } -monster.events = { - "GrandCommanderSoerenDeath", -} - monster.light = { level = 0, color = 0, diff --git a/data-otservbr-global/monster/quests/the_secret_library/stolen_tome_of_portals.lua b/data-otservbr-global/monster/quests/the_secret_library/stolen_tome_of_portals.lua index be93e4d9bfa..b3bfece6e89 100644 --- a/data-otservbr-global/monster/quests/the_secret_library/stolen_tome_of_portals.lua +++ b/data-otservbr-global/monster/quests/the_secret_library/stolen_tome_of_portals.lua @@ -47,10 +47,6 @@ monster.flags = { canWalkOnPoison = true, } -monster.events = { - "gorzindelDeath", -} - monster.light = { level = 0, color = 0, diff --git a/data-otservbr-global/npc/emael.lua b/data-otservbr-global/npc/emael.lua index b2e27b43f77..a7de2721fb2 100644 --- a/data-otservbr-global/npc/emael.lua +++ b/data-otservbr-global/npc/emael.lua @@ -68,7 +68,7 @@ local function creatureSayCallback(npc, creature, type, message) if player:removeMoney(1000000) then npcHandler:say("Ah, I see you killed a lot of dangerous creatures. Here's your podium of vigour!", npc, creature) local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX) - if inbox and inbox:getEmptySlots() > 0 then + if inbox then local decoKit = inbox:addItem(23398, 1) if decoKit then decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "Unwrap it in your own house to create a <" .. ItemType(38707):getName() .. ">.") diff --git a/data-otservbr-global/npc/emperor_kruzak.lua b/data-otservbr-global/npc/emperor_kruzak.lua index 9aa681d756d..53ce265ffab 100644 --- a/data-otservbr-global/npc/emperor_kruzak.lua +++ b/data-otservbr-global/npc/emperor_kruzak.lua @@ -81,7 +81,7 @@ local function creatureSayCallback(npc, creature, type, message) if player:getStorageValue(Storage.OutfitQuest.GoldenOutfit) < 1 then if player:getMoney() + player:getBankBalance() >= 500000000 then local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX) - if inbox and inbox:getEmptySlots() > 0 then + if inbox then local decoKit = inbox:addItem(23398, 1) local decoItemName = ItemType(31510):getName() decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "You bought this item in the Store.\nUnwrap it in your own house to create a " .. decoItemName .. ".") diff --git a/data-otservbr-global/npc/hireling.lua b/data-otservbr-global/npc/hireling.lua index a22763aad6f..c96c8330e05 100644 --- a/data-otservbr-global/npc/hireling.lua +++ b/data-otservbr-global/npc/hireling.lua @@ -480,7 +480,7 @@ function createHirelingType(HirelingName) if player:getFreeCapacity() < itType:getWeight(1) then npcHandler:say("Sorry, but you don't have enough capacity.", npc, creature) - elseif not inbox or inbox:getEmptySlots() == 0 then + elseif not inbox then player:getPosition():sendMagicEffect(CONST_ME_POFF) npcHandler:say("Sorry, you don't have enough room on your inbox", npc, creature) elseif not player:removeMoneyBank(15000) then diff --git a/data-otservbr-global/npc/king_tibianus.lua b/data-otservbr-global/npc/king_tibianus.lua index ccf5d5cf2b5..a8a24facf67 100644 --- a/data-otservbr-global/npc/king_tibianus.lua +++ b/data-otservbr-global/npc/king_tibianus.lua @@ -86,7 +86,7 @@ local function creatureSayCallback(npc, creature, type, message) if player:getStorageValue(Storage.OutfitQuest.GoldenOutfit) < 1 then if player:getMoney() + player:getBankBalance() >= 500000000 then local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX) - if inbox and inbox:getEmptySlots() > 0 then + if inbox then local decoKit = inbox:addItem(23398, 1) local decoItemName = ItemType(31510):getName() decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "Unwrap it in your own house to create a " .. decoItemName .. ".") diff --git a/data-otservbr-global/npc/queen_eloise.lua b/data-otservbr-global/npc/queen_eloise.lua index 6b08660b923..c44617b10dd 100644 --- a/data-otservbr-global/npc/queen_eloise.lua +++ b/data-otservbr-global/npc/queen_eloise.lua @@ -76,7 +76,7 @@ local function creatureSayCallback(npc, creature, type, message) if player:getStorageValue(Storage.OutfitQuest.GoldenOutfit) < 1 then if player:getMoney() + player:getBankBalance() >= 500000000 then local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX) - if inbox and inbox:getEmptySlots() > 0 then + if inbox then local decoKit = inbox:addItem(23398, 1) local decoItemName = ItemType(31510):getName() decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "You bought this item in the Store.\nUnwrap it in your own house to create a " .. decoItemName .. ".") diff --git a/data-otservbr-global/npc/walter_jaeger.lua b/data-otservbr-global/npc/walter_jaeger.lua index 5ddd2463376..340aaae1eb6 100644 --- a/data-otservbr-global/npc/walter_jaeger.lua +++ b/data-otservbr-global/npc/walter_jaeger.lua @@ -282,7 +282,7 @@ local function processItemInboxPurchase(player, name, id) end local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX) - if inbox and inbox:getEmptySlots() > 0 then + if inbox then local decoKit = inbox:addItem(23398, 1) if decoKit then decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "You bought this item with the Walter Jaeger.\nUnwrap it in your own house to create a <" .. name .. ">.") diff --git a/data-otservbr-global/scripts/quests/primal_ordeal_quest/magma_bubble_fight.lua b/data-otservbr-global/scripts/quests/primal_ordeal_quest/magma_bubble_fight.lua index 2c5d8957a28..f13c55d89e4 100644 --- a/data-otservbr-global/scripts/quests/primal_ordeal_quest/magma_bubble_fight.lua +++ b/data-otservbr-global/scripts/quests/primal_ordeal_quest/magma_bubble_fight.lua @@ -95,25 +95,25 @@ end encounter:register() local function addShieldStack(player) - local currentIcon = player:getIcon() + local currentIcon = player:getIcon("magma-bubble") if not currentIcon or currentIcon.category ~= CreatureIconCategory_Quests or currentIcon.icon ~= CreatureIconQuests_GreenShield then - player:setIcon(CreatureIconCategory_Quests, CreatureIconQuests_GreenShield, 5) + player:setIcon("magma-bubble", CreatureIconCategory_Quests, CreatureIconQuests_GreenShield, 5) return true end - player:setIcon(CreatureIconCategory_Quests, CreatureIconQuests_GreenShield, currentIcon.count + 5) + player:setIcon("magma-bubble", CreatureIconCategory_Quests, CreatureIconQuests_GreenShield, currentIcon.count + 5) end local function tickShields(player) - local currentIcon = player:getIcon() + local currentIcon = player:getIcon("magma-bubble") if not currentIcon or currentIcon.category ~= CreatureIconCategory_Quests or currentIcon.icon ~= CreatureIconQuests_GreenShield then return 0 end if currentIcon.count <= 0 then - player:clearIcon() + player:removeIcon("magma-bubble") return 0 end local newCount = currentIcon.count - 1 - player:setIcon(CreatureIconCategory_Quests, CreatureIconQuests_GreenShield, newCount) + player:setIcon("magma-bubble", CreatureIconCategory_Quests, CreatureIconQuests_GreenShield, newCount) return newCount end @@ -294,3 +294,24 @@ function magmaBubbleDeath.onDeath() end magmaBubbleDeath:register() + +local zoneEvent = ZoneEvent(bossZone) +function zoneEvent.afterEnter(_zone, creature) + local player = creature:getPlayer() + if not player then + return false + end + + player:setIcon("magma-bubble", CreatureIconCategory_Quests, CreatureIconQuests_GreenShield, 0) +end + +function zoneEvent.afterLeave(_zone, creature) + local player = creature:getPlayer() + if not player then + return false + end + + player:removeIcon("magma-bubble") +end + +zoneEvent:register() diff --git a/data/events/scripts/player.lua b/data/events/scripts/player.lua index a93ffe80ea3..10c8c0e76bc 100644 --- a/data/events/scripts/player.lua +++ b/data/events/scripts/player.lua @@ -1,6 +1,3 @@ -CONTAINER_WEIGHT_CHECK = true -- true = enable / false = disable -CONTAINER_WEIGHT_MAX = 1000000 -- 1000000 = 10k = 10000.00 oz - local storeItemID = { -- registered item ids here are not tradable with players -- these items can be set to moveable at items.xml @@ -240,19 +237,13 @@ function Player:onMoveItem(item, count, fromPosition, toPosition, fromCylinder, return false end - -- No move if item count > 20 items + -- No move if tile item count > 20 items local tile = Tile(toPosition) if tile and tile:getItemCount() > 20 then self:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE) return false end - -- No move parcel very heavy - if CONTAINER_WEIGHT_CHECK and ItemType(item:getId()):isContainer() and item:getWeight() > CONTAINER_WEIGHT_MAX then - self:sendCancelMessage("Your cannot move this item too heavy.") - return false - end - -- Players cannot throw items on teleports if blockTeleportTrashing and toPosition.x ~= CONTAINER_POSITION then local thing = Tile(toPosition):getItemByType(ITEM_TYPE_TELEPORT) diff --git a/data/items/appearances.dat b/data/items/appearances.dat index 58614bcd4bf..9f0de0ece0e 100644 Binary files a/data/items/appearances.dat and b/data/items/appearances.dat differ diff --git a/data/libs/functions/creature.lua b/data/libs/functions/creature.lua index 04dcb9e8473..86245cc2491 100644 --- a/data/libs/functions/creature.lua +++ b/data/libs/functions/creature.lua @@ -203,7 +203,3 @@ function Creature:addEventStamina(target) end end end - -function Creature:clearIcon() - self:setIcon(0, 0) -end diff --git a/data/libs/hireling_lib.lua b/data/libs/hireling_lib.lua index bf21b328661..beadcdb6cf1 100644 --- a/data/libs/hireling_lib.lua +++ b/data/libs/hireling_lib.lua @@ -394,7 +394,7 @@ function Hireling:returnToLamp(player_id) end local inbox = owner:getSlotItem(CONST_SLOT_STORE_INBOX) - if not inbox or inbox:getEmptySlots() < 1 then + if not inbox then owner:getPosition():sendMagicEffect(CONST_ME_POFF) return owner:sendTextMessage(MESSAGE_FAILURE, "You don't have enough room in your inbox.") end @@ -553,7 +553,7 @@ function Player:addNewHireling(name, sex) end local inbox = self:getSlotItem(CONST_SLOT_STORE_INBOX) - if not inbox or inbox:getEmptySlots() < 1 then + if not inbox then self:getPosition():sendMagicEffect(CONST_ME_POFF) self:sendTextMessage(MESSAGE_FAILURE, "You don't have enough room in your inbox.") return false diff --git a/data/modules/scripts/daily_reward/daily_reward.lua b/data/modules/scripts/daily_reward/daily_reward.lua index 8f39e6c0287..1c6feba310c 100644 --- a/data/modules/scripts/daily_reward/daily_reward.lua +++ b/data/modules/scripts/daily_reward/daily_reward.lua @@ -463,7 +463,7 @@ function Player.selectDailyReward(self, msg) -- Adding items to store inbox local inbox = self:getSlotItem(CONST_SLOT_STORE_INBOX) - if inbox and inbox:getEmptySlots() < columnsPicked then + if not inbox then self:sendError("You do not have enough space in your store inbox.") return false end diff --git a/data/modules/scripts/gamestore/init.lua b/data/modules/scripts/gamestore/init.lua index 9ae19559049..20b3cf74803 100644 --- a/data/modules/scripts/gamestore/init.lua +++ b/data/modules/scripts/gamestore/init.lua @@ -1503,7 +1503,7 @@ function GameStore.processItemPurchase(player, offerId, offerCount, moveable) end local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX) - if inbox and inbox:getEmptySlots() > offerCount then + if inbox then for t = 1, offerCount do local inboxItem = inbox:addItem(offerId, offerCount or 1) if moveable ~= true and inboxItem then @@ -1521,7 +1521,7 @@ function GameStore.processChargesPurchase(player, itemtype, name, charges, movea end local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX) - if inbox and inbox:getEmptySlots() > 1 then + if inbox then local inboxItem = inbox:addItem(itemtype, charges) if moveable ~= true and inboxItem then @@ -1582,7 +1582,7 @@ function GameStore.processStackablePurchase(player, offerId, offerCount, offerNa end local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX) - if inbox and inbox:getEmptySlots() > 0 then + if inbox then if (isKeg and offerCount > 500) or offerCount > 100 then local parcel = inbox:addItem(PARCEL_ID, 1) parcel:setAttribute(ITEM_ATTRIBUTE_STORE, systemTime()) @@ -1637,7 +1637,7 @@ function GameStore.processHouseRelatedPurchase(player, offer) if type(itemIds) ~= "table" then itemIds = { itemIds } end - if inbox and inbox:getEmptySlots() >= #itemIds then + if inbox then for _, itemId in ipairs(itemIds) do local decoKit = inbox:addItem(23398, 1) if decoKit then @@ -1652,6 +1652,7 @@ function GameStore.processHouseRelatedPurchase(player, offer) end end end + player:sendUpdateContainer(inbox) else return error({ code = 0, message = "Please make sure you have free slots in your store inbox." }) end diff --git a/data/scripts/eventcallbacks/player/on_rotate_item.lua b/data/scripts/eventcallbacks/player/on_rotate_item.lua index 407402056b8..3692fcdc655 100644 --- a/data/scripts/eventcallbacks/player/on_rotate_item.lua +++ b/data/scripts/eventcallbacks/player/on_rotate_item.lua @@ -1,7 +1,7 @@ local callback = EventCallback() function callback.playerOnRotateItem(player, item, position) - if item:getActionId() == 100 then + if item:getActionId() == IMMOVABLE_ACTION_ID then player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE) return false end diff --git a/data/scripts/talkactions/god/goto_house.lua b/data/scripts/talkactions/god/goto_house.lua new file mode 100644 index 00000000000..407649c8329 --- /dev/null +++ b/data/scripts/talkactions/god/goto_house.lua @@ -0,0 +1,28 @@ +local sellHouse = TalkAction("/gotohouse") + +function sellHouse.onSay(player, words, param) + local targetPlayer = Player(param) + if targetPlayer then + local targetHouse = targetPlayer:getHouse() + if not targetHouse then + targetPlayer:sendCancelMessage(string.format("The player %s not have house.", player:getName())) + return + end + + targetPlayer:teleportTo(targetHouse:getExitPosition()) + else + local house = player:getHouse() + if not house then + player:sendCancelMessage("You not have house. For goto house of one player use the player name param, usage: /gotohouse playername") + return + end + + player:teleportTo(house:getExitPosition()) + end + + return false +end + +sellHouse:separator(" ") +sellHouse:groupType("god") +sellHouse:register() diff --git a/src/account/account.cpp b/src/account/account.cpp index 30b92b5c694..5c093f917bd 100644 --- a/src/account/account.cpp +++ b/src/account/account.cpp @@ -170,7 +170,7 @@ namespace account { } void Account::addPremiumDays(const int32_t &days) { - uint32_t timeLeft = static_cast((m_account.premiumLastDay - getTimeNow()) % 86400); + auto timeLeft = static_cast((m_account.premiumLastDay - getTimeNow()) % 86400); setPremiumDays(m_account.premiumRemainingDays + days); if (timeLeft > 0) { @@ -199,8 +199,8 @@ namespace account { time_t currentTime = getTimeNow(); - uint32_t daysLeft = static_cast((lastDay - currentTime) / 86400); - uint32_t timeLeft = static_cast((lastDay - currentTime) % 86400); + auto daysLeft = static_cast((lastDay - currentTime) / 86400); + auto timeLeft = static_cast((lastDay - currentTime) % 86400); m_account.premiumRemainingDays = daysLeft > 0 ? daysLeft : 0; diff --git a/src/config/config_definitions.hpp b/src/config/config_definitions.hpp index 081a5e7d702..eb8d2b07e86 100644 --- a/src/config/config_definitions.hpp +++ b/src/config/config_definitions.hpp @@ -156,7 +156,7 @@ enum integerConfig_t { STATUS_PORT, STAIRHOP_DELAY, MAX_CONTAINER, - MAX_ITEM, + MAX_CONTAINER_ITEM, MARKET_OFFER_DURATION, DEPOT_BOXES, FREE_DEPOT_LIMIT, diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index a4e4e6750e3..eb6d901d1f2 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -236,7 +236,7 @@ bool ConfigManager::load() { integer[WHITE_SKULL_TIME] = getGlobalNumber(L, "whiteSkullTime", 15 * 60 * 1000); integer[STAIRHOP_DELAY] = getGlobalNumber(L, "stairJumpExhaustion", 2000); integer[MAX_CONTAINER] = getGlobalNumber(L, "maxContainer", 500); - integer[MAX_ITEM] = getGlobalNumber(L, "maxItem", 10000); + integer[MAX_CONTAINER_ITEM] = getGlobalNumber(L, "maxItem", 5000); integer[EXP_FROM_PLAYERS_LEVEL_RANGE] = getGlobalNumber(L, "expFromPlayersLevelRange", 75); integer[CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES] = getGlobalNumber(L, "checkExpiredMarketOffersEachMinutes", 60); integer[MAX_MARKET_OFFERS_AT_A_TIME_PER_PLAYER] = getGlobalNumber(L, "maxMarketOffersAtATimePerPlayer", 100); diff --git a/src/core.hpp b/src/core.hpp index ab48ea9d7d2..40e60b2dc27 100644 --- a/src/core.hpp +++ b/src/core.hpp @@ -16,7 +16,7 @@ static constexpr auto STATUS_SERVER_DEVELOPERS = "OpenTibiaBR Organization"; static constexpr auto AUTHENTICATOR_DIGITS = 6U; static constexpr auto AUTHENTICATOR_PERIOD = 30U; -static constexpr auto CLIENT_VERSION = 1320; +static constexpr auto CLIENT_VERSION = 1321; #define CLIENT_VERSION_UPPER (CLIENT_VERSION / 100) #define CLIENT_VERSION_LOWER (CLIENT_VERSION % 100) diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index bc5fda492e7..78d7720bf57 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -1788,8 +1788,7 @@ const phmap::parallel_flat_hash_set> Creature::getZones() return Zone::getZones(getPosition()); } -void Creature::setIcon(CreatureIcon icon) { - creatureIcon = icon; +void Creature::iconChanged() const { if (!tile) { return; } diff --git a/src/creatures/creature.hpp b/src/creatures/creature.hpp index 9070ae41230..3349ddb83a9 100644 --- a/src/creatures/creature.hpp +++ b/src/creatures/creature.hpp @@ -222,16 +222,41 @@ class Creature : virtual public Thing { varBuffs[buff] += modifier; } - virtual CreatureIcon getIcon() const { - return creatureIcon; + virtual std::vector getIcons() const { + std::vector icons; + icons.reserve(creatureIcons.size()); + for (const auto &[_, icon] : creatureIcons) { + if (icon.isSet()) { + icons.push_back(icon); + } + } + return icons; + } + + virtual CreatureIcon getIcon(const std::string &key) const { + if (!creatureIcons.contains(key)) { + return CreatureIcon(); + } + return creatureIcons.at(key); } - void setIcon(CreatureIcon icon); + void setIcon(const std::string &key, CreatureIcon icon) { + creatureIcons[key] = icon; + iconChanged(); + } + + void removeIcon(const std::string &key) { + creatureIcons.erase(key); + iconChanged(); + } - void clearIcon() { - setIcon(CreatureIcon()); + void clearIcons() { + creatureIcons.clear(); + iconChanged(); } + void iconChanged() const; + const Outfit_t getCurrentOutfit() const { return currentOutfit; } @@ -726,7 +751,8 @@ class Creature : virtual public Thing { uint8_t wheelOfDestinyDrainBodyDebuff = 0; - CreatureIcon creatureIcon = CreatureIcon(); + // use map here instead of phmap to keep the keys in a predictable order + std::map creatureIcons = {}; // creature script events bool hasEventRegistered(CreatureEventType_t event) const { diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index f344529a4d2..43d41567bfa 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -2122,12 +2122,12 @@ void Monster::configureForgeSystem() { if (monsterForgeClassification == ForgeClassifications_t::FORGE_FIENDISH_MONSTER) { setForgeStack(15); - setIcon(CreatureIcon(CreatureIconModifications_t::Fiendish, 0 /* don't show stacks on fiends */)); + setIcon("forge", CreatureIcon(CreatureIconModifications_t::Fiendish, 0 /* don't show stacks on fiends */)); g_game().updateCreatureIcon(this); } else if (monsterForgeClassification == ForgeClassifications_t::FORGE_INFLUENCED_MONSTER) { auto stack = static_cast(normal_random(1, 5)); setForgeStack(stack); - setIcon(CreatureIcon(CreatureIconModifications_t::Influenced, stack)); + setIcon("forge", CreatureIcon(CreatureIconModifications_t::Influenced, stack)); g_game().updateCreatureIcon(this); } @@ -2153,7 +2153,7 @@ void Monster::clearFiendishStatus() { health = mType->info.health * mType->getHealthMultiplier(); healthMax = mType->info.healthMax * mType->getHealthMultiplier(); - clearIcon(); + removeIcon("forge"); g_game().updateCreatureIcon(this); g_game().sendUpdateCreature(this); } diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index f1c966fcbc3..a479b494a9a 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -160,18 +160,19 @@ class Monster final : public Creature { return challengeFocusDuration > 0; } - CreatureIcon getIcon() const override { - if (creatureIcon.isSet()) { - return creatureIcon; + std::vector getIcons() const override { + const auto creatureIcons = Creature::getIcons(); + if (!creatureIcons.empty()) { + return creatureIcons; } if (challengeMeleeDuration > 0 && mType->info.targetDistance > targetDistance) { - return CreatureIcon(CreatureIconModifications_t::TurnedMelee); + return { CreatureIcon(CreatureIconModifications_t::TurnedMelee) }; } else if (varBuffs[BUFF_DAMAGERECEIVED] > 100) { - return CreatureIcon(CreatureIconModifications_t::HigherDamageReceived); + return { CreatureIcon(CreatureIconModifications_t::HigherDamageReceived) }; } else if (varBuffs[BUFF_DAMAGEDEALT] < 100) { - return CreatureIcon(CreatureIconModifications_t::LowerDamageDealt); + return { CreatureIcon(CreatureIconModifications_t::LowerDamageDealt) }; } - return CreatureIcon(); + return {}; } void setNormalCreatureLight() override; diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index 83793f7a17a..5f02435cf5a 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -242,7 +242,7 @@ void SpawnMonster::checkSpawnMonster() { } if (spawnedMonsterMap.size() < spawnMonsterMap.size()) { - checkSpawnMonsterEvent = g_scheduler().addEvent(getInterval(), std::bind(&SpawnMonster::checkSpawnMonster, this), __FUNCTION__); + checkSpawnMonsterEvent = g_scheduler().addEvent(getInterval(), std::bind(&SpawnMonster::checkSpawnMonster, this), "SpawnMonster::checkSpawnMonster"); } } @@ -251,7 +251,7 @@ void SpawnMonster::scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, uint spawnMonster(spawnMonsterId, sb.monsterType, sb.pos, sb.direction); } else { g_game().addMagicEffect(sb.pos, CONST_ME_TELEPORT); - g_scheduler().addEvent(1400, std::bind(&SpawnMonster::scheduleSpawn, this, spawnMonsterId, sb, interval - NONBLOCKABLE_SPAWN_MONSTER_INTERVAL), __FUNCTION__); + g_scheduler().addEvent(1400, std::bind(&SpawnMonster::scheduleSpawn, this, spawnMonsterId, sb, interval - NONBLOCKABLE_SPAWN_MONSTER_INTERVAL), "SpawnMonster::scheduleSpawn"); } } diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index c1667482c86..b018a9d5e25 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -1928,7 +1928,7 @@ void Player::onCloseContainer(const Container* container) { } void Player::onSendContainer(const Container* container) { - if (!client) { + if (!client || !container) { return; } @@ -7569,9 +7569,9 @@ void Player::setHazardSystemPoints(int32_t count) { addStorageValue(STORAGEVALUE_HAZARDCOUNT, std::max(0, std::min(0xFFFF, count)), true); reloadHazardSystemPointsCounter = true; if (count > 0) { - setIcon(CreatureIcon(CreatureIconQuests_t::Hazard, count)); + setIcon("hazard", CreatureIcon(CreatureIconQuests_t::Hazard, count)); } else { - clearIcon(); + removeIcon("hazard"); } } diff --git a/src/enums/item_attribute.hpp b/src/enums/item_attribute.hpp index 40934aa61ee..a245f3d5eb9 100644 --- a/src/enums/item_attribute.hpp +++ b/src/enums/item_attribute.hpp @@ -44,6 +44,7 @@ enum ItemAttribute_t : uint64_t { STORE = 31, CUSTOM = 32, LOOTMESSAGE_SUFFIX = 33, + STORE_INBOX_CATEGORY = 34, }; enum ItemDecayState_t : uint8_t { diff --git a/src/game/game.cpp b/src/game/game.cpp index 528f89e4b32..be3fbf37542 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -616,7 +616,12 @@ Thing* Game::internalGetThing(Player* player, const Position &pos, int32_t index } uint8_t slot = pos.z; - return parentContainer->getItemByIndex(player->getContainerIndex(fromCid) + slot); + auto containerIndex = player->getContainerIndex(fromCid) + slot; + if (parentContainer->isStoreInboxFiltered()) { + return parentContainer->getFilteredItemByIndex(containerIndex); + } + + return parentContainer->getItemByIndex(containerIndex); } else if (pos.y == 0x20 || pos.y == 0x21) { // '0x20' -> From depot. // '0x21' -> From inbox. @@ -1590,8 +1595,9 @@ ReturnValue Game::checkMoveItemToCylinder(Player* player, Cylinder* fromCylinder } const Container* topParentContainer = toCylinderContainer->getRootContainer(); - - if (!item->isStoreItem() && (containerID == ITEM_STORE_INBOX || topParentContainer->getParent() && topParentContainer->getParent()->getContainer() && topParentContainer->getParent()->getContainer()->getID() == ITEM_STORE_INBOX)) { + const auto parentContainer = topParentContainer->getParent() ? topParentContainer->getParent()->getContainer() : nullptr; + auto isStoreInbox = parentContainer && parentContainer->isStoreInbox(); + if (!item->isStoreItem() && (containerID == ITEM_STORE_INBOX || isStoreInbox)) { return RETURNVALUE_CONTAINERNOTENOUGHROOM; } @@ -1605,7 +1611,7 @@ ReturnValue Game::checkMoveItemToCylinder(Player* player, Cylinder* fromCylinder isValidMoveItem = true; } - if (topParentContainer->getParent() && topParentContainer->getParent()->getContainer() && (topParentContainer->getParent()->getContainer()->isDepotChest() || topParentContainer->getParent()->getContainer()->getID() == ITEM_STORE_INBOX)) { + if (parentContainer && (parentContainer->isDepotChest() || isStoreInbox)) { isValidMoveItem = true; } @@ -1859,9 +1865,18 @@ ReturnValue Game::internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder, return retMaxCount; } - // looting analyser from this point forward + auto fromContainer = fromCylinder ? fromCylinder->getContainer() : nullptr; + auto toContainer = toCylinder ? toCylinder->getContainer() : nullptr; + auto player = actor ? actor->getPlayer() : nullptr; + if (player) { + // Update containers + player->onSendContainer(toContainer); + player->onSendContainer(fromContainer); + } + + // Actor related actions if (fromCylinder && actor && toCylinder) { - if (!fromCylinder->getContainer() || !actor->getPlayer() || !toCylinder->getContainer()) { + if (!fromContainer || !toContainer || !player) { return ret; } @@ -1876,7 +1891,8 @@ ReturnValue Game::internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder, return ret; } - if (it.isCorpse && toCylinder->getContainer()->getTopParent() == player && item->getIsLootTrackeable()) { + // Looting analyser + if (it.isCorpse && toContainer->getTopParent() == player && item->getIsLootTrackeable()) { player->sendLootStats(item, static_cast(item->getItemCount())); } } @@ -2679,9 +2695,9 @@ ReturnValue Game::internalCollectLootItems(Player* player, Item* item, ObjectCat ReturnValue Game::collectRewardChestItems(Player* player, uint32_t maxMoveItems /* = 0*/) { // Check if have item on player reward chest RewardChest* rewardChest = player->getRewardChest(); - if (!rewardChest || rewardChest->empty()) { - g_logger().debug("Reward chest is wrong or empty"); - return RETURNVALUE_NOTPOSSIBLE; + if (rewardChest->empty()) { + g_logger().debug("Reward chest is empty"); + return RETURNVALUE_REWARDCHESTISEMPTY; } auto rewardItemsVector = player->getRewardsFromContainer(rewardChest->getContainer()); @@ -4114,7 +4130,7 @@ void Game::playerStashWithdraw(uint32_t playerId, uint16_t itemId, uint32_t coun } } -void Game::playerSeekInContainer(uint32_t playerId, uint8_t containerId, uint16_t index) { +void Game::playerSeekInContainer(uint32_t playerId, uint8_t containerId, uint16_t index, uint8_t containerCategory) { Player* player = getPlayerByID(playerId); if (!player) { return; @@ -4125,6 +4141,12 @@ void Game::playerSeekInContainer(uint32_t playerId, uint8_t containerId, uint16_ return; } + if (container->isStoreInbox()) { + auto enumName = magic_enum::enum_name(static_cast(containerCategory)).data(); + container->setAttribute(ItemAttribute_t::STORE_INBOX_CATEGORY, enumName); + g_logger().debug("Setting new container with store inbox category name {}", enumName); + } + if ((index % container->capacity()) != 0 || index >= container->size()) { return; } @@ -9836,6 +9858,18 @@ void Game::playerRewardChestCollect(uint32_t playerId, const Position &pos, uint return; } + // Updates the parent of the reward chest and reward containers to avoid memory usage after cleaning + RewardChest* playerRewardChest = player->getRewardChest(); + if (playerRewardChest->empty()) { + player->sendCancelMessage(RETURNVALUE_REWARDCHESTISEMPTY); + return; + } + + playerRewardChest->setParent(item->getContainer()->getParent()->getTile()); + for (const auto &[mapRewardId, reward] : player->rewardMap) { + reward->setParent(playerRewardChest); + } + std::lock_guard lock(player->quickLootMutex); ReturnValue returnValue = collectRewardChestItems(player, maxMoveItems); diff --git a/src/game/game.hpp b/src/game/game.hpp index 5c333eb476c..c7ca9885949 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -295,7 +295,7 @@ class Game { void playerWrapableItem(uint32_t playerId, const Position &pos, uint8_t stackPos, const uint16_t itemId); void playerWriteItem(uint32_t playerId, uint32_t windowTextId, const std::string &text); void playerBrowseField(uint32_t playerId, const Position &pos); - void playerSeekInContainer(uint32_t playerId, uint8_t containerId, uint16_t index); + void playerSeekInContainer(uint32_t playerId, uint8_t containerId, uint16_t index, uint8_t containerCategory); void playerUpdateHouseWindow(uint32_t playerId, uint8_t listId, uint32_t windowTextId, const std::string &text); void playerRequestTrade(uint32_t playerId, const Position &pos, uint8_t stackPos, uint32_t tradePlayerId, uint16_t itemId); void playerAcceptTrade(uint32_t playerId); 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/game/scheduling/task.hpp b/src/game/scheduling/task.hpp index 72379a8ec31..99d452e6a60 100644 --- a/src/game/scheduling/task.hpp +++ b/src/game/scheduling/task.hpp @@ -14,12 +14,12 @@ class Task { // DO NOT allocate this class on the stack Task(std::function &&f, std::string context) : context(std::move(context)), func(std::move(f)) { - assert(!context.empty() && "Context cannot be empty!"); + assert(!this->context.empty() && "Context cannot be empty!"); } Task(std::function &&f, std::string context, uint32_t delay) : delay(delay), context(std::move(context)), func(std::move(f)) { - assert(!context.empty() && "Context cannot be empty!"); + assert(!this->context.empty() && "Context cannot be empty!"); } virtual ~Task() = default; @@ -45,16 +45,27 @@ class Task { bool hasTraceableContext() const { return std::set { + "Creature::checkCreatureWalk", + "Decay::checkDecay", + "Game::checkCreatureAttack", "Game::checkCreatures", "Game::checkImbuements", "Game::checkLight", "Game::createFiendishMonsters", "Game::createInfluencedMonsters", + "Game::updateCreatureWalk", "Game::updateForgeableMonsters", "GlobalEvents::think", + "LuaEnvironment::executeTimerEvent", + "Modules::executeOnRecvbyte", "OutputMessagePool::sendAll", + "ProtocolGame::addGameTask", + "ProtocolGame::parsePacketFromDispatcher", + "Raids::checkRaids", "SpawnMonster::checkSpawnMonster", + "SpawnNpc::checkSpawnNpc", "Webhook::run", + "sendRecvMessageCallback", } .contains(context); } diff --git a/src/items/containers/container.cpp b/src/items/containers/container.cpp index ea3259a685d..fbe9486c3bb 100644 --- a/src/items/containers/container.cpp +++ b/src/items/containers/container.cpp @@ -16,8 +16,11 @@ Container::Container(uint16_t type) : Container(type, items[type].maxItems) { - if (getID() == ITEM_GOLD_POUCH) { + m_maxItems = static_cast(g_configManager().getNumber(MAX_CONTAINER_ITEM)); + if (getID() == ITEM_GOLD_POUCH || isStoreInbox()) { pagination = true; + m_maxItems = 2000; + maxSize = 32; } } @@ -229,10 +232,86 @@ std::ostringstream &Container::getContentDescription(std::ostringstream &os, boo return os; } +bool Container::isStoreInbox() const { + return getID() == ITEM_STORE_INBOX; +} + +bool Container::isStoreInboxFiltered() const { + auto attribute = getAttribute(ItemAttribute_t::STORE_INBOX_CATEGORY); + if (isStoreInbox() && !attribute.empty() && attribute != "All") { + return true; + } + + return false; +} + +std::deque Container::getStoreInboxFilteredItems() const { + const auto enumName = getAttribute(ItemAttribute_t::STORE_INBOX_CATEGORY); + ItemDeque storeInboxFilteredList; + if (isStoreInboxFiltered()) { + for (Item* item : getItemList()) { + auto itemId = item->getID(); + auto attribute = item->getCustomAttribute("unWrapId"); + uint16_t unWrapId = attribute ? static_cast(attribute->getInteger()) : 0; + if (unWrapId != 0) { + itemId = unWrapId; + } + const auto &itemType = Item::items.getItemType(itemId); + auto primaryType = toPascalCase(itemType.m_primaryType); + auto name = toPascalCase(enumName); + g_logger().debug("Get filtered items, primaty type {}, enum name {}", primaryType, name); + if (primaryType == name) { + storeInboxFilteredList.push_back(item); + } + } + } + + return storeInboxFilteredList; +} + +phmap::flat_hash_set Container::getStoreInboxValidCategories() const { + phmap::flat_hash_set validCategories; + for (const auto &item : itemlist) { + auto itemId = item->getID(); + auto attribute = item->getCustomAttribute("unWrapId"); + uint16_t unWrapId = attribute ? static_cast(attribute->getInteger()) : 0; + if (unWrapId != 0) { + itemId = unWrapId; + } + const auto &itemType = Item::items.getItemType(itemId); + auto convertedString = toPascalCase(itemType.m_primaryType); + g_logger().debug("Store item '{}', primary type {}", itemId, convertedString); + auto category = magic_enum::enum_cast(convertedString); + if (category.has_value()) { + g_logger().debug("Adding valid category {}", static_cast(category.value())); + validCategories.insert(category.value()); + } + } + + return validCategories; +} + +Item* Container::getFilteredItemByIndex(size_t index) const { + const auto &filteredItems = getStoreInboxFilteredItems(); + if (index >= filteredItems.size()) { + return nullptr; + } + + auto item = filteredItems[index]; + + auto it = std::find(itemlist.begin(), itemlist.end(), item); + if (it == itemlist.end()) { + return nullptr; + } + + return *it; +} + Item* Container::getItemByIndex(size_t index) const { if (index >= size()) { return nullptr; } + return itemlist[index]; } @@ -366,22 +445,21 @@ ReturnValue Container::queryAdd(int32_t addIndex, const Thing &addThing, uint32_ } if (const Container* topParentContainer = getTopParentContainer()) { - uint32_t maxItem = static_cast(g_configManager().getNumber(MAX_ITEM)); if (const Container* addContainer = item->getContainer()) { uint32_t addContainerCount = addContainer->getContainerHoldingCount() + 1; uint32_t maxContainer = static_cast(g_configManager().getNumber(MAX_CONTAINER)); if (addContainerCount + topParentContainer->getContainerHoldingCount() > maxContainer) { - return RETURNVALUE_NOTPOSSIBLE; + return RETURNVALUE_CONTAINERISFULL; } uint32_t addItemCount = addContainer->getItemHoldingCount() + 1; - if (addItemCount + topParentContainer->getItemHoldingCount() > maxItem) { - return RETURNVALUE_NOTPOSSIBLE; + if (addItemCount + topParentContainer->getItemHoldingCount() > m_maxItems) { + return RETURNVALUE_CONTAINERISFULL; } } - if (topParentContainer->getItemHoldingCount() + 1 > maxItem) { - return RETURNVALUE_NOTPOSSIBLE; + if (topParentContainer->getItemHoldingCount() + 1 > m_maxItems) { + return RETURNVALUE_CONTAINERISFULL; } } diff --git a/src/items/containers/container.hpp b/src/items/containers/container.hpp index 30bc4003229..b58929b3628 100644 --- a/src/items/containers/container.hpp +++ b/src/items/containers/container.hpp @@ -115,6 +115,11 @@ class Container : public Item, public Cylinder { bool hasParent() const; void addItem(Item* item); StashContainerList getStowableItems() const; + bool isStoreInbox() const; + bool isStoreInboxFiltered() const; + std::deque getStoreInboxFilteredItems() const; + phmap::flat_hash_set getStoreInboxValidCategories() const; + Item* getFilteredItemByIndex(size_t index) const; Item* getItemByIndex(size_t index) const; bool isHoldingItem(const Item* item) const; bool isHoldingItemWithId(const uint16_t id) const; @@ -168,6 +173,7 @@ class Container : public Item, public Cylinder { protected: std::ostringstream &getContentDescription(std::ostringstream &os, bool oldProtocol) const; + uint32_t m_maxItems; uint32_t maxSize; uint32_t totalWeight = 0; ItemDeque itemlist; diff --git a/src/items/containers/mailbox/mailbox.cpp b/src/items/containers/mailbox/mailbox.cpp index 248c5c85367..af3b79eae13 100644 --- a/src/items/containers/mailbox/mailbox.cpp +++ b/src/items/containers/mailbox/mailbox.cpp @@ -80,6 +80,16 @@ bool Mailbox::sendItem(Item* item) const { return false; } + if (item && item->getContainer() && item->getTile()) { + SpectatorHashSet spectators; + g_game().map.getSpectators(spectators, item->getTile()->getPosition(), false, true); + for (Creature* spectator : spectators) { + if (spectator && spectator->getPlayer()) { + spectator->getPlayer()->autoCloseContainers(item->getContainer()); + } + } + } + Player* player = g_game().getPlayerByName(receiver, true); std::string writer; time_t date = time(0); diff --git a/src/items/decay/decay.cpp b/src/items/decay/decay.cpp index 0dbf2fa0f6c..e08854c111a 100644 --- a/src/items/decay/decay.cpp +++ b/src/items/decay/decay.cpp @@ -132,7 +132,7 @@ void Decay::checkDecay() { } if (it != end) { - eventId = g_scheduler().addEvent(std::max(SCHEDULER_MINTICKS, static_cast(it->first - timestamp)), std::bind(&Decay::checkDecay, this), __FUNCTION__); + eventId = g_scheduler().addEvent(std::max(SCHEDULER_MINTICKS, static_cast(it->first - timestamp)), std::bind(&Decay::checkDecay, this), "Decay::checkDecay"); } } diff --git a/src/items/functions/item/attribute.hpp b/src/items/functions/item/attribute.hpp index 20cf4369435..4a68cc0ec7b 100644 --- a/src/items/functions/item/attribute.hpp +++ b/src/items/functions/item/attribute.hpp @@ -57,6 +57,7 @@ class ItemAttributeHelper { case ItemAttribute_t::PLURALNAME: case ItemAttribute_t::SPECIAL: case ItemAttribute_t::LOOTMESSAGE_SUFFIX: + case ItemAttribute_t::STORE_INBOX_CATEGORY: return true; default: return false; diff --git a/src/items/functions/item/item_parse.cpp b/src/items/functions/item/item_parse.cpp index 0500d82915c..2172326f097 100644 --- a/src/items/functions/item/item_parse.cpp +++ b/src/items/functions/item/item_parse.cpp @@ -56,7 +56,6 @@ void ItemParse::initParse(const std::string &tmpStrValue, pugi::xml_node attribu ItemParse::parseMagicLevelPoint(tmpStrValue, valueAttribute, itemType); ItemParse::parseFieldAbsorbPercent(tmpStrValue, valueAttribute, itemType); ItemParse::parseAbsorbPercent(tmpStrValue, valueAttribute, itemType); - ItemParse::parseSupressDrunk(tmpStrValue, valueAttribute, itemType); ItemParse::parseField(tmpStrValue, attributeNode, valueAttribute, itemType); ItemParse::parseReplaceable(tmpStrValue, valueAttribute, itemType); diff --git a/src/items/item.cpp b/src/items/item.cpp index 45c977fe05e..f38ac6c9ec1 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -794,6 +794,17 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream &propStream) { break; } + case ATTR_STORE_INBOX_CATEGORY: { + std::string category; + if (!propStream.readString(category)) { + g_logger().error("[{}] failed to read store inbox category from item {}", __FUNCTION__, getName()); + return ATTR_READ_ERROR; + } + + setAttribute(ItemAttribute_t::STORE_INBOX_CATEGORY, category); + break; + } + default: return ATTR_READ_ERROR; } @@ -954,6 +965,10 @@ void Item::serializeAttr(PropWriteStream &propWriteStream) const { propWriteStream.write(ATTR_AMOUNT); propWriteStream.write(getAttribute(AMOUNT)); } + if (hasAttribute(STORE_INBOX_CATEGORY)) { + propWriteStream.write(ATTR_STORE_INBOX_CATEGORY); + propWriteStream.writeString(getString(ItemAttribute_t::STORE_INBOX_CATEGORY)); + } // Serialize custom attributes, only serialize if the map not is empty if (hasCustomAttribute()) { diff --git a/src/items/items.cpp b/src/items/items.cpp index 6611af095b8..986f2b2728d 100644 --- a/src/items/items.cpp +++ b/src/items/items.cpp @@ -176,6 +176,7 @@ void Items::loadFromProtobuf() { iType.clockExpire = object.flags().clockexpire(); iType.expire = object.flags().expire(); iType.expireStop = object.flags().expirestop(); + iType.isWrapKit = object.flags().wrapkit(); if (!iType.name.empty()) { nameToItems.insert({ asLowerCaseString(iType.name), diff --git a/src/items/items.hpp b/src/items/items.hpp index 6d0559cc0f7..b21dbc7e414 100644 --- a/src/items/items.hpp +++ b/src/items/items.hpp @@ -341,6 +341,7 @@ class ItemType { bool isCorpse = false; bool loaded = false; bool spellbook = false; + bool isWrapKit = false; }; class Items { diff --git a/src/items/items_definitions.hpp b/src/items/items_definitions.hpp index 0c38634fcf3..8b54fa679b5 100644 --- a/src/items/items_definitions.hpp +++ b/src/items/items_definitions.hpp @@ -60,6 +60,7 @@ enum ReturnValue { RETURNVALUE_CANNOTPICKUP, RETURNVALUE_THISISIMPOSSIBLE, RETURNVALUE_DEPOTISFULL, + RETURNVALUE_CONTAINERISFULL, RETURNVALUE_CREATUREDOESNOTEXIST, RETURNVALUE_CANNOTUSETHISOBJECT, RETURNVALUE_PLAYERWITHTHISNAMEISNOTONLINE, @@ -234,6 +235,7 @@ enum AttrTypes_t { ATTR_AMOUNT = 39, ATTR_TIER = 40, ATTR_CUSTOM = 41, + ATTR_STORE_INBOX_CATEGORY = 42, // Always the last ATTR_NONE = 0 @@ -261,6 +263,130 @@ enum ImbuementTypes_t : int64_t { IMBUEMENT_INCREASE_CAPACITY = 17 }; +enum class ContainerCategory_t : uint8_t { + All, + Ammunition, + AmuletsAndNecklaces, + Animals, + Annelids, + Arachnids, + Armors, + ArtificialTiles, + AstralShapers, + AttackRunes, + AxeWeapons, + Bats, + Bears, + Birds, + BlessingCharms, + Blobs, + Books, + Boots, + Bushes, + Cactuses, + Canines, + Casks, + Closets, + ClothingAccessories, + ClubWeapons, + Coffins, + Constructions, + Containers, + ContestPrizes, + CreatureProducts, + Decoration, + Demons, + DistanceWeapons, + DocumentsAndPapers, + DollsAndBears, + Doors, + Dragons, + Dreamhaunters, + Dropdowns, + EnchantedItems, + EventCreatures, + ExerciseWeapons, + FansiteItems, + Ferns, + Fields, + Flags, + FloorDecorations, + FloraAndMinerals, + Flowers, + FluidContainers, + Food, + Furniture, + GameTokens, + Ghosts, + Glires, + Grass, + HealingRunes, + Helmets, + HiveBorn, + Illumination, + Keys, + KitchenTools, + Ladders, + Legs, + LightSources, + Liquids, + MachinesObjects, + Machines, + MagicalItems, + Metals, + Mollusks, + Mushrooms, + MusicalInstruments, + NaturalProducts, + NaturalTiles, + OtherItems, + Outlaws, + PaintingEquipment, + PartyItems, + Pillars, + PlantsAndHerbs, + Plants, + Portals, + QuestItems, + QuestObjects, + Quivers, + Refuse, + Remains, + Rings, + Rocks, + Rods, + Rubbish, + Shields, + ShrinesAndAltars, + Signs, + Skeletons, + Spellbooks, + Stairs, + Statues, + SupportRunes, + SwordWeapons, + Tables, + TamingItems, + Teleporters, + ToolsObjects, + Tools, + TortureInstruments, + TournamentRewards, + TrainingWeapons, + Transportation, + Traps, + Trees, + Trophies, + UndeadHumanoids, + Ungulates, + Utilities, + Valuables, + WallHangings, + Walls, + Wands, + Windows +}; + enum SlotPositionBits : uint32_t { SLOTP_WHEREEVER = 0xFFFFFFFF, SLOTP_HEAD = 1 << 0, 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/creature/actions.cpp b/src/lua/creature/actions.cpp index e760d3c66ff..34fa1cba896 100644 --- a/src/lua/creature/actions.cpp +++ b/src/lua/creature/actions.cpp @@ -310,17 +310,17 @@ ReturnValue Actions::internalUseItem(Player* player, const Position &pos, uint8_ // reward chest if (container->getRewardChest() != nullptr && container->getParent()) { - RewardChest* myRewardChest = player->getRewardChest(); - if (myRewardChest->size() == 0) { + RewardChest* playerRewardChest = player->getRewardChest(); + if (playerRewardChest->empty()) { return RETURNVALUE_REWARDCHESTISEMPTY; } - myRewardChest->setParent(container->getParent()->getTile()); + playerRewardChest->setParent(container->getParent()->getTile()); for (const auto &[mapRewardId, reward] : player->rewardMap) { - reward->setParent(myRewardChest); + reward->setParent(playerRewardChest); } - openContainer = myRewardChest; + openContainer = playerRewardChest; } auto rewardId = container->getAttribute(ItemAttribute_t::DATE); diff --git a/src/lua/creature/raids.cpp b/src/lua/creature/raids.cpp index 93936d94a93..27fec5d2792 100644 --- a/src/lua/creature/raids.cpp +++ b/src/lua/creature/raids.cpp @@ -131,7 +131,7 @@ void Raids::checkRaids() { } } - checkRaidsEvent = g_scheduler().addEvent(CHECK_RAIDS_INTERVAL * 1000, std::bind(&Raids::checkRaids, this), __FUNCTION__); + checkRaidsEvent = g_scheduler().addEvent(CHECK_RAIDS_INTERVAL * 1000, std::bind(&Raids::checkRaids, this), "Raids::checkRaids"); } void Raids::clear() { 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/creature_functions.cpp b/src/lua/functions/creatures/creature_functions.cpp index b97f3eff491..d1ec3d19dab 100644 --- a/src/lua/functions/creatures/creature_functions.cpp +++ b/src/lua/functions/creatures/creature_functions.cpp @@ -766,7 +766,7 @@ int CreatureFunctions::luaCreatureTeleportTo(lua_State* L) { const Position oldPosition = creature->getPosition(); if (auto ret = g_game().internalTeleport(creature, position, pushMovement); ret != RETURNVALUE_NOERROR) { - g_logger().error("[{}] Failed to teleport creature {}, on position {}, error code: {}", __FUNCTION__, creature->getName(), oldPosition.toString(), getReturnMessage(ret)); + g_logger().debug("[{}] Failed to teleport creature {}, on position {}, error code: {}", __FUNCTION__, creature->getName(), oldPosition.toString(), getReturnMessage(ret)); pushBoolean(L, false); return 1; } @@ -982,46 +982,95 @@ int CreatureFunctions::luaCreatureGetZones(lua_State* L) { } int CreatureFunctions::luaCreatureSetIcon(lua_State* L) { - // creature:setIcon(category, icon[, number]) - Creature* creature = getUserdata(L, 1); + // creature:setIcon(key, category, icon[, number]) + auto creature = getUserdata(L, 1); if (!creature) { reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); pushBoolean(L, false); return 1; } - auto category = getNumber(L, 2); - auto count = getNumber(L, 4, 0); + const auto key = getString(L, 2); + auto category = getNumber(L, 3); + auto count = getNumber(L, 5, 0); CreatureIcon creatureIcon; if (category == CreatureIconCategory_t::Modifications) { - auto icon = getNumber(L, 3); + auto icon = getNumber(L, 5); creatureIcon = CreatureIcon(icon, count); } else { - auto icon = getNumber(L, 3); + auto icon = getNumber(L, 4); creatureIcon = CreatureIcon(icon, count); } - creature->setIcon(creatureIcon); + creature->setIcon(key, creatureIcon); pushBoolean(L, true); return 1; } -int CreatureFunctions::luaCreatureGetIcon(lua_State* L) { - // creature:getIcon() - Creature* creature = getUserdata(L, 1); +int CreatureFunctions::luaCreatureGetIcons(lua_State* L) { + // creature:getIcons() + const auto creature = getUserdata(L, 1); if (!creature) { reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); pushBoolean(L, false); return 1; } - auto creatureIcon = creature->getIcon(); - if (creatureIcon.isNone()) { + auto icons = creature->getIcons(); + lua_createtable(L, static_cast(icons.size()), 0); + for (auto &icon : icons) { + lua_createtable(L, 0, 3); + setField(L, "category", static_cast(icon.category)); + setField(L, "icon", icon.serialize()); + setField(L, "count", icon.count); + lua_rawseti(L, -2, static_cast(icon.category)); + } + return 1; +} + +int CreatureFunctions::luaCreatureGetIcon(lua_State* L) { + // creature:getIcon(key) + const auto creature = getUserdata(L, 1); + if (!creature) { + reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + const auto key = getString(L, 2); + auto icon = creature->getIcon(key); + if (icon.isSet()) { + lua_createtable(L, 0, 3); + setField(L, "category", static_cast(icon.category)); + setField(L, "icon", icon.serialize()); + setField(L, "count", icon.count); + } else { lua_pushnil(L); + } + return 1; +} + +int CreatureFunctions::luaCreatureRemoveIcon(lua_State* L) { + // creature:removeIcon(key) + auto creature = getUserdata(L, 1); + if (!creature) { + reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); + pushBoolean(L, false); return 1; } - lua_createtable(L, 0, 3); - setField(L, "category", static_cast(creatureIcon.category)); - setField(L, "icon", creatureIcon.serialize()); - setField(L, "count", creatureIcon.count); + const auto key = getString(L, 2); + creature->removeIcon(key); + pushBoolean(L, true); + return 1; +} + +int CreatureFunctions::luaCreatureClearIcons(lua_State* L) { + // creature:clearIcons() + auto creature = getUserdata(L, 1); + if (!creature) { + reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + creature->clearIcons(); + pushBoolean(L, true); return 1; } diff --git a/src/lua/functions/creatures/creature_functions.hpp b/src/lua/functions/creatures/creature_functions.hpp index 1d1b22a9f71..426e4859104 100644 --- a/src/lua/functions/creatures/creature_functions.hpp +++ b/src/lua/functions/creatures/creature_functions.hpp @@ -82,6 +82,9 @@ class CreatureFunctions final : LuaScriptInterface { registerMethod(L, "Creature", "getZones", CreatureFunctions::luaCreatureGetZones); registerMethod(L, "Creature", "setIcon", CreatureFunctions::luaCreatureSetIcon); registerMethod(L, "Creature", "getIcon", CreatureFunctions::luaCreatureGetIcon); + registerMethod(L, "Creature", "getIcons", CreatureFunctions::luaCreatureGetIcons); + registerMethod(L, "Creature", "removeIcon", CreatureFunctions::luaCreatureRemoveIcon); + registerMethod(L, "Creature", "clearIcons", CreatureFunctions::luaCreatureClearIcons); CombatFunctions::init(L); MonsterFunctions::init(L); @@ -178,5 +181,8 @@ class CreatureFunctions final : LuaScriptInterface { static int luaCreatureGetZones(lua_State* L); static int luaCreatureSetIcon(lua_State* L); + static int luaCreatureGetIcons(lua_State* L); static int luaCreatureGetIcon(lua_State* L); + static int luaCreatureRemoveIcon(lua_State* L); + static int luaCreatureClearIcons(lua_State* L); }; diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp index 0989be8449c..bca5a2e421e 100644 --- a/src/lua/functions/creatures/monster/monster_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_functions.cpp @@ -470,12 +470,7 @@ int MonsterFunctions::luaMonsterSetForgeStack(lua_State* L) { auto icon = stack < 15 ? CreatureIconModifications_t::Influenced : CreatureIconModifications_t::Fiendish; - monster->setIcon(CreatureIcon( - icon, - icon == CreatureIconModifications_t::Influenced - ? static_cast(stack) - : 0 // don't show the stack for fiendish - )); + monster->setIcon("forge", CreatureIcon(icon, icon == CreatureIconModifications_t::Influenced ? static_cast(stack) : 0)); g_game().updateCreatureIcon(monster); g_game().sendUpdateCreature(monster); 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/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 97c3de27cb5..4b72347b231 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -1945,6 +1945,25 @@ int PlayerFunctions::luaPlayerSendContainer(lua_State* L) { return 1; } +int PlayerFunctions::luaPlayerSendUpdateContainer(lua_State* L) { + // player:sendUpdateContainer(container) + Player* player = getUserdata(L, 1); + if (!player) { + lua_pushnil(L); + return 1; + } + + const auto container = getUserdata(L, 2); + if (!container) { + reportErrorFunc("Container is nullptr"); + return 1; + } + + player->onSendContainer(container); + pushBoolean(L, true); + return 1; +} + int PlayerFunctions::luaPlayerGetMoney(lua_State* L) { // player:getMoney() Player* player = getUserdata(L, 1); diff --git a/src/lua/functions/creatures/player/player_functions.hpp b/src/lua/functions/creatures/player/player_functions.hpp index 5637b439e0e..ff047fb125d 100644 --- a/src/lua/functions/creatures/player/player_functions.hpp +++ b/src/lua/functions/creatures/player/player_functions.hpp @@ -177,6 +177,7 @@ class PlayerFunctions final : LuaScriptInterface { registerMethod(L, "Player", "removeStashItem", PlayerFunctions::luaPlayerRemoveStashItem); registerMethod(L, "Player", "removeItem", PlayerFunctions::luaPlayerRemoveItem); registerMethod(L, "Player", "sendContainer", PlayerFunctions::luaPlayerSendContainer); + registerMethod(L, "Player", "sendUpdateContainer", PlayerFunctions::luaPlayerSendUpdateContainer); registerMethod(L, "Player", "getMoney", PlayerFunctions::luaPlayerGetMoney); registerMethod(L, "Player", "addMoney", PlayerFunctions::luaPlayerAddMoney); @@ -515,6 +516,7 @@ class PlayerFunctions final : LuaScriptInterface { static int luaPlayerRemoveStashItem(lua_State* L); static int luaPlayerRemoveItem(lua_State* L); static int luaPlayerSendContainer(lua_State* L); + static int luaPlayerSendUpdateContainer(lua_State* L); static int luaPlayerGetMoney(lua_State* L); static int luaPlayerAddMoney(lua_State* L); 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; +} diff --git a/src/protobuf/CMakeLists.txt b/src/protobuf/CMakeLists.txt index 0a897799764..4ca2bac2ce2 100644 --- a/src/protobuf/CMakeLists.txt +++ b/src/protobuf/CMakeLists.txt @@ -22,7 +22,7 @@ add_custom_command( ${CMAKE_CURRENT_BINARY_DIR}/appearances.pb.cc ${CMAKE_CURRENT_SOURCE_DIR}/appearances.pb.cc - # Copy files "appearances.pb.hpp" to the "src/protobuf" folder + # Copy files "appearances.pb.h" to the "src/protobuf" folder COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/appearances.pb.h ${CMAKE_CURRENT_SOURCE_DIR}/appearances.pb.h diff --git a/src/protobuf/appearances.pb.cc b/src/protobuf/appearances.pb.cc index cc96bcf7310..ced2a617345 100644 --- a/src/protobuf/appearances.pb.cc +++ b/src/protobuf/appearances.pb.cc @@ -223,7 +223,8 @@ PROTOBUF_CONSTEXPR AppearanceFlags::AppearanceFlags( , /*decltype(_impl_.wearout_)*/false , /*decltype(_impl_.clockexpire_)*/false , /*decltype(_impl_.expire_)*/false - , /*decltype(_impl_.expirestop_)*/false} {} + , /*decltype(_impl_.expirestop_)*/false + , /*decltype(_impl_.wrapkit_)*/false} {} struct AppearanceFlagsDefaultTypeInternal { PROTOBUF_CONSTEXPR AppearanceFlagsDefaultTypeInternal() : _instance(::_pbi::ConstantInitialized{}) {} @@ -477,7 +478,8 @@ PROTOBUF_CONSTEXPR SpecialMeaningAppearanceIds::SpecialMeaningAppearanceIds( , /*decltype(_impl_.crystal_coin_id_)*/0u , /*decltype(_impl_.tibia_coin_id_)*/0u , /*decltype(_impl_.stamped_letter_id_)*/0u - , /*decltype(_impl_.supply_stash_id_)*/0u} {} + , /*decltype(_impl_.supply_stash_id_)*/0u + , /*decltype(_impl_.reward_chest_id_)*/0u} {} struct SpecialMeaningAppearanceIdsDefaultTypeInternal { PROTOBUF_CONSTEXPR SpecialMeaningAppearanceIdsDefaultTypeInternal() : _instance(::_pbi::ConstantInitialized{}) {} @@ -679,6 +681,7 @@ const uint32_t TableStruct_appearances_2eproto::offsets[] PROTOBUF_SECTION_VARIA PROTOBUF_FIELD_OFFSET(::Canary::protobuf::appearances::AppearanceFlags, _impl_.clockexpire_), PROTOBUF_FIELD_OFFSET(::Canary::protobuf::appearances::AppearanceFlags, _impl_.expire_), PROTOBUF_FIELD_OFFSET(::Canary::protobuf::appearances::AppearanceFlags, _impl_.expirestop_), + PROTOBUF_FIELD_OFFSET(::Canary::protobuf::appearances::AppearanceFlags, _impl_.wrapkit_), 0, 15, 16, @@ -735,6 +738,7 @@ const uint32_t TableStruct_appearances_2eproto::offsets[] PROTOBUF_SECTION_VARIA 52, 53, 54, + 55, PROTOBUF_FIELD_OFFSET(::Canary::protobuf::appearances::AppearanceFlagUpgradeClassification, _impl_._has_bits_), PROTOBUF_FIELD_OFFSET(::Canary::protobuf::appearances::AppearanceFlagUpgradeClassification, _internal_metadata_), ~0u, // no _extensions_ @@ -897,12 +901,14 @@ const uint32_t TableStruct_appearances_2eproto::offsets[] PROTOBUF_SECTION_VARIA PROTOBUF_FIELD_OFFSET(::Canary::protobuf::appearances::SpecialMeaningAppearanceIds, _impl_.tibia_coin_id_), PROTOBUF_FIELD_OFFSET(::Canary::protobuf::appearances::SpecialMeaningAppearanceIds, _impl_.stamped_letter_id_), PROTOBUF_FIELD_OFFSET(::Canary::protobuf::appearances::SpecialMeaningAppearanceIds, _impl_.supply_stash_id_), + PROTOBUF_FIELD_OFFSET(::Canary::protobuf::appearances::SpecialMeaningAppearanceIds, _impl_.reward_chest_id_), 0, 1, 2, 3, 4, 5, + 6, }; static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { { 0, 9, -1, sizeof(::Canary::protobuf::appearances::Coordinate)}, @@ -913,24 +919,24 @@ static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protode { 70, 85, -1, sizeof(::Canary::protobuf::appearances::SpriteInfo)}, { 94, 103, -1, sizeof(::Canary::protobuf::appearances::FrameGroup)}, { 106, 117, -1, sizeof(::Canary::protobuf::appearances::Appearance)}, - { 122, 184, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlags)}, - { 240, 247, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagUpgradeClassification)}, - { 248, 255, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagBank)}, - { 256, 263, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagWrite)}, - { 264, 271, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagWriteOnce)}, - { 272, 280, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagLight)}, - { 282, 289, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagHeight)}, - { 290, 298, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagShift)}, - { 300, 307, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagClothes)}, - { 308, 315, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagDefaultAction)}, - { 316, 327, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagMarket)}, - { 332, 344, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagNPC)}, - { 350, 357, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagAutomap)}, - { 358, 365, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagHook)}, - { 366, 373, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagLenshelp)}, - { 374, 381, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagChangedToExpire)}, - { 382, 389, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagCyclopedia)}, - { 390, 402, -1, sizeof(::Canary::protobuf::appearances::SpecialMeaningAppearanceIds)}, + { 122, 185, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlags)}, + { 242, 249, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagUpgradeClassification)}, + { 250, 257, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagBank)}, + { 258, 265, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagWrite)}, + { 266, 273, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagWriteOnce)}, + { 274, 282, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagLight)}, + { 284, 291, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagHeight)}, + { 292, 300, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagShift)}, + { 302, 309, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagClothes)}, + { 310, 317, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagDefaultAction)}, + { 318, 329, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagMarket)}, + { 334, 346, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagNPC)}, + { 352, 359, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagAutomap)}, + { 360, 367, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagHook)}, + { 368, 375, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagLenshelp)}, + { 376, 383, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagChangedToExpire)}, + { 384, 391, -1, sizeof(::Canary::protobuf::appearances::AppearanceFlagCyclopedia)}, + { 392, 405, -1, sizeof(::Canary::protobuf::appearances::SpecialMeaningAppearanceIds)}, }; static const ::_pb::Message* const file_default_instances[] = { @@ -999,7 +1005,7 @@ const char descriptor_table_protodef_appearances_2eproto[] PROTOBUF_SECTION_VARI "2\'.Canary.protobuf.appearances.FrameGrou" "p\022;\n\005flags\030\003 \001(\0132,.Canary.protobuf.appea" "rances.AppearanceFlags\022\014\n\004name\030\004 \001(\014\022\023\n\013" - "description\030\005 \001(\014\"\235\017\n\017AppearanceFlags\022=\n" + "description\030\005 \001(\014\"\256\017\n\017AppearanceFlags\022=\n" "\004bank\030\001 \001(\0132/.Canary.protobuf.appearance" "s.AppearanceFlagBank\022\014\n\004clip\030\002 \001(\010\022\016\n\006bo" "ttom\030\003 \001(\010\022\013\n\003top\030\004 \001(\010\022\021\n\tcontainer\030\005 \001" @@ -1048,81 +1054,82 @@ const char descriptor_table_protodef_appearances_2eproto[] PROTOBUF_SECTION_VARI "everse_addons_south\0303 \001(\010\022\034\n\024reverse_add" "ons_north\0304 \001(\010\022\017\n\007wearout\0305 \001(\010\022\023\n\013cloc" "kexpire\0306 \001(\010\022\016\n\006expire\0307 \001(\010\022\022\n\nexpires" - "top\0308 \001(\010\"E\n#AppearanceFlagUpgradeClassi" - "fication\022\036\n\026upgrade_classification\030\001 \001(\r" - "\"\'\n\022AppearanceFlagBank\022\021\n\twaypoints\030\001 \001(" - "\r\".\n\023AppearanceFlagWrite\022\027\n\017max_text_len" - "gth\030\001 \001(\r\"7\n\027AppearanceFlagWriteOnce\022\034\n\024" - "max_text_length_once\030\001 \001(\r\"8\n\023Appearance" - "FlagLight\022\022\n\nbrightness\030\001 \001(\r\022\r\n\005color\030\002" - " \001(\r\")\n\024AppearanceFlagHeight\022\021\n\televatio" - "n\030\001 \001(\r\"+\n\023AppearanceFlagShift\022\t\n\001x\030\001 \001(" - "\r\022\t\n\001y\030\002 \001(\r\"%\n\025AppearanceFlagClothes\022\014\n" - "\004slot\030\001 \001(\r\"Y\n\033AppearanceFlagDefaultActi" - "on\022:\n\006action\030\001 \001(\0162*.Canary.protobuf.app" - "earances.PLAYER_ACTION\"\362\001\n\024AppearanceFla" - "gMarket\022<\n\010category\030\001 \001(\0162*.Canary.proto" - "buf.appearances.ITEM_CATEGORY\022\032\n\022trade_a" - "s_object_id\030\002 \001(\r\022\031\n\021show_as_object_id\030\003" - " \001(\r\022N\n\026restrict_to_profession\030\005 \003(\0162..C" - "anary.protobuf.appearances.PLAYER_PROFES" - "SION\022\025\n\rminimum_level\030\006 \001(\r\"\245\001\n\021Appearan" - "ceFlagNPC\022\014\n\004name\030\001 \001(\014\022\020\n\010location\030\002 \001(" - "\014\022\022\n\nsale_price\030\003 \001(\r\022\021\n\tbuy_price\030\004 \001(\r" - "\022\037\n\027currency_object_type_id\030\005 \001(\r\022(\n cur" - "rency_quest_flag_display_name\030\006 \001(\014\"&\n\025A" - "ppearanceFlagAutomap\022\r\n\005color\030\001 \001(\r\"O\n\022A" - "ppearanceFlagHook\0229\n\tdirection\030\001 \001(\0162&.C" - "anary.protobuf.appearances.HOOK_TYPE\"$\n\026" - "AppearanceFlagLenshelp\022\n\n\002id\030\001 \001(\r\"=\n\035Ap" - "pearanceFlagChangedToExpire\022\034\n\024former_ob" - "ject_typeid\030\001 \001(\r\"3\n\030AppearanceFlagCyclo" - "pedia\022\027\n\017cyclopedia_type\030\001 \001(\r\"\261\001\n\033Speci" - "alMeaningAppearanceIds\022\024\n\014gold_coin_id\030\001" - " \001(\r\022\030\n\020platinum_coin_id\030\002 \001(\r\022\027\n\017crysta" - "l_coin_id\030\003 \001(\r\022\025\n\rtibia_coin_id\030\004 \001(\r\022\031" - "\n\021stamped_letter_id\030\005 \001(\r\022\027\n\017supply_stas" - "h_id\030\006 \001(\r*\224\001\n\rPLAYER_ACTION\022\026\n\022PLAYER_A" - "CTION_NONE\020\000\022\026\n\022PLAYER_ACTION_LOOK\020\001\022\025\n\021" - "PLAYER_ACTION_USE\020\002\022\026\n\022PLAYER_ACTION_OPE" - "N\020\003\022$\n PLAYER_ACTION_AUTOWALK_HIGHLIGHT\020" - "\004*\315\005\n\rITEM_CATEGORY\022\030\n\024ITEM_CATEGORY_ARM" - "ORS\020\001\022\031\n\025ITEM_CATEGORY_AMULETS\020\002\022\027\n\023ITEM" - "_CATEGORY_BOOTS\020\003\022\034\n\030ITEM_CATEGORY_CONTA" - "INERS\020\004\022\034\n\030ITEM_CATEGORY_DECORATION\020\005\022\026\n" - "\022ITEM_CATEGORY_FOOD\020\006\022\036\n\032ITEM_CATEGORY_H" - "ELMETS_HATS\020\007\022\026\n\022ITEM_CATEGORY_LEGS\020\010\022\030\n" - "\024ITEM_CATEGORY_OTHERS\020\t\022\031\n\025ITEM_CATEGORY" - "_POTIONS\020\n\022\027\n\023ITEM_CATEGORY_RINGS\020\013\022\027\n\023I" - "TEM_CATEGORY_RUNES\020\014\022\031\n\025ITEM_CATEGORY_SH" - "IELDS\020\r\022\027\n\023ITEM_CATEGORY_TOOLS\020\016\022\033\n\027ITEM" - "_CATEGORY_VALUABLES\020\017\022\034\n\030ITEM_CATEGORY_A" - "MMUNITION\020\020\022\026\n\022ITEM_CATEGORY_AXES\020\021\022\027\n\023I" - "TEM_CATEGORY_CLUBS\020\022\022\"\n\036ITEM_CATEGORY_DI" - "STANCE_WEAPONS\020\023\022\030\n\024ITEM_CATEGORY_SWORDS" - "\020\024\022\034\n\030ITEM_CATEGORY_WANDS_RODS\020\025\022!\n\035ITEM" - "_CATEGORY_PREMIUM_SCROLLS\020\026\022\035\n\031ITEM_CATE" - "GORY_TIBIA_COINS\020\027\022#\n\037ITEM_CATEGORY_CREA" - "TURE_PRODUCTS\020\030\022\030\n\024ITEM_CATEGORY_QUIVER\020" - "\031*\355\001\n\021PLAYER_PROFESSION\022\"\n\025PLAYER_PROFES" - "SION_ANY\020\377\377\377\377\377\377\377\377\377\001\022\032\n\026PLAYER_PROFESSION" - "_NONE\020\000\022\034\n\030PLAYER_PROFESSION_KNIGHT\020\001\022\035\n" - "\031PLAYER_PROFESSION_PALADIN\020\002\022\036\n\032PLAYER_P" - "ROFESSION_SORCERER\020\003\022\033\n\027PLAYER_PROFESSIO" - "N_DRUID\020\004\022\036\n\032PLAYER_PROFESSION_PROMOTED\020" - "\n*\203\001\n\023ANIMATION_LOOP_TYPE\022)\n\034ANIMATION_L" - "OOP_TYPE_PINGPONG\020\377\377\377\377\377\377\377\377\377\001\022 \n\034ANIMATIO" - "N_LOOP_TYPE_INFINITE\020\000\022\037\n\033ANIMATION_LOOP" - "_TYPE_COUNTED\020\001*4\n\tHOOK_TYPE\022\023\n\017HOOK_TYP" - "E_SOUTH\020\001\022\022\n\016HOOK_TYPE_EAST\020\002*\201\001\n\021FIXED_" - "FRAME_GROUP\022!\n\035FIXED_FRAME_GROUP_OUTFIT_" - "IDLE\020\000\022#\n\037FIXED_FRAME_GROUP_OUTFIT_MOVIN" - "G\020\001\022$\n FIXED_FRAME_GROUP_OBJECT_INITIAL\020" - "\002" + "top\0308 \001(\010\022\017\n\007wrapkit\0309 \001(\010\"E\n#Appearance" + "FlagUpgradeClassification\022\036\n\026upgrade_cla" + "ssification\030\001 \001(\r\"\'\n\022AppearanceFlagBank\022" + "\021\n\twaypoints\030\001 \001(\r\".\n\023AppearanceFlagWrit" + "e\022\027\n\017max_text_length\030\001 \001(\r\"7\n\027Appearance" + "FlagWriteOnce\022\034\n\024max_text_length_once\030\001 " + "\001(\r\"8\n\023AppearanceFlagLight\022\022\n\nbrightness" + "\030\001 \001(\r\022\r\n\005color\030\002 \001(\r\")\n\024AppearanceFlagH" + "eight\022\021\n\televation\030\001 \001(\r\"+\n\023AppearanceFl" + "agShift\022\t\n\001x\030\001 \001(\r\022\t\n\001y\030\002 \001(\r\"%\n\025Appeara" + "nceFlagClothes\022\014\n\004slot\030\001 \001(\r\"Y\n\033Appearan" + "ceFlagDefaultAction\022:\n\006action\030\001 \001(\0162*.Ca" + "nary.protobuf.appearances.PLAYER_ACTION\"" + "\362\001\n\024AppearanceFlagMarket\022<\n\010category\030\001 \001" + "(\0162*.Canary.protobuf.appearances.ITEM_CA" + "TEGORY\022\032\n\022trade_as_object_id\030\002 \001(\r\022\031\n\021sh" + "ow_as_object_id\030\003 \001(\r\022N\n\026restrict_to_pro" + "fession\030\005 \003(\0162..Canary.protobuf.appearan" + "ces.PLAYER_PROFESSION\022\025\n\rminimum_level\030\006" + " \001(\r\"\245\001\n\021AppearanceFlagNPC\022\014\n\004name\030\001 \001(\014" + "\022\020\n\010location\030\002 \001(\014\022\022\n\nsale_price\030\003 \001(\r\022\021" + "\n\tbuy_price\030\004 \001(\r\022\037\n\027currency_object_typ" + "e_id\030\005 \001(\r\022(\n currency_quest_flag_displa" + "y_name\030\006 \001(\014\"&\n\025AppearanceFlagAutomap\022\r\n" + "\005color\030\001 \001(\r\"O\n\022AppearanceFlagHook\0229\n\tdi" + "rection\030\001 \001(\0162&.Canary.protobuf.appearan" + "ces.HOOK_TYPE\"$\n\026AppearanceFlagLenshelp\022" + "\n\n\002id\030\001 \001(\r\"=\n\035AppearanceFlagChangedToEx" + "pire\022\034\n\024former_object_typeid\030\001 \001(\r\"3\n\030Ap" + "pearanceFlagCyclopedia\022\027\n\017cyclopedia_typ" + "e\030\001 \001(\r\"\312\001\n\033SpecialMeaningAppearanceIds\022" + "\024\n\014gold_coin_id\030\001 \001(\r\022\030\n\020platinum_coin_i" + "d\030\002 \001(\r\022\027\n\017crystal_coin_id\030\003 \001(\r\022\025\n\rtibi" + "a_coin_id\030\004 \001(\r\022\031\n\021stamped_letter_id\030\005 \001" + "(\r\022\027\n\017supply_stash_id\030\006 \001(\r\022\027\n\017reward_ch" + "est_id\030\007 \001(\r*\224\001\n\rPLAYER_ACTION\022\026\n\022PLAYER" + "_ACTION_NONE\020\000\022\026\n\022PLAYER_ACTION_LOOK\020\001\022\025" + "\n\021PLAYER_ACTION_USE\020\002\022\026\n\022PLAYER_ACTION_O" + "PEN\020\003\022$\n PLAYER_ACTION_AUTOWALK_HIGHLIGH" + "T\020\004*\315\005\n\rITEM_CATEGORY\022\030\n\024ITEM_CATEGORY_A" + "RMORS\020\001\022\031\n\025ITEM_CATEGORY_AMULETS\020\002\022\027\n\023IT" + "EM_CATEGORY_BOOTS\020\003\022\034\n\030ITEM_CATEGORY_CON" + "TAINERS\020\004\022\034\n\030ITEM_CATEGORY_DECORATION\020\005\022" + "\026\n\022ITEM_CATEGORY_FOOD\020\006\022\036\n\032ITEM_CATEGORY" + "_HELMETS_HATS\020\007\022\026\n\022ITEM_CATEGORY_LEGS\020\010\022" + "\030\n\024ITEM_CATEGORY_OTHERS\020\t\022\031\n\025ITEM_CATEGO" + "RY_POTIONS\020\n\022\027\n\023ITEM_CATEGORY_RINGS\020\013\022\027\n" + "\023ITEM_CATEGORY_RUNES\020\014\022\031\n\025ITEM_CATEGORY_" + "SHIELDS\020\r\022\027\n\023ITEM_CATEGORY_TOOLS\020\016\022\033\n\027IT" + "EM_CATEGORY_VALUABLES\020\017\022\034\n\030ITEM_CATEGORY" + "_AMMUNITION\020\020\022\026\n\022ITEM_CATEGORY_AXES\020\021\022\027\n" + "\023ITEM_CATEGORY_CLUBS\020\022\022\"\n\036ITEM_CATEGORY_" + "DISTANCE_WEAPONS\020\023\022\030\n\024ITEM_CATEGORY_SWOR" + "DS\020\024\022\034\n\030ITEM_CATEGORY_WANDS_RODS\020\025\022!\n\035IT" + "EM_CATEGORY_PREMIUM_SCROLLS\020\026\022\035\n\031ITEM_CA" + "TEGORY_TIBIA_COINS\020\027\022#\n\037ITEM_CATEGORY_CR" + "EATURE_PRODUCTS\020\030\022\030\n\024ITEM_CATEGORY_QUIVE" + "R\020\031*\355\001\n\021PLAYER_PROFESSION\022\"\n\025PLAYER_PROF" + "ESSION_ANY\020\377\377\377\377\377\377\377\377\377\001\022\032\n\026PLAYER_PROFESSI" + "ON_NONE\020\000\022\034\n\030PLAYER_PROFESSION_KNIGHT\020\001\022" + "\035\n\031PLAYER_PROFESSION_PALADIN\020\002\022\036\n\032PLAYER" + "_PROFESSION_SORCERER\020\003\022\033\n\027PLAYER_PROFESS" + "ION_DRUID\020\004\022\036\n\032PLAYER_PROFESSION_PROMOTE" + "D\020\n*\203\001\n\023ANIMATION_LOOP_TYPE\022)\n\034ANIMATION" + "_LOOP_TYPE_PINGPONG\020\377\377\377\377\377\377\377\377\377\001\022 \n\034ANIMAT" + "ION_LOOP_TYPE_INFINITE\020\000\022\037\n\033ANIMATION_LO" + "OP_TYPE_COUNTED\020\001*4\n\tHOOK_TYPE\022\023\n\017HOOK_T" + "YPE_SOUTH\020\001\022\022\n\016HOOK_TYPE_EAST\020\002*\201\001\n\021FIXE" + "D_FRAME_GROUP\022!\n\035FIXED_FRAME_GROUP_OUTFI" + "T_IDLE\020\000\022#\n\037FIXED_FRAME_GROUP_OUTFIT_MOV" + "ING\020\001\022$\n FIXED_FRAME_GROUP_OBJECT_INITIA" + "L\020\002" ; static ::_pbi::once_flag descriptor_table_appearances_2eproto_once; const ::_pbi::DescriptorTable descriptor_table_appearances_2eproto = { - false, false, 6201, descriptor_table_protodef_appearances_2eproto, + false, false, 6243, descriptor_table_protodef_appearances_2eproto, "appearances.proto", &descriptor_table_appearances_2eproto_once, nullptr, 0, 26, schemas, file_default_instances, TableStruct_appearances_2eproto::offsets, @@ -4077,6 +4084,9 @@ class AppearanceFlags::_Internal { static void set_has_expirestop(HasBits* has_bits) { (*has_bits)[1] |= 4194304u; } + static void set_has_wrapkit(HasBits* has_bits) { + (*has_bits)[1] |= 8388608u; + } }; const ::Canary::protobuf::appearances::AppearanceFlagBank& @@ -4206,7 +4216,8 @@ AppearanceFlags::AppearanceFlags(const AppearanceFlags& from) , decltype(_impl_.wearout_){} , decltype(_impl_.clockexpire_){} , decltype(_impl_.expire_){} - , decltype(_impl_.expirestop_){}}; + , decltype(_impl_.expirestop_){} + , decltype(_impl_.wrapkit_){}}; _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); if (from._internal_has_bank()) { @@ -4255,8 +4266,8 @@ AppearanceFlags::AppearanceFlags(const AppearanceFlags& from) _this->_impl_.upgradeclassification_ = new ::Canary::protobuf::appearances::AppearanceFlagUpgradeClassification(*from._impl_.upgradeclassification_); } ::memcpy(&_impl_.clip_, &from._impl_.clip_, - static_cast(reinterpret_cast(&_impl_.expirestop_) - - reinterpret_cast(&_impl_.clip_)) + sizeof(_impl_.expirestop_)); + static_cast(reinterpret_cast(&_impl_.wrapkit_) - + reinterpret_cast(&_impl_.clip_)) + sizeof(_impl_.wrapkit_)); // @@protoc_insertion_point(copy_constructor:Canary.protobuf.appearances.AppearanceFlags) } @@ -4323,6 +4334,7 @@ inline void AppearanceFlags::SharedCtor( , decltype(_impl_.clockexpire_){false} , decltype(_impl_.expire_){false} , decltype(_impl_.expirestop_){false} + , decltype(_impl_.wrapkit_){false} }; } @@ -4453,10 +4465,10 @@ void AppearanceFlags::Clear() { reinterpret_cast(&_impl_.reverse_addons_east_) - reinterpret_cast(&_impl_.unwrap_)) + sizeof(_impl_.reverse_addons_east_)); } - if (cached_has_bits & 0x007f0000u) { + if (cached_has_bits & 0x00ff0000u) { ::memset(&_impl_.reverse_addons_west_, 0, static_cast( - reinterpret_cast(&_impl_.expirestop_) - - reinterpret_cast(&_impl_.reverse_addons_west_)) + sizeof(_impl_.expirestop_)); + reinterpret_cast(&_impl_.wrapkit_) - + reinterpret_cast(&_impl_.reverse_addons_west_)) + sizeof(_impl_.wrapkit_)); } _impl_._has_bits_.Clear(); _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); @@ -4961,6 +4973,15 @@ const char* AppearanceFlags::_InternalParse(const char* ptr, ::_pbi::ParseContex } else goto handle_unusual; continue; + // optional bool wrapkit = 57; + case 57: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 200)) { + _Internal::set_has_wrapkit(&_impl_._has_bits_); + _impl_.wrapkit_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; default: goto handle_unusual; } // switch @@ -5359,6 +5380,12 @@ uint8_t* AppearanceFlags::_InternalSerialize( target = ::_pbi::WireFormatLite::WriteBoolToArray(56, this->_internal_expirestop(), target); } + // optional bool wrapkit = 57; + if (cached_has_bits & 0x00800000u) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteBoolToArray(57, this->_internal_wrapkit(), target); + } + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); @@ -5666,7 +5693,7 @@ size_t AppearanceFlags::ByteSizeLong() const { } } - if (cached_has_bits & 0x007f0000u) { + if (cached_has_bits & 0x00ff0000u) { // optional bool reverse_addons_west = 50; if (cached_has_bits & 0x00010000u) { total_size += 2 + 1; @@ -5702,6 +5729,11 @@ size_t AppearanceFlags::ByteSizeLong() const { total_size += 2 + 1; } + // optional bool wrapkit = 57; + if (cached_has_bits & 0x00800000u) { + total_size += 2 + 1; + } + } return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); } @@ -5900,7 +5932,7 @@ void AppearanceFlags::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const } _this->_impl_._has_bits_[1] |= cached_has_bits; } - if (cached_has_bits & 0x007f0000u) { + if (cached_has_bits & 0x00ff0000u) { if (cached_has_bits & 0x00010000u) { _this->_impl_.reverse_addons_west_ = from._impl_.reverse_addons_west_; } @@ -5922,6 +5954,9 @@ void AppearanceFlags::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const if (cached_has_bits & 0x00400000u) { _this->_impl_.expirestop_ = from._impl_.expirestop_; } + if (cached_has_bits & 0x00800000u) { + _this->_impl_.wrapkit_ = from._impl_.wrapkit_; + } _this->_impl_._has_bits_[1] |= cached_has_bits; } _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); @@ -5945,8 +5980,8 @@ void AppearanceFlags::InternalSwap(AppearanceFlags* other) { swap(_impl_._has_bits_[1], other->_impl_._has_bits_[1]); _impl_.npcsaledata_.InternalSwap(&other->_impl_.npcsaledata_); ::PROTOBUF_NAMESPACE_ID::internal::memswap< - PROTOBUF_FIELD_OFFSET(AppearanceFlags, _impl_.expirestop_) - + sizeof(AppearanceFlags::_impl_.expirestop_) + PROTOBUF_FIELD_OFFSET(AppearanceFlags, _impl_.wrapkit_) + + sizeof(AppearanceFlags::_impl_.wrapkit_) - PROTOBUF_FIELD_OFFSET(AppearanceFlags, _impl_.bank_)>( reinterpret_cast(&_impl_.bank_), reinterpret_cast(&other->_impl_.bank_)); @@ -9523,6 +9558,9 @@ class SpecialMeaningAppearanceIds::_Internal { static void set_has_supply_stash_id(HasBits* has_bits) { (*has_bits)[0] |= 32u; } + static void set_has_reward_chest_id(HasBits* has_bits) { + (*has_bits)[0] |= 64u; + } }; SpecialMeaningAppearanceIds::SpecialMeaningAppearanceIds(::PROTOBUF_NAMESPACE_ID::Arena* arena, @@ -9542,12 +9580,13 @@ SpecialMeaningAppearanceIds::SpecialMeaningAppearanceIds(const SpecialMeaningApp , decltype(_impl_.crystal_coin_id_){} , decltype(_impl_.tibia_coin_id_){} , decltype(_impl_.stamped_letter_id_){} - , decltype(_impl_.supply_stash_id_){}}; + , decltype(_impl_.supply_stash_id_){} + , decltype(_impl_.reward_chest_id_){}}; _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); ::memcpy(&_impl_.gold_coin_id_, &from._impl_.gold_coin_id_, - static_cast(reinterpret_cast(&_impl_.supply_stash_id_) - - reinterpret_cast(&_impl_.gold_coin_id_)) + sizeof(_impl_.supply_stash_id_)); + static_cast(reinterpret_cast(&_impl_.reward_chest_id_) - + reinterpret_cast(&_impl_.gold_coin_id_)) + sizeof(_impl_.reward_chest_id_)); // @@protoc_insertion_point(copy_constructor:Canary.protobuf.appearances.SpecialMeaningAppearanceIds) } @@ -9564,6 +9603,7 @@ inline void SpecialMeaningAppearanceIds::SharedCtor( , decltype(_impl_.tibia_coin_id_){0u} , decltype(_impl_.stamped_letter_id_){0u} , decltype(_impl_.supply_stash_id_){0u} + , decltype(_impl_.reward_chest_id_){0u} }; } @@ -9591,10 +9631,10 @@ void SpecialMeaningAppearanceIds::Clear() { (void) cached_has_bits; cached_has_bits = _impl_._has_bits_[0]; - if (cached_has_bits & 0x0000003fu) { + if (cached_has_bits & 0x0000007fu) { ::memset(&_impl_.gold_coin_id_, 0, static_cast( - reinterpret_cast(&_impl_.supply_stash_id_) - - reinterpret_cast(&_impl_.gold_coin_id_)) + sizeof(_impl_.supply_stash_id_)); + reinterpret_cast(&_impl_.reward_chest_id_) - + reinterpret_cast(&_impl_.gold_coin_id_)) + sizeof(_impl_.reward_chest_id_)); } _impl_._has_bits_.Clear(); _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); @@ -9661,6 +9701,15 @@ const char* SpecialMeaningAppearanceIds::_InternalParse(const char* ptr, ::_pbi: } else goto handle_unusual; continue; + // optional uint32 reward_chest_id = 7; + case 7: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 56)) { + _Internal::set_has_reward_chest_id(&has_bits); + _impl_.reward_chest_id_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; default: goto handle_unusual; } // switch @@ -9728,6 +9777,12 @@ uint8_t* SpecialMeaningAppearanceIds::_InternalSerialize( target = ::_pbi::WireFormatLite::WriteUInt32ToArray(6, this->_internal_supply_stash_id(), target); } + // optional uint32 reward_chest_id = 7; + if (cached_has_bits & 0x00000040u) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(7, this->_internal_reward_chest_id(), target); + } + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); @@ -9745,7 +9800,7 @@ size_t SpecialMeaningAppearanceIds::ByteSizeLong() const { (void) cached_has_bits; cached_has_bits = _impl_._has_bits_[0]; - if (cached_has_bits & 0x0000003fu) { + if (cached_has_bits & 0x0000007fu) { // optional uint32 gold_coin_id = 1; if (cached_has_bits & 0x00000001u) { total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_gold_coin_id()); @@ -9776,6 +9831,11 @@ size_t SpecialMeaningAppearanceIds::ByteSizeLong() const { total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_supply_stash_id()); } + // optional uint32 reward_chest_id = 7; + if (cached_has_bits & 0x00000040u) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_reward_chest_id()); + } + } return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); } @@ -9796,7 +9856,7 @@ void SpecialMeaningAppearanceIds::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to (void) cached_has_bits; cached_has_bits = from._impl_._has_bits_[0]; - if (cached_has_bits & 0x0000003fu) { + if (cached_has_bits & 0x0000007fu) { if (cached_has_bits & 0x00000001u) { _this->_impl_.gold_coin_id_ = from._impl_.gold_coin_id_; } @@ -9815,6 +9875,9 @@ void SpecialMeaningAppearanceIds::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to if (cached_has_bits & 0x00000020u) { _this->_impl_.supply_stash_id_ = from._impl_.supply_stash_id_; } + if (cached_has_bits & 0x00000040u) { + _this->_impl_.reward_chest_id_ = from._impl_.reward_chest_id_; + } _this->_impl_._has_bits_[0] |= cached_has_bits; } _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); @@ -9836,8 +9899,8 @@ void SpecialMeaningAppearanceIds::InternalSwap(SpecialMeaningAppearanceIds* othe _internal_metadata_.InternalSwap(&other->_internal_metadata_); swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]); ::PROTOBUF_NAMESPACE_ID::internal::memswap< - PROTOBUF_FIELD_OFFSET(SpecialMeaningAppearanceIds, _impl_.supply_stash_id_) - + sizeof(SpecialMeaningAppearanceIds::_impl_.supply_stash_id_) + PROTOBUF_FIELD_OFFSET(SpecialMeaningAppearanceIds, _impl_.reward_chest_id_) + + sizeof(SpecialMeaningAppearanceIds::_impl_.reward_chest_id_) - PROTOBUF_FIELD_OFFSET(SpecialMeaningAppearanceIds, _impl_.gold_coin_id_)>( reinterpret_cast(&_impl_.gold_coin_id_), reinterpret_cast(&other->_impl_.gold_coin_id_)); diff --git a/src/protobuf/appearances.pb.h b/src/protobuf/appearances.pb.h index 64f6a7485db..65bc2c7e9cc 100644 --- a/src/protobuf/appearances.pb.h +++ b/src/protobuf/appearances.pb.h @@ -2307,6 +2307,7 @@ class AppearanceFlags final : kClockexpireFieldNumber = 54, kExpireFieldNumber = 55, kExpirestopFieldNumber = 56, + kWrapkitFieldNumber = 57, }; // repeated .Canary.protobuf.appearances.AppearanceFlagNPC npcsaledata = 40; int npcsaledata_size() const; @@ -3116,6 +3117,19 @@ class AppearanceFlags final : void _internal_set_expirestop(bool value); public: + // optional bool wrapkit = 57; + bool has_wrapkit() const; + private: + bool _internal_has_wrapkit() const; + public: + void clear_wrapkit(); + bool wrapkit() const; + void set_wrapkit(bool value); + private: + bool _internal_wrapkit() const; + void _internal_set_wrapkit(bool value); + public: + // @@protoc_insertion_point(class_scope:Canary.protobuf.appearances.AppearanceFlags) private: class _Internal; @@ -3182,6 +3196,7 @@ class AppearanceFlags final : bool clockexpire_; bool expire_; bool expirestop_; + bool wrapkit_; }; union { Impl_ _impl_; }; friend struct ::TableStruct_appearances_2eproto; @@ -6066,6 +6081,7 @@ class SpecialMeaningAppearanceIds final : kTibiaCoinIdFieldNumber = 4, kStampedLetterIdFieldNumber = 5, kSupplyStashIdFieldNumber = 6, + kRewardChestIdFieldNumber = 7, }; // optional uint32 gold_coin_id = 1; bool has_gold_coin_id() const; @@ -6145,6 +6161,19 @@ class SpecialMeaningAppearanceIds final : void _internal_set_supply_stash_id(uint32_t value); public: + // optional uint32 reward_chest_id = 7; + bool has_reward_chest_id() const; + private: + bool _internal_has_reward_chest_id() const; + public: + void clear_reward_chest_id(); + uint32_t reward_chest_id() const; + void set_reward_chest_id(uint32_t value); + private: + uint32_t _internal_reward_chest_id() const; + void _internal_set_reward_chest_id(uint32_t value); + public: + // @@protoc_insertion_point(class_scope:Canary.protobuf.appearances.SpecialMeaningAppearanceIds) private: class _Internal; @@ -6161,6 +6190,7 @@ class SpecialMeaningAppearanceIds final : uint32_t tibia_coin_id_; uint32_t stamped_letter_id_; uint32_t supply_stash_id_; + uint32_t reward_chest_id_; }; union { Impl_ _impl_; }; friend struct ::TableStruct_appearances_2eproto; @@ -10187,6 +10217,34 @@ inline void AppearanceFlags::set_expirestop(bool value) { // @@protoc_insertion_point(field_set:Canary.protobuf.appearances.AppearanceFlags.expirestop) } +// optional bool wrapkit = 57; +inline bool AppearanceFlags::_internal_has_wrapkit() const { + bool value = (_impl_._has_bits_[1] & 0x00800000u) != 0; + return value; +} +inline bool AppearanceFlags::has_wrapkit() const { + return _internal_has_wrapkit(); +} +inline void AppearanceFlags::clear_wrapkit() { + _impl_.wrapkit_ = false; + _impl_._has_bits_[1] &= ~0x00800000u; +} +inline bool AppearanceFlags::_internal_wrapkit() const { + return _impl_.wrapkit_; +} +inline bool AppearanceFlags::wrapkit() const { + // @@protoc_insertion_point(field_get:Canary.protobuf.appearances.AppearanceFlags.wrapkit) + return _internal_wrapkit(); +} +inline void AppearanceFlags::_internal_set_wrapkit(bool value) { + _impl_._has_bits_[1] |= 0x00800000u; + _impl_.wrapkit_ = value; +} +inline void AppearanceFlags::set_wrapkit(bool value) { + _internal_set_wrapkit(value); + // @@protoc_insertion_point(field_set:Canary.protobuf.appearances.AppearanceFlags.wrapkit) +} + // ------------------------------------------------------------------- // AppearanceFlagUpgradeClassification @@ -11319,6 +11377,34 @@ inline void SpecialMeaningAppearanceIds::set_supply_stash_id(uint32_t value) { // @@protoc_insertion_point(field_set:Canary.protobuf.appearances.SpecialMeaningAppearanceIds.supply_stash_id) } +// optional uint32 reward_chest_id = 7; +inline bool SpecialMeaningAppearanceIds::_internal_has_reward_chest_id() const { + bool value = (_impl_._has_bits_[0] & 0x00000040u) != 0; + return value; +} +inline bool SpecialMeaningAppearanceIds::has_reward_chest_id() const { + return _internal_has_reward_chest_id(); +} +inline void SpecialMeaningAppearanceIds::clear_reward_chest_id() { + _impl_.reward_chest_id_ = 0u; + _impl_._has_bits_[0] &= ~0x00000040u; +} +inline uint32_t SpecialMeaningAppearanceIds::_internal_reward_chest_id() const { + return _impl_.reward_chest_id_; +} +inline uint32_t SpecialMeaningAppearanceIds::reward_chest_id() const { + // @@protoc_insertion_point(field_get:Canary.protobuf.appearances.SpecialMeaningAppearanceIds.reward_chest_id) + return _internal_reward_chest_id(); +} +inline void SpecialMeaningAppearanceIds::_internal_set_reward_chest_id(uint32_t value) { + _impl_._has_bits_[0] |= 0x00000040u; + _impl_.reward_chest_id_ = value; +} +inline void SpecialMeaningAppearanceIds::set_reward_chest_id(uint32_t value) { + _internal_set_reward_chest_id(value); + // @@protoc_insertion_point(field_set:Canary.protobuf.appearances.SpecialMeaningAppearanceIds.reward_chest_id) +} + #ifdef __GNUC__ #pragma GCC diagnostic pop #endif // __GNUC__ diff --git a/src/protobuf/appearances.proto b/src/protobuf/appearances.proto index 2bb1690fc31..ed924febfc4 100644 --- a/src/protobuf/appearances.proto +++ b/src/protobuf/appearances.proto @@ -177,6 +177,7 @@ message AppearanceFlags { optional bool clockexpire = 54; optional bool expire = 55; optional bool expirestop = 56; + optional bool wrapkit = 57; } message AppearanceFlagUpgradeClassification { @@ -261,6 +262,7 @@ message SpecialMeaningAppearanceIds { optional uint32 tibia_coin_id = 4; optional uint32 stamped_letter_id = 5; optional uint32 supply_stash_id = 6; + optional uint32 reward_chest_id = 7; } enum FIXED_FRAME_GROUP { diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 4dbb0d33056..f4b70f31d4d 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -207,6 +207,32 @@ namespace { } } } + + /** + * @brief Sends the container category to the network message. + * + * @note The default value is "all", which is the first enum (0). The message must always start with "All". + * + * @details for example of enum see the ContainerCategory_t + * + * @param msg The network message to send the category to. + */ + template + void sendContainerCategory(NetworkMessage &msg, phmap::flat_hash_set categories = {}, uint8_t categoryType = 0) { + msg.addByte(categoryType); + g_logger().debug("Sendding category type '{}', categories total size '{}'", categoryType, categories.size()); + msg.addByte(categories.size()); + for (auto value : categories) { + if (value == T::All) { + continue; + } + + g_logger().debug("Sendding category number '{}', category name '{}'", static_cast(value), magic_enum::enum_name(value).data()); + msg.addByte(static_cast(value)); + msg.addString(toStartCaseWithSpace(magic_enum::enum_name(value).data())); + } + } + } // namespace ProtocolGame::ProtocolGame(Connection_ptr initConnection) : @@ -277,6 +303,10 @@ void ProtocolGame::AddItem(NetworkMessage &msg, uint16_t id, uint8_t count, uint msg.add(it.charges); msg.addByte(0x01); // Brand-new } + + if (it.isWrapKit && !oldProtocol) { + msg.add(0x00); + } } void ProtocolGame::AddItem(NetworkMessage &msg, const Item* item) { @@ -404,6 +434,15 @@ void ProtocolGame::AddItem(NetworkMessage &msg, const Item* item) { msg.addByte(item->getSubType() == it.charges ? 0x01 : 0x00); // Brand-new } } + + if (it.isWrapKit && !oldProtocol) { + uint16_t unWrapId = item->getCustomAttribute("unWrapId") ? static_cast(item->getCustomAttribute("unWrapId")->getInteger()) : 0; + if (unWrapId != 0) { + msg.add(unWrapId); + } else { + msg.add(0x00); + } + } } void ProtocolGame::release() { @@ -1275,7 +1314,8 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage msg, uint8_t recvbyt // case 0xDF, 0xE0, 0xE1, 0xFB, 0xFC, 0xFD, 0xFE Premium Shop. default: - g_logger().debug("Player: {} sent an unknown packet header: x0{}", player->getName(), static_cast(recvbyte)); + std::string hexString = fmt::format("0x{:02x}", recvbyte); + g_logger().debug("Player '{}' sent unknown packet header: hex[{}], decimal[{}]", player->getName(), asUpperCaseString(hexString), recvbyte); break; } } @@ -3069,7 +3109,8 @@ void ProtocolGame::parseBrowseField(NetworkMessage &msg) { void ProtocolGame::parseSeekInContainer(NetworkMessage &msg) { uint8_t containerId = msg.getByte(); uint16_t index = msg.get(); - addGameTask(&Game::playerSeekInContainer, player->getID(), containerId, index); + auto primaryType = msg.getByte(); + addGameTask(&Game::playerSeekInContainer, player->getID(), containerId, index, primaryType); } // Send methods @@ -3134,12 +3175,12 @@ void ProtocolGame::addCreatureIcon(NetworkMessage &msg, const Creature* creature return; } - const auto icon = creature->getIcon(); - // 0 = no icon, 1 = we'll send an icon - if (icon.isNone()) { - msg.addByte(0); - } else { - msg.addByte(1); + const auto icons = creature->getIcons(); + // client only supports 3 icons, otherwise it will crash + const auto count = icons.size() > 3 ? 3 : icons.size(); + msg.addByte(count); + for (uint8_t i = 0; i < count; ++i) { + const auto &icon = icons[i]; msg.addByte(icon.serialize()); msg.addByte(static_cast(icon.category)); msg.add(icon.count); @@ -4186,6 +4227,10 @@ void ProtocolGame::sendUnjustifiedPoints(const uint8_t &dayProgress, const uint8 } void ProtocolGame::sendContainer(uint8_t cid, const Container* container, bool hasParent, uint16_t firstIndex) { + if (!player) { + return; + } + NetworkMessage msg; msg.addByte(0x6E); @@ -4199,6 +4244,8 @@ void ProtocolGame::sendContainer(uint8_t cid, const Container* container, bool h msg.addString(container->getName()); } + const auto itemsStoreInboxToSend = container->getStoreInboxFilteredItems(); + msg.addByte(container->capacity()); msg.addByte(hasParent ? 0x01 : 0x00); @@ -4212,6 +4259,9 @@ void ProtocolGame::sendContainer(uint8_t cid, const Container* container, bool h msg.addByte(container->hasPagination() ? 0x01 : 0x00); // Pagination uint32_t containerSize = container->size(); + if (!itemsStoreInboxToSend.empty()) { + containerSize = itemsStoreInboxToSend.size(); + } msg.add(containerSize); msg.add(firstIndex); @@ -4223,17 +4273,58 @@ void ProtocolGame::sendContainer(uint8_t cid, const Container* container, bool h maxItemsToSend = container->capacity(); } + const ItemDeque &itemList = container->getItemList(); if (firstIndex >= containerSize) { msg.addByte(0x00); + } else if (container->getID() == ITEM_STORE_INBOX && !itemsStoreInboxToSend.empty()) { + msg.addByte(std::min(maxItemsToSend, containerSize)); + for (const auto item : itemsStoreInboxToSend) { + AddItem(msg, item); + } } else { msg.addByte(std::min(maxItemsToSend, containerSize)); uint32_t i = 0; - const ItemDeque &itemList = container->getItemList(); for (ItemDeque::const_iterator it = itemList.begin() + firstIndex, end = itemList.end(); i < maxItemsToSend && it != end; ++it, ++i) { AddItem(msg, *it); } } + + // From here on down is for version 13.21+ + if (oldProtocol) { + writeToOutputBuffer(msg); + return; + } + + if (container->isStoreInbox()) { + auto categories = container->getStoreInboxValidCategories(); + const auto enumName = container->getAttribute(ItemAttribute_t::STORE_INBOX_CATEGORY); + auto category = magic_enum::enum_cast(enumName); + if (category.has_value()) { + bool toSendCategory = false; + // Check if category exist in the deque + for (const auto &tempCategory : categories) { + if (tempCategory == category.value()) { + toSendCategory = true; + g_logger().debug("found category {}", toSendCategory); + } + } + + if (!toSendCategory) { + Container* container = player->getContainerByID(cid); + if (container) { + container->removeAttribute(ItemAttribute_t::STORE_INBOX_CATEGORY); + } + } + sendContainerCategory(msg, categories, static_cast(category.value())); + } else { + sendContainerCategory(msg, categories); + } + } else { + msg.addByte(0x00); + msg.addByte(0x00); + } + writeToOutputBuffer(msg); } diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp index 6040f029383..f7f0a725177 100644 --- a/src/utils/tools.cpp +++ b/src/utils/tools.cpp @@ -303,7 +303,7 @@ std::string toPascalCase(const std::string &str) { result += std::toupper(ch); capitalizeNext = false; } else { - result += std::tolower(ch); + result += ch; // Keep the character as is. } } } @@ -343,6 +343,22 @@ std::string toKebabCase(const std::string &str) { return result; } +std::string toStartCaseWithSpace(const std::string &str) { + std::string result; + for (size_t i = 0; i < str.length(); ++i) { + char ch = str[i]; + if (i == 0 || std::isupper(ch)) { + if (i > 0) { + result += ' '; + } + result += std::toupper(ch); + } else { + result += std::tolower(ch); + } + } + return result; +} + StringVector explodeString(const std::string &inString, const std::string &separator, int32_t limit /* = -1*/) { StringVector returnVector; std::string::size_type start = 0, end = 0; @@ -1258,6 +1274,9 @@ const char* getReturnMessage(ReturnValue value) { case RETURNVALUE_DEPOTISFULL: return "You cannot put more items in this depot."; + case RETURNVALUE_CONTAINERISFULL: + return "You cannot put more items in this container."; + case RETURNVALUE_CANNOTUSETHISOBJECT: return "You cannot use this object."; diff --git a/src/utils/tools.hpp b/src/utils/tools.hpp index dd47aea55eb..e6f70973344 100644 --- a/src/utils/tools.hpp +++ b/src/utils/tools.hpp @@ -33,6 +33,7 @@ std::string toCamelCase(const std::string &str); std::string toPascalCase(const std::string &str); std::string toSnakeCase(const std::string &str); std::string toKebabCase(const std::string &str); +std::string toStartCaseWithSpace(const std::string &str); using StringVector = std::vector; using IntegerVector = std::vector;