From 6fde2592dd5540ebafbdc0d61c5783347ae629a1 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Tue, 21 Nov 2023 12:01:57 -0300 Subject: [PATCH 01/18] improve: "/i" create a big count of stackable items (#1863) --- data/scripts/talkactions/god/create_item.lua | 36 +++++++------------- 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/data/scripts/talkactions/god/create_item.lua b/data/scripts/talkactions/god/create_item.lua index 4ad618120e0..7a6285ef101 100644 --- a/data/scripts/talkactions/god/create_item.lua +++ b/data/scripts/talkactions/god/create_item.lua @@ -1,25 +1,3 @@ -local invalidIds = { - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 10, - 11, - 13, - 14, - 15, - 19, - 21, - 26, - 27, - 28, - 35, - 43, -} - local createItem = TalkAction("/i") function createItem.onSay(player, words, param) @@ -37,7 +15,7 @@ function createItem.onSay(player, words, param) end end - if table.contains(invalidIds, itemType:getId()) then + if itemType:getId() < 100 then return true end @@ -45,7 +23,17 @@ function createItem.onSay(player, words, param) local count = tonumber(split[2]) if count then if itemType:isStackable() then - count = math.min(10000, math.max(1, count)) + local item = Game.createItem(itemType:getId(), count) + if not item then + player:sendCancelMessage("Cannot create item") + return true + end + + local ret = player:addItemEx(item, INDEX_WHEREEVER, FLAG_NOLIMIT) + if ret ~= RETURNVALUE_NOERROR then + player:sendCancelMessage(ret) + end + return true elseif not itemType:isFluidContainer() then local min = 100 if charges > 0 then From 71bdb2f9041d767a56f4694e5c286af3e75be693 Mon Sep 17 00:00:00 2001 From: Felipe Muniz Date: Tue, 21 Nov 2023 13:31:00 -0300 Subject: [PATCH 02/18] fix: don't send inbox store item to market (#1859) --- src/creatures/players/player.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 9002feea81f..7c20e7406de 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -6722,6 +6722,10 @@ std::pair>, std::mapisStoreItem()) { + continue; + } + const ItemType &itemType = Item::items[item->getID()]; if (itemType.wareId == 0) { continue; From f58413e452a60a7935245b2312f9726b48a4f6ab Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Tue, 21 Nov 2023 16:12:14 -0300 Subject: [PATCH 03/18] fix: check conditions permitted on pz (#1865) Resolves #1656 --- src/creatures/combat/combat.cpp | 5 +++-- src/creatures/players/player.cpp | 21 +++++++++++++++++++++ src/creatures/players/player.hpp | 2 ++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 9a80ad55ce3..2a646be071c 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -297,19 +297,20 @@ ReturnValue Combat::canDoCombat(std::shared_ptr attacker, std::shared_ return RETURNVALUE_NOERROR; } + auto targetPlayer = target ? target->getPlayer() : nullptr; if (target) { std::shared_ptr tile = target->getTile(); if (tile->hasProperty(CONST_PROP_BLOCKPROJECTILE)) { return RETURNVALUE_NOTENOUGHROOM; } if (tile->hasFlag(TILESTATE_PROTECTIONZONE)) { - return RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE; + auto permittedOnPz = targetPlayer ? targetPlayer->hasPermittedConditionInPZ() : false; + return permittedOnPz ? RETURNVALUE_NOERROR : RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE; } } if (attacker) { const std::shared_ptr attackerMaster = attacker->getMaster(); - auto targetPlayer = target ? target->getPlayer() : nullptr; if (targetPlayer) { if (targetPlayer->hasFlag(PlayerFlags_t::CannotBeAttacked)) { return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 7c20e7406de..f63d3799918 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -7716,3 +7716,24 @@ std::shared_ptr Player::getLootPouch() { return container; } + +bool Player::hasPermittedConditionInPZ() const { + static const std::unordered_set allowedConditions = { + CONDITION_ENERGY, + CONDITION_FIRE, + CONDITION_POISON, + CONDITION_BLEEDING, + CONDITION_CURSED, + CONDITION_DAZZLED + }; + + bool hasPermittedCondition = false; + for (auto condition : allowedConditions) { + if (getCondition(condition)) { + hasPermittedCondition = true; + break; + } + } + + return hasPermittedCondition; +} diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 5000f1ccf6e..6e3ed43b90d 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -2544,6 +2544,8 @@ class Player final : public Creature, public Cylinder, public Bankable { std::shared_ptr getLootPouch(); + bool hasPermittedConditionInPZ() const; + private: friend class PlayerLock; std::mutex mutex; From 1be3b4307a6f9d91cfdc643879bc980690cfea8a Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Tue, 21 Nov 2023 12:51:22 -0800 Subject: [PATCH 04/18] feat: reset house on inactivity (#1864) --- config.lua.dist | 2 ++ .../talkactions/player/server_info.lua | 3 +++ src/config/config_definitions.hpp | 2 ++ src/config/configmanager.cpp | 2 ++ .../functions/core/game/config_functions.cpp | 1 + src/map/house/house.cpp | 26 +++++++++++++++---- 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/config.lua.dist b/config.lua.dist index a6bd7b6ccaf..6c882bd4b47 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -286,6 +286,7 @@ houseRentRate = 1.0 houseOwnedByAccount = false houseBuyLevel = 100 housePurchasedShowPrice = false +houseLoseAfterInactivity = 30 -- days; 0 = never onlyInvitedCanMoveHouseItems = true togglehouseTransferOnRestart = true @@ -480,6 +481,7 @@ vipBonusSkill = 0 vipAutoLootVipOnly = false vipStayOnline = false vipFamiliarTimeCooldownReduction = 0 +vipKeepHouse = false -- NOTE: set rewardChestCollectEnabled to true to enable the reward collect system -- NOTE set rewardChestMaxCollectItems max items per collect action diff --git a/data/scripts/talkactions/player/server_info.lua b/data/scripts/talkactions/player/server_info.lua index 5c19c48df7f..136a271f7e4 100644 --- a/data/scripts/talkactions/player/server_info.lua +++ b/data/scripts/talkactions/player/server_info.lua @@ -54,10 +54,13 @@ function serverInfo.onSay(player, words, param) .. configManager.getNumber(configKeys.RATE_SPAWN) .. "x" end + local loseHouseText = configManager.getNumber(configKeys.HOUSE_LOSE_AFTER_INACTIVITY) > 0 and configManager.getNumber(configKeys.HOUSE_LOSE_AFTER_INACTIVITY) .. " days" or "never" text = text .. "\n\nMore Server Info: \n" .. "\nLevel to buy house: " .. configManager.getNumber(configKeys.HOUSE_BUY_LEVEL) + .. "\nLose house after inactivity: " + .. loseHouseText .. "\nProtection level: " .. configManager.getNumber(configKeys.PROTECTION_LEVEL) .. "\nWorldType: " diff --git a/src/config/config_definitions.hpp b/src/config/config_definitions.hpp index 417a5348a96..bf86266f3a9 100644 --- a/src/config/config_definitions.hpp +++ b/src/config/config_definitions.hpp @@ -84,6 +84,7 @@ enum booleanConfig_t { TOGGLE_ATTACK_SPEED_ONFIST, VIP_SYSTEM_ENABLED, VIP_AUTOLOOT_VIP_ONLY, + VIP_KEEP_HOUSE, VIP_STAY_ONLINE, REWARD_CHEST_COLLECT_ENABLED, TOGGLE_MOUNT_IN_PZ, @@ -145,6 +146,7 @@ enum integerConfig_t { RATE_KILLING_IN_THE_NAME_OF_POINTS, HOUSE_PRICE_PER_SQM, HOUSE_BUY_LEVEL, + HOUSE_LOSE_AFTER_INACTIVITY, MAX_MESSAGEBUFFER, ACTIONS_DELAY_INTERVAL, EX_ACTIONS_DELAY_INTERVAL, diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 8f46b9b3f4d..5d52ca11b2a 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -223,6 +223,7 @@ bool ConfigManager::load() { integer[HOUSE_PRICE_PER_SQM] = getGlobalNumber(L, "housePriceEachSQM", 1000); integer[HOUSE_BUY_LEVEL] = getGlobalNumber(L, "houseBuyLevel", 0); + integer[HOUSE_LOSE_AFTER_INACTIVITY] = getGlobalNumber(L, "houseLoseAfterInactivity", 0); boolean[HOUSE_PURSHASED_SHOW_PRICE] = getGlobalBoolean(L, "housePurchasedShowPrice", false); boolean[ONLY_INVITED_CAN_MOVE_HOUSE_ITEMS] = getGlobalBoolean(L, "onlyInvitedCanMoveHouseItems", true); @@ -384,6 +385,7 @@ bool ConfigManager::load() { integer[VIP_BONUS_LOOT] = getGlobalNumber(L, "vipBonusLoot", 0); integer[VIP_BONUS_SKILL] = getGlobalNumber(L, "vipBonusSkill", 0); boolean[VIP_AUTOLOOT_VIP_ONLY] = getGlobalBoolean(L, "vipAutoLootVipOnly", false); + boolean[VIP_KEEP_HOUSE] = getGlobalBoolean(L, "vipKeepHouse", false); boolean[VIP_STAY_ONLINE] = getGlobalBoolean(L, "vipStayOnline", false); integer[VIP_FAMILIAR_TIME_COOLDOWN_REDUCTION] = getGlobalNumber(L, "vipFamiliarTimeCooldownReduction", 0); diff --git a/src/lua/functions/core/game/config_functions.cpp b/src/lua/functions/core/game/config_functions.cpp index bdeaa226fc1..96ce09cdcc7 100644 --- a/src/lua/functions/core/game/config_functions.cpp +++ b/src/lua/functions/core/game/config_functions.cpp @@ -103,6 +103,7 @@ void ConfigFunctions::init(lua_State* L) { registerEnumIn(L, "configKeys", RATE_KILLING_IN_THE_NAME_OF_POINTS); registerEnumIn(L, "configKeys", HOUSE_PRICE_PER_SQM); registerEnumIn(L, "configKeys", HOUSE_BUY_LEVEL); + registerEnumIn(L, "configKeys", HOUSE_LOSE_AFTER_INACTIVITY); registerEnumIn(L, "configKeys", MAX_MESSAGEBUFFER); registerEnumIn(L, "configKeys", ACTIONS_DELAY_INTERVAL); registerEnumIn(L, "configKeys", EX_ACTIONS_DELAY_INTERVAL); diff --git a/src/map/house/house.cpp b/src/map/house/house.cpp index 20af6ee4042..828753b8c56 100644 --- a/src/map/house/house.cpp +++ b/src/map/house/house.cpp @@ -752,11 +752,6 @@ void Houses::payHouses(RentPeriod_t rentPeriod) const { continue; } - const uint32_t rent = house->getRent(); - if (rent == 0 || house->getPaidUntil() > currentTime) { - continue; - } - const uint32_t ownerId = house->getOwner(); const auto &town = g_game().map.towns.getTown(house->getTownId()); if (!town) { @@ -770,6 +765,27 @@ void Houses::payHouses(RentPeriod_t rentPeriod) const { continue; } + // Player hasn't logged in for a while, reset house owner + auto daysToReset = g_configManager().getNumber(HOUSE_LOSE_AFTER_INACTIVITY); + if (daysToReset > 0) { + auto daysSinceLastLogin = (currentTime - player->getLastLoginSaved()) / (60 * 60 * 24); + bool vipKeep = g_configManager().getBoolean(VIP_KEEP_HOUSE) && player->isVip(); + bool activityKeep = daysSinceLastLogin < daysToReset; + if (vipKeep && !activityKeep) { + g_logger().info("Player {} has not logged in for {} days, but is a VIP, so the house will not be reset.", player->getName(), daysToReset); + } else if (!vipKeep && !activityKeep) { + g_logger().info("Player {} has not logged in for {} days, so the house will be reset.", player->getName(), daysToReset); + house->setOwner(0, true, player); + g_saveManager().savePlayer(player); + continue; + } + } + + const uint32_t rent = house->getRent(); + if (rent == 0 || house->getPaidUntil() > currentTime) { + continue; + } + if (player->getBankBalance() >= rent) { g_game().removeMoney(player, rent, 0, true); From fd0602a45daeb26f5e9e0976b309c54d156b679d Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Tue, 21 Nov 2023 23:41:19 -0300 Subject: [PATCH 05/18] fix: infinity prey time if stamina system is disabled (#1873) Resolves #518 --- data-canary/scripts/creaturescripts/login.lua | 4 ++-- .../scripts/creaturescripts/logout.lua | 4 ++-- .../scripts/creaturescripts/others/login.lua | 2 +- .../scripts/creaturescripts/others/logout.lua | 4 ++-- data/events/scripts/player.lua | 19 +++++++++++-------- data/global.lua | 4 ++-- 6 files changed, 20 insertions(+), 17 deletions(-) diff --git a/data-canary/scripts/creaturescripts/login.lua b/data-canary/scripts/creaturescripts/login.lua index edcb45ac327..5455b801d8c 100644 --- a/data-canary/scripts/creaturescripts/login.lua +++ b/data-canary/scripts/creaturescripts/login.lua @@ -14,7 +14,7 @@ function login.onLogin(player) end -- Stamina - nextUseStaminaTime[player.uid] = 0 + _G.NextUseStaminaTime[player.uid] = 0 -- Promotion local vocation = player:getVocation() @@ -98,7 +98,7 @@ function login.onLogin(player) local playerId = player:getId() -- Stamina - nextUseStaminaTime[playerId] = 1 + _G.NextUseStaminaTime[playerId] = 1 -- EXP Stamina nextUseXpStamina[playerId] = 1 diff --git a/data-canary/scripts/creaturescripts/logout.lua b/data-canary/scripts/creaturescripts/logout.lua index df1ff64b8ce..22612d2a7e4 100644 --- a/data-canary/scripts/creaturescripts/logout.lua +++ b/data-canary/scripts/creaturescripts/logout.lua @@ -2,8 +2,8 @@ local logout = CreatureEvent("PlayerLogout") function logout.onLogout(player) local playerId = player:getId() - if nextUseStaminaTime[playerId] then - nextUseStaminaTime[playerId] = nil + if _G.NextUseStaminaTime[playerId] then + _G.NextUseStaminaTime[playerId] = nil end if onExerciseTraining[playerId] then diff --git a/data-otservbr-global/scripts/creaturescripts/others/login.lua b/data-otservbr-global/scripts/creaturescripts/others/login.lua index 45e2cabc7fc..180a4e68d7d 100644 --- a/data-otservbr-global/scripts/creaturescripts/others/login.lua +++ b/data-otservbr-global/scripts/creaturescripts/others/login.lua @@ -208,7 +208,7 @@ function playerLogin.onLogin(player) player:initializeLoyaltySystem() -- Stamina - nextUseStaminaTime[playerId] = 1 + _G.NextUseStaminaTime[playerId] = 1 -- EXP Stamina nextUseXpStamina[playerId] = 1 diff --git a/data-otservbr-global/scripts/creaturescripts/others/logout.lua b/data-otservbr-global/scripts/creaturescripts/others/logout.lua index 314246a981a..eb68a47cf3f 100644 --- a/data-otservbr-global/scripts/creaturescripts/others/logout.lua +++ b/data-otservbr-global/scripts/creaturescripts/others/logout.lua @@ -2,8 +2,8 @@ local playerLogout = CreatureEvent("PlayerLogout") function playerLogout.onLogout(player) local playerId = player:getId() - if nextUseStaminaTime[playerId] ~= nil then - nextUseStaminaTime[playerId] = nil + if _G.NextUseStaminaTime[playerId] ~= nil then + _G.NextUseStaminaTime[playerId] = nil end player:saveSpecialStorage() diff --git a/data/events/scripts/player.lua b/data/events/scripts/player.lua index 333d506919d..a8649d5b08e 100644 --- a/data/events/scripts/player.lua +++ b/data/events/scripts/player.lua @@ -96,7 +96,7 @@ local soulCondition = Condition(CONDITION_SOUL, CONDITIONID_DEFAULT) soulCondition:setTicks(4 * 60 * 1000) soulCondition:setParameter(CONDITION_PARAM_SOULGAIN, 1) -local function useStamina(player) +local function useStamina(player, isStaminaEnabled) if not player then return false end @@ -107,12 +107,12 @@ local function useStamina(player) end local playerId = player:getId() - if not playerId or not nextUseStaminaTime[playerId] then + if not playerId or not _G.NextUseStaminaTime[playerId] then return false end local currentTime = os.time() - local timePassed = currentTime - nextUseStaminaTime[playerId] + local timePassed = currentTime - _G.NextUseStaminaTime[playerId] if timePassed <= 0 then return end @@ -123,14 +123,16 @@ local function useStamina(player) else staminaMinutes = 0 end - nextUseStaminaTime[playerId] = currentTime + 120 + _G.NextUseStaminaTime[playerId] = currentTime + 120 player:removePreyStamina(120) else staminaMinutes = staminaMinutes - 1 - nextUseStaminaTime[playerId] = currentTime + 60 + _G.NextUseStaminaTime[playerId] = currentTime + 60 player:removePreyStamina(60) end - player:setStamina(staminaMinutes) + if isStaminaEnabled then + player:setStamina(staminaMinutes) + end end local function useStaminaXpBoost(player) @@ -514,8 +516,9 @@ function Player:onGainExperience(target, exp, rawExp) -- Stamina Bonus local staminaBonusXp = 1 - if configManager.getBoolean(configKeys.STAMINA_SYSTEM) then - useStamina(self) + local isStaminaEnabled = configManager.getBoolean(configKeys.STAMINA_SYSTEM) + useStamina(self, isStaminaEnabled) + if isStaminaEnabled then staminaBonusXp = self:getFinalBonusStamina() self:setStaminaXpBoost(staminaBonusXp * 100) end diff --git a/data/global.lua b/data/global.lua index 9fad23e8787..7a26302b345 100644 --- a/data/global.lua +++ b/data/global.lua @@ -105,8 +105,8 @@ if onExerciseTraining == nil then end -- Stamina -if nextUseStaminaTime == nil then - nextUseStaminaTime = {} +if not _G.NextUseStaminaTime then + _G.NextUseStaminaTime = {} end if nextUseXpStamina == nil then From 5d5338ea54a6544eb2be13021bd587512600f0db Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Wed, 22 Nov 2023 00:22:02 -0300 Subject: [PATCH 06/18] fix: marry system (#1874) Resolves #588 --- data-otservbr-global/npc/lynda.lua | 63 ++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/data-otservbr-global/npc/lynda.lua b/data-otservbr-global/npc/lynda.lua index 3176a464d70..82a8cf8cc7d 100644 --- a/data-otservbr-global/npc/lynda.lua +++ b/data-otservbr-global/npc/lynda.lua @@ -155,6 +155,61 @@ local function creatureSayCallback(npc, creature, type, message) end end +local function tryEngage(npc, creature, message, keywords, parameters, node) + local player = Player(creature) + local playerStatus = getPlayerMarriageStatus(player:getGuid()) + local playerSpouse = getPlayerSpouse(player:getGuid()) + if playerStatus == MARRIED_STATUS then -- check if the player is already married + npcHandler:say("You are already married to {" .. player:getName() .. "}.", npc, creature) + elseif playerStatus == PROPOSED_STATUS then --check if the player already made a proposal to some1 else + npcHandler:say("You already made a wedding proposal to {" .. player:getName() .. "}. You can always remove the proposal by saying {remove} proposal.", npc, creature) + else + local candidate = getPlayerGUIDByName(message) + if candidate == 0 then -- check if there is actually a player called like this + npcHandler:say("A player with this name does not exist.", npc, creature) + elseif candidate == player:getGuid() then -- if it's himself, cannot marry + npcHandler:say("You REALLY want to marry yourself? c'mon, be serious.", npc, creature) + else + if player:getItemCount(ITEM_WEDDING_RING) == 0 or player:getItemCount(9586) == 0 then -- check for items (wedding ring and outfit box) + npcHandler:say("As I said, you need a wedding ring and the wedding outfit box in order to marry.", npc, creature) + else + local candidateStatus = getPlayerMarriageStatus(candidate) + local candidateSpouse = getPlayerSpouse(candidate) + if candidateStatus == MARRIED_STATUS then -- if the player you want to marry is already married and to whom + npcHandler:say("{" .. getPlayerNameById(candidate) .. "} is already married to {" .. getPlayerNameById(candidateSpouse) .. "}.", npc, creature) + elseif candidateStatus == PROPACCEPT_STATUS then -- if the player you want to marry is already going to marry some1 else + npcHandler:say("{" .. getPlayerNameById(candidate) .. "} is already engaged to {" .. getPlayerNameById(candidateSpouse) .. "} and they will going to marry soon.", npc, creature) + elseif candidateStatus == PROPOSED_STATUS then -- if he/she already made a proposal to some1 + if candidateSpouse == player:getGuid() then -- if this someone is you. + -- if this some1 is not you + npcHandler:say("Since both of you are willing to marry, I accept to celebrate your marriage, go prepare yourself, and tell me when you are ready for the {celebration}", npc, creature) + player:removeItem(ITEM_WEDDING_RING, 1) + player:removeItem(9586, 1) -- wedding outfit box + player:addOutfit(329) --Wife + player:addOutfit(328) --Husb + setPlayerMarriageStatus(player:getGuid(), PROPACCEPT_STATUS) + setPlayerMarriageStatus(candidate, PROPACCEPT_STATUS) + setPlayerSpouse(player:getGuid(), candidate) + local player = Player(getPlayerNameById(candidate)) + player:addOutfit(329) + player:addOutfit(328) + else + npcHandler:say("{" .. getPlayerNameById(candidate) .. "} already made a wedding proposal to {" .. getPlayerNameById(candidateSpouse) .. "}.", npc, creature) + end + else -- if the player i want to propose doesn't have other proposal + npcHandler:say("Ok, now let's wait and see if {" .. getPlayerNameById(candidate) .. "} accepts your proposal. I'll give you back your wedding ring as soon as {" .. getPlayerNameById(candidate) .. "} accepts your proposal or you {remove} it.", npc, creature) + player:removeItem(ITEM_WEDDING_RING, 1) + player:removeItem(9586, 1) + setPlayerMarriageStatus(player:getGuid(), PROPOSED_STATUS) + setPlayerSpouse(player:getGuid(), candidate) + end + end + end + end + keywordHandler:moveUp(player, 1) + return false +end + local function confirmWedding(npc, creature, message, keywords, parameters, node) local player = Player(creature) local playerStatus = getPlayerMarriageStatus(player:getGuid()) @@ -199,12 +254,12 @@ local function confirmRemoveEngage(npc, creature, message, keywords, parameters, setPlayerMarriageStatus(player:getGuid(), 0) setPlayerSpouse(player:getGuid(), -1) npcHandler:say(parameters.text, npc, creature) - keywordHandler:moveUp(parameters.moveup) + keywordHandler:moveUp(player, parameters.moveup) end node:addChildKeyword({ "yes" }, removeEngage, { moveup = 3, text = "Ok, your marriage proposal to {" .. getPlayerNameById(playerSpouse) .. "} has been removed. Take your wedding ring back." }) else npcHandler:say("You don't have any pending proposal to be removed.", npc, creature) - keywordHandler:moveUp(2) + keywordHandler:moveUp(player, 2) end return true end @@ -225,12 +280,12 @@ local function confirmDivorce(npc, creature, message, keywords, parameters, node setPlayerMarriageStatus(spouse, 0) setPlayerSpouse(spouse, -1) npcHandler:say(parameters.text, npc, creature) - keywordHandler:moveUp(parameters.moveup) + keywordHandler:moveUp(player, parameters.moveup) end node:addChildKeyword({ "yes" }, divorce, { moveup = 3, text = "Ok, you are now divorced of {" .. getPlayerNameById(playerSpouse) .. "}. Think better next time after marrying someone." }) else npcHandler:say("You aren't married to get a divorce.", npc, creature) - keywordHandler:moveUp(2) + keywordHandler:moveUp(player, 2) end return true end From 3283a23ee819f00762f6f5c4a418902ae5872ee3 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Wed, 22 Nov 2023 03:15:05 -0300 Subject: [PATCH 07/18] fix: prey reroll time on old protocol (#1876) Resolves #1837 --- src/server/network/protocol/protocolgame.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index dc6cea87b90..d60c653aced 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -6899,7 +6899,10 @@ void ProtocolGame::sendPreyData(const std::unique_ptr &slot) { } if (oldProtocol) { - msg.add(static_cast(std::max(std::max(static_cast(((slot->freeRerollTimeStamp - OTSYS_TIME()) / 1000)), 0), 0))); + auto currentTime = OTSYS_TIME(); + auto timeDiffMs = (slot->freeRerollTimeStamp > currentTime) ? (slot->freeRerollTimeStamp - currentTime) : 0; + auto timeDiffMinutes = timeDiffMs / 60000; + msg.add(timeDiffMinutes ? timeDiffMinutes : 0); } else { msg.add(std::max(static_cast(((slot->freeRerollTimeStamp - OTSYS_TIME()) / 1000)), 0)); msg.addByte(static_cast(slot->option)); From 8e2f21371bcc7af4a992cfdff7afc06802e40da9 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Wed, 22 Nov 2023 03:29:48 -0300 Subject: [PATCH 08/18] fix: hireling stash and bank (#1875) Resolves #1869 --- data-otservbr-global/npc/hireling.lua | 14 +++++++++++--- src/game/game.cpp | 2 ++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/data-otservbr-global/npc/hireling.lua b/data-otservbr-global/npc/hireling.lua index 7caf05d9bcd..519ff32773a 100644 --- a/data-otservbr-global/npc/hireling.lua +++ b/data-otservbr-global/npc/hireling.lua @@ -7,6 +7,7 @@ function createHirelingType(HirelingName) end local npcConfig = {} + local enableBankSystem = {} npcConfig.name = HirelingName npcConfig.description = HirelingName @@ -355,6 +356,7 @@ function createHirelingType(HirelingName) end npcType.onDisappear = function(npc, creature) + enableBankSystem[creature:getId()] = nil npcHandler:onDisappear(npc, creature) end @@ -363,6 +365,7 @@ function createHirelingType(HirelingName) end npcType.onCloseChannel = function(npc, creature) + enableBankSystem[creature:getId()] = nil npcHandler:onCloseChannel(npc, creature) end @@ -586,7 +589,9 @@ function createHirelingType(HirelingName) elseif MsgContains(message, "stash") then if hireling:hasSkill(HIRELING_SKILLS.STEWARD) then npcHandler:say(GREETINGS.STASH, npc, creature) + player:setSpecialContainersAvailable(true) player:openStash(true) + player:sendTextMessage(MESSAGE_FAILURE, "Your supply stash contains " .. player:getStashCount() .. " item" .. (player:getStashCount() > 1 and "s." or ".")) else sendSkillNotLearned(npc, creature, HIRELING_SKILLS.STEWARD) end @@ -614,15 +619,18 @@ function createHirelingType(HirelingName) npcHandler:setTopic(playerId, TOPIC.SERVICES) npcHandler:say("Alright then, I will be here.", npc, creature) end - elseif npcHandler:getTopic(playerId) >= TOPIC.BANK and npcHandler:getTopic(playerId) < TOPIC.FOOD then + elseif npcHandler:getTopic(playerId) == TOPIC.BANK then + enableBankSystem[playerId] = true + elseif npcHandler:getTopic(playerId) >= TOPIC.FOOD and npcHandler:getTopic(playerId) < TOPIC.GOODS then + handleFoodActions(npc, creature, message) + end + if enableBankSystem[playerId] then -- Parse bank npc:parseBank(message, npc, creature, npcHandler) -- Parse guild bank npc:parseGuildBank(message, npc, creature, playerId, npcHandler) -- Normal messages npc:parseBankMessages(message, npc, creature, npcHandler) - elseif npcHandler:getTopic(playerId) >= TOPIC.FOOD and npcHandler:getTopic(playerId) < TOPIC.GOODS then - handleFoodActions(npc, creature, message) end return true end diff --git a/src/game/game.cpp b/src/game/game.cpp index 0ff4a08e6e3..5aa0e6eecca 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -4214,6 +4214,8 @@ void Game::playerStashWithdraw(uint32_t playerId, uint16_t itemId, uint32_t coun if (player->isDepotSearchOpenOnItem(itemId)) { player->requestDepotSearchItem(itemId, 0); } + + player->sendOpenStash(true); } void Game::playerSeekInContainer(uint32_t playerId, uint8_t containerId, uint16_t index, uint8_t containerCategory) { From ac056e734de6ca8d7ae7389873fea54f1d5a2679 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Wed, 22 Nov 2023 03:50:13 -0300 Subject: [PATCH 09/18] fix: block re-login with different protocols (#1877) Resolves #1668 --- src/creatures/players/player.hpp | 4 ++++ src/server/network/protocol/protocolgame.cpp | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 6e3ed43b90d..9757717e675 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -344,6 +344,10 @@ class Player final : public Creature, public Cylinder, public Bankable { operatingSystem = clientos; } + bool isOldProtocol() { + return client && client->oldProtocol; + } + uint32_t getProtocolVersion() const { if (!client) { return 0; diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index d60c653aced..b9e6e673c10 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -469,6 +469,8 @@ void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingS writeToOutputBuffer(opcodeMessage); } + g_logger().debug("Player logging in in version '{}' and oldProtocol '{}'", getVersion(), oldProtocol); + // dispatcher thread std::shared_ptr foundPlayer = g_game().getPlayerUniqueLogin(name); if (!foundPlayer) { @@ -734,6 +736,11 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage &msg) { std::shared_ptr foundPlayer = g_game().getPlayerUniqueLogin(characterName); if (foundPlayer && foundPlayer->client) { + if (foundPlayer->getProtocolVersion() != getVersion() && foundPlayer->isOldProtocol() != oldProtocol) { + disconnectClient(fmt::format("You are already logged in using protocol '{}'. Please log out from the other session to connect here.", foundPlayer->getProtocolVersion())); + return; + } + foundPlayer->client->disconnectClient("You are already connected through another client. Please use only one client at a time!"); } From 2a3a88b2bf887bfc5aaa93bd79cf9ddeab8f80a2 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Wed, 22 Nov 2023 04:02:55 -0300 Subject: [PATCH 10/18] improve: rename global variables to use _G lua table (#1878) Renamed global variables and removed unused tables. --- .../actions/other/exercise_training.lua | 12 +++--- data-canary/scripts/creaturescripts/login.lua | 8 ++-- .../scripts/creaturescripts/logout.lua | 6 +-- .../actions/other/exercise_training.lua | 12 +++--- .../scripts/actions/other/potions.lua | 8 ++-- .../scripts/creaturescripts/others/login.lua | 10 ++--- .../scripts/creaturescripts/others/logout.lua | 6 +-- data/events/scripts/player.lua | 16 ++++---- data/global.lua | 39 ++++++------------- data/libs/exercise_training.lua | 8 ++-- data/libs/reward_boss/monster.lua | 4 +- data/libs/reward_boss/player.lua | 4 +- data/libs/reward_boss/reward_boss.lua | 6 +-- data/scripts/reward_chest/boss_death.lua | 4 +- .../reward_chest/boss_health_change.lua | 2 +- .../gm/teleport_to_active_player.lua | 2 +- .../talkactions/gm/teleport_to_player.lua | 2 +- data/scripts/talkactions/player/online.lua | 2 +- 18 files changed, 67 insertions(+), 84 deletions(-) diff --git a/data-canary/scripts/actions/other/exercise_training.lua b/data-canary/scripts/actions/other/exercise_training.lua index 042e759f9b7..ecf414a8029 100644 --- a/data-canary/scripts/actions/other/exercise_training.lua +++ b/data-canary/scripts/actions/other/exercise_training.lua @@ -5,7 +5,7 @@ function exerciseTraining.onUse(player, item, fromPosition, target, toPosition, local targetId = target:getId() if target:isItem() and (table.contains(HouseDummies, targetId) or table.contains(FreeDummies, targetId)) then - if onExerciseTraining[playerId] then + if _G.OnExerciseTraining[playerId] then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "This exercise dummy can only be used after a 30 second cooldown.") LeaveTraining(playerId) return true @@ -26,7 +26,7 @@ function exerciseTraining.onUse(player, item, fromPosition, target, toPosition, if table.contains(HouseDummies, targetId) then local playersOnDummy = 0 - for _, playerTraining in pairs(onExerciseTraining) do + for _, playerTraining in pairs(_G.OnExerciseTraining) do if playerTraining.dummyPos == targetPos then playersOnDummy = playersOnDummy + 1 end @@ -43,10 +43,10 @@ function exerciseTraining.onUse(player, item, fromPosition, target, toPosition, return true end - onExerciseTraining[playerId] = {} - if not onExerciseTraining[playerId].event then - onExerciseTraining[playerId].event = addEvent(ExerciseEvent, 0, playerId, targetPos, item.itemid, targetId) - onExerciseTraining[playerId].dummyPos = targetPos + _G.OnExerciseTraining[playerId] = {} + if not _G.OnExerciseTraining[playerId].event then + _G.OnExerciseTraining[playerId].event = addEvent(ExerciseEvent, 0, playerId, targetPos, item.itemid, targetId) + _G.OnExerciseTraining[playerId].dummyPos = targetPos player:setTraining(true) player:setStorageValue(Storage.IsTraining, os.time() + 30) end diff --git a/data-canary/scripts/creaturescripts/login.lua b/data-canary/scripts/creaturescripts/login.lua index 5455b801d8c..ed0bd894034 100644 --- a/data-canary/scripts/creaturescripts/login.lua +++ b/data-canary/scripts/creaturescripts/login.lua @@ -33,9 +33,9 @@ function login.onLogin(player) player:registerEvent("DropLoot") player:registerEvent("BossParticipation") - if onExerciseTraining[player:getId()] then -- onLogin & onLogout - stopEvent(onExerciseTraining[player:getId()].event) - onExerciseTraining[player:getId()] = nil + if _G.OnExerciseTraining[player:getId()] then -- onLogin & onLogout + stopEvent(_G.OnExerciseTraining[player:getId()].event) + _G.OnExerciseTraining[player:getId()] = nil player:setTraining(false) end @@ -101,7 +101,7 @@ function login.onLogin(player) _G.NextUseStaminaTime[playerId] = 1 -- EXP Stamina - nextUseXpStamina[playerId] = 1 + _G.NextUseXpStamina[playerId] = 1 -- Set Client XP Gain Rate -- if configManager.getBoolean(configKeys.XP_DISPLAY_MODE) then diff --git a/data-canary/scripts/creaturescripts/logout.lua b/data-canary/scripts/creaturescripts/logout.lua index 22612d2a7e4..598a55878d2 100644 --- a/data-canary/scripts/creaturescripts/logout.lua +++ b/data-canary/scripts/creaturescripts/logout.lua @@ -6,9 +6,9 @@ function logout.onLogout(player) _G.NextUseStaminaTime[playerId] = nil end - if onExerciseTraining[playerId] then - stopEvent(onExerciseTraining[playerId].event) - onExerciseTraining[playerId] = nil + if _G.OnExerciseTraining[playerId] then + stopEvent(_G.OnExerciseTraining[playerId].event) + _G.OnExerciseTraining[playerId] = nil player:setTraining(false) end return true diff --git a/data-otservbr-global/scripts/actions/other/exercise_training.lua b/data-otservbr-global/scripts/actions/other/exercise_training.lua index 1844ab0df8f..e7f67c854be 100644 --- a/data-otservbr-global/scripts/actions/other/exercise_training.lua +++ b/data-otservbr-global/scripts/actions/other/exercise_training.lua @@ -16,7 +16,7 @@ function exerciseTraining.onUse(player, item, fromPosition, target, toPosition, local targetId = target:getId() if target:isItem() and isDummy(targetId) then - if onExerciseTraining[playerId] then + if _G.OnExerciseTraining[playerId] then player:sendTextMessage(MESSAGE_FAILURE, "You are already training!") return true end @@ -42,7 +42,7 @@ function exerciseTraining.onUse(player, item, fromPosition, target, toPosition, return true end local playersOnDummy = 0 - for _, playerTraining in pairs(onExerciseTraining) do + for _, playerTraining in pairs(_G.OnExerciseTraining) do if playerTraining.dummyPos == targetPos then playersOnDummy = playersOnDummy + 1 end @@ -59,10 +59,10 @@ function exerciseTraining.onUse(player, item, fromPosition, target, toPosition, return true end - onExerciseTraining[playerId] = {} - if not onExerciseTraining[playerId].event then - onExerciseTraining[playerId].event = addEvent(ExerciseEvent, 0, playerId, targetPos, item.itemid, targetId) - onExerciseTraining[playerId].dummyPos = targetPos + _G.OnExerciseTraining[playerId] = {} + if not _G.OnExerciseTraining[playerId].event then + _G.OnExerciseTraining[playerId].event = addEvent(ExerciseEvent, 0, playerId, targetPos, item.itemid, targetId) + _G.OnExerciseTraining[playerId].dummyPos = targetPos player:setTraining(true) player:setStorageValue(Storage.IsTraining, os.time() + cooldown) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have started training on an exercise dummy.") diff --git a/data-otservbr-global/scripts/actions/other/potions.lua b/data-otservbr-global/scripts/actions/other/potions.lua index 38c323e978e..1cc63639189 100644 --- a/data-otservbr-global/scripts/actions/other/potions.lua +++ b/data-otservbr-global/scripts/actions/other/potions.lua @@ -230,10 +230,10 @@ function flaskPotion.onUse(player, item, fromPosition, target, toPosition, isHot end -- Delay potion - if not playerDelayPotion[player:getId()] then - playerDelayPotion[player:getId()] = 0 + if not _G.PlayerDelayPotion[player:getId()] then + _G.PlayerDelayPotion[player:getId()] = 0 end - if playerDelayPotion[player:getId()] > systemTime() then + if _G.PlayerDelayPotion[player:getId()] > systemTime() then player:sendTextMessage(MESSAGE_FAILURE, Game.getReturnMessage(RETURNVALUE_YOUAREEXHAUSTED)) return true end @@ -284,7 +284,7 @@ function flaskPotion.onUse(player, item, fromPosition, target, toPosition, isHot player:getPosition():sendSingleSoundEffect(SOUND_EFFECT_TYPE_ITEM_USE_POTION, player:isInGhostMode() and nil or player) -- Delay potion - playerDelayPotion[player:getId()] = systemTime() + 500 + _G.PlayerDelayPotion[player:getId()] = systemTime() + 500 if potion.func then potion.func(player) diff --git a/data-otservbr-global/scripts/creaturescripts/others/login.lua b/data-otservbr-global/scripts/creaturescripts/others/login.lua index 180a4e68d7d..b7009b530e9 100644 --- a/data-otservbr-global/scripts/creaturescripts/others/login.lua +++ b/data-otservbr-global/scripts/creaturescripts/others/login.lua @@ -211,10 +211,10 @@ function playerLogin.onLogin(player) _G.NextUseStaminaTime[playerId] = 1 -- EXP Stamina - nextUseXpStamina[playerId] = 1 + _G.NextUseXpStamina[playerId] = 1 -- Concoction Duration - nextUseConcoctionTime[playerId] = 1 + _G.NextUseConcoctionTime[playerId] = 1 if player:getAccountType() == ACCOUNT_TYPE_TUTOR then local msg = [[:: Tutor Rules @@ -275,10 +275,10 @@ function playerLogin.onLogin(player) player:getFinalLowLevelBonus() - if onExerciseTraining[player:getId()] then + if _G.OnExerciseTraining[player:getId()] then -- onLogin & onLogout - stopEvent(onExerciseTraining[player:getId()].event) - onExerciseTraining[player:getId()] = nil + stopEvent(_G.OnExerciseTraining[player:getId()].event) + _G.OnExerciseTraining[player:getId()] = nil player:setTraining(false) end return true diff --git a/data-otservbr-global/scripts/creaturescripts/others/logout.lua b/data-otservbr-global/scripts/creaturescripts/others/logout.lua index eb68a47cf3f..ecdb13378d2 100644 --- a/data-otservbr-global/scripts/creaturescripts/others/logout.lua +++ b/data-otservbr-global/scripts/creaturescripts/others/logout.lua @@ -22,9 +22,9 @@ function playerLogout.onLogout(player) end end - if onExerciseTraining[playerId] then - stopEvent(onExerciseTraining[playerId].event) - onExerciseTraining[playerId] = nil + if _G.OnExerciseTraining[playerId] then + stopEvent(_G.OnExerciseTraining[playerId].event) + _G.OnExerciseTraining[playerId] = nil player:setTraining(false) end diff --git a/data/events/scripts/player.lua b/data/events/scripts/player.lua index a8649d5b08e..39219a06d56 100644 --- a/data/events/scripts/player.lua +++ b/data/events/scripts/player.lua @@ -151,7 +151,7 @@ local function useStaminaXpBoost(player) end local currentTime = os.time() - local timePassed = currentTime - nextUseXpStamina[playerId] + local timePassed = currentTime - _G.NextUseXpStamina[playerId] if timePassed <= 0 then return end @@ -162,10 +162,10 @@ local function useStaminaXpBoost(player) else staminaMinutes = 0 end - nextUseXpStamina[playerId] = currentTime + 120 + _G.NextUseXpStamina[playerId] = currentTime + 120 else staminaMinutes = staminaMinutes - 1 - nextUseXpStamina[playerId] = currentTime + 60 + _G.NextUseXpStamina[playerId] = currentTime + 60 end player:setExpBoostStamina(staminaMinutes * 60) end @@ -176,22 +176,22 @@ local function useConcoctionTime(player) end local playerId = player:getId() - if not playerId or not nextUseConcoctionTime[playerId] then + if not playerId or not _G.NextUseConcoctionTime[playerId] then return false end local currentTime = os.time() - local timePassed = currentTime - nextUseConcoctionTime[playerId] + local timePassed = currentTime - _G.NextUseConcoctionTime[playerId] if timePassed <= 0 then return false end local deduction = 60 if timePassed > 60 then - nextUseConcoctionTime[playerId] = currentTime + 120 + _G.NextUseConcoctionTime[playerId] = currentTime + 120 deduction = 120 else - nextUseConcoctionTime[playerId] = currentTime + 60 + _G.NextUseConcoctionTime[playerId] = currentTime + 60 end Concoction.experienceTick(player, deduction) end @@ -392,7 +392,7 @@ end function Player:onMoveCreature(creature, fromPosition, toPosition) local player = creature:getPlayer() - if player and onExerciseTraining[player:getId()] and not self:getGroup():hasFlag(PlayerFlag_CanPushAllCreatures) then + if player and _G.OnExerciseTraining[player:getId()] and not self:getGroup():hasFlag(PlayerFlag_CanPushAllCreatures) then self:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE) return false end diff --git a/data/global.lua b/data/global.lua index 7a26302b345..04a8ff78fe9 100644 --- a/data/global.lua +++ b/data/global.lua @@ -82,26 +82,13 @@ SCARLETT_MAY_DIE = 0 ropeSpots = { 386, 421, 386, 7762, 12202, 12936, 14238, 17238, 23363, 21965, 21966, 21967, 21968 } specialRopeSpots = { 12935 } --- Impact Analyser -if not GlobalBosses then - GlobalBosses = {} +-- Global tables for systems +if not _G.GlobalBosses then + _G.GlobalBosses = {} end --- Healing --- Global table to insert data -if healingImpact == nil then - healingImpact = {} -end - --- Damage --- Global table to insert data -if damageImpact == nil then - damageImpact = {} -end - --- Exercise Training -if onExerciseTraining == nil then - onExerciseTraining = {} +if not _G.OnExerciseTraining then + _G.OnExerciseTraining = {} end -- Stamina @@ -109,21 +96,17 @@ if not _G.NextUseStaminaTime then _G.NextUseStaminaTime = {} end -if nextUseXpStamina == nil then - nextUseXpStamina = {} -end - -if nextUseConcoctionTime == nil then - nextUseConcoctionTime = {} +if not _G.NextUseXpStamina then + _G.NextUseXpStamina = {} end -if lastItemImbuing == nil then - lastItemImbuing = {} +if not _G._G.NextUseConcoctionTime then + _G._G.NextUseConcoctionTime = {} end -- Delay potion -if not playerDelayPotion then - playerDelayPotion = {} +if not _G.PlayerDelayPotion then + _G.PlayerDelayPotion = {} end table.contains = function(array, value) diff --git a/data/libs/exercise_training.lua b/data/libs/exercise_training.lua index 3fcbf89dd87..df9f9e31241 100644 --- a/data/libs/exercise_training.lua +++ b/data/libs/exercise_training.lua @@ -36,9 +36,9 @@ ExerciseWeaponsTable = { local dummies = Game.getDummies() function LeaveTraining(playerId) - if onExerciseTraining[playerId] then - stopEvent(onExerciseTraining[playerId].event) - onExerciseTraining[playerId] = nil + if _G.OnExerciseTraining[playerId] then + stopEvent(_G.OnExerciseTraining[playerId].event) + _G.OnExerciseTraining[playerId] = nil end local player = Player(playerId) @@ -120,6 +120,6 @@ function ExerciseEvent(playerId, tilePosition, weaponId, dummyId) end local vocation = player:getVocation() - onExerciseTraining[playerId].event = addEvent(ExerciseEvent, vocation:getBaseAttackSpeed() / configManager.getFloat(configKeys.RATE_EXERCISE_TRAINING_SPEED), playerId, tilePosition, weaponId, dummyId) + _G.OnExerciseTraining[playerId].event = addEvent(ExerciseEvent, vocation:getBaseAttackSpeed() / configManager.getFloat(configKeys.RATE_EXERCISE_TRAINING_SPEED), playerId, tilePosition, weaponId, dummyId) return true end diff --git a/data/libs/reward_boss/monster.lua b/data/libs/reward_boss/monster.lua index 028f325bb4c..95df4f4acc5 100644 --- a/data/libs/reward_boss/monster.lua +++ b/data/libs/reward_boss/monster.lua @@ -4,11 +4,11 @@ function Monster.setReward(self, enable) error("Rewards can only be enabled to rewards bosses.") return false end - GlobalBosses[self:getId()] = {} + _G.GlobalBosses[self:getId()] = {} self:registerEvent("BossDeath") self:registerEvent("BossThink") else - GlobalBosses[self:getId()] = nil + _G.GlobalBosses[self:getId()] = nil self:unregisterEvent("BossDeath") self:unregisterEvent("BossThink") end diff --git a/data/libs/reward_boss/player.lua b/data/libs/reward_boss/player.lua index 2f8887280f4..7a525efa2fd 100644 --- a/data/libs/reward_boss/player.lua +++ b/data/libs/reward_boss/player.lua @@ -3,12 +3,12 @@ function Player.getRewardChest(self, autocreate) end function Player.inBossFight(self) - if not next(GlobalBosses) then + if not next(_G.GlobalBosses) then return false end local playerGuid = self:getGuid() - for _, info in pairs(GlobalBosses) do + for _, info in pairs(_G.GlobalBosses) do local stats = info[playerGuid] if stats and stats.active then return stats diff --git a/data/libs/reward_boss/reward_boss.lua b/data/libs/reward_boss/reward_boss.lua index fb7c75ec5ee..8913d746a84 100644 --- a/data/libs/reward_boss/reward_boss.lua +++ b/data/libs/reward_boss/reward_boss.lua @@ -96,14 +96,14 @@ function InsertRewardItems(playerGuid, timestamp, itemList) end function GetPlayerStats(bossId, playerGuid, autocreate) - local ret = GlobalBosses[bossId][playerGuid] + local ret = _G.GlobalBosses[bossId][playerGuid] if not ret and autocreate then ret = { bossId = bossId, damageIn = 0, -- damage taken from the boss healing = 0, -- healing (other players) done } - GlobalBosses[bossId][playerGuid] = ret + _G.GlobalBosses[bossId][playerGuid] = ret return ret end return ret @@ -115,7 +115,7 @@ function ResetAndSetTargetList(creature) end local bossId = creature:getId() - local info = GlobalBosses[bossId] + local info = _G.GlobalBosses[bossId] -- Reset all players' status for _, player in pairs(info) do player.active = false diff --git a/data/scripts/reward_chest/boss_death.lua b/data/scripts/reward_chest/boss_death.lua index 81a64e0cd84..04bb12b84bc 100644 --- a/data/scripts/reward_chest/boss_death.lua +++ b/data/scripts/reward_chest/boss_death.lua @@ -28,7 +28,7 @@ function bossDeath.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUn local totalDamageOut, totalDamageIn, totalHealing = 0.1, 0.1, 0.1 local scores = {} - local info = GlobalBosses[bossId] + local info = _G.GlobalBosses[bossId] local damageMap = creature:getDamageMap() for guid, stats in pairs(info) do @@ -124,7 +124,7 @@ function bossDeath.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUn end end end - GlobalBosses[bossId] = nil + _G.GlobalBosses[bossId] = nil end return true end diff --git a/data/scripts/reward_chest/boss_health_change.lua b/data/scripts/reward_chest/boss_health_change.lua index 75250de64a6..037d6aa567a 100644 --- a/data/scripts/reward_chest/boss_health_change.lua +++ b/data/scripts/reward_chest/boss_health_change.lua @@ -1,7 +1,7 @@ local bossParticipation = CreatureEvent("BossParticipation") function bossParticipation.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin) - if not next(GlobalBosses) then + if not next(_G.GlobalBosses) then return primaryDamage, primaryType, secondaryDamage, secondaryType end diff --git a/data/scripts/talkactions/gm/teleport_to_active_player.lua b/data/scripts/talkactions/gm/teleport_to_active_player.lua index 662a007aedf..abb71116a37 100644 --- a/data/scripts/talkactions/gm/teleport_to_active_player.lua +++ b/data/scripts/talkactions/gm/teleport_to_active_player.lua @@ -9,7 +9,7 @@ function teleportToCreature.onSay(player, words, param) for _, targetPlayer in ipairs(players) do local isGhost = targetPlayer:isInGhostMode() - local isTraining = onExerciseTraining[targetPlayer:getId()] + local isTraining = _G.OnExerciseTraining[targetPlayer:getId()] local isIdle = targetPlayer:getIdleTime() >= 5 * 60 * 1000 local isActive = not isGhost and not isTraining and not isIdle if isActive then diff --git a/data/scripts/talkactions/gm/teleport_to_player.lua b/data/scripts/talkactions/gm/teleport_to_player.lua index 73eab7b5bec..04bcdf0dc91 100644 --- a/data/scripts/talkactions/gm/teleport_to_player.lua +++ b/data/scripts/talkactions/gm/teleport_to_player.lua @@ -18,7 +18,7 @@ function teleportToCreature.onSay(player, words, param) table.insert(playerList, targetPlayer) else local isGhost = targetPlayer:isInGhostMode() - local isTraining = onExerciseTraining[targetPlayer:getId()] + local isTraining = _G.OnExerciseTraining[targetPlayer:getId()] local isIdle = targetPlayer:getIdleTime() >= 5 * 60 * 1000 local isActive = not isGhost and not isTraining and not isIdle if isActive then diff --git a/data/scripts/talkactions/player/online.lua b/data/scripts/talkactions/player/online.lua index be3c988259c..bdedf5b3af0 100644 --- a/data/scripts/talkactions/player/online.lua +++ b/data/scripts/talkactions/player/online.lua @@ -16,7 +16,7 @@ function playersOnline.onSay(player, words, param) for _, targetPlayer in ipairs(players) do if hasAccess or not targetPlayer:isInGhostMode() then - if onExerciseTraining[targetPlayer:getId()] then + if _G.OnExerciseTraining[targetPlayer:getId()] then table.insert(onlineList.Training, targetPlayer) elseif targetPlayer:getIdleTime() >= 5 * 60 * 1000 then table.insert(onlineList.Idle, targetPlayer) From fc0b9a3823ccf77303419c38d1ac4ebe536805ad Mon Sep 17 00:00:00 2001 From: Travisani <98285577+Gtravisani@users.noreply.github.com> Date: Wed, 22 Nov 2023 10:50:10 -0300 Subject: [PATCH 11/18] fix: loot message typo (#1879) --- src/creatures/creature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index 46804d6b51b..4d9894772f0 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -754,7 +754,7 @@ bool Creature::dropCorpse(std::shared_ptr lastHitCreature, std::shared auto monster = getMonster(); if (monster && !monster->isRewardBoss()) { std::ostringstream lootMessage; - lootMessage << "Loot of " << getNameDescription() << ": " << corpse->getContainer()->getContentDescription(player->getProtocolVersion() < 1200); + lootMessage << "Loot of " << getNameDescription() << ": " << corpse->getContainer()->getContentDescription(player->getProtocolVersion() < 1200) << "."; auto suffix = corpse->getContainer()->getAttribute(ItemAttribute_t::LOOTMESSAGE_SUFFIX); if (!suffix.empty()) { lootMessage << suffix; From 125c5f42f31ee40f9e83cbd0a7323ea65d56bc9e Mon Sep 17 00:00:00 2001 From: Luan Luciano Date: Thu, 23 Nov 2023 01:52:41 -0300 Subject: [PATCH 12/18] fix: wroght-iron bed attribute rotateTo (#1886) When using the attribute rotateTo, some beds had the wrong client id. --- data/items/items.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/items/items.xml b/data/items/items.xml index 5542b35d43f..407f909b6e8 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -59786,7 +59786,7 @@ - + @@ -59826,7 +59826,7 @@ - + @@ -59836,7 +59836,7 @@ - + From 94f9769a3c44eec7f5d09ad878f918145ea5f7b0 Mon Sep 17 00:00:00 2001 From: Beats Date: Thu, 23 Nov 2023 11:43:17 -0400 Subject: [PATCH 13/18] improve: ConfigManager better functionality/code clarity/maintainability (#1887) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revamp of the ConfigManager component, aimed at not only improving functionality and code clarity but also enhancing long-term maintainability. • Changes Made: ◦ Extensively restructured the ConfigManager to optimize functionality, making it more robust and versatile. ◦ Focused on enhancing code clarity, making the codebase more comprehensible and maintainable for future contributors. ◦ Introduced magic enums to streamline enum registration for the Lua interface, improving maintainability and reducing potential issues. --- src/config/config_definitions.hpp | 29 +- src/config/configmanager.cpp | 741 +++++++++--------- src/config/configmanager.hpp | 27 +- .../functions/core/game/config_functions.cpp | 265 +------ src/lua/functions/core/game/lua_enums.cpp | 36 +- src/pch.hpp | 13 +- src/server/network/protocol/protocollogin.cpp | 2 +- .../network/protocol/protocolstatus.cpp | 4 +- 8 files changed, 431 insertions(+), 686 deletions(-) diff --git a/src/config/config_definitions.hpp b/src/config/config_definitions.hpp index bf86266f3a9..dc7f1caac24 100644 --- a/src/config/config_definitions.hpp +++ b/src/config/config_definitions.hpp @@ -10,7 +10,7 @@ #pragma once // Enum -enum booleanConfig_t { +enum ConfigKey_t : uint16_t { ALLOW_CHANGEOUTFIT, ONE_PLAYER_ON_ACCOUNT, AIMBOT_HOTKEY_ENABLED, @@ -91,11 +91,6 @@ enum booleanConfig_t { TOGGLE_HOUSE_TRANSFER_ON_SERVER_RESTART, TOGGLE_RECEIVE_REWARD, TOGGLE_MAINTAIN_MODE, - - LAST_BOOLEAN_CONFIG -}; - -enum stringConfig_t { MAP_NAME, MAP_DOWNLOAD_URL, MAP_AUTHOR, @@ -128,11 +123,6 @@ enum stringConfig_t { TIBIADROME_CONCOCTION_TICK_TYPE, M_CONST, MAINTAIN_MODE_MESSAGE, - - LAST_STRING_CONFIG -}; - -enum integerConfig_t { SQL_PORT, MAX_PLAYERS, PZ_LOCKED, @@ -251,53 +241,38 @@ enum integerConfig_t { PARALLELISM, BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN, BOSS_DEFAULT_TIME_TO_DEFEAT, - VIP_BONUS_EXP, VIP_BONUS_LOOT, VIP_BONUS_SKILL, VIP_FAMILIAR_TIME_COOLDOWN_REDUCTION, - REWARD_CHEST_MAX_COLLECT_ITEMS, DISCORD_WEBHOOK_DELAY_MS, - PVP_MAX_LEVEL_DIFFERENCE, - - LAST_INTEGER_CONFIG -}; - -enum floatingConfig_t { BESTIARY_RATE_CHARM_SHOP_PRICE, - RATE_HEALTH_REGEN, RATE_HEALTH_REGEN_SPEED, RATE_MANA_REGEN, RATE_MANA_REGEN_SPEED, RATE_SOUL_REGEN, RATE_SOUL_REGEN_SPEED, - RATE_SPELL_COOLDOWN, RATE_ATTACK_SPEED, RATE_OFFLINE_TRAINING_SPEED, RATE_EXERCISE_TRAINING_SPEED, - RATE_MONSTER_HEALTH, RATE_MONSTER_ATTACK, RATE_MONSTER_DEFENSE, RATE_BOSS_HEALTH, RATE_BOSS_ATTACK, RATE_BOSS_DEFENSE, - RATE_NPC_HEALTH, RATE_NPC_ATTACK, RATE_NPC_DEFENSE, LOYALTY_BONUS_PERCENTAGE_MULTIPLIER, PARTY_SHARE_LOOT_BOOSTS_DIMINISHING_FACTOR, - PVP_RATE_DAMAGE_TAKEN_PER_LEVEL, PVP_RATE_DAMAGE_REDUCTION_PER_LEVEL, - HOUSE_PRICE_RENT_MULTIPLIER, HOUSE_RENT_RATE, - - LAST_FLOATING_CONFIG + LOGLEVEL, }; diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 5d52ca11b2a..11b1ec90a04 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -19,62 +19,6 @@ #define lua_strlen lua_rawlen #endif -namespace { - - std::string getGlobalString(lua_State* L, const char* identifier, const char* defaultValue) { - lua_getglobal(L, identifier); - if (!lua_isstring(L, -1)) { - return defaultValue; - } - - size_t len = lua_strlen(L, -1); - std::string ret(lua_tostring(L, -1), len); - lua_pop(L, 1); - return ret; - } - - int32_t getGlobalNumber(lua_State* L, const char* identifier, const int32_t defaultValue = 0) { - lua_getglobal(L, identifier); - if (!lua_isnumber(L, -1)) { - return defaultValue; - } - - int32_t val = lua_tonumber(L, -1); - lua_pop(L, 1); - return val; - } - - bool getGlobalBoolean(lua_State* L, const char* identifier, const bool defaultValue) { - lua_getglobal(L, identifier); - if (!lua_isboolean(L, -1)) { - if (!lua_isstring(L, -1)) { - return defaultValue; - } - - size_t len = lua_strlen(L, -1); - std::string ret(lua_tostring(L, -1), len); - lua_pop(L, 1); - return booleanString(ret); - } - - int val = lua_toboolean(L, -1); - lua_pop(L, 1); - return val != 0; - } - - float getGlobalFloat(lua_State* L, const char* identifier, const float defaultValue = 0.0) { - lua_getglobal(L, identifier); - if (!lua_isnumber(L, -1)) { - return defaultValue; - } - - float val = lua_tonumber(L, -1); - lua_pop(L, 1); - return val; - } - -} - bool ConfigManager::load() { lua_State* L = luaL_newstate(); if (!L) { @@ -90,318 +34,318 @@ bool ConfigManager::load() { } #ifndef DEBUG_LOG - g_logger().setLevel(getGlobalString(L, "logLevel", "info")); + g_logger().setLevel(loadStringConfig(L, LOGLEVEL, "logLevel", "info")); #endif // Parse config // Info that must be loaded one time (unless we reset the modules involved) if (!loaded) { - boolean[BIND_ONLY_GLOBAL_ADDRESS] = getGlobalBoolean(L, "bindOnlyGlobalAddress", false); - boolean[OPTIMIZE_DATABASE] = getGlobalBoolean(L, "startupDatabaseOptimization", true); - boolean[TOGGLE_MAP_CUSTOM] = getGlobalBoolean(L, "toggleMapCustom", true); - boolean[TOGGLE_MAINTAIN_MODE] = getGlobalBoolean(L, "toggleMaintainMode", false); - string[MAINTAIN_MODE_MESSAGE] = getGlobalString(L, "maintainModeMessage", ""); - - string[IP] = getGlobalString(L, "ip", "127.0.0.1"); - string[MAP_NAME] = getGlobalString(L, "mapName", "canary"); - string[MAP_DOWNLOAD_URL] = getGlobalString(L, "mapDownloadUrl", ""); - string[MAP_AUTHOR] = getGlobalString(L, "mapAuthor", "Eduardo Dantas"); - - string[MAP_CUSTOM_NAME] = getGlobalString(L, "mapCustomName", ""); - string[MAP_CUSTOM_AUTHOR] = getGlobalString(L, "mapCustomAuthor", "OTServBR"); - - string[HOUSE_RENT_PERIOD] = getGlobalString(L, "houseRentPeriod", "never"); - floating[HOUSE_PRICE_RENT_MULTIPLIER] = getGlobalFloat(L, "housePriceRentMultiplier", 1.0); - floating[HOUSE_RENT_RATE] = getGlobalFloat(L, "houseRentRate", 1.0); - string[MYSQL_HOST] = getGlobalString(L, "mysqlHost", "127.0.0.1"); - string[MYSQL_USER] = getGlobalString(L, "mysqlUser", "root"); - string[MYSQL_PASS] = getGlobalString(L, "mysqlPass", ""); - string[MYSQL_DB] = getGlobalString(L, "mysqlDatabase", "canary"); - string[MYSQL_SOCK] = getGlobalString(L, "mysqlSock", ""); - - string[AUTH_TYPE] = getGlobalString(L, "authType", "password"); - boolean[RESET_SESSIONS_ON_STARTUP] = getGlobalBoolean(L, "resetSessionsOnStartup", false); - - integer[SQL_PORT] = getGlobalNumber(L, "mysqlPort", 3306); - integer[GAME_PORT] = getGlobalNumber(L, "gameProtocolPort", 7172); - integer[LOGIN_PORT] = getGlobalNumber(L, "loginProtocolPort", 7171); - integer[STATUS_PORT] = getGlobalNumber(L, "statusProtocolPort", 7171); - - integer[MARKET_OFFER_DURATION] = getGlobalNumber(L, "marketOfferDuration", 30 * 24 * 60 * 60); - - integer[FREE_DEPOT_LIMIT] = getGlobalNumber(L, "freeDepotLimit", 2000); - integer[PREMIUM_DEPOT_LIMIT] = getGlobalNumber(L, "premiumDepotLimit", 8000); - integer[DEPOT_BOXES] = getGlobalNumber(L, "depotBoxes", 20); - integer[STASH_ITEMS] = getGlobalNumber(L, "stashItemCount", 5000); - - boolean[OLD_PROTOCOL] = getGlobalBoolean(L, "allowOldProtocol", true); + loadBoolConfig(L, BIND_ONLY_GLOBAL_ADDRESS, "bindOnlyGlobalAddress", false); + loadBoolConfig(L, OPTIMIZE_DATABASE, "startupDatabaseOptimization", true); + loadBoolConfig(L, TOGGLE_MAP_CUSTOM, "toggleMapCustom", true); + loadBoolConfig(L, TOGGLE_MAINTAIN_MODE, "toggleMaintainMode", false); + loadStringConfig(L, MAINTAIN_MODE_MESSAGE, "maintainModeMessage", ""); + + loadStringConfig(L, IP, "ip", "127.0.0.1"); + loadStringConfig(L, MAP_NAME, "mapName", "canary"); + loadStringConfig(L, MAP_DOWNLOAD_URL, "mapDownloadUrl", ""); + loadStringConfig(L, MAP_AUTHOR, "mapAuthor", "Eduardo Dantas"); + + loadStringConfig(L, MAP_CUSTOM_NAME, "mapCustomName", ""); + loadStringConfig(L, MAP_CUSTOM_AUTHOR, "mapCustomAuthor", "OTServBR"); + + loadStringConfig(L, HOUSE_RENT_PERIOD, "houseRentPeriod", "never"); + loadFloatConfig(L, HOUSE_PRICE_RENT_MULTIPLIER, "housePriceRentMultiplier", 1.0); + loadFloatConfig(L, HOUSE_RENT_RATE, "houseRentRate", 1.0); + loadStringConfig(L, MYSQL_HOST, "mysqlHost", "127.0.0.1"); + loadStringConfig(L, MYSQL_USER, "mysqlUser", "root"); + loadStringConfig(L, MYSQL_PASS, "mysqlPass", ""); + loadStringConfig(L, MYSQL_DB, "mysqlDatabase", "canary"); + loadStringConfig(L, MYSQL_SOCK, "mysqlSock", ""); + + loadStringConfig(L, AUTH_TYPE, "authType", "password"); + loadBoolConfig(L, RESET_SESSIONS_ON_STARTUP, "resetSessionsOnStartup", false); + + loadIntConfig(L, SQL_PORT, "mysqlPort", 3306); + loadIntConfig(L, GAME_PORT, "gameProtocolPort", 7172); + loadIntConfig(L, LOGIN_PORT, "loginProtocolPort", 7171); + loadIntConfig(L, STATUS_PORT, "statusProtocolPort", 7171); + + loadIntConfig(L, MARKET_OFFER_DURATION, "marketOfferDuration", 30 * 24 * 60 * 60); + + loadIntConfig(L, FREE_DEPOT_LIMIT, "freeDepotLimit", 2000); + loadIntConfig(L, PREMIUM_DEPOT_LIMIT, "premiumDepotLimit", 8000); + loadIntConfig(L, DEPOT_BOXES, "depotBoxes", 20); + loadIntConfig(L, STASH_ITEMS, "stashItemCount", 5000); + + loadBoolConfig(L, OLD_PROTOCOL, "allowOldProtocol", true); } - boolean[ALLOW_CHANGEOUTFIT] = getGlobalBoolean(L, "allowChangeOutfit", true); - boolean[ONE_PLAYER_ON_ACCOUNT] = getGlobalBoolean(L, "onePlayerOnlinePerAccount", true); - boolean[AIMBOT_HOTKEY_ENABLED] = getGlobalBoolean(L, "hotkeyAimbotEnabled", true); - boolean[REMOVE_RUNE_CHARGES] = getGlobalBoolean(L, "removeChargesFromRunes", true); - boolean[EXPERIENCE_FROM_PLAYERS] = getGlobalBoolean(L, "experienceByKillingPlayers", false); - boolean[FREE_PREMIUM] = getGlobalBoolean(L, "freePremium", false); - boolean[REPLACE_KICK_ON_LOGIN] = getGlobalBoolean(L, "replaceKickOnLogin", true); - boolean[MARKET_PREMIUM] = getGlobalBoolean(L, "premiumToCreateMarketOffer", true); - boolean[EMOTE_SPELLS] = getGlobalBoolean(L, "emoteSpells", false); - boolean[STAMINA_SYSTEM] = getGlobalBoolean(L, "staminaSystem", true); - boolean[WARN_UNSAFE_SCRIPTS] = getGlobalBoolean(L, "warnUnsafeScripts", true); - boolean[CONVERT_UNSAFE_SCRIPTS] = getGlobalBoolean(L, "convertUnsafeScripts", true); - boolean[CLASSIC_ATTACK_SPEED] = getGlobalBoolean(L, "classicAttackSpeed", false); - boolean[TOGGLE_ATTACK_SPEED_ONFIST] = getGlobalBoolean(L, "toggleAttackSpeedOnFist", false); - integer[MULTIPLIER_ATTACKONFIST] = getGlobalNumber(L, "multiplierSpeedOnFist", 5); - integer[MAX_SPEED_ATTACKONFIST] = getGlobalNumber(L, "maxSpeedOnFist", 500); - boolean[SCRIPTS_CONSOLE_LOGS] = getGlobalBoolean(L, "showScriptsLogInConsole", true); - boolean[STASH_MOVING] = getGlobalBoolean(L, "stashMoving", false); - boolean[ALLOW_BLOCK_SPAWN] = getGlobalBoolean(L, "allowBlockSpawn", true); - boolean[REMOVE_WEAPON_AMMO] = getGlobalBoolean(L, "removeWeaponAmmunition", true); - boolean[REMOVE_WEAPON_CHARGES] = getGlobalBoolean(L, "removeWeaponCharges", true); - boolean[REMOVE_POTION_CHARGES] = getGlobalBoolean(L, "removeChargesFromPotions", true); - boolean[GLOBAL_SERVER_SAVE_NOTIFY_MESSAGE] = getGlobalBoolean(L, "globalServerSaveNotifyMessage", true); - boolean[GLOBAL_SERVER_SAVE_CLEAN_MAP] = getGlobalBoolean(L, "globalServerSaveCleanMap", false); - boolean[GLOBAL_SERVER_SAVE_CLOSE] = getGlobalBoolean(L, "globalServerSaveClose", false); - boolean[FORCE_MONSTERTYPE_LOAD] = getGlobalBoolean(L, "forceMonsterTypesOnLoad", true); - boolean[HOUSE_OWNED_BY_ACCOUNT] = getGlobalBoolean(L, "houseOwnedByAccount", false); - boolean[CLEAN_PROTECTION_ZONES] = getGlobalBoolean(L, "cleanProtectionZones", false); - boolean[GLOBAL_SERVER_SAVE_SHUTDOWN] = getGlobalBoolean(L, "globalServerSaveShutdown", true); - boolean[PUSH_WHEN_ATTACKING] = getGlobalBoolean(L, "pushWhenAttacking", false); - - boolean[WEATHER_RAIN] = getGlobalBoolean(L, "weatherRain", false); - boolean[WEATHER_THUNDER] = getGlobalBoolean(L, "thunderEffect", false); - boolean[ALL_CONSOLE_LOG] = getGlobalBoolean(L, "allConsoleLog", false); - boolean[TOGGLE_FREE_QUEST] = getGlobalBoolean(L, "toggleFreeQuest", true); - boolean[AUTOLOOT] = getGlobalBoolean(L, "autoLoot", false); - boolean[AUTOBANK] = getGlobalBoolean(L, "autoBank", false); - boolean[STAMINA_TRAINER] = getGlobalBoolean(L, "staminaTrainer", false); - boolean[STAMINA_PZ] = getGlobalBoolean(L, "staminaPz", false); - boolean[SORT_LOOT_BY_CHANCE] = getGlobalBoolean(L, "sortLootByChance", false); - boolean[TOGGLE_SAVE_INTERVAL] = getGlobalBoolean(L, "toggleSaveInterval", false); - boolean[TOGGLE_SAVE_INTERVAL_CLEAN_MAP] = getGlobalBoolean(L, "toggleSaveIntervalCleanMap", false); - boolean[TELEPORT_SUMMONS] = getGlobalBoolean(L, "teleportSummons", false); - boolean[ALLOW_RELOAD] = getGlobalBoolean(L, "allowReload", false); - - boolean[ONLY_PREMIUM_ACCOUNT] = getGlobalBoolean(L, "onlyPremiumAccount", false); - boolean[RATE_USE_STAGES] = getGlobalBoolean(L, "rateUseStages", false); - boolean[TOGGLE_IMBUEMENT_SHRINE_STORAGE] = getGlobalBoolean(L, "toggleImbuementShrineStorage", true); - boolean[TOGGLE_IMBUEMENT_NON_AGGRESSIVE_FIGHT_ONLY] = getGlobalBoolean(L, "toggleImbuementNonAggressiveFightOnly", false); - - boolean[TOGGLE_DOWNLOAD_MAP] = getGlobalBoolean(L, "toggleDownloadMap", false); - boolean[USE_ANY_DATAPACK_FOLDER] = getGlobalBoolean(L, "useAnyDatapackFolder", false); - boolean[INVENTORY_GLOW] = getGlobalBoolean(L, "inventoryGlowOnFiveBless", false); - boolean[XP_DISPLAY_MODE] = getGlobalBoolean(L, "experienceDisplayRates", true); - - string[DEFAULT_PRIORITY] = getGlobalString(L, "defaultPriority", "high"); - string[SERVER_NAME] = getGlobalString(L, "serverName", ""); - string[SERVER_MOTD] = getGlobalString(L, "serverMotd", ""); - string[OWNER_NAME] = getGlobalString(L, "ownerName", ""); - string[OWNER_EMAIL] = getGlobalString(L, "ownerEmail", ""); - string[URL] = getGlobalString(L, "url", ""); - string[LOCATION] = getGlobalString(L, "location", ""); - string[WORLD_TYPE] = getGlobalString(L, "worldType", "pvp"); - string[STORE_IMAGES_URL] = getGlobalString(L, "coinImagesURL", ""); - string[DISCORD_WEBHOOK_URL] = getGlobalString(L, "discordWebhookURL", ""); - string[SAVE_INTERVAL_TYPE] = getGlobalString(L, "saveIntervalType", ""); - string[GLOBAL_SERVER_SAVE_TIME] = getGlobalString(L, "globalServerSaveTime", "06:00"); - string[DATA_DIRECTORY] = getGlobalString(L, "dataPackDirectory", "data-otservbr-global"); - string[CORE_DIRECTORY] = getGlobalString(L, "coreDirectory", "data"); - - string[FORGE_FIENDISH_INTERVAL_TYPE] = getGlobalString(L, "forgeFiendishIntervalType", "hour"); - string[FORGE_FIENDISH_INTERVAL_TIME] = getGlobalString(L, "forgeFiendishIntervalTime", "1"); - - integer[MAX_PLAYERS] = getGlobalNumber(L, "maxPlayers"); - integer[PZ_LOCKED] = getGlobalNumber(L, "pzLocked", 60000); - integer[DEFAULT_DESPAWNRANGE] = getGlobalNumber(L, "deSpawnRange", 2); - integer[DEFAULT_DESPAWNRADIUS] = getGlobalNumber(L, "deSpawnRadius", 50); - integer[RATE_EXPERIENCE] = getGlobalNumber(L, "rateExp", 1); - integer[RATE_SKILL] = getGlobalNumber(L, "rateSkill", 1); - integer[RATE_LOOT] = getGlobalNumber(L, "rateLoot", 1); - integer[RATE_MAGIC] = getGlobalNumber(L, "rateMagic", 1); - integer[RATE_SPAWN] = getGlobalNumber(L, "rateSpawn", 1); - integer[RATE_KILLING_IN_THE_NAME_OF_POINTS] = getGlobalNumber(L, "rateKillingInTheNameOfPoints", 1); - - integer[HOUSE_PRICE_PER_SQM] = getGlobalNumber(L, "housePriceEachSQM", 1000); - integer[HOUSE_BUY_LEVEL] = getGlobalNumber(L, "houseBuyLevel", 0); - integer[HOUSE_LOSE_AFTER_INACTIVITY] = getGlobalNumber(L, "houseLoseAfterInactivity", 0); - boolean[HOUSE_PURSHASED_SHOW_PRICE] = getGlobalBoolean(L, "housePurchasedShowPrice", false); - boolean[ONLY_INVITED_CAN_MOVE_HOUSE_ITEMS] = getGlobalBoolean(L, "onlyInvitedCanMoveHouseItems", true); - - integer[ACTIONS_DELAY_INTERVAL] = getGlobalNumber(L, "timeBetweenActions", 200); - integer[EX_ACTIONS_DELAY_INTERVAL] = getGlobalNumber(L, "timeBetweenExActions", 1000); - integer[MAX_MESSAGEBUFFER] = getGlobalNumber(L, "maxMessageBuffer", 4); - integer[KICK_AFTER_MINUTES] = getGlobalNumber(L, "kickIdlePlayerAfterMinutes", 15); - integer[PROTECTION_LEVEL] = getGlobalNumber(L, "protectionLevel", 1); - integer[DEATH_LOSE_PERCENT] = getGlobalNumber(L, "deathLosePercent", -1); - integer[STATUSQUERY_TIMEOUT] = getGlobalNumber(L, "statusTimeout", 5000); - integer[FRAG_TIME] = getGlobalNumber(L, "timeToDecreaseFrags", 24 * 60 * 60 * 1000); - 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_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); - integer[MAX_PACKETS_PER_SECOND] = getGlobalNumber(L, "maxPacketsPerSecond", 25); - integer[COMPRESSION_LEVEL] = getGlobalNumber(L, "packetCompressionLevel", 6); - integer[STORE_COIN_PACKET] = getGlobalNumber(L, "coinPacketSize", 25); - integer[DAY_KILLS_TO_RED] = getGlobalNumber(L, "dayKillsToRedSkull", 3); - integer[WEEK_KILLS_TO_RED] = getGlobalNumber(L, "weekKillsToRedSkull", 5); - integer[MONTH_KILLS_TO_RED] = getGlobalNumber(L, "monthKillsToRedSkull", 10); - integer[RED_SKULL_DURATION] = getGlobalNumber(L, "redSkullDuration", 30); - integer[BLACK_SKULL_DURATION] = getGlobalNumber(L, "blackSkullDuration", 45); - integer[ORANGE_SKULL_DURATION] = getGlobalNumber(L, "orangeSkullDuration", 7); - integer[GLOBAL_SERVER_SAVE_NOTIFY_DURATION] = getGlobalNumber(L, "globalServerSaveNotifyDuration", 5); - - integer[PARTY_LIST_MAX_DISTANCE] = getGlobalNumber(L, "partyListMaxDistance", 0); - - integer[PUSH_DELAY] = getGlobalNumber(L, "pushDelay", 1000); - integer[PUSH_DISTANCE_DELAY] = getGlobalNumber(L, "pushDistanceDelay", 1500); - - integer[STAMINA_ORANGE_DELAY] = getGlobalNumber(L, "staminaOrangeDelay", 1); - integer[STAMINA_GREEN_DELAY] = getGlobalNumber(L, "staminaGreenDelay", 5); - integer[STAMINA_PZ_GAIN] = getGlobalNumber(L, "staminaPzGain", 1); - integer[STAMINA_TRAINER_DELAY] = getGlobalNumber(L, "staminaTrainerDelay", 5); - integer[STAMINA_TRAINER_GAIN] = getGlobalNumber(L, "staminaTrainerGain", 1); - integer[SAVE_INTERVAL_TIME] = getGlobalNumber(L, "saveIntervalTime", 1); - integer[MAX_ALLOWED_ON_A_DUMMY] = getGlobalNumber(L, "maxAllowedOnADummy", 1); - integer[FREE_QUEST_STAGE] = getGlobalNumber(L, "freeQuestStage", 1); - integer[DEPOTCHEST] = getGlobalNumber(L, "depotChest", 4); - integer[CRITICALCHANCE] = getGlobalNumber(L, "criticalChance", 10); - - integer[ADVENTURERSBLESSING_LEVEL] = getGlobalNumber(L, "adventurersBlessingLevel", 21); - integer[FORGE_MAX_ITEM_TIER] = getGlobalNumber(L, "forgeMaxItemTier", 10); - integer[FORGE_COST_ONE_SLIVER] = getGlobalNumber(L, "forgeCostOneSliver", 20); - integer[FORGE_SLIVER_AMOUNT] = getGlobalNumber(L, "forgeSliverAmount", 3); - integer[FORGE_CORE_COST] = getGlobalNumber(L, "forgeCoreCost", 50); - integer[FORGE_MAX_DUST] = getGlobalNumber(L, "forgeMaxDust", 225); - integer[FORGE_FUSION_DUST_COST] = getGlobalNumber(L, "forgeFusionCost", 100); - integer[FORGE_TRANSFER_DUST_COST] = getGlobalNumber(L, "forgeTransferCost", 100); - integer[FORGE_BASE_SUCCESS_RATE] = getGlobalNumber(L, "forgeBaseSuccessRate", 50); - integer[FORGE_BONUS_SUCCESS_RATE] = getGlobalNumber(L, "forgeBonusSuccessRate", 15); - integer[FORGE_TIER_LOSS_REDUCTION] = getGlobalNumber(L, "forgeTierLossReduction", 50); - integer[FORGE_AMOUNT_MULTIPLIER] = getGlobalNumber(L, "forgeAmountMultiplier", 3); - integer[FORGE_MIN_SLIVERS] = getGlobalNumber(L, "forgeMinSlivers", 3); - integer[FORGE_MAX_SLIVERS] = getGlobalNumber(L, "forgeMaxSlivers", 7); - integer[FORGE_INFLUENCED_CREATURES_LIMIT] = getGlobalNumber(L, "forgeInfluencedLimit", 300); - integer[FORGE_FIENDISH_CREATURES_LIMIT] = getGlobalNumber(L, "forgeFiendishLimit", 3); - integer[DISCORD_WEBHOOK_DELAY_MS] = getGlobalNumber(L, "discordWebhookDelayMs", Webhook::DEFAULT_DELAY_MS); - - floating[BESTIARY_RATE_CHARM_SHOP_PRICE] = getGlobalFloat(L, "bestiaryRateCharmShopPrice", 1.0); - floating[RATE_HEALTH_REGEN] = getGlobalFloat(L, "rateHealthRegen", 1.0); - floating[RATE_HEALTH_REGEN_SPEED] = getGlobalFloat(L, "rateHealthRegenSpeed", 1.0); - floating[RATE_MANA_REGEN] = getGlobalFloat(L, "rateManaRegen", 1.0); - floating[RATE_MANA_REGEN_SPEED] = getGlobalFloat(L, "rateManaRegenSpeed", 1.0); - floating[RATE_SOUL_REGEN] = getGlobalFloat(L, "rateSoulRegen", 1.0); - floating[RATE_SOUL_REGEN_SPEED] = getGlobalFloat(L, "rateSoulRegenSpeed", 1.0); - floating[RATE_SPELL_COOLDOWN] = getGlobalFloat(L, "rateSpellCooldown", 1.0); - floating[RATE_ATTACK_SPEED] = getGlobalFloat(L, "rateAttackSpeed", 1.0); - floating[RATE_OFFLINE_TRAINING_SPEED] = getGlobalFloat(L, "rateOfflineTrainingSpeed", 1.0); - floating[RATE_EXERCISE_TRAINING_SPEED] = getGlobalFloat(L, "rateExerciseTrainingSpeed", 1.0); - - floating[RATE_MONSTER_HEALTH] = getGlobalFloat(L, "rateMonsterHealth", 1.0); - floating[RATE_MONSTER_ATTACK] = getGlobalFloat(L, "rateMonsterAttack", 1.0); - floating[RATE_MONSTER_DEFENSE] = getGlobalFloat(L, "rateMonsterDefense", 1.0); - floating[RATE_BOSS_HEALTH] = getGlobalFloat(L, "rateBossHealth", 1.0); - floating[RATE_BOSS_ATTACK] = getGlobalFloat(L, "rateBossAttack", 1.0); - floating[RATE_BOSS_DEFENSE] = getGlobalFloat(L, "rateBossDefense", 1.0); - integer[BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN] = getGlobalNumber(L, "bossDefaultTimeToFightAgain", 20 * 60 * 60); - integer[BOSS_DEFAULT_TIME_TO_DEFEAT] = getGlobalNumber(L, "bossDefaultTimeToDefeat", 20 * 60); - - floating[RATE_NPC_HEALTH] = getGlobalFloat(L, "rateNpcHealth", 1.0); - floating[RATE_NPC_ATTACK] = getGlobalFloat(L, "rateNpcAttack", 1.0); - floating[RATE_NPC_DEFENSE] = getGlobalFloat(L, "rateNpcDefense", 1.0); - - boolean[PREY_ENABLED] = getGlobalBoolean(L, "preySystemEnabled", true); - boolean[PREY_FREE_THIRD_SLOT] = getGlobalBoolean(L, "preyFreeThirdSlot", false); - integer[PREY_REROLL_PRICE_LEVEL] = getGlobalNumber(L, "preyRerollPricePerLevel", 200); - integer[PREY_SELECTION_LIST_PRICE] = getGlobalNumber(L, "preySelectListPrice", 5); - integer[PREY_BONUS_TIME] = getGlobalNumber(L, "preyBonusTime", 7200); - integer[PREY_BONUS_REROLL_PRICE] = getGlobalNumber(L, "preyBonusRerollPrice", 1); - integer[PREY_FREE_REROLL_TIME] = getGlobalNumber(L, "preyFreeRerollTime", 72000); - - boolean[TASK_HUNTING_ENABLED] = getGlobalBoolean(L, "taskHuntingSystemEnabled", true); - boolean[TASK_HUNTING_FREE_THIRD_SLOT] = getGlobalBoolean(L, "taskHuntingFreeThirdSlot", false); - integer[TASK_HUNTING_LIMIT_EXHAUST] = getGlobalNumber(L, "taskHuntingLimitedTasksExhaust", 72000); - integer[TASK_HUNTING_REROLL_PRICE_LEVEL] = getGlobalNumber(L, "taskHuntingRerollPricePerLevel", 200); - integer[TASK_HUNTING_SELECTION_LIST_PRICE] = getGlobalNumber(L, "taskHuntingSelectListPrice", 5); - integer[TASK_HUNTING_BONUS_REROLL_PRICE] = getGlobalNumber(L, "taskHuntingBonusRerollPrice", 1); - integer[TASK_HUNTING_FREE_REROLL_TIME] = getGlobalNumber(L, "taskHuntingFreeRerollTime", 72000); - - integer[BESTIARY_KILL_MULTIPLIER] = getGlobalNumber(L, "bestiaryKillMultiplier", 1); - integer[BOSSTIARY_KILL_MULTIPLIER] = getGlobalNumber(L, "bosstiaryKillMultiplier", 1); - boolean[BOOSTED_BOSS_SLOT] = getGlobalBoolean(L, "boostedBossSlot", true); - integer[BOOSTED_BOSS_LOOT_BONUS] = getGlobalNumber(L, "boostedBossLootBonus", 250); - integer[BOOSTED_BOSS_KILL_BONUS] = getGlobalNumber(L, "boostedBossKillBonus", 3); - - integer[FAMILIAR_TIME] = getGlobalNumber(L, "familiarTime", 30); - - boolean[TOGGLE_GOLD_POUCH_ALLOW_ANYTHING] = getGlobalBoolean(L, "toggleGoldPouchAllowAnything", false); - boolean[TOGGLE_GOLD_POUCH_QUICKLOOT_ONLY] = getGlobalBoolean(L, "toggleGoldPouchQuickLootOnly", false); - boolean[TOGGLE_SERVER_IS_RETRO] = getGlobalBoolean(L, "toggleServerIsRetroPVP", false); - boolean[TOGGLE_TRAVELS_FREE] = getGlobalBoolean(L, "toggleTravelsFree", false); - integer[BUY_AOL_COMMAND_FEE] = getGlobalNumber(L, "buyAolCommandFee", 0); - integer[BUY_BLESS_COMMAND_FEE] = getGlobalNumber(L, "buyBlessCommandFee", 0); - boolean[TELEPORT_PLAYER_TO_VOCATION_ROOM] = getGlobalBoolean(L, "teleportPlayerToVocationRoom", true); - - boolean[TOGGLE_HAZARDSYSTEM] = getGlobalBoolean(L, "toogleHazardSystem", true); - integer[HAZARD_CRITICAL_INTERVAL] = getGlobalNumber(L, "hazardCriticalInterval", 2000); - integer[HAZARD_CRITICAL_CHANCE] = getGlobalNumber(L, "hazardCriticalChance", 750); - integer[HAZARD_CRITICAL_MULTIPLIER] = getGlobalNumber(L, "hazardCriticalMultiplier", 25); - integer[HAZARD_DAMAGE_MULTIPLIER] = getGlobalNumber(L, "hazardDamageMultiplier", 200); - integer[HAZARD_DODGE_MULTIPLIER] = getGlobalNumber(L, "hazardDodgeMultiplier", 85); - integer[HAZARD_PODS_DROP_MULTIPLIER] = getGlobalNumber(L, "hazardPodsDropMultiplier", 87); - integer[HAZARD_PODS_TIME_TO_DAMAGE] = getGlobalNumber(L, "hazardPodsTimeToDamage", 2000); - integer[HAZARD_PODS_TIME_TO_SPAWN] = getGlobalNumber(L, "hazardPodsTimeToSpawn", 4000); - integer[HAZARD_EXP_BONUS_MULTIPLIER] = getGlobalNumber(L, "hazardExpBonusMultiplier", 2); - integer[HAZARD_LOOT_BONUS_MULTIPLIER] = getGlobalNumber(L, "hazardLootBonusMultiplier", 2); - integer[HAZARD_PODS_DAMAGE] = getGlobalNumber(L, "hazardPodsDamage", 5); - integer[HAZARD_SPAWN_PLUNDER_MULTIPLIER] = getGlobalNumber(L, "hazardSpawnPlunderMultiplier", 25); - integer[LOW_LEVEL_BONUS_EXP] = getGlobalNumber(L, "lowLevelBonusExp", 50); - - boolean[LOYALTY_ENABLED] = getGlobalBoolean(L, "loyaltyEnabled", true); - integer[LOYALTY_POINTS_PER_CREATION_DAY] = getGlobalNumber(L, "loyaltyPointsPerCreationDay", 1); - integer[LOYALTY_POINTS_PER_PREMIUM_DAY_SPENT] = getGlobalNumber(L, "loyaltyPointsPerPremiumDaySpent", 0); - integer[LOYALTY_POINTS_PER_PREMIUM_DAY_PURCHASED] = getGlobalNumber(L, "loyaltyPointsPerPremiumDayPurchased", 0); - floating[LOYALTY_BONUS_PERCENTAGE_MULTIPLIER] = getGlobalFloat(L, "loyaltyBonusPercentageMultiplier", 1.0); - - boolean[TOGGLE_WHEELSYSTEM] = getGlobalBoolean(L, "wheelSystemEnabled", true); - integer[WHEEL_POINTS_PER_LEVEL] = getGlobalNumber(L, "wheelPointsPerLevel", 1); - - boolean[PARTY_AUTO_SHARE_EXPERIENCE] = getGlobalBoolean(L, "partyAutoShareExperience", true); - boolean[PARTY_SHARE_LOOT_BOOSTS] = getGlobalBoolean(L, "partyShareLootBoosts", true); - floating[PARTY_SHARE_LOOT_BOOSTS_DIMINISHING_FACTOR] = getGlobalFloat(L, "partyShareLootBoostsDimishingFactor", 0.7f); - integer[TIBIADROME_CONCOCTION_COOLDOWN] = getGlobalNumber(L, "tibiadromeConcoctionCooldown", 24 * 60 * 60); - integer[TIBIADROME_CONCOCTION_DURATION] = getGlobalNumber(L, "tibiadromeConcoctionDuration", 1 * 60 * 60); - string[TIBIADROME_CONCOCTION_TICK_TYPE] = getGlobalString(L, "tibiadromeConcoctionTickType", "online"); - - string[M_CONST] = getGlobalString(L, "memoryConst", "1<<16"); - integer[T_CONST] = getGlobalNumber(L, "temporaryConst", 2); - integer[PARALLELISM] = getGlobalNumber(L, "parallelism", 2); + loadBoolConfig(L, ALLOW_CHANGEOUTFIT, "allowChangeOutfit", true); + loadBoolConfig(L, ONE_PLAYER_ON_ACCOUNT, "onePlayerOnlinePerAccount", true); + loadBoolConfig(L, AIMBOT_HOTKEY_ENABLED, "hotkeyAimbotEnabled", true); + loadBoolConfig(L, REMOVE_RUNE_CHARGES, "removeChargesFromRunes", true); + loadBoolConfig(L, EXPERIENCE_FROM_PLAYERS, "experienceByKillingPlayers", false); + loadBoolConfig(L, FREE_PREMIUM, "freePremium", false); + loadBoolConfig(L, REPLACE_KICK_ON_LOGIN, "replaceKickOnLogin", true); + loadBoolConfig(L, MARKET_PREMIUM, "premiumToCreateMarketOffer", true); + loadBoolConfig(L, EMOTE_SPELLS, "emoteSpells", false); + loadBoolConfig(L, STAMINA_SYSTEM, "staminaSystem", true); + loadBoolConfig(L, WARN_UNSAFE_SCRIPTS, "warnUnsafeScripts", true); + loadBoolConfig(L, CONVERT_UNSAFE_SCRIPTS, "convertUnsafeScripts", true); + loadBoolConfig(L, CLASSIC_ATTACK_SPEED, "classicAttackSpeed", false); + loadBoolConfig(L, TOGGLE_ATTACK_SPEED_ONFIST, "toggleAttackSpeedOnFist", false); + loadIntConfig(L, MULTIPLIER_ATTACKONFIST, "multiplierSpeedOnFist", 5); + loadIntConfig(L, MAX_SPEED_ATTACKONFIST, "maxSpeedOnFist", 500); + loadBoolConfig(L, SCRIPTS_CONSOLE_LOGS, "showScriptsLogInConsole", true); + loadBoolConfig(L, STASH_MOVING, "stashMoving", false); + loadBoolConfig(L, ALLOW_BLOCK_SPAWN, "allowBlockSpawn", true); + loadBoolConfig(L, REMOVE_WEAPON_AMMO, "removeWeaponAmmunition", true); + loadBoolConfig(L, REMOVE_WEAPON_CHARGES, "removeWeaponCharges", true); + loadBoolConfig(L, REMOVE_POTION_CHARGES, "removeChargesFromPotions", true); + loadBoolConfig(L, GLOBAL_SERVER_SAVE_NOTIFY_MESSAGE, "globalServerSaveNotifyMessage", true); + loadBoolConfig(L, GLOBAL_SERVER_SAVE_CLEAN_MAP, "globalServerSaveCleanMap", false); + loadBoolConfig(L, GLOBAL_SERVER_SAVE_CLOSE, "globalServerSaveClose", false); + loadBoolConfig(L, FORCE_MONSTERTYPE_LOAD, "forceMonsterTypesOnLoad", true); + loadBoolConfig(L, HOUSE_OWNED_BY_ACCOUNT, "houseOwnedByAccount", false); + loadBoolConfig(L, CLEAN_PROTECTION_ZONES, "cleanProtectionZones", false); + loadBoolConfig(L, GLOBAL_SERVER_SAVE_SHUTDOWN, "globalServerSaveShutdown", true); + loadBoolConfig(L, PUSH_WHEN_ATTACKING, "pushWhenAttacking", false); + + loadBoolConfig(L, WEATHER_RAIN, "weatherRain", false); + loadBoolConfig(L, WEATHER_THUNDER, "thunderEffect", false); + loadBoolConfig(L, ALL_CONSOLE_LOG, "allConsoleLog", false); + loadBoolConfig(L, TOGGLE_FREE_QUEST, "toggleFreeQuest", true); + loadBoolConfig(L, AUTOLOOT, "autoLoot", false); + loadBoolConfig(L, AUTOBANK, "autoBank", false); + loadBoolConfig(L, STAMINA_TRAINER, "staminaTrainer", false); + loadBoolConfig(L, STAMINA_PZ, "staminaPz", false); + loadBoolConfig(L, SORT_LOOT_BY_CHANCE, "sortLootByChance", false); + loadBoolConfig(L, TOGGLE_SAVE_INTERVAL, "toggleSaveInterval", false); + loadBoolConfig(L, TOGGLE_SAVE_INTERVAL_CLEAN_MAP, "toggleSaveIntervalCleanMap", false); + loadBoolConfig(L, TELEPORT_SUMMONS, "teleportSummons", false); + loadBoolConfig(L, ALLOW_RELOAD, "allowReload", false); + + loadBoolConfig(L, ONLY_PREMIUM_ACCOUNT, "onlyPremiumAccount", false); + loadBoolConfig(L, RATE_USE_STAGES, "rateUseStages", false); + loadBoolConfig(L, TOGGLE_IMBUEMENT_SHRINE_STORAGE, "toggleImbuementShrineStorage", true); + loadBoolConfig(L, TOGGLE_IMBUEMENT_NON_AGGRESSIVE_FIGHT_ONLY, "toggleImbuementNonAggressiveFightOnly", false); + + loadBoolConfig(L, TOGGLE_DOWNLOAD_MAP, "toggleDownloadMap", false); + loadBoolConfig(L, USE_ANY_DATAPACK_FOLDER, "useAnyDatapackFolder", false); + loadBoolConfig(L, INVENTORY_GLOW, "inventoryGlowOnFiveBless", false); + loadBoolConfig(L, XP_DISPLAY_MODE, "experienceDisplayRates", true); + + loadStringConfig(L, DEFAULT_PRIORITY, "defaultPriority", "high"); + loadStringConfig(L, SERVER_NAME, "serverName", ""); + loadStringConfig(L, SERVER_MOTD, "serverMotd", ""); + loadStringConfig(L, OWNER_NAME, "ownerName", ""); + loadStringConfig(L, OWNER_EMAIL, "ownerEmail", ""); + loadStringConfig(L, URL, "url", ""); + loadStringConfig(L, LOCATION, "location", ""); + loadStringConfig(L, WORLD_TYPE, "worldType", "pvp"); + loadStringConfig(L, STORE_IMAGES_URL, "coinImagesURL", ""); + loadStringConfig(L, DISCORD_WEBHOOK_URL, "discordWebhookURL", ""); + loadStringConfig(L, SAVE_INTERVAL_TYPE, "saveIntervalType", ""); + loadStringConfig(L, GLOBAL_SERVER_SAVE_TIME, "globalServerSaveTime", "06:00"); + loadStringConfig(L, DATA_DIRECTORY, "dataPackDirectory", "data-otservbr-global"); + loadStringConfig(L, CORE_DIRECTORY, "coreDirectory", "data"); + + loadStringConfig(L, FORGE_FIENDISH_INTERVAL_TYPE, "forgeFiendishIntervalType", "hour"); + loadStringConfig(L, FORGE_FIENDISH_INTERVAL_TIME, "forgeFiendishIntervalTime", "1"); + + loadIntConfig(L, MAX_PLAYERS, "maxPlayers", 0); + loadIntConfig(L, PZ_LOCKED, "pzLocked", 60000); + loadIntConfig(L, DEFAULT_DESPAWNRANGE, "deSpawnRange", 2); + loadIntConfig(L, DEFAULT_DESPAWNRADIUS, "deSpawnRadius", 50); + loadIntConfig(L, RATE_EXPERIENCE, "rateExp", 1); + loadIntConfig(L, RATE_SKILL, "rateSkill", 1); + loadIntConfig(L, RATE_LOOT, "rateLoot", 1); + loadIntConfig(L, RATE_MAGIC, "rateMagic", 1); + loadIntConfig(L, RATE_SPAWN, "rateSpawn", 1); + loadIntConfig(L, RATE_KILLING_IN_THE_NAME_OF_POINTS, "rateKillingInTheNameOfPoints", 1); + + loadIntConfig(L, HOUSE_PRICE_PER_SQM, "housePriceEachSQM", 1000); + loadIntConfig(L, HOUSE_BUY_LEVEL, "houseBuyLevel", 0); + loadIntConfig(L, HOUSE_LOSE_AFTER_INACTIVITY, "houseLoseAfterInactivity", 0); + loadBoolConfig(L, HOUSE_PURSHASED_SHOW_PRICE, "housePurchasedShowPrice", false); + loadBoolConfig(L, ONLY_INVITED_CAN_MOVE_HOUSE_ITEMS, "onlyInvitedCanMoveHouseItems", true); + + loadIntConfig(L, ACTIONS_DELAY_INTERVAL, "timeBetweenActions", 200); + loadIntConfig(L, EX_ACTIONS_DELAY_INTERVAL, "timeBetweenExActions", 1000); + loadIntConfig(L, MAX_MESSAGEBUFFER, "maxMessageBuffer", 4); + loadIntConfig(L, KICK_AFTER_MINUTES, "kickIdlePlayerAfterMinutes", 15); + loadIntConfig(L, PROTECTION_LEVEL, "protectionLevel", 1); + loadIntConfig(L, DEATH_LOSE_PERCENT, "deathLosePercent", -1); + loadIntConfig(L, STATUSQUERY_TIMEOUT, "statusTimeout", 5000); + loadIntConfig(L, FRAG_TIME, "timeToDecreaseFrags", 24 * 60 * 60 * 1000); + loadIntConfig(L, WHITE_SKULL_TIME, "whiteSkullTime", 15 * 60 * 1000); + loadIntConfig(L, STAIRHOP_DELAY, "stairJumpExhaustion", 2000); + loadIntConfig(L, MAX_CONTAINER, "maxContainer", 500); + loadIntConfig(L, MAX_CONTAINER_ITEM, "maxItem", 5000); + loadIntConfig(L, EXP_FROM_PLAYERS_LEVEL_RANGE, "expFromPlayersLevelRange", 75); + loadIntConfig(L, CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES, "checkExpiredMarketOffersEachMinutes", 60); + loadIntConfig(L, MAX_MARKET_OFFERS_AT_A_TIME_PER_PLAYER, "maxMarketOffersAtATimePerPlayer", 100); + loadIntConfig(L, MAX_PACKETS_PER_SECOND, "maxPacketsPerSecond", 25); + loadIntConfig(L, COMPRESSION_LEVEL, "packetCompressionLevel", 6); + loadIntConfig(L, STORE_COIN_PACKET, "coinPacketSize", 25); + loadIntConfig(L, DAY_KILLS_TO_RED, "dayKillsToRedSkull", 3); + loadIntConfig(L, WEEK_KILLS_TO_RED, "weekKillsToRedSkull", 5); + loadIntConfig(L, MONTH_KILLS_TO_RED, "monthKillsToRedSkull", 10); + loadIntConfig(L, RED_SKULL_DURATION, "redSkullDuration", 30); + loadIntConfig(L, BLACK_SKULL_DURATION, "blackSkullDuration", 45); + loadIntConfig(L, ORANGE_SKULL_DURATION, "orangeSkullDuration", 7); + loadIntConfig(L, GLOBAL_SERVER_SAVE_NOTIFY_DURATION, "globalServerSaveNotifyDuration", 5); + + loadIntConfig(L, PARTY_LIST_MAX_DISTANCE, "partyListMaxDistance", 0); + + loadIntConfig(L, PUSH_DELAY, "pushDelay", 1000); + loadIntConfig(L, PUSH_DISTANCE_DELAY, "pushDistanceDelay", 1500); + + loadIntConfig(L, STAMINA_ORANGE_DELAY, "staminaOrangeDelay", 1); + loadIntConfig(L, STAMINA_GREEN_DELAY, "staminaGreenDelay", 5); + loadIntConfig(L, STAMINA_PZ_GAIN, "staminaPzGain", 1); + loadIntConfig(L, STAMINA_TRAINER_DELAY, "staminaTrainerDelay", 5); + loadIntConfig(L, STAMINA_TRAINER_GAIN, "staminaTrainerGain", 1); + loadIntConfig(L, SAVE_INTERVAL_TIME, "saveIntervalTime", 1); + loadIntConfig(L, MAX_ALLOWED_ON_A_DUMMY, "maxAllowedOnADummy", 1); + loadIntConfig(L, FREE_QUEST_STAGE, "freeQuestStage", 1); + loadIntConfig(L, DEPOTCHEST, "depotChest", 4); + loadIntConfig(L, CRITICALCHANCE, "criticalChance", 10); + + loadIntConfig(L, ADVENTURERSBLESSING_LEVEL, "adventurersBlessingLevel", 21); + loadIntConfig(L, FORGE_MAX_ITEM_TIER, "forgeMaxItemTier", 10); + loadIntConfig(L, FORGE_COST_ONE_SLIVER, "forgeCostOneSliver", 20); + loadIntConfig(L, FORGE_SLIVER_AMOUNT, "forgeSliverAmount", 3); + loadIntConfig(L, FORGE_CORE_COST, "forgeCoreCost", 50); + loadIntConfig(L, FORGE_MAX_DUST, "forgeMaxDust", 225); + loadIntConfig(L, FORGE_FUSION_DUST_COST, "forgeFusionCost", 100); + loadIntConfig(L, FORGE_TRANSFER_DUST_COST, "forgeTransferCost", 100); + loadIntConfig(L, FORGE_BASE_SUCCESS_RATE, "forgeBaseSuccessRate", 50); + loadIntConfig(L, FORGE_BONUS_SUCCESS_RATE, "forgeBonusSuccessRate", 15); + loadIntConfig(L, FORGE_TIER_LOSS_REDUCTION, "forgeTierLossReduction", 50); + loadIntConfig(L, FORGE_AMOUNT_MULTIPLIER, "forgeAmountMultiplier", 3); + loadIntConfig(L, FORGE_MIN_SLIVERS, "forgeMinSlivers", 3); + loadIntConfig(L, FORGE_MAX_SLIVERS, "forgeMaxSlivers", 7); + loadIntConfig(L, FORGE_INFLUENCED_CREATURES_LIMIT, "forgeInfluencedLimit", 300); + loadIntConfig(L, FORGE_FIENDISH_CREATURES_LIMIT, "forgeFiendishLimit", 3); + loadIntConfig(L, DISCORD_WEBHOOK_DELAY_MS, "discordWebhookDelayMs", Webhook::DEFAULT_DELAY_MS); + + loadFloatConfig(L, BESTIARY_RATE_CHARM_SHOP_PRICE, "bestiaryRateCharmShopPrice", 1.0); + loadFloatConfig(L, RATE_HEALTH_REGEN, "rateHealthRegen", 1.0); + loadFloatConfig(L, RATE_HEALTH_REGEN_SPEED, "rateHealthRegenSpeed", 1.0); + loadFloatConfig(L, RATE_MANA_REGEN, "rateManaRegen", 1.0); + loadFloatConfig(L, RATE_MANA_REGEN_SPEED, "rateManaRegenSpeed", 1.0); + loadFloatConfig(L, RATE_SOUL_REGEN, "rateSoulRegen", 1.0); + loadFloatConfig(L, RATE_SOUL_REGEN_SPEED, "rateSoulRegenSpeed", 1.0); + loadFloatConfig(L, RATE_SPELL_COOLDOWN, "rateSpellCooldown", 1.0); + loadFloatConfig(L, RATE_ATTACK_SPEED, "rateAttackSpeed", 1.0); + loadFloatConfig(L, RATE_OFFLINE_TRAINING_SPEED, "rateOfflineTrainingSpeed", 1.0); + loadFloatConfig(L, RATE_EXERCISE_TRAINING_SPEED, "rateExerciseTrainingSpeed", 1.0); + + loadFloatConfig(L, RATE_MONSTER_HEALTH, "rateMonsterHealth", 1.0); + loadFloatConfig(L, RATE_MONSTER_ATTACK, "rateMonsterAttack", 1.0); + loadFloatConfig(L, RATE_MONSTER_DEFENSE, "rateMonsterDefense", 1.0); + loadFloatConfig(L, RATE_BOSS_HEALTH, "rateBossHealth", 1.0); + loadFloatConfig(L, RATE_BOSS_ATTACK, "rateBossAttack", 1.0); + loadFloatConfig(L, RATE_BOSS_DEFENSE, "rateBossDefense", 1.0); + loadIntConfig(L, BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN, "bossDefaultTimeToFightAgain", 20 * 60 * 60); + loadIntConfig(L, BOSS_DEFAULT_TIME_TO_DEFEAT, "bossDefaultTimeToDefeat", 20 * 60); + + loadFloatConfig(L, RATE_NPC_HEALTH, "rateNpcHealth", 1.0); + loadFloatConfig(L, RATE_NPC_ATTACK, "rateNpcAttack", 1.0); + loadFloatConfig(L, RATE_NPC_DEFENSE, "rateNpcDefense", 1.0); + + loadBoolConfig(L, PREY_ENABLED, "preySystemEnabled", true); + loadBoolConfig(L, PREY_FREE_THIRD_SLOT, "preyFreeThirdSlot", false); + loadIntConfig(L, PREY_REROLL_PRICE_LEVEL, "preyRerollPricePerLevel", 200); + loadIntConfig(L, PREY_SELECTION_LIST_PRICE, "preySelectListPrice", 5); + loadIntConfig(L, PREY_BONUS_TIME, "preyBonusTime", 7200); + loadIntConfig(L, PREY_BONUS_REROLL_PRICE, "preyBonusRerollPrice", 1); + loadIntConfig(L, PREY_FREE_REROLL_TIME, "preyFreeRerollTime", 72000); + + loadBoolConfig(L, TASK_HUNTING_ENABLED, "taskHuntingSystemEnabled", true); + loadBoolConfig(L, TASK_HUNTING_FREE_THIRD_SLOT, "taskHuntingFreeThirdSlot", false); + loadIntConfig(L, TASK_HUNTING_LIMIT_EXHAUST, "taskHuntingLimitedTasksExhaust", 72000); + loadIntConfig(L, TASK_HUNTING_REROLL_PRICE_LEVEL, "taskHuntingRerollPricePerLevel", 200); + loadIntConfig(L, TASK_HUNTING_SELECTION_LIST_PRICE, "taskHuntingSelectListPrice", 5); + loadIntConfig(L, TASK_HUNTING_BONUS_REROLL_PRICE, "taskHuntingBonusRerollPrice", 1); + loadIntConfig(L, TASK_HUNTING_FREE_REROLL_TIME, "taskHuntingFreeRerollTime", 72000); + + loadIntConfig(L, BESTIARY_KILL_MULTIPLIER, "bestiaryKillMultiplier", 1); + loadIntConfig(L, BOSSTIARY_KILL_MULTIPLIER, "bosstiaryKillMultiplier", 1); + loadBoolConfig(L, BOOSTED_BOSS_SLOT, "boostedBossSlot", true); + loadIntConfig(L, BOOSTED_BOSS_LOOT_BONUS, "boostedBossLootBonus", 250); + loadIntConfig(L, BOOSTED_BOSS_KILL_BONUS, "boostedBossKillBonus", 3); + + loadIntConfig(L, FAMILIAR_TIME, "familiarTime", 30); + + loadBoolConfig(L, TOGGLE_GOLD_POUCH_ALLOW_ANYTHING, "toggleGoldPouchAllowAnything", false); + loadBoolConfig(L, TOGGLE_GOLD_POUCH_QUICKLOOT_ONLY, "toggleGoldPouchQuickLootOnly", false); + loadBoolConfig(L, TOGGLE_SERVER_IS_RETRO, "toggleServerIsRetroPVP", false); + loadBoolConfig(L, TOGGLE_TRAVELS_FREE, "toggleTravelsFree", false); + loadIntConfig(L, BUY_AOL_COMMAND_FEE, "buyAolCommandFee", 0); + loadIntConfig(L, BUY_BLESS_COMMAND_FEE, "buyBlessCommandFee", 0); + loadBoolConfig(L, TELEPORT_PLAYER_TO_VOCATION_ROOM, "teleportPlayerToVocationRoom", true); + + loadBoolConfig(L, TOGGLE_HAZARDSYSTEM, "toogleHazardSystem", true); + loadIntConfig(L, HAZARD_CRITICAL_INTERVAL, "hazardCriticalInterval", 2000); + loadIntConfig(L, HAZARD_CRITICAL_CHANCE, "hazardCriticalChance", 750); + loadIntConfig(L, HAZARD_CRITICAL_MULTIPLIER, "hazardCriticalMultiplier", 25); + loadIntConfig(L, HAZARD_DAMAGE_MULTIPLIER, "hazardDamageMultiplier", 200); + loadIntConfig(L, HAZARD_DODGE_MULTIPLIER, "hazardDodgeMultiplier", 85); + loadIntConfig(L, HAZARD_PODS_DROP_MULTIPLIER, "hazardPodsDropMultiplier", 87); + loadIntConfig(L, HAZARD_PODS_TIME_TO_DAMAGE, "hazardPodsTimeToDamage", 2000); + loadIntConfig(L, HAZARD_PODS_TIME_TO_SPAWN, "hazardPodsTimeToSpawn", 4000); + loadIntConfig(L, HAZARD_EXP_BONUS_MULTIPLIER, "hazardExpBonusMultiplier", 2); + loadIntConfig(L, HAZARD_LOOT_BONUS_MULTIPLIER, "hazardLootBonusMultiplier", 2); + loadIntConfig(L, HAZARD_PODS_DAMAGE, "hazardPodsDamage", 5); + loadIntConfig(L, HAZARD_SPAWN_PLUNDER_MULTIPLIER, "hazardSpawnPlunderMultiplier", 25); + loadIntConfig(L, LOW_LEVEL_BONUS_EXP, "lowLevelBonusExp", 50); + + loadBoolConfig(L, LOYALTY_ENABLED, "loyaltyEnabled", true); + loadIntConfig(L, LOYALTY_POINTS_PER_CREATION_DAY, "loyaltyPointsPerCreationDay", 1); + loadIntConfig(L, LOYALTY_POINTS_PER_PREMIUM_DAY_SPENT, "loyaltyPointsPerPremiumDaySpent", 0); + loadIntConfig(L, LOYALTY_POINTS_PER_PREMIUM_DAY_PURCHASED, "loyaltyPointsPerPremiumDayPurchased", 0); + loadFloatConfig(L, LOYALTY_BONUS_PERCENTAGE_MULTIPLIER, "loyaltyBonusPercentageMultiplier", 1.0); + + loadBoolConfig(L, TOGGLE_WHEELSYSTEM, "wheelSystemEnabled", true); + loadIntConfig(L, WHEEL_POINTS_PER_LEVEL, "wheelPointsPerLevel", 1); + + loadBoolConfig(L, PARTY_AUTO_SHARE_EXPERIENCE, "partyAutoShareExperience", true); + loadBoolConfig(L, PARTY_SHARE_LOOT_BOOSTS, "partyShareLootBoosts", true); + loadFloatConfig(L, PARTY_SHARE_LOOT_BOOSTS_DIMINISHING_FACTOR, "partyShareLootBoostsDimishingFactor", 0.7f); + loadIntConfig(L, TIBIADROME_CONCOCTION_COOLDOWN, "tibiadromeConcoctionCooldown", 24 * 60 * 60); + loadIntConfig(L, TIBIADROME_CONCOCTION_DURATION, "tibiadromeConcoctionDuration", 1 * 60 * 60); + loadStringConfig(L, TIBIADROME_CONCOCTION_TICK_TYPE, "tibiadromeConcoctionTickType", "online"); + + loadStringConfig(L, M_CONST, "memoryConst", "1<<16"); + loadIntConfig(L, T_CONST, "temporaryConst", 2); + loadIntConfig(L, PARALLELISM, "parallelism", 2); // Vip System - boolean[VIP_SYSTEM_ENABLED] = getGlobalBoolean(L, "vipSystemEnabled", false); - integer[VIP_BONUS_EXP] = getGlobalNumber(L, "vipBonusExp", 0); - integer[VIP_BONUS_LOOT] = getGlobalNumber(L, "vipBonusLoot", 0); - integer[VIP_BONUS_SKILL] = getGlobalNumber(L, "vipBonusSkill", 0); - boolean[VIP_AUTOLOOT_VIP_ONLY] = getGlobalBoolean(L, "vipAutoLootVipOnly", false); - boolean[VIP_KEEP_HOUSE] = getGlobalBoolean(L, "vipKeepHouse", false); - boolean[VIP_STAY_ONLINE] = getGlobalBoolean(L, "vipStayOnline", false); - integer[VIP_FAMILIAR_TIME_COOLDOWN_REDUCTION] = getGlobalNumber(L, "vipFamiliarTimeCooldownReduction", 0); - - boolean[REWARD_CHEST_COLLECT_ENABLED] = getGlobalBoolean(L, "rewardChestCollectEnabled", true); - integer[REWARD_CHEST_MAX_COLLECT_ITEMS] = getGlobalNumber(L, "rewardChestMaxCollectItems", 200); + loadBoolConfig(L, VIP_SYSTEM_ENABLED, "vipSystemEnabled", false); + loadIntConfig(L, VIP_BONUS_EXP, "vipBonusExp", 0); + loadIntConfig(L, VIP_BONUS_LOOT, "vipBonusLoot", 0); + loadIntConfig(L, VIP_BONUS_SKILL, "vipBonusSkill", 0); + loadBoolConfig(L, VIP_AUTOLOOT_VIP_ONLY, "vipAutoLootVipOnly", false); + loadBoolConfig(L, VIP_KEEP_HOUSE, "vipKeepHouse", false); + loadBoolConfig(L, VIP_STAY_ONLINE, "vipStayOnline", false); + loadIntConfig(L, VIP_FAMILIAR_TIME_COOLDOWN_REDUCTION, "vipFamiliarTimeCooldownReduction", 0); + + loadBoolConfig(L, REWARD_CHEST_COLLECT_ENABLED, "rewardChestCollectEnabled", true); + loadIntConfig(L, REWARD_CHEST_MAX_COLLECT_ITEMS, "rewardChestMaxCollectItems", 200); // PVP System - floating[PVP_RATE_DAMAGE_TAKEN_PER_LEVEL] = getGlobalFloat(L, "pvpRateDamageTakenPerLevel", 0.0); - floating[PVP_RATE_DAMAGE_REDUCTION_PER_LEVEL] = getGlobalFloat(L, "pvpRateDamageReductionPerLevel", 0.0); - integer[PVP_MAX_LEVEL_DIFFERENCE] = getGlobalNumber(L, "pvpMaxLevelDifference", 0); + loadFloatConfig(L, PVP_RATE_DAMAGE_TAKEN_PER_LEVEL, "pvpRateDamageTakenPerLevel", 0.0); + loadFloatConfig(L, PVP_RATE_DAMAGE_REDUCTION_PER_LEVEL, "pvpRateDamageReductionPerLevel", 0.0); + loadIntConfig(L, PVP_MAX_LEVEL_DIFFERENCE, "pvpMaxLevelDifference", 0); - boolean[TOGGLE_MOUNT_IN_PZ] = getGlobalBoolean(L, "toggleMountInProtectionZone", false); + loadBoolConfig(L, TOGGLE_MOUNT_IN_PZ, "toggleMountInProtectionZone", false); - boolean[TOGGLE_HOUSE_TRANSFER_ON_SERVER_RESTART] = getGlobalBoolean(L, "togglehouseTransferOnRestart", false); + loadBoolConfig(L, TOGGLE_HOUSE_TRANSFER_ON_SERVER_RESTART, "togglehouseTransferOnRestart", false); - boolean[TOGGLE_RECEIVE_REWARD] = getGlobalBoolean(L, "toggleReceiveReward", false); + loadBoolConfig(L, TOGGLE_RECEIVE_REWARD, "toggleReceiveReward", false); loaded = true; lua_close(L); @@ -409,51 +353,86 @@ bool ConfigManager::load() { } bool ConfigManager::reload() { - bool result = load(); + const bool result = load(); if (transformToSHA1(getString(SERVER_MOTD)) != g_game().getMotdHash()) { g_game().incrementMotdNum(); } return result; } -static std::string dummyStr; +std::string ConfigManager::loadStringConfig(lua_State* L, const ConfigKey_t &key, const char* identifier, const std::string &defaultValue) { + std::string value = defaultValue; + lua_getglobal(L, identifier); + if (lua_isstring(L, -1)) { + value = lua_tostring(L, -1); + } + configs[key] = value; + lua_pop(L, 1); + return value; +} -const std::string &ConfigManager::getString(stringConfig_t what) const { - if (what >= LAST_STRING_CONFIG) { - g_logger().warn("[ConfigManager::getString] - Accessing invalid index: {}", fmt::underlying(what)); - return dummyStr; +int32_t ConfigManager::loadIntConfig(lua_State* L, const ConfigKey_t &key, const char* identifier, const int32_t &defaultValue) { + int32_t value = defaultValue; + lua_getglobal(L, identifier); + if (lua_isnumber(L, -1)) { + value = static_cast(lua_tointeger(L, -1)); } - return string[what]; + configs[key] = value; + lua_pop(L, 1); + return value; } -int32_t ConfigManager::getNumber(integerConfig_t what) const { - if (what >= LAST_INTEGER_CONFIG) { - g_logger().warn("[ConfigManager::getNumber] - Accessing invalid index: {}", fmt::underlying(what)); - return 0; +bool ConfigManager::loadBoolConfig(lua_State* L, const ConfigKey_t &key, const char* identifier, const bool &defaultValue) { + bool value = defaultValue; + lua_getglobal(L, identifier); + if (lua_isboolean(L, -1)) { + value = static_cast(lua_toboolean(L, -1)); } - return integer[what]; + configs[key] = value; + lua_pop(L, 1); + return value; } -int16_t ConfigManager::getShortNumber(integerConfig_t what) const { - if (what >= LAST_INTEGER_CONFIG) { - g_logger().warn("[ConfigManager::getShortNumber] - Accessing invalid index: {}", fmt::underlying(what)); - return 0; +float ConfigManager::loadFloatConfig(lua_State* L, const ConfigKey_t &key, const char* identifier, const float &defaultValue) { + float value = defaultValue; + lua_getglobal(L, identifier); + if (lua_isnumber(L, -1)) { + value = static_cast(lua_tonumber(L, -1)); } - return integer[what]; + configs[key] = value; + lua_pop(L, 1); + return value; } -bool ConfigManager::getBoolean(booleanConfig_t what) const { - if (what >= LAST_BOOLEAN_CONFIG) { - g_logger().warn("[ConfigManager::getBoolean] - Accessing invalid index: {}", fmt::underlying(what)); - return false; +const std::string &ConfigManager::getString(const ConfigKey_t &key) const { + static const std::string dummyStr; + if (configs.contains(key) && std::holds_alternative(configs.at(key))) { + return std::get(configs.at(key)); + } + g_logger().warn("[ConfigManager::getString] - Accessing invalid or wrong type index: {}", fmt::underlying(key)); + return dummyStr; +} + +int32_t ConfigManager::getNumber(const ConfigKey_t &key) const { + if (configs.contains(key) && std::holds_alternative(configs.at(key))) { + return std::get(configs.at(key)); + } + g_logger().warn("[ConfigManager::getNumber] - Accessing invalid or wrong type index: {}", fmt::underlying(key)); + return 0; +} + +bool ConfigManager::getBoolean(const ConfigKey_t &key) const { + if (configs.contains(key) && std::holds_alternative(configs.at(key))) { + return std::get(configs.at(key)); } - return boolean[what]; + g_logger().warn("[ConfigManager::getBoolean] - Accessing invalid or wrong type index: {}", fmt::underlying(key)); + return false; } -float ConfigManager::getFloat(floatingConfig_t what) const { - if (what >= LAST_FLOATING_CONFIG) { - g_logger().warn("[ConfigManager::getFLoat] - Accessing invalid index: {}", fmt::underlying(what)); - return 0; +float ConfigManager::getFloat(const ConfigKey_t &key) const { + if (configs.contains(key) && std::holds_alternative(configs.at(key))) { + return std::get(configs.at(key)); } - return floating[what]; + g_logger().warn("[ConfigManager::getFloat] - Accessing invalid or wrong type index: {}", fmt::underlying(key)); + return 0.0f; } diff --git a/src/config/configmanager.hpp b/src/config/configmanager.hpp index 21e42f9e768..39d88a046f2 100644 --- a/src/config/configmanager.hpp +++ b/src/config/configmanager.hpp @@ -12,6 +12,8 @@ #include "declarations.hpp" #include "lib/di/container.hpp" +using ConfigValue = std::variant; + class ConfigManager { public: ConfigManager() = default; @@ -27,28 +29,27 @@ class ConfigManager { bool load(); bool reload(); - const std::string &getString(stringConfig_t what) const; - int32_t getNumber(integerConfig_t what) const; - int16_t getShortNumber(integerConfig_t what) const; - bool getBoolean(booleanConfig_t what) const; - float getFloat(floatingConfig_t what) const; - const std::string &setConfigFileLua(const std::string &what) { configFileLua = { what }; return configFileLua; }; - const std::string &getConfigFileLua() const { + [[nodiscard]] const std::string &getConfigFileLua() const { return configFileLua; }; -private: - std::string configFileLua = { "config.lua" }; + [[nodiscard]] const std::string &getString(const ConfigKey_t &key) const; + [[nodiscard]] int32_t getNumber(const ConfigKey_t &key) const; + [[nodiscard]] bool getBoolean(const ConfigKey_t &key) const; + [[nodiscard]] float getFloat(const ConfigKey_t &key) const; - std::string string[LAST_STRING_CONFIG] = {}; - int32_t integer[LAST_INTEGER_CONFIG] = {}; - bool boolean[LAST_BOOLEAN_CONFIG] = {}; - float floating[LAST_FLOATING_CONFIG] = {}; +private: + phmap::flat_hash_map configs; + std::string loadStringConfig(lua_State* L, const ConfigKey_t &key, const char* identifier, const std::string &defaultValue); + int32_t loadIntConfig(lua_State* L, const ConfigKey_t &key, const char* identifier, const int32_t &defaultValue); + bool loadBoolConfig(lua_State* L, const ConfigKey_t &key, const char* identifier, const bool &defaultValue); + float loadFloatConfig(lua_State* L, const ConfigKey_t &key, const char* identifier, const float &defaultValue); + std::string configFileLua = { "config.lua" }; bool loaded = false; }; diff --git a/src/lua/functions/core/game/config_functions.cpp b/src/lua/functions/core/game/config_functions.cpp index 96ce09cdcc7..dae41773882 100644 --- a/src/lua/functions/core/game/config_functions.cpp +++ b/src/lua/functions/core/game/config_functions.cpp @@ -14,265 +14,44 @@ void ConfigFunctions::init(lua_State* L) { registerTable(L, "configManager"); - registerMethod(L, "configManager", "getString", ConfigFunctions::luaConfigManagerGetString); - registerMethod(L, "configManager", "getNumber", ConfigFunctions::luaConfigManagerGetNumber); - registerMethod(L, "configManager", "getBoolean", ConfigFunctions::luaConfigManagerGetBoolean); - registerMethod(L, "configManager", "getFloat", ConfigFunctions::luaConfigManagerGetFloat); - -#define registerEnumIn(L, tableName, value) \ - do { \ - std::string enumName = #value; \ - registerVariable(L, tableName, enumName.substr(enumName.find_last_of(':') + 1), value); \ + registerMethod(L, "configManager", "getString", luaConfigManagerGetString); + registerMethod(L, "configManager", "getNumber", luaConfigManagerGetNumber); + registerMethod(L, "configManager", "getBoolean", luaConfigManagerGetBoolean); + registerMethod(L, "configManager", "getFloat", luaConfigManagerGetFloat); + +#define registerMagicEnumIn(L, tableName, enumValue) \ + do { \ + auto number = magic_enum::enum_integer(enumValue); \ + auto name = magic_enum::enum_name(enumValue).data(); \ + registerVariable(L, tableName, name, value); \ } while (0) registerTable(L, "configKeys"); - registerEnumIn(L, "configKeys", ALLOW_CHANGEOUTFIT); - registerEnumIn(L, "configKeys", ONE_PLAYER_ON_ACCOUNT); - registerEnumIn(L, "configKeys", AIMBOT_HOTKEY_ENABLED); - registerEnumIn(L, "configKeys", REMOVE_RUNE_CHARGES); - registerEnumIn(L, "configKeys", EXPERIENCE_FROM_PLAYERS); - registerEnumIn(L, "configKeys", FREE_PREMIUM); - registerEnumIn(L, "configKeys", REPLACE_KICK_ON_LOGIN); - registerEnumIn(L, "configKeys", BIND_ONLY_GLOBAL_ADDRESS); - registerEnumIn(L, "configKeys", OPTIMIZE_DATABASE); - registerEnumIn(L, "configKeys", MARKET_PREMIUM); - registerEnumIn(L, "configKeys", EMOTE_SPELLS); - registerEnumIn(L, "configKeys", STAMINA_SYSTEM); - registerEnumIn(L, "configKeys", WARN_UNSAFE_SCRIPTS); - registerEnumIn(L, "configKeys", CONVERT_UNSAFE_SCRIPTS); - registerEnumIn(L, "configKeys", ALLOW_BLOCK_SPAWN); - registerEnumIn(L, "configKeys", CLASSIC_ATTACK_SPEED); - registerEnumIn(L, "configKeys", REMOVE_WEAPON_AMMO); - registerEnumIn(L, "configKeys", REMOVE_WEAPON_CHARGES); - registerEnumIn(L, "configKeys", REMOVE_POTION_CHARGES); - registerEnumIn(L, "configKeys", WEATHER_RAIN); - registerEnumIn(L, "configKeys", ALLOW_RELOAD); - registerEnumIn(L, "configKeys", WEATHER_THUNDER); - registerEnumIn(L, "configKeys", TOGGLE_FREE_QUEST); - registerEnumIn(L, "configKeys", FREE_QUEST_STAGE); - registerEnumIn(L, "configKeys", ALL_CONSOLE_LOG); - registerEnumIn(L, "configKeys", GLOBAL_SERVER_SAVE_NOTIFY_MESSAGE); - registerEnumIn(L, "configKeys", GLOBAL_SERVER_SAVE_NOTIFY_DURATION); - registerEnumIn(L, "configKeys", XP_DISPLAY_MODE); - registerEnumIn(L, "configKeys", GLOBAL_SERVER_SAVE_CLEAN_MAP); - registerEnumIn(L, "configKeys", GLOBAL_SERVER_SAVE_CLOSE); - registerEnumIn(L, "configKeys", GLOBAL_SERVER_SAVE_SHUTDOWN); - registerEnumIn(L, "configKeys", MAP_NAME); - registerEnumIn(L, "configKeys", TOGGLE_MAP_CUSTOM); - registerEnumIn(L, "configKeys", MAP_CUSTOM_NAME); - registerEnumIn(L, "configKeys", HOUSE_RENT_PERIOD); - registerEnumIn(L, "configKeys", SERVER_NAME); - registerEnumIn(L, "configKeys", SERVER_MOTD); - registerEnumIn(L, "configKeys", OWNER_NAME); - registerEnumIn(L, "configKeys", OWNER_EMAIL); - registerEnumIn(L, "configKeys", URL); - registerEnumIn(L, "configKeys", LOCATION); - registerEnumIn(L, "configKeys", IP); - registerEnumIn(L, "configKeys", WORLD_TYPE); - registerEnumIn(L, "configKeys", MYSQL_HOST); - registerEnumIn(L, "configKeys", MYSQL_USER); - registerEnumIn(L, "configKeys", MYSQL_PASS); - registerEnumIn(L, "configKeys", MYSQL_DB); - registerEnumIn(L, "configKeys", MYSQL_SOCK); - registerEnumIn(L, "configKeys", DEFAULT_PRIORITY); - registerEnumIn(L, "configKeys", MAP_AUTHOR); - registerEnumIn(L, "configKeys", STORE_IMAGES_URL); - registerEnumIn(L, "configKeys", PARTY_LIST_MAX_DISTANCE); - registerEnumIn(L, "configKeys", SQL_PORT); - registerEnumIn(L, "configKeys", MAX_PLAYERS); - registerEnumIn(L, "configKeys", PZ_LOCKED); - registerEnumIn(L, "configKeys", DEFAULT_DESPAWNRANGE); - registerEnumIn(L, "configKeys", PREY_ENABLED); - registerEnumIn(L, "configKeys", PREY_FREE_THIRD_SLOT); - registerEnumIn(L, "configKeys", PREY_REROLL_PRICE_LEVEL); - registerEnumIn(L, "configKeys", PREY_BONUS_TIME); - registerEnumIn(L, "configKeys", PREY_BONUS_REROLL_PRICE); - registerEnumIn(L, "configKeys", PREY_FREE_REROLL_TIME); - registerEnumIn(L, "configKeys", TASK_HUNTING_ENABLED); - registerEnumIn(L, "configKeys", TASK_HUNTING_FREE_THIRD_SLOT); - registerEnumIn(L, "configKeys", TASK_HUNTING_LIMIT_EXHAUST); - registerEnumIn(L, "configKeys", TASK_HUNTING_REROLL_PRICE_LEVEL); - registerEnumIn(L, "configKeys", TASK_HUNTING_SELECTION_LIST_PRICE); - registerEnumIn(L, "configKeys", TASK_HUNTING_BONUS_REROLL_PRICE); - registerEnumIn(L, "configKeys", TASK_HUNTING_FREE_REROLL_TIME); - registerEnumIn(L, "configKeys", DEFAULT_DESPAWNRADIUS); - registerEnumIn(L, "configKeys", RATE_EXPERIENCE); - registerEnumIn(L, "configKeys", RATE_SKILL); - registerEnumIn(L, "configKeys", RATE_LOOT); - registerEnumIn(L, "configKeys", RATE_MAGIC); - registerEnumIn(L, "configKeys", RATE_SPAWN); - registerEnumIn(L, "configKeys", RATE_KILLING_IN_THE_NAME_OF_POINTS); - registerEnumIn(L, "configKeys", HOUSE_PRICE_PER_SQM); - registerEnumIn(L, "configKeys", HOUSE_BUY_LEVEL); - registerEnumIn(L, "configKeys", HOUSE_LOSE_AFTER_INACTIVITY); - registerEnumIn(L, "configKeys", MAX_MESSAGEBUFFER); - registerEnumIn(L, "configKeys", ACTIONS_DELAY_INTERVAL); - registerEnumIn(L, "configKeys", EX_ACTIONS_DELAY_INTERVAL); - registerEnumIn(L, "configKeys", KICK_AFTER_MINUTES); - registerEnumIn(L, "configKeys", PROTECTION_LEVEL); - registerEnumIn(L, "configKeys", DEATH_LOSE_PERCENT); - registerEnumIn(L, "configKeys", STATUSQUERY_TIMEOUT); - registerEnumIn(L, "configKeys", FRAG_TIME); - registerEnumIn(L, "configKeys", WHITE_SKULL_TIME); - registerEnumIn(L, "configKeys", GAME_PORT); - registerEnumIn(L, "configKeys", LOGIN_PORT); - registerEnumIn(L, "configKeys", STATUS_PORT); - registerEnumIn(L, "configKeys", STAIRHOP_DELAY); - registerEnumIn(L, "configKeys", MARKET_OFFER_DURATION); - registerEnumIn(L, "configKeys", CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES); - registerEnumIn(L, "configKeys", MAX_MARKET_OFFERS_AT_A_TIME_PER_PLAYER); - registerEnumIn(L, "configKeys", EXP_FROM_PLAYERS_LEVEL_RANGE); - registerEnumIn(L, "configKeys", MAX_PACKETS_PER_SECOND); - registerEnumIn(L, "configKeys", STORE_COIN_PACKET); - registerEnumIn(L, "configKeys", DAY_KILLS_TO_RED); - registerEnumIn(L, "configKeys", WEEK_KILLS_TO_RED); - registerEnumIn(L, "configKeys", MONTH_KILLS_TO_RED); - registerEnumIn(L, "configKeys", RED_SKULL_DURATION); - registerEnumIn(L, "configKeys", BLACK_SKULL_DURATION); - registerEnumIn(L, "configKeys", ORANGE_SKULL_DURATION); - registerEnumIn(L, "configKeys", RATE_MONSTER_HEALTH); - registerEnumIn(L, "configKeys", RATE_MONSTER_ATTACK); - registerEnumIn(L, "configKeys", RATE_MONSTER_DEFENSE); - registerEnumIn(L, "configKeys", RATE_BOSS_HEALTH); - registerEnumIn(L, "configKeys", RATE_BOSS_ATTACK); - registerEnumIn(L, "configKeys", RATE_BOSS_DEFENSE); - registerEnumIn(L, "configKeys", BOSS_DEFAULT_TIME_TO_FIGHT_AGAIN); - registerEnumIn(L, "configKeys", BOSS_DEFAULT_TIME_TO_DEFEAT); - registerEnumIn(L, "configKeys", RATE_NPC_HEALTH); - registerEnumIn(L, "configKeys", RATE_NPC_ATTACK); - registerEnumIn(L, "configKeys", RATE_NPC_DEFENSE); - - registerEnumIn(L, "configKeys", RATE_HEALTH_REGEN); - registerEnumIn(L, "configKeys", RATE_HEALTH_REGEN_SPEED); - registerEnumIn(L, "configKeys", RATE_MANA_REGEN); - registerEnumIn(L, "configKeys", RATE_MANA_REGEN_SPEED); - registerEnumIn(L, "configKeys", RATE_SOUL_REGEN); - registerEnumIn(L, "configKeys", RATE_SOUL_REGEN_SPEED); - - registerEnumIn(L, "configKeys", RATE_SPELL_COOLDOWN); - registerEnumIn(L, "configKeys", RATE_ATTACK_SPEED); - registerEnumIn(L, "configKeys", RATE_OFFLINE_TRAINING_SPEED); - registerEnumIn(L, "configKeys", RATE_EXERCISE_TRAINING_SPEED); - - registerEnumIn(L, "configKeys", STAMINA_TRAINER); - registerEnumIn(L, "configKeys", STAMINA_PZ); - registerEnumIn(L, "configKeys", STAMINA_ORANGE_DELAY); - registerEnumIn(L, "configKeys", STAMINA_GREEN_DELAY); - registerEnumIn(L, "configKeys", STAMINA_TRAINER_DELAY); - registerEnumIn(L, "configKeys", STAMINA_PZ_GAIN); - registerEnumIn(L, "configKeys", STAMINA_TRAINER_GAIN); - registerEnumIn(L, "configKeys", SORT_LOOT_BY_CHANCE); - registerEnumIn(L, "configKeys", MAX_ALLOWED_ON_A_DUMMY); - - registerEnumIn(L, "configKeys", PUSH_WHEN_ATTACKING); - registerEnumIn(L, "configKeys", TOGGLE_SAVE_INTERVAL); - registerEnumIn(L, "configKeys", SAVE_INTERVAL_TYPE); - registerEnumIn(L, "configKeys", TOGGLE_SAVE_INTERVAL_CLEAN_MAP); - registerEnumIn(L, "configKeys", SAVE_INTERVAL_TIME); - registerEnumIn(L, "configKeys", RATE_USE_STAGES); - registerEnumIn(L, "configKeys", TOGGLE_IMBUEMENT_SHRINE_STORAGE); - registerEnumIn(L, "configKeys", TOGGLE_IMBUEMENT_NON_AGGRESSIVE_FIGHT_ONLY); - registerEnumIn(L, "configKeys", GLOBAL_SERVER_SAVE_TIME); - registerEnumIn(L, "configKeys", DATA_DIRECTORY); - registerEnumIn(L, "configKeys", CORE_DIRECTORY); - - registerEnumIn(L, "configKeys", FORGE_COST_ONE_SLIVER); - registerEnumIn(L, "configKeys", FORGE_SLIVER_AMOUNT); - registerEnumIn(L, "configKeys", FORGE_CORE_COST); - registerEnumIn(L, "configKeys", FORGE_MAX_DUST); - registerEnumIn(L, "configKeys", FORGE_FUSION_DUST_COST); - registerEnumIn(L, "configKeys", FORGE_TRANSFER_DUST_COST); - registerEnumIn(L, "configKeys", FORGE_BASE_SUCCESS_RATE); - registerEnumIn(L, "configKeys", FORGE_BONUS_SUCCESS_RATE); - registerEnumIn(L, "configKeys", FORGE_TIER_LOSS_REDUCTION); - registerEnumIn(L, "configKeys", FORGE_AMOUNT_MULTIPLIER); - registerEnumIn(L, "configKeys", FORGE_INFLUENCED_CREATURES_LIMIT); - - registerEnumIn(L, "configKeys", BESTIARY_KILL_MULTIPLIER); - registerEnumIn(L, "configKeys", BOSSTIARY_KILL_MULTIPLIER); - registerEnumIn(L, "configKeys", BOOSTED_BOSS_SLOT); - registerEnumIn(L, "configKeys", BOOSTED_BOSS_LOOT_BONUS); - registerEnumIn(L, "configKeys", BOOSTED_BOSS_KILL_BONUS); - registerEnumIn(L, "configKeys", BESTIARY_RATE_CHARM_SHOP_PRICE); - - registerEnumIn(L, "configKeys", FAMILIAR_TIME); - - registerEnumIn(L, "configKeys", TOGGLE_GOLD_POUCH_ALLOW_ANYTHING); - registerEnumIn(L, "configKeys", TOGGLE_SERVER_IS_RETRO); - registerEnumIn(L, "configKeys", TOGGLE_TRAVELS_FREE); - registerEnumIn(L, "configKeys", BUY_AOL_COMMAND_FEE); - registerEnumIn(L, "configKeys", BUY_BLESS_COMMAND_FEE); - registerEnumIn(L, "configKeys", TELEPORT_PLAYER_TO_VOCATION_ROOM); - - registerEnumIn(L, "configKeys", HAZARD_SPAWN_PLUNDER_MULTIPLIER); - registerEnumIn(L, "configKeys", HAZARD_CRITICAL_INTERVAL); - registerEnumIn(L, "configKeys", HAZARD_CRITICAL_CHANCE); - registerEnumIn(L, "configKeys", HAZARD_CRITICAL_MULTIPLIER); - registerEnumIn(L, "configKeys", HAZARD_DAMAGE_MULTIPLIER); - registerEnumIn(L, "configKeys", HAZARD_DODGE_MULTIPLIER); - registerEnumIn(L, "configKeys", HAZARD_PODS_DROP_MULTIPLIER); - registerEnumIn(L, "configKeys", HAZARD_PODS_TIME_TO_DAMAGE); - registerEnumIn(L, "configKeys", HAZARD_PODS_TIME_TO_SPAWN); - registerEnumIn(L, "configKeys", HAZARD_EXP_BONUS_MULTIPLIER); - registerEnumIn(L, "configKeys", HAZARD_LOOT_BONUS_MULTIPLIER); - registerEnumIn(L, "configKeys", HAZARD_PODS_DAMAGE); - registerEnumIn(L, "configKeys", TOGGLE_HAZARDSYSTEM); - registerEnumIn(L, "configKeys", LOW_LEVEL_BONUS_EXP); - - registerEnumIn(L, "configKeys", LOYALTY_ENABLED); - registerEnumIn(L, "configKeys", LOYALTY_POINTS_PER_CREATION_DAY); - registerEnumIn(L, "configKeys", LOYALTY_POINTS_PER_PREMIUM_DAY_SPENT); - registerEnumIn(L, "configKeys", LOYALTY_POINTS_PER_PREMIUM_DAY_PURCHASED); - registerEnumIn(L, "configKeys", LOYALTY_BONUS_PERCENTAGE_MULTIPLIER); - - registerEnumIn(L, "configKeys", PARTY_SHARE_LOOT_BOOSTS); - registerEnumIn(L, "configKeys", PARTY_SHARE_LOOT_BOOSTS_DIMINISHING_FACTOR); - registerEnumIn(L, "configKeys", TIBIADROME_CONCOCTION_COOLDOWN); - registerEnumIn(L, "configKeys", TIBIADROME_CONCOCTION_DURATION); - registerEnumIn(L, "configKeys", TIBIADROME_CONCOCTION_TICK_TYPE); - - registerEnumIn(L, "configKeys", AUTH_TYPE); - registerEnumIn(L, "configKeys", RESET_SESSIONS_ON_STARTUP); - - registerEnumIn(L, "configKeys", TOGGLE_ATTACK_SPEED_ONFIST); - registerEnumIn(L, "configKeys", MULTIPLIER_ATTACKONFIST); - registerEnumIn(L, "configKeys", MAX_SPEED_ATTACKONFIST); - - registerEnumIn(L, "configKeys", M_CONST); - registerEnumIn(L, "configKeys", T_CONST); - registerEnumIn(L, "configKeys", PARALLELISM); - - registerEnumIn(L, "configKeys", AUTOLOOT); - - registerEnumIn(L, "configKeys", VIP_SYSTEM_ENABLED); - registerEnumIn(L, "configKeys", VIP_BONUS_EXP); - registerEnumIn(L, "configKeys", VIP_BONUS_LOOT); - registerEnumIn(L, "configKeys", VIP_BONUS_SKILL); - registerEnumIn(L, "configKeys", VIP_AUTOLOOT_VIP_ONLY); - registerEnumIn(L, "configKeys", VIP_STAY_ONLINE); - registerEnumIn(L, "configKeys", VIP_FAMILIAR_TIME_COOLDOWN_REDUCTION); - - registerEnumIn(L, "configKeys", TOGGLE_HOUSE_TRANSFER_ON_SERVER_RESTART); - - registerEnumIn(L, "configKeys", TOGGLE_RECEIVE_REWARD); -#undef registerEnumIn + for (auto value : magic_enum::enum_values()) { + auto enumName = magic_enum::enum_name(value).data(); + if (enumName) { + registerMagicEnumIn(L, "configKeys", value); + g_logger().debug("Registering ConfigManager enum name '{}' value '{}' to lua", enumName, fmt::underlying(value)); + } + } +#undef registerMagicEnumIn } int ConfigFunctions::luaConfigManagerGetString(lua_State* L) { - pushString(L, g_configManager().getString(getNumber(L, -1))); + pushString(L, g_configManager().getString(getNumber(L, -1))); return 1; } int ConfigFunctions::luaConfigManagerGetNumber(lua_State* L) { - lua_pushnumber(L, g_configManager().getNumber(getNumber(L, -1))); + lua_pushnumber(L, g_configManager().getNumber(getNumber(L, -1))); return 1; } int ConfigFunctions::luaConfigManagerGetBoolean(lua_State* L) { - pushBoolean(L, g_configManager().getBoolean(getNumber(L, -1))); + pushBoolean(L, g_configManager().getBoolean(getNumber(L, -1))); return 1; } int ConfigFunctions::luaConfigManagerGetFloat(lua_State* L) { - lua_pushnumber(L, g_configManager().getFloat(getNumber(L, -1))); + lua_pushnumber(L, g_configManager().getFloat(getNumber(L, -1))); return 1; } diff --git a/src/lua/functions/core/game/lua_enums.cpp b/src/lua/functions/core/game/lua_enums.cpp index f462aaae26a..5d566920c7b 100644 --- a/src/lua/functions/core/game/lua_enums.cpp +++ b/src/lua/functions/core/game/lua_enums.cpp @@ -18,7 +18,7 @@ #include "declarations.hpp" #include "game/functions/game_reload.hpp" -#define registerEnumClass(luaState, enumClassType) \ +#define registerMagicEnum(luaState, enumClassType) \ { \ auto number = magic_enum::enum_integer(enumClassType); \ auto name = magic_enum::enum_name(enumClassType).data(); \ @@ -26,7 +26,7 @@ } \ void(0) -#define registerEnumClassNamespace(luaState, luaNamespace, enumClassType) \ +#define registerMagicEnumNamespace(luaState, luaNamespace, enumClassType) \ { \ auto number = magic_enum::enum_integer(enumClassType); \ auto name = std::string(luaNamespace) + magic_enum::enum_name(enumClassType).data(); \ @@ -277,7 +277,7 @@ void LuaEnums::initCombatEnums(lua_State* L) { void LuaEnums::initCombatParamEnums(lua_State* L) { for (auto value : magic_enum::enum_values()) { - registerEnumClass(L, value); + registerMagicEnum(L, value); } } @@ -443,7 +443,7 @@ void LuaEnums::initConditionParamEnums(lua_State* L) { void LuaEnums::initAttributeConditionSubIdEnums(lua_State* L) { std::string luaNamespace = "AttrSubId_"; for (auto value : magic_enum::enum_values()) { - registerEnumClassNamespace(L, luaNamespace, value); + registerMagicEnumNamespace(L, luaNamespace, value); } } @@ -849,7 +849,7 @@ void LuaEnums::initItemTypeEnums(lua_State* L) { void LuaEnums::initFluidEnums(lua_State* L) { for (auto value : magic_enum::enum_values()) { - registerEnumClass(L, value); + registerMagicEnum(L, value); } } @@ -920,22 +920,22 @@ void LuaEnums::initItemIdEnums(lua_State* L) { void LuaEnums::initPlayerFlagEnums(lua_State* L) { for (auto value : magic_enum::enum_values()) { - registerEnumClass(L, value); + registerMagicEnum(L, value); } } void LuaEnums::initCreatureIconEnums(lua_State* L) { std::string luaNamespace = "CreatureIconCategory_"; for (auto value : magic_enum::enum_values()) { - registerEnumClassNamespace(L, luaNamespace, value); + registerMagicEnumNamespace(L, luaNamespace, value); } luaNamespace = "CreatureIconModifications_"; for (auto value : magic_enum::enum_values()) { - registerEnumClassNamespace(L, luaNamespace, value); + registerMagicEnumNamespace(L, luaNamespace, value); } luaNamespace = "CreatureIconQuests_"; for (auto value : magic_enum::enum_values()) { - registerEnumClassNamespace(L, luaNamespace, value); + registerMagicEnumNamespace(L, luaNamespace, value); } } @@ -1205,7 +1205,7 @@ void LuaEnums::initReturnValueEnums(lua_State* L) { // Reload void LuaEnums::initReloadTypeEnums(lua_State* L) { for (auto value : magic_enum::enum_values()) { - registerEnumClass(L, value); + registerMagicEnum(L, value); } } @@ -1231,7 +1231,7 @@ void LuaEnums::initCreaturesEventEnums(lua_State* L) { void LuaEnums::initForgeEnums(lua_State* L) { for (auto value : magic_enum::enum_values()) { - registerEnumClass(L, value); + registerMagicEnum(L, value); } } @@ -1245,7 +1245,7 @@ void LuaEnums::initWebhookEnums(lua_State* L) { void LuaEnums::initBosstiaryEnums(lua_State* L) { for (auto value : magic_enum::enum_values()) { - registerEnumClass(L, value); + registerMagicEnum(L, value); } } @@ -1769,30 +1769,30 @@ void LuaEnums::initSoundEnums(lua_State* L) { void LuaEnums::initWheelEnums(lua_State* L) { std::string wheelNamespace = "WHEEL_INSTANT_"; for (auto value : magic_enum::enum_values()) { - registerEnumClassNamespace(L, wheelNamespace, value); + registerMagicEnumNamespace(L, wheelNamespace, value); } wheelNamespace = "WHEEL_STAGE_"; for (auto value : magic_enum::enum_values()) { - registerEnumClassNamespace(L, wheelNamespace, value); + registerMagicEnumNamespace(L, wheelNamespace, value); } wheelNamespace = "WHEEL_GRADE_"; for (auto value : magic_enum::enum_values()) { - registerEnumClassNamespace(L, wheelNamespace, value); + registerMagicEnumNamespace(L, wheelNamespace, value); } wheelNamespace = "WHEEL_AVATAR_SKILL_"; for (auto value : magic_enum::enum_values()) { - registerEnumClassNamespace(L, wheelNamespace, value); + registerMagicEnumNamespace(L, wheelNamespace, value); } wheelNamespace = "WHEEL_STAT_"; for (auto value : magic_enum::enum_values()) { - registerEnumClassNamespace(L, wheelNamespace, value); + registerMagicEnumNamespace(L, wheelNamespace, value); } wheelNamespace = "WHEEL_BOOST_"; for (auto value : magic_enum::enum_values()) { - registerEnumClassNamespace(L, wheelNamespace, value); + registerMagicEnumNamespace(L, wheelNamespace, value); } } diff --git a/src/pch.hpp b/src/pch.hpp index 0f5775b73d3..04f692a0bf1 100644 --- a/src/pch.hpp +++ b/src/pch.hpp @@ -95,7 +95,18 @@ #include "lua/global/shared_object.hpp" -// Magic Enum +/** + * @brief Magic Enum is a C++ library that facilitates easy conversion between enums and strings. + * By default, the range of supported enum values is from -128 to 128. We need extends that range. + * + * @def MAGIC_ENUM_RANGE_MIN + * @note Sets the lower limit of the enum value range to -500. + * + * @def MAGIC_ENUM_RANGE_MAX + * @note Sets the upper limit of the enum value range to 500. + */ +#define MAGIC_ENUM_RANGE_MIN -500 +#define MAGIC_ENUM_RANGE_MAX 500 #include // Memory Mapped File diff --git a/src/server/network/protocol/protocollogin.cpp b/src/server/network/protocol/protocollogin.cpp index 1f0a3b57ad9..4c905f58fcb 100644 --- a/src/server/network/protocol/protocollogin.cpp +++ b/src/server/network/protocol/protocollogin.cpp @@ -77,7 +77,7 @@ void ProtocolLogin::getCharacterList(const std::string &accountDescriptor, const output->addString(g_configManager().getString(SERVER_NAME)); output->addString(g_configManager().getString(IP)); - output->add(g_configManager().getShortNumber(GAME_PORT)); + output->add(g_configManager().getNumber(GAME_PORT)); output->addByte(0); diff --git a/src/server/network/protocol/protocolstatus.cpp b/src/server/network/protocol/protocolstatus.cpp index f07e5e85d18..40dcffa646b 100644 --- a/src/server/network/protocol/protocolstatus.cpp +++ b/src/server/network/protocol/protocolstatus.cpp @@ -82,7 +82,7 @@ void ProtocolStatus::sendStatusString() { uint64_t uptime = (OTSYS_TIME() - ProtocolStatus::start) / 1000; serverinfo.append_attribute("uptime") = std::to_string(uptime).c_str(); serverinfo.append_attribute("ip") = g_configManager().getString(IP).c_str(); - serverinfo.append_attribute("servername") = g_configManager().getString(stringConfig_t::SERVER_NAME).c_str(); + serverinfo.append_attribute("servername") = g_configManager().getString(ConfigKey_t::SERVER_NAME).c_str(); serverinfo.append_attribute("port") = std::to_string(g_configManager().getNumber(LOGIN_PORT)).c_str(); serverinfo.append_attribute("location") = g_configManager().getString(LOCATION).c_str(); serverinfo.append_attribute("url") = g_configManager().getString(URL).c_str(); @@ -154,7 +154,7 @@ void ProtocolStatus::sendInfo(uint16_t requestedInfo, const std::string &charact if (requestedInfo & REQUEST_BASIC_SERVER_INFO) { output->addByte(0x10); - output->addString(g_configManager().getString(stringConfig_t::SERVER_NAME)); + output->addString(g_configManager().getString(ConfigKey_t::SERVER_NAME)); output->addString(g_configManager().getString(IP)); output->addString(std::to_string(g_configManager().getNumber(LOGIN_PORT))); } From 0fe949c38d4635f4c7d2a2da560eb25326bf6d10 Mon Sep 17 00:00:00 2001 From: Renato Machado Date: Thu, 23 Nov 2023 14:39:21 -0300 Subject: [PATCH 14/18] perf: OTSYS_TIME cached (#1853) OTSYS_TIME is widely used, so let's cache it, its value is updated every dispatcher cycle. --- src/game/scheduling/dispatcher.cpp | 16 ++++++++-------- src/game/scheduling/dispatcher.hpp | 9 ++------- src/game/scheduling/task.cpp | 2 -- src/game/scheduling/task.hpp | 16 ++++++---------- src/utils/tools.cpp | 7 ++++++- src/utils/tools.hpp | 1 + 6 files changed, 23 insertions(+), 28 deletions(-) diff --git a/src/game/scheduling/dispatcher.cpp b/src/game/scheduling/dispatcher.cpp index a24ef770466..fff897def89 100644 --- a/src/game/scheduling/dispatcher.cpp +++ b/src/game/scheduling/dispatcher.cpp @@ -21,13 +21,13 @@ Dispatcher &Dispatcher::getInstance() { } void Dispatcher::init() { - updateClock(); + UPDATE_OTSYS_TIME(); threadPool.addLoad([this] { std::unique_lock asyncLock(dummyMutex); while (!threadPool.getIoContext().stopped()) { - updateClock(); + UPDATE_OTSYS_TIME(); executeEvents(); executeScheduledEvents(); @@ -104,7 +104,7 @@ void Dispatcher::executeScheduledEvents() { auto it = scheduledTasks.begin(); while (it != scheduledTasks.end()) { const auto &task = *it; - if (task->getTime() > Task::TIME_NOW) { + if (task->getTime() > OTSYS_TIME()) { break; } @@ -168,17 +168,17 @@ void Dispatcher::mergeEvents() { checkPendingTasks(); } -std::chrono::nanoseconds Dispatcher::timeUntilNextScheduledTask() const { - static constexpr auto CHRONO_NANO_0 = std::chrono::nanoseconds(0); - static constexpr auto CHRONO_MILI_MAX = std::chrono::milliseconds::max(); +std::chrono::milliseconds Dispatcher::timeUntilNextScheduledTask() const { + constexpr auto CHRONO_0 = std::chrono::milliseconds(0); + constexpr auto CHRONO_MILI_MAX = std::chrono::milliseconds::max(); if (scheduledTasks.empty()) { return CHRONO_MILI_MAX; } const auto &task = *scheduledTasks.begin(); - const auto timeRemaining = task->getTime() - Task::TIME_NOW; - return std::max(timeRemaining, CHRONO_NANO_0); + const auto timeRemaining = std::chrono::milliseconds(task->getTime() - OTSYS_TIME()); + return std::max(timeRemaining, CHRONO_0); } void Dispatcher::addEvent(std::function &&f, std::string_view context, uint32_t expiresAfterMs) { diff --git a/src/game/scheduling/dispatcher.hpp b/src/game/scheduling/dispatcher.hpp index 9d0a5c51750..1c5f72658c1 100644 --- a/src/game/scheduling/dispatcher.hpp +++ b/src/game/scheduling/dispatcher.hpp @@ -32,7 +32,7 @@ enum class DispatcherType : uint8_t { struct DispatcherContext { bool isOn() const { - return Task::TIME_NOW != SYSTEM_TIME_ZERO; + return OTSYS_TIME() != 0; } bool isGroup(const TaskGroup _group) const { @@ -134,11 +134,6 @@ class Dispatcher { private: thread_local static DispatcherContext dispacherContext; - // Update Time Cache - static void updateClock() { - Task::TIME_NOW = std::chrono::system_clock::now(); - } - const auto &getThreadTask() const { return threads[ThreadPool::getThreadId()]; } @@ -159,7 +154,7 @@ class Dispatcher { inline void executeSerialEvents(std::vector &tasks); inline void executeParallelEvents(std::vector &tasks, const uint8_t groupId); - inline std::chrono::nanoseconds timeUntilNextScheduledTask() const; + inline std::chrono::milliseconds timeUntilNextScheduledTask() const; inline void checkPendingTasks() { hasPendingTasks = false; diff --git a/src/game/scheduling/task.cpp b/src/game/scheduling/task.cpp index c9e6157ab9e..28418a4a169 100644 --- a/src/game/scheduling/task.cpp +++ b/src/game/scheduling/task.cpp @@ -10,8 +10,6 @@ #include "pch.hpp" #include "task.hpp" #include "lib/logging/log_with_spd_log.hpp" - -std::chrono::system_clock::time_point Task::TIME_NOW = SYSTEM_TIME_ZERO; std::atomic_uint_fast64_t Task::LAST_EVENT_ID = 0; bool Task::execute() const { diff --git a/src/game/scheduling/task.hpp b/src/game/scheduling/task.hpp index f42602242c5..18dfd5d39de 100644 --- a/src/game/scheduling/task.hpp +++ b/src/game/scheduling/task.hpp @@ -11,19 +11,15 @@ #include "utils/tools.hpp" #include -static constexpr auto SYSTEM_TIME_ZERO = std::chrono::system_clock::time_point(std::chrono::milliseconds(0)); - class Task { public: - static std::chrono::system_clock::time_point TIME_NOW; - Task(uint32_t expiresAfterMs, std::function &&f, std::string_view context) : - func(std::move(f)), context(context), utime(TIME_NOW), expiration(expiresAfterMs > 0 ? TIME_NOW + std::chrono::milliseconds(expiresAfterMs) : SYSTEM_TIME_ZERO) { + func(std::move(f)), context(context), utime(OTSYS_TIME()), expiration(expiresAfterMs > 0 ? OTSYS_TIME() + expiresAfterMs : 0) { assert(!this->context.empty() && "Context cannot be empty!"); } Task(std::function &&f, std::string_view context, uint32_t delay, bool cycle = false, bool log = true) : - func(std::move(f)), context(context), utime(TIME_NOW + std::chrono::milliseconds(delay)), delay(delay), cycle(cycle), log(log) { + func(std::move(f)), context(context), utime(OTSYS_TIME() + delay), delay(delay), cycle(cycle), log(log) { assert(!this->context.empty() && "Context cannot be empty!"); } @@ -54,7 +50,7 @@ class Task { } bool hasExpired() const { - return expiration != SYSTEM_TIME_ZERO && expiration < TIME_NOW; + return expiration != 0 && expiration < OTSYS_TIME(); } bool isCycle() const { @@ -75,7 +71,7 @@ class Task { static std::atomic_uint_fast64_t LAST_EVENT_ID; void updateTime() { - utime = TIME_NOW + std::chrono::milliseconds(delay); + utime = OTSYS_TIME() + delay; } bool hasTraceableContext() const { @@ -117,8 +113,8 @@ class Task { std::function func = nullptr; std::string_view context; - std::chrono::system_clock::time_point utime = SYSTEM_TIME_ZERO; - std::chrono::system_clock::time_point expiration = SYSTEM_TIME_ZERO; + int64_t utime = 0; + int64_t expiration = 0; uint64_t id = 0; uint32_t delay = 0; diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp index a4b9f8d31db..ce42c04ad50 100644 --- a/src/utils/tools.cpp +++ b/src/utils/tools.cpp @@ -1454,8 +1454,13 @@ const char* getReturnMessage(ReturnValue value) { } } +int64_t OTSYSTIME = 0; +void UPDATE_OTSYS_TIME() { + OTSYSTIME = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); +} + int64_t OTSYS_TIME() { - return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + return OTSYSTIME; } SpellGroup_t stringToSpellGroup(const std::string &value) { diff --git a/src/utils/tools.hpp b/src/utils/tools.hpp index 099f77afc67..580a962c9e4 100644 --- a/src/utils/tools.hpp +++ b/src/utils/tools.hpp @@ -135,6 +135,7 @@ bool isCaskItem(uint16_t itemId); std::string getObjectCategoryName(ObjectCategory_t category); int64_t OTSYS_TIME(); +void UPDATE_OTSYS_TIME(); SpellGroup_t stringToSpellGroup(const std::string &value); From b9cf8e186ed41bad7cde09559bde7393c33c5a86 Mon Sep 17 00:00:00 2001 From: sebbesiren <35768829+sebbesiren@users.noreply.github.com> Date: Thu, 23 Nov 2023 19:29:44 +0100 Subject: [PATCH 15/18] fix: hazard level up (#1883) Fixes a typo in level up function as well as simplified levelUp function. Also set maxLevel base as 1 to handle nil values in getPlayerMaxLevel --- data/libs/hazard_lib.lua | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/data/libs/hazard_lib.lua b/data/libs/hazard_lib.lua index d10650a81b6..c13f120f740 100644 --- a/data/libs/hazard_lib.lua +++ b/data/libs/hazard_lib.lua @@ -88,30 +88,22 @@ function Hazard:getPlayerMaxLevel(player) local fromStorage = player:getStorageValue(self.storageMax) return fromStorage <= 0 and 1 or fromStorage end - local fromKV = player:kv():scoped(self.name):get("maxLevel") + local fromKV = player:kv():scoped(self.name):get("maxLevel") or 1 + return fromKV <= 0 and 1 or fromKV end function Hazard:levelUp(player) - if self.storageMax and self.storageCurrent then - local current = self:getPlayerCurrentLevel(player) - local max = self:getPlayerMaxLevel(player) - if current == max then - self:setPlayerMaxLevel(player, max + 1) - end - return - end - - local current = player:kv(self.name):get("currentLevel") - local max = player:kv(self.name):get("maxLevel") + local current = self:getPlayerCurrentLevel(player) + local max = self:getPlayerMaxLevel(player) if current == max then - player:kv(self.name):set("maxLevel", max + 1) + self:setPlayerMaxLevel(player, max + 1) end end function Hazard:setPlayerMaxLevel(player, level) - if level > self.maxLevelLevel then - level = self.maxLevelLevel + if level > self.maxLevel then + level = self.maxLevel end if self.storageMax then From 2bb39d322745dc551e414e65cfdadb16aab95d37 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Thu, 23 Nov 2023 15:42:11 -0300 Subject: [PATCH 16/18] fix: wrong getNumber param (#1889) ConfigManager::getNumber only accept config enum --- data-canary/scripts/movements/tiles.lua | 2 +- data-otservbr-global/scripts/movements/others/tiles.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data-canary/scripts/movements/tiles.lua b/data-canary/scripts/movements/tiles.lua index 8deccc1823a..5f0f74e2c80 100644 --- a/data-canary/scripts/movements/tiles.lua +++ b/data-canary/scripts/movements/tiles.lua @@ -33,7 +33,7 @@ function tile.onStepIn(creature, item, position, fromPosition) local depotItem = playerPosition:getTile():getItemByType(ITEM_TYPE_DEPOT) if depotItem ~= nil then local depotItems = 0 - for id = 1, configManager.getNumber("depotBoxes") do + for id = 1, configManager.getNumber(configKeys.DEPOT_BOXES) do depotItems = depotItems + player:getDepotChest(id, true):getItemHoldingCount() end player:sendTextMessage(MESSAGE_FAILURE, "Your depot contains " .. depotItems .. " item" .. (depotItems > 1 and "s." or ".") .. "\ diff --git a/data-otservbr-global/scripts/movements/others/tiles.lua b/data-otservbr-global/scripts/movements/others/tiles.lua index 9773d74d18a..b797f0380a3 100644 --- a/data-otservbr-global/scripts/movements/others/tiles.lua +++ b/data-otservbr-global/scripts/movements/others/tiles.lua @@ -33,7 +33,7 @@ function tiles.onStepIn(creature, item, position, fromPosition) if depotItem ~= nil then local depotItems = 0 - for id = 1, configManager.getNumber("depotBoxes") do + for id = 1, configManager.getNumber(configKeys.DEPOT_BOXES) do depotItems = depotItems + player:getDepotChest(id, true):getItemHoldingCount() end From 60dd5bd2bc5e5b9ab1afa1a33e875084570f8af0 Mon Sep 17 00:00:00 2001 From: Karin Date: Thu, 23 Nov 2023 16:05:01 -0300 Subject: [PATCH 17/18] fix: moving loot pouch to stash (#1885) --- src/game/game.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/game/game.cpp b/src/game/game.cpp index 5aa0e6eecca..8b2489c3df6 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -1625,6 +1625,10 @@ ReturnValue Game::checkMoveItemToCylinder(std::shared_ptr player, std::s isValidMoveItem = true; } + if (item->getID() == ITEM_GOLD_POUCH) { + isValidMoveItem = true; + } + if (!isValidMoveItem) { return RETURNVALUE_NOTPOSSIBLE; } From 45e622d5c3c9a8aa2931214bb449b3a8d1c782d5 Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Thu, 23 Nov 2023 11:51:12 -0800 Subject: [PATCH 18/18] fix: switch target when unreachable/untarggetable (#1890) --- src/creatures/monsters/monster.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index b2660933a27..0446addb0c6 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -773,7 +773,9 @@ void Monster::onThink(uint32_t interval) { } } else if (!targetList.empty()) { const bool attackedCreatureIsDisconnected = attackedCreature && attackedCreature->getPlayer() && attackedCreature->getPlayer()->isDisconnected(); - if (!attackedCreature || attackedCreatureIsDisconnected) { + const bool attackedCreatureIsUnattackable = attackedCreature && !canUseAttack(getPosition(), attackedCreature); + const bool attackedCreatureIsUnreachable = targetDistance <= 1 && attackedCreature && followCreature && !hasFollowPath; + if (!attackedCreature || attackedCreatureIsDisconnected || attackedCreatureIsUnattackable || attackedCreatureIsUnreachable) { if (!followCreature || !hasFollowPath || attackedCreatureIsDisconnected) { searchTarget(TARGETSEARCH_NEAREST); } else if (attackedCreature && isFleeing() && !canUseAttack(getPosition(), attackedCreature)) {