From 955fddea27c32c67710bdb51da1bd6c78423fba0 Mon Sep 17 00:00:00 2001 From: Karin Date: Fri, 19 Apr 2024 17:26:31 -0300 Subject: [PATCH 01/12] fix: block gold pouch using in the obtain method (#2559) --- src/game/game.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/game/game.cpp b/src/game/game.cpp index cac0b596ebf..a11fde62c2c 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -5384,6 +5384,11 @@ void Game::playerSetManagedContainer(uint32_t playerId, ObjectCategory_t categor return; } + if (container->getID() == ITEM_GOLD_POUCH && !isLootContainer) { + player->sendTextMessage(MESSAGE_FAILURE, "You can only set the gold pouch as a loot container."); + return; + } + if (container->getHoldingPlayer() != player) { player->sendCancelMessage("You must be holding the container to set it as a loot container."); return; From b73df39ce9832e5c000906e8c757272ca7ce02e7 Mon Sep 17 00:00:00 2001 From: Eduardo Augusto <38956084+duuh30@users.noreply.github.com> Date: Mon, 22 Apr 2024 12:56:23 -0300 Subject: [PATCH 02/12] fix: two handed weapons (#2570) --- src/creatures/players/player.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 75e5b5ef2af..618ffea1081 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -3271,13 +3271,6 @@ ReturnValue Player::queryAdd(int32_t index, const std::shared_ptr &thing, case CONST_SLOT_LEFT: { if (item->isQuiver()) { ret = RETURNVALUE_CANNOTBEDRESSED; - } else if (slotPosition & SLOTP_LEFT) { - WeaponType_t type = item->getWeaponType(); - if (type == WEAPON_NONE || type == WEAPON_SHIELD || type == WEAPON_AMMO) { - ret = RETURNVALUE_CANNOTBEDRESSED; - } else { - ret = RETURNVALUE_NOERROR; - } } else if (slotPosition & SLOTP_TWO_HAND) { if (inventory[CONST_SLOT_RIGHT]) { WeaponType_t type = item->getWeaponType(); @@ -3290,6 +3283,13 @@ ReturnValue Player::queryAdd(int32_t index, const std::shared_ptr &thing, } else { ret = RETURNVALUE_NOERROR; } + } else if (slotPosition & SLOTP_LEFT) { + WeaponType_t type = item->getWeaponType(); + if (type == WEAPON_NONE || type == WEAPON_SHIELD || type == WEAPON_AMMO) { + ret = RETURNVALUE_CANNOTBEDRESSED; + } else { + ret = RETURNVALUE_NOERROR; + } } else if (inventory[CONST_SLOT_RIGHT]) { std::shared_ptr rightItem = inventory[CONST_SLOT_RIGHT]; WeaponType_t type = item->getWeaponType(), rightType = rightItem->getWeaponType(); From 3fcf5856002a021820f1aec2341108bf5e2b04c7 Mon Sep 17 00:00:00 2001 From: Elson Costa Date: Tue, 23 Apr 2024 09:07:35 -0300 Subject: [PATCH 03/12] fix: improves on gamestore, fix on gitignore and items (#2415) --- .gitignore | 12 +++-- data/items/items.xml | 4 +- data/modules/scripts/gamestore/gamestore.lua | 2 +- data/modules/scripts/gamestore/init.lua | 46 +++++++++++--------- src/game/game.cpp | 8 ++-- 5 files changed, 38 insertions(+), 34 deletions(-) diff --git a/.gitignore b/.gitignore index d233ae17409..a73a8c3022c 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,9 @@ bld/ [Ll]og/ build/ vcproj/ +!vcproj/canary.sln +!vcproj/canary.vcxproj +!vcproj/settings.props # Visual Studio 2015/2017 cache/options directory .vs/ @@ -372,9 +375,7 @@ config.lua config_canary.lua client_assertions.txt .env -otservbr.otbm -canary.otbm -otservbr-custom.otbm +data-otservbr-global/world/otservbr.otbm # Extensions *.ini @@ -382,9 +383,6 @@ otservbr-custom.otbm *.exe *.manifest *.rar -*-house.xml -*-monster.xml -*-npc.xml monster_count.txt # SFTP for Sublime @@ -398,4 +396,4 @@ canary.old vcpkg_installed # CLION -cmake-build-debug* +cmake-build-* diff --git a/data/items/items.xml b/data/items/items.xml index 765c6e9db51..0bc1c782452 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -75427,7 +75427,7 @@ Granted by TibiaGoals.com"/> - + @@ -75455,7 +75455,7 @@ Granted by TibiaGoals.com"/> - + diff --git a/data/modules/scripts/gamestore/gamestore.lua b/data/modules/scripts/gamestore/gamestore.lua index 852bd4d4405..f000c4079af 100644 --- a/data/modules/scripts/gamestore/gamestore.lua +++ b/data/modules/scripts/gamestore/gamestore.lua @@ -6770,7 +6770,7 @@ for k, category in ipairs(GameStore.Categories) do offer.type = GameStore.OfferTypes.OFFER_TYPE_NONE end if not offer.coinType then - offer.coinType = GameStore.CoinType.Coin + offer.coinType = GameStore.CoinType.Transferable end end end diff --git a/data/modules/scripts/gamestore/init.lua b/data/modules/scripts/gamestore/init.lua index 6146f562307..e53bab813f0 100644 --- a/data/modules/scripts/gamestore/init.lua +++ b/data/modules/scripts/gamestore/init.lua @@ -206,6 +206,13 @@ GameStore.DefaultDescriptions = { TEMPLE = { "Need a quick way home? Buy this transportation service to get instantly teleported to your home temple. \n\nNote, you cannot use this service while having a battle sign or a protection zone block. Further, the service will not work in no-logout zones or close to your home temple." }, } +GameStore.ItemLimit = { + PREY_WILDCARD = 50, + INSTANT_REWARD_ACCESS = 90, + EXPBOOST = 6, + HIRELING = 10, +} + --==Parsing==-- GameStore.isItsPacket = function(byte) for k, v in pairs(GameStore.RecivedPackets) do @@ -507,8 +514,8 @@ function parseBuyStoreOffer(playerId, msg) if not pcallOk then local alertMessage = pcallError.code and pcallError.message or "Something went wrong. Your purchase has been cancelled." - if not pcallError.code then -- unhandled error - -- log some debugging info + -- unhandled error + if not pcallError.code then logger.warn("[parseBuyStoreOffer] - Purchase failed due to an unhandled script error. Stacktrace: {}", pcallError) end @@ -618,7 +625,6 @@ function sendOfferDescription(player, offerId, description) end function Player.canBuyOffer(self, offer) - local playerId = self:getId() local disabled, disabledReason = 0, "" if offer.disabled or not offer.type then disabled = 1 @@ -697,12 +703,12 @@ function Player.canBuyOffer(self, offer) disabledReason = "You already have this mount." end elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_INSTANT_REWARD_ACCESS then - if self:getCollectionTokens() >= 90 then + if self:getCollectionTokens() >= GameStore.ItemLimit.INSTANT_REWARD_ACCESS then disabled = 1 disabledReason = "You already have maximum of reward tokens." end elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_PREYBONUS then - if self:getPreyCards() >= 50 then + if self:getPreyCards() >= GameStore.ItemLimit.PREY_WILDCARD then disabled = 1 disabledReason = "You already have maximum of prey wildcards." end @@ -722,7 +728,7 @@ function Player.canBuyOffer(self, offer) disabledReason = "You already have 3 slots released." end elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_EXPBOOST then - if self:getStorageValue(GameStore.Storages.expBoostCount) == 6 then + if self:getStorageValue(GameStore.Storages.expBoostCount) == GameStore.ItemLimit.EXPBOOST then disabled = 1 disabledReason = "You can't buy XP Boost for today." end @@ -731,7 +737,7 @@ function Player.canBuyOffer(self, offer) disabledReason = "You already have an active XP boost." end elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HIRELING then - if self:getHirelingsCount() >= 10 then + if self:getHirelingsCount() >= GameStore.ItemLimit.HIRELING then disabled = 1 disabledReason = "You already have bought the maximum number of allowed hirelings." end @@ -1565,8 +1571,9 @@ function GameStore.processAllBlessingsPurchase(player, count) end function GameStore.processInstantRewardAccess(player, offerCount) - if player:getCollectionTokens() + offerCount >= 91 then - return error({ code = 1, message = "You cannot own more than 90 reward tokens." }) + local limit = GameStore.ItemLimit.INSTANT_REWARD_ACCESS + if player:getCollectionTokens() + offerCount >= limit + 1 then + return error({ code = 1, message = "You cannot own more than " .. limit .. " reward tokens." }) end player:setCollectionTokens(player:getCollectionTokens() + offerCount) end @@ -1641,7 +1648,6 @@ function GameStore.processHouseRelatedPurchase(player, offer) decoKit:setAttribute(ITEM_ATTRIBUTE_STORE, systemTime()) end end - player:sendUpdateContainer(inbox) else for i = 1, offer.count do local decoKit = inbox:addItem(ITEM_DECORATION_KIT, 1) @@ -1653,10 +1659,10 @@ function GameStore.processHouseRelatedPurchase(player, offer) decoKit:setAttribute(ITEM_ATTRIBUTE_STORE, systemTime()) end end - player:sendUpdateContainer(inbox) end end end + player:sendUpdateContainer(inbox) end end @@ -1677,11 +1683,7 @@ function GameStore.processOutfitPurchase(player, offerSexIdTable, addon) elseif player:hasOutfit(looktype, _addon) then return error({ code = 0, message = "You already own this outfit." }) else - if - not (player:addOutfitAddon(looktype, _addon)) -- TFS call failed - or (not player:hasOutfit(looktype, _addon)) -- Additional check; if the looktype doesn't match player sex for example, - -- then the TFS check will still pass... bug? (TODO) - then + if not player:addOutfitAddon(looktype, _addon) or not player:hasOutfit(looktype, _addon) then error({ code = 0, message = "There has been an issue with your outfit purchase. Your purchase has been cancelled." }) else player:addOutfitAddon(offerSexIdTable.male, _addon) @@ -1769,8 +1771,9 @@ function GameStore.processTaskHuntingThirdSlot(player) end function GameStore.processPreyBonusReroll(player, offerCount) - if player:getPreyCards() + offerCount >= 51 then - return error({ code = 1, message = "You cannot own more than 50 prey wildcards." }) + local limit = GameStore.ItemLimit.PREY_WILDCARD + if player:getPreyCards() + offerCount >= limit + 1 then + return error({ code = 1, message = "You cannot own more than " .. limit .. " prey wildcards." }) end player:addPreyCards(offerCount) end @@ -1812,8 +1815,8 @@ function GameStore.processHirelingPurchase(player, offer, productType, hirelingN return addPlayerEvent(sendStorePurchaseSuccessful, 650, player:getId(), message) -- If not, we ask him to do! else - if player:getHirelingsCount() >= 10 then - return error({ code = 1, message = "You cannot have more than 10 hirelings." }) + if player:getHirelingsCount() >= GameStore.ItemLimit.HIRELING then + return error({ code = 1, message = "You cannot have more than " .. GameStore.ItemLimit.HIRELING .. " hirelings." }) end -- TODO: Use the correct dialog (byte 0xDB) on client 1205+ -- for compatibility, request name using the change name dialog @@ -2219,7 +2222,8 @@ function sendHomePage(playerId) msg:sendToPlayer(player) end -function Player:openStore(serviceName) --exporting the method so other scripts can use to open store +--exporting the method so other scripts can use to open store +function Player:openStore(serviceName) local playerId = self:getId() openStore(playerId) diff --git a/src/game/game.cpp b/src/game/game.cpp index a11fde62c2c..0dfba6f6724 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -7841,9 +7841,11 @@ void Game::addBestiaryList(uint16_t raceid, std::string name) { } void Game::broadcastMessage(const std::string &text, MessageClasses type) const { - g_logger().info("Broadcasted message: {}", text); - for (const auto &it : players) { - it.second->sendTextMessage(type, text); + if (!text.empty()) { + g_logger().info("Broadcasted message: {}", text); + for (const auto &it : players) { + it.second->sendTextMessage(type, text); + } } } From bd72f6618e11221e2d8b8d58c3436e34e44fe0d9 Mon Sep 17 00:00:00 2001 From: Felipe Paluco <87909998+FelipePaluco@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:20:33 -0300 Subject: [PATCH 04/12] fix: transcendence applies correct status without unlocked stages (#2566) Fix the logic to ensure correct status application when using tiered legs and undergoing transcendence without any avatar stages unlocked. Remove the early return that blocks code execution in this scenario. Ensure proper application of damage reduction, 100% critical chance, and 15% critical damage. --- src/creatures/players/player.cpp | 2 +- src/creatures/players/wheel/player_wheel.cpp | 24 +++++++++++-------- .../players/wheel/wheel_definitions.hpp | 5 ++-- .../creatures/player/player_functions.cpp | 4 ++-- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 618ffea1081..bcb31a4abfa 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -6688,7 +6688,7 @@ void Player::triggerTranscendance() { outfit.lookType = getVocation()->getAvatarLookType(); outfitCondition->setOutfit(outfit); addCondition(outfitCondition); - wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR, OTSYS_TIME() + duration); + wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR_FORGE, OTSYS_TIME() + duration); g_game().addMagicEffect(getPosition(), CONST_ME_AVATAR_APPEAR); sendTextMessage(MESSAGE_ATTENTION, "Transcendance was triggered."); sendSkills(); diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index 77a15f7cbf7..20e8f683bf9 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -2410,21 +2410,25 @@ int32_t PlayerWheel::checkBattleHealingAmount() const { } int32_t PlayerWheel::checkAvatarSkill(WheelAvatarSkill_t skill) const { - if (skill == WheelAvatarSkill_t::NONE || getOnThinkTimer(WheelOnThink_t::AVATAR) <= OTSYS_TIME()) { + if (skill == WheelAvatarSkill_t::NONE || (getOnThinkTimer(WheelOnThink_t::AVATAR_SPELL) <= OTSYS_TIME() && getOnThinkTimer(WheelOnThink_t::AVATAR_FORGE) <= OTSYS_TIME())) { return 0; } uint8_t stage = 0; - if (getInstant("Avatar of Light")) { - stage = getStage(WheelStage_t::AVATAR_OF_LIGHT); - } else if (getInstant("Avatar of Steel")) { - stage = getStage(WheelStage_t::AVATAR_OF_STEEL); - } else if (getInstant("Avatar of Nature")) { - stage = getStage(WheelStage_t::AVATAR_OF_NATURE); - } else if (getInstant("Avatar of Storm")) { - stage = getStage(WheelStage_t::AVATAR_OF_STORM); + if (getOnThinkTimer(WheelOnThink_t::AVATAR_SPELL) > OTSYS_TIME()) { + if (getInstant("Avatar of Light")) { + stage = getStage(WheelStage_t::AVATAR_OF_LIGHT); + } else if (getInstant("Avatar of Steel")) { + stage = getStage(WheelStage_t::AVATAR_OF_STEEL); + } else if (getInstant("Avatar of Nature")) { + stage = getStage(WheelStage_t::AVATAR_OF_NATURE); + } else if (getInstant("Avatar of Storm")) { + stage = getStage(WheelStage_t::AVATAR_OF_STORM); + } else { + return 0; + } } else { - return 0; + stage = 3; } if (skill == WheelAvatarSkill_t::DAMAGE_REDUCTION) { diff --git a/src/creatures/players/wheel/wheel_definitions.hpp b/src/creatures/players/wheel/wheel_definitions.hpp index 8432e9dca21..c23d2adf53f 100644 --- a/src/creatures/players/wheel/wheel_definitions.hpp +++ b/src/creatures/players/wheel/wheel_definitions.hpp @@ -105,9 +105,10 @@ enum class WheelOnThink_t : uint8_t { FOCUS_MASTERY = 4, GIFT_OF_LIFE = 5, DIVINE_EMPOWERMENT = 6, - AVATAR = 7, + AVATAR_SPELL = 7, + AVATAR_FORGE = 8, - TOTAL_COUNT = 8 + TOTAL_COUNT = 9 }; enum class WheelStat_t : uint8_t { diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 3ba2c43841d..621023e5c52 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -3997,9 +3997,9 @@ int PlayerFunctions::luaPlayerAvatarTimer(lua_State* L) { } if (lua_gettop(L) == 1) { - lua_pushnumber(L, (lua_Number)player->wheel()->getOnThinkTimer(WheelOnThink_t::AVATAR)); + lua_pushnumber(L, (lua_Number)player->wheel()->getOnThinkTimer(WheelOnThink_t::AVATAR_SPELL)); } else { - player->wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR, getNumber(L, 2)); + player->wheel()->setOnThinkTimer(WheelOnThink_t::AVATAR_SPELL, getNumber(L, 2)); pushBoolean(L, true); } return 1; From e60612e9ad6da36c2de1824e597e5c3b6efd7615 Mon Sep 17 00:00:00 2001 From: Karin Date: Thu, 25 Apr 2024 10:12:36 -0300 Subject: [PATCH 05/12] fix: alana sio only to "aleta som" users (#2564) --- data/scripts/spells/house/kick.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/data/scripts/spells/house/kick.lua b/data/scripts/spells/house/kick.lua index b4b583c1007..265ac48f796 100644 --- a/data/scripts/spells/house/kick.lua +++ b/data/scripts/spells/house/kick.lua @@ -4,6 +4,7 @@ function spell.onCastSpell(player, variant) local targetPlayer = Player(variant:getString()) or player local guest = targetPlayer:getTile():getHouse() local owner = player:getTile():getHouse() + -- Owner kick yourself from house if targetPlayer == player then player:getPosition():sendMagicEffect(CONST_ME_POFF) @@ -11,6 +12,13 @@ function spell.onCastSpell(player, variant) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) return true end + + if not owner:canEditAccessList(GUEST_LIST, player) then + player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE) + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + if not owner or not guest or not guest:kickPlayer(player, targetPlayer) then player:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE) player:getPosition():sendMagicEffect(CONST_ME_POFF) From 72794c40f1d13ffe64613cf67bae6c7bb1208281 Mon Sep 17 00:00:00 2001 From: Karin Date: Thu, 25 Apr 2024 10:12:52 -0300 Subject: [PATCH 06/12] fix: destroy field is working inside pz (#2558) --- data/scripts/runes/destroy_field_rune.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/data/scripts/runes/destroy_field_rune.lua b/data/scripts/runes/destroy_field_rune.lua index 024dbc1bcd7..8752be7eb51 100644 --- a/data/scripts/runes/destroy_field_rune.lua +++ b/data/scripts/runes/destroy_field_rune.lua @@ -4,6 +4,13 @@ local fields = { 105, 2118, 2119, 2120, 2121, 2122, 2123, 2124, 2125, 2126, 2132 local rune = Spell("rune") function rune.onCastSpell(creature, variant, isHotkey) + local inPz = creature:getTile():hasFlag(TILESTATE_PROTECTIONZONE) + if inPz then + creature:sendCancelMessage(RETURNVALUE_NOTPOSSIBLE) + creature:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + local position = Variant.getPosition(variant) local tile = Tile(position) local field = tile and tile:getItemByType(ITEM_TYPE_MAGICFIELD) From 69b25aa431c869e8758a2624fd9dd18de8508b87 Mon Sep 17 00:00:00 2001 From: Karin Date: Thu, 25 Apr 2024 10:13:12 -0300 Subject: [PATCH 07/12] fix: stamina not recovering when dead (#2557) --- src/creatures/players/player.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index bcb31a4abfa..39a8841fdf2 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -2918,6 +2918,7 @@ bool Player::spawn() { getParent()->postAddNotification(static_self_cast(), nullptr, 0); g_game().addCreatureCheck(static_self_cast()); g_game().addPlayer(static_self_cast()); + static_self_cast()->onChangeZone(static_self_cast()->getZoneType()); return true; } @@ -4673,6 +4674,8 @@ void Player::onPlacedCreature() { removePlayer(true); } + this->onChangeZone(this->getZoneType()); + sendUnjustifiedPoints(); } From 38651ca103ee8674bdd6bcb7a994e2cea2220afd Mon Sep 17 00:00:00 2001 From: Aluisio Penna Date: Thu, 25 Apr 2024 10:15:52 -0300 Subject: [PATCH 08/12] fix: kill count of each type of minotaur in 'Turmoil of War' quest log (#2569) --- .../quests/killing_in_the_name_of/monster_kill.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua b/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua index b2ba681d251..f8f7ddc5c6b 100644 --- a/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua +++ b/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua @@ -57,7 +57,7 @@ function deathEvent.onDeath(creature, _corpse, _lastHitKiller, mostDamageKiller) end end -- Minotaurs - killCheck(player, targetName, Storage.KillingInTheNameOf.BudrikMinos, 0, tasks.Budrik[1].creatures, nil, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.MinotaurCount) + killCheck(player, targetName, Storage.KillingInTheNameOf.BudrikMinos, 0, tasks.Budrik[1].creatures, Storage.Quest.U8_5.KillingInTheNameOf.AltKillCount.MinotaurCount, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.MinotaurCount) -- Necromancers and Priestesses killCheck(player, targetName, Storage.KillingInTheNameOf.LugriNecromancers, 0, tasks.Lugri[1].creatures, Storage.Quest.U8_5.KillingInTheNameOf.AltKillCount.NecromancerCount, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.NecromancerCount) killCheck(player, targetName, Storage.KillingInTheNameOf.LugriNecromancers, 3, tasks.Lugri[1].creatures, Storage.Quest.U8_5.KillingInTheNameOf.AltKillCount.NecromancerCount, Storage.Quest.U8_5.KillingInTheNameOf.MonsterKillCount.NecromancerCount) From dd8cdde213f4258f8f577462d12622bb88872e3d Mon Sep 17 00:00:00 2001 From: svetrey <51045033+svetrey@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:17:02 +0100 Subject: [PATCH 09/12] fix: diamond arrow static attack points (#2560) --- data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua b/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua index 61984209287..36be8dc38b4 100644 --- a/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua +++ b/data-otservbr-global/scripts/weapons/scripts/diamond_arrow.lua @@ -16,7 +16,7 @@ combat:setParameter(COMBAT_PARAM_BLOCKARMOR, true) function onGetFormulaValues(player, skill, attack, factor) local distanceSkill = player:getEffectiveSkillLevel(SKILL_DISTANCE) local min = (player:getLevel() / 5) - local max = (0.09 * factor) * distanceSkill * 37 + (player:getLevel() / 5) + local max = (0.09 * factor) * distanceSkill * attack + (player:getLevel() / 5) return -min, -max end From 0f91db0e379714e20652fda85714a4259e7a2bba Mon Sep 17 00:00:00 2001 From: Jeswill David Bolivar Mendoza <76903590+jeswilldbm@users.noreply.github.com> Date: Thu, 25 Apr 2024 15:17:53 +0200 Subject: [PATCH 10/12] fix: missing attribute in bone fiddle (#2531) --- data/items/items.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/items/items.xml b/data/items/items.xml index 0bc1c782452..421e981ad02 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -53105,6 +53105,9 @@ hands of its owner. Granted by TibiaRoyal.com"/> + + + From 13798e59702e8de713ae8f78fded17e2e5de35e2 Mon Sep 17 00:00:00 2001 From: Marco Date: Thu, 25 Apr 2024 10:18:20 -0300 Subject: [PATCH 11/12] feat: include day count in getTimeInWords function (#2525) --- data/libs/functions/functions.lua | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/data/libs/functions/functions.lua b/data/libs/functions/functions.lua index 9eb1b0b57b5..c2349ce7fd6 100644 --- a/data/libs/functions/functions.lua +++ b/data/libs/functions/functions.lua @@ -67,17 +67,28 @@ end function getTimeInWords(secsParam) local secs = tonumber(secsParam) + local days = math.floor(secs / (24 * 3600)) + secs = secs - (days * 24 * 3600) local hours, minutes, seconds = getHours(secs), getMinutes(secs), getSeconds(secs) local timeStr = "" + if days > 0 then + timeStr = days .. (days > 1 and " days" or " day") + end + if hours > 0 then - timeStr = hours .. (hours > 1 and " hours" or " hour") + if timeStr ~= "" then + timeStr = timeStr .. ", " + end + + timeStr = timeStr .. hours .. (hours > 1 and " hours" or " hour") end if minutes > 0 then if timeStr ~= "" then timeStr = timeStr .. ", " end + timeStr = timeStr .. minutes .. (minutes > 1 and " minutes" or " minute") end @@ -85,9 +96,9 @@ function getTimeInWords(secsParam) if timeStr ~= "" then timeStr = timeStr .. " and " end + timeStr = timeStr .. seconds .. (seconds > 1 and " seconds" or " second") end - return timeStr end From 09984296e7172f550d3889670205bc7f0bda5bca Mon Sep 17 00:00:00 2001 From: Elson Costa Date: Thu, 25 Apr 2024 10:18:39 -0300 Subject: [PATCH 12/12] feat: playerOnWalk event callback and afk talkaction (#2518) --- data/scripts/eventcallbacks/README.md | 1 + data/scripts/talkactions/gm/afk.lua | 94 +++++++++++++++++++++ src/creatures/players/player.cpp | 2 + src/lua/callbacks/callbacks_definitions.hpp | 1 + src/lua/callbacks/event_callback.cpp | 23 +++++ src/lua/callbacks/event_callback.hpp | 1 + 6 files changed, 122 insertions(+) create mode 100644 data/scripts/talkactions/gm/afk.lua diff --git a/data/scripts/eventcallbacks/README.md b/data/scripts/eventcallbacks/README.md index bdafcb41b33..601653574bd 100644 --- a/data/scripts/eventcallbacks/README.md +++ b/data/scripts/eventcallbacks/README.md @@ -47,6 +47,7 @@ Event callbacks are available for several categories of game entities, such as ` - `(void)` `playerOnCombat` - `(void)` `playerOnInventoryUpdate` - `(bool)` `playerOnRotateItem` +- `(void)` `playerOnWalk` - `(void)` `monsterOnDropLoot` - `(void)` `monsterPostDropLoot` - `(void)` `monsterOnSpawn` diff --git a/data/scripts/talkactions/gm/afk.lua b/data/scripts/talkactions/gm/afk.lua new file mode 100644 index 00000000000..6167a2b6068 --- /dev/null +++ b/data/scripts/talkactions/gm/afk.lua @@ -0,0 +1,94 @@ +local afk = TalkAction("/afk") + +playersAFKs = {} + +local function checkIsAFK(id) + for index, item in pairs(playersAFKs) do + if id == item.id then + return { afk = true, index = index } + end + end + return { afk = false } +end + +local function showAfkMessage(playerPosition) + local spectators = Game.getSpectators(playerPosition, false, true, 8, 8, 8, 8) + if #spectators > 0 then + for _, spectator in ipairs(spectators) do + spectator:say("AFK !", TALKTYPE_MONSTER_SAY, false, spectator, playerPosition) + end + end +end + +function afk.onSay(player, words, param) + if param == "" then + player:sendCancelMessage("You need to specify on/off param.") + return true + end + + local id, playerPosition = player:getId(), player:getPosition() + local isAfk = checkIsAFK(id) + if param == "on" then + if isAfk.afk then + player:sendCancelMessage("You are already AFK!") + return true + end + + table.insert(playersAFKs, { id = id, position = playerPosition }) + if player:isInGhostMode() then + player:setGhostMode(false) + end + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are now AFK!") + playerPosition:sendMagicEffect(CONST_ME_REDSMOKE) + showAfkMessage(playerPosition) + elseif param == "off" then + if isAfk.afk then + table.remove(playersAFKs, isAfk.index) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are no longer AFK!") + playerPosition:sendMagicEffect(CONST_ME_REDSMOKE) + end + end + + return true +end + +afk:separator(" ") +afk:groupType("gamemaster") +afk:register() + +------------------ AFK Effect Message ------------------ +local afkEffect = GlobalEvent("GodAfkEffect") +function afkEffect.onThink(interval) + for _, player in ipairs(playersAFKs) do + showAfkMessage(player.position) + end + return true +end + +afkEffect:interval(5000) +afkEffect:register() + +------------------ Stop AFK Message when moves ------------------ +local callback = EventCallback() +function callback.playerOnWalk(player, creature, creaturePos, toPos) + local isAfk = checkIsAFK(player:getId()) + if isAfk.afk then + table.remove(playersAFKs, isAfk.index) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are no longer AFK!") + end + return true +end + +callback:register() + +------------------ Player Logout ------------------ +local godAfkLogout = CreatureEvent("GodAfkLogout") +function godAfkLogout.onLogout(player) + local isAfk = checkIsAFK(player:getId()) + if isAfk.afk then + table.remove(playersAFKs, isAfk.index) + end + return true +end + +godAfkLogout:register() diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 39a8841fdf2..7e53f476683 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -1926,6 +1926,8 @@ void Player::onWalk(Direction &dir) { Creature::onWalk(dir); setNextActionTask(nullptr); setNextAction(OTSYS_TIME() + getStepDuration(dir)); + + g_callbacks().executeCallback(EventCallback_t::playerOnWalk, &EventCallback::playerOnWalk, getPlayer(), dir); } void Player::onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) { diff --git a/src/lua/callbacks/callbacks_definitions.hpp b/src/lua/callbacks/callbacks_definitions.hpp index 521a2c4cda7..3b8016f5f5b 100644 --- a/src/lua/callbacks/callbacks_definitions.hpp +++ b/src/lua/callbacks/callbacks_definitions.hpp @@ -56,6 +56,7 @@ enum class EventCallback_t : uint16_t { playerOnCombat, playerOnInventoryUpdate, playerOnRotateItem, + playerOnWalk, // Monster monsterOnDropLoot, monsterPostDropLoot, diff --git a/src/lua/callbacks/event_callback.cpp b/src/lua/callbacks/event_callback.cpp index 51c03131fa9..d2b88c30f3a 100644 --- a/src/lua/callbacks/event_callback.cpp +++ b/src/lua/callbacks/event_callback.cpp @@ -985,6 +985,29 @@ bool EventCallback::playerOnRotateItem(std::shared_ptr player, std::shar return getScriptInterface()->callFunction(3); } +void EventCallback::playerOnWalk(std::shared_ptr player, Direction &dir) const { + if (!getScriptInterface()->reserveScriptEnv()) { + g_logger().error("[EventCallback::eventOnWalk - " + "Player {}] " + "Call stack overflow. Too many lua script calls being nested.", + player->getName()); + return; + } + + ScriptEnvironment* scriptEnvironment = getScriptInterface()->getScriptEnv(); + scriptEnvironment->setScriptId(getScriptId(), getScriptInterface()); + + lua_State* L = getScriptInterface()->getLuaState(); + getScriptInterface()->pushFunction(getScriptId()); + + LuaScriptInterface::pushUserdata(L, player); + LuaScriptInterface::setMetatable(L, -1, "Player"); + + lua_pushnumber(L, dir); + + getScriptInterface()->callVoidFunction(2); +} + void EventCallback::playerOnStorageUpdate(std::shared_ptr player, const uint32_t key, const int32_t value, int32_t oldValue, uint64_t currentTime) const { if (!getScriptInterface()->reserveScriptEnv()) { g_logger().error("[EventCallback::eventOnStorageUpdate - " diff --git a/src/lua/callbacks/event_callback.hpp b/src/lua/callbacks/event_callback.hpp index d9dc4a9b110..9141235a028 100644 --- a/src/lua/callbacks/event_callback.hpp +++ b/src/lua/callbacks/event_callback.hpp @@ -116,6 +116,7 @@ class EventCallback : public Script { void playerOnCombat(std::shared_ptr player, std::shared_ptr target, std::shared_ptr item, CombatDamage &damage) const; void playerOnInventoryUpdate(std::shared_ptr player, std::shared_ptr item, Slots_t slot, bool equip) const; bool playerOnRotateItem(std::shared_ptr player, std::shared_ptr item, const Position &position) const; + void playerOnWalk(std::shared_ptr player, Direction &dir) const; // Monster void monsterOnDropLoot(std::shared_ptr monster, std::shared_ptr corpse) const;