diff --git a/data/items/items.xml b/data/items/items.xml index b1a1c6b003f..765c6e9db51 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -34797,7 +34797,7 @@ - + diff --git a/data/modules/scripts/gamestore/init.lua b/data/modules/scripts/gamestore/init.lua index a927715cd93..6146f562307 100644 --- a/data/modules/scripts/gamestore/init.lua +++ b/data/modules/scripts/gamestore/init.lua @@ -402,7 +402,7 @@ function parseBuyStoreOffer(playerId, msg) -- All guarding conditions under which the offer should not be processed must be included here if - (table.contains(GameStore.OfferTypes, offer.type) == false) -- we've got an invalid offer type + not table.contains(GameStore.OfferTypes, offer.type) -- we've got an invalid offer type or not player or (player:getVocation():getId() == 0) and (not GameStore.haveOfferRook(id)) -- we don't have such offer or not offer @@ -620,7 +620,7 @@ end function Player.canBuyOffer(self, offer) local playerId = self:getId() local disabled, disabledReason = 0, "" - if offer.disabled == true or not offer.type then + if offer.disabled or not offer.type then disabled = 1 end @@ -692,8 +692,7 @@ function Player.canBuyOffer(self, offer) disabledReason = "The offer is fake." end elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_MOUNT then - local hasMount = self:hasMount(offer.id) - if hasMount == true then + if self:hasMount(offer.id) then disabled = 1 disabledReason = "You already have this mount." end @@ -723,12 +722,11 @@ function Player.canBuyOffer(self, offer) disabledReason = "You already have 3 slots released." end elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_EXPBOOST then - local remainingBoost = self:getExpBoostStamina() if self:getStorageValue(GameStore.Storages.expBoostCount) == 6 then disabled = 1 disabledReason = "You can't buy XP Boost for today." end - if remainingBoost > 0 then + if self:getExpBoostStamina() > 0 then disabled = 1 disabledReason = "You already have an active XP boost." end @@ -786,9 +784,9 @@ function Player.canReceiveStoreItems(self, offerId, offerCount) local inboxItems = inbox:getItems(true) local slotsOccupied = #inboxItems local maxCapacity = inbox:getMaxCapacity() - local slotsAvailable = maxCapacity - slotsOccupied if slotsOccupied + slotsNeeded > maxCapacity then + local slotsAvailable = maxCapacity - slotsOccupied return false, string.format("Not enough free slots in your store inbox. You need %d more slot(s). Currently occupied: %d/%d", slotsNeeded - slotsAvailable, slotsOccupied, maxCapacity) end @@ -1104,14 +1102,16 @@ function sendStoreTransactionHistory(playerId, page, entriesPerPage) if not player then return false end - local oldProtocol = player:getClient().version < 1200 - local totalEntries = GameStore.retrieveHistoryTotalPages(player:getAccountId()) - local totalPages = math.ceil(totalEntries / entriesPerPage) + local entries = GameStore.retrieveHistoryEntries(player:getAccountId(), page, entriesPerPage) -- this makes everything easy! if #entries == 0 then return addPlayerEvent(sendStoreError, 250, playerId, GameStore.StoreErrors.STORE_ERROR_HISTORY, "You don't have any entries yet.") end + local oldProtocol = player:getClient().version < 1200 + local totalEntries = GameStore.retrieveHistoryTotalPages(player:getAccountId()) + local totalPages = math.ceil(totalEntries / entriesPerPage) + local msg = NetworkMessage() msg:addByte(GameStore.SendingPackets.S_OpenTransactionHistory) msg:addU32(totalPages > 0 and page - 1 or 0x0) -- current page @@ -1165,10 +1165,8 @@ function sendStoreError(playerId, errorType, message) local msg = NetworkMessage() msg:addByte(GameStore.SendingPackets.S_StoreError) - msg:addByte(errorType) msg:addString(message, "sendStoreError - message") - msg:sendToPlayer(player) end @@ -1183,7 +1181,7 @@ function sendStoreBalanceUpdating(playerId, updating) msg:addByte(0x00) msg:sendToPlayer(player) - if updating == true then + if updating then sendUpdatedStoreBalances(playerId) end end @@ -1194,7 +1192,6 @@ function sendUpdatedStoreBalances(playerId) return false end - local oldProtocol = player:getClient().version < 1200 local msg = NetworkMessage() msg:addByte(GameStore.SendingPackets.S_CoinBalanceUpdating) msg:addByte(0x01) @@ -1205,6 +1202,8 @@ function sendUpdatedStoreBalances(playerId) -- Send total of coins (transferable and normal coin) msg:addU32(player:getTibiaCoins()) msg:addU32(player:getTransferableCoins()) -- How many are Transferable + + local oldProtocol = player:getClient().version < 1200 if not oldProtocol then -- How many are reserved for a Character Auction -- We currently do not have this system implemented, so we will send 0 @@ -1346,7 +1345,7 @@ end GameStore.retrieveHistoryTotalPages = function(accountId) local resultId = db.storeQuery("SELECT count(id) as total FROM store_history WHERE account_id = " .. accountId) - if resultId == false then + if not resultId then return 0 end @@ -1360,7 +1359,7 @@ GameStore.retrieveHistoryEntries = function(accountId, currentPage, entriesPerPa local offset = currentPage > 1 and entriesPerPage * (currentPage - 1) or 0 local resultId = db.storeQuery("SELECT * FROM `store_history` WHERE `account_id` = " .. accountId .. " ORDER BY `time` DESC LIMIT " .. offset .. ", " .. entriesPerPage .. ";") - if resultId ~= false then + if resultId then repeat local entry = { mode = Result.getNumber(resultId, "mode"), @@ -1602,7 +1601,7 @@ function GameStore.processStackablePurchase(player, offerId, offerCount, offerNa local countToAdd = math.min(remainingCount, stackSize) local inboxItem = inbox:addItem(offerId, countToAdd) if inboxItem then - if movable ~= true then + if not movable then inboxItem:setAttribute(ITEM_ATTRIBUTE_STORE, systemTime()) end else @@ -1631,21 +1630,32 @@ function GameStore.processHouseRelatedPurchase(player, offer) local inbox = player:getStoreInbox() if inbox then for _, itemId in ipairs(itemIds) do - for i = 1, offer.count do + if isCaskItem(itemId) then local decoKit = inbox:addItem(ITEM_DECORATION_KIT, 1) if decoKit then decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "You bought this item in the Store.\nUnwrap it in your own house to create a <" .. ItemType(itemId):getName() .. ">.") decoKit:setCustomAttribute("unWrapId", itemId) - if isCaskItem(itemId) then - decoKit:setAttribute(ITEM_ATTRIBUTE_DATE, offer.count) - end + decoKit:setAttribute(ITEM_ATTRIBUTE_DATE, offer.count) - if offer.movable ~= true then + if not offer.movable then 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) + if decoKit then + decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "You bought this item in the Store.\nUnwrap it in your own house to create a <" .. ItemType(itemId):getName() .. ">.") + decoKit:setCustomAttribute("unWrapId", itemId) + + if not offer.movable then + decoKit:setAttribute(ITEM_ATTRIBUTE_STORE, systemTime()) + end + end + player:sendUpdateContainer(inbox) + end end - player:sendUpdateContainer(inbox) end end end @@ -1689,8 +1699,6 @@ function GameStore.processMountPurchase(player, offerId) end function GameStore.processNameChangePurchase(player, offer, productType, newName) - local playerId = player:getId() - if productType == GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE then local tile = Tile(player:getPosition()) if tile then @@ -1720,11 +1728,11 @@ function GameStore.processNameChangePurchase(player, offer, productType, newName else message = "Your character has been renamed successfully." end - addPlayerEvent(sendStorePurchaseSuccessful, 500, playerId, message) + addPlayerEvent(sendStorePurchaseSuccessful, 500, player:getId(), message) player:changeName(newName) else - return addPlayerEvent(sendRequestPurchaseData, 250, playerId, offer.id, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE) + return addPlayerEvent(sendRequestPurchaseData, 250, player:getId(), offer.id, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE) end end @@ -1780,9 +1788,6 @@ function GameStore.processTempleTeleportPurchase(player) end function GameStore.processHirelingPurchase(player, offer, productType, hirelingName, chosenSex) - local playerId = player:getId() - local offerId = offer.id - if player:getClient().version < 1200 then return error({ code = 1, message = "You cannot buy hirelings on client 10, please relog on client 12 and try again." }) end @@ -1804,7 +1809,7 @@ function GameStore.processHirelingPurchase(player, offer, productType, hirelingN player:makeCoinTransaction(offer, hirelingName) local message = "You have successfully bought " .. hirelingName - return addPlayerEvent(sendStorePurchaseSuccessful, 650, playerId, message) + return addPlayerEvent(sendStorePurchaseSuccessful, 650, player:getId(), message) -- If not, we ask him to do! else if player:getHirelingsCount() >= 10 then @@ -1812,14 +1817,50 @@ function GameStore.processHirelingPurchase(player, offer, productType, hirelingN end -- TODO: Use the correct dialog (byte 0xDB) on client 1205+ -- for compatibility, request name using the change name dialog - return addPlayerEvent(sendRequestPurchaseData, 250, playerId, offerId, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_HIRELING) + return addPlayerEvent(sendRequestPurchaseData, 250, player:getId(), offer.id, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_HIRELING) end end -function GameStore.processHirelingChangeNamePurchase(player, offer, productType, newHirelingName) - local playerId = player:getId() - local offerId = offer.id +-- Hireling Helpers +local function HandleHirelingNameChange(playerId, offer, newHirelingName) + local player = Player(playerId) + if not player then + return + end + + local functionCallback = function(playerIdInFunction, data, hireling) + local playerInFunction = Player(playerIdInFunction) + if not playerInFunction then + return + end + + if not hireling then + return playerInFunction:showInfoModal("Error", "Your must select a hireling.") + end + if hireling.active > 0 then + return playerInFunction:showInfoModal("Error", "Your hireling must be inside his/her lamp.") + end + + local oldName = hireling.name + hireling.name = data.newHirelingName + + if not playerInFunction:makeCoinTransaction(data.offer, oldName .. " to " .. hireling.name) then + return playerInFunction:showInfoModal("Error", "Transaction error") + end + + local lamp = playerInFunction:findHirelingLamp(hireling:getId()) + if lamp then + lamp:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "This mysterious lamp summons your very own personal hireling.\nThis item cannot be traded.\nThis magic lamp is the home of " .. hireling:getName() .. ".") + end + logger.debug("{} has been renamed to {}", oldName, hireling.name) + sendUpdatedStoreBalances(playerIdInFunction) + end + + player:sendHirelingSelectionModal("Choose a Hireling", "Select a hireling below", functionCallback, { offer = offer, newHirelingName = newHirelingName }) +end + +function GameStore.processHirelingChangeNamePurchase(player, offer, productType, newHirelingName) if player:getClient().version < 1200 then return error({ code = 1, @@ -1838,17 +1879,60 @@ function GameStore.processHirelingChangeNamePurchase(player, offer, productType, end) local message = "Close the store window to select which hireling should be renamed to " .. newHirelingName + local playerId = player:getId() addPlayerEvent(sendStorePurchaseSuccessful, 200, playerId, message) - addPlayerEvent(HandleHirelingNameChange, 550, playerId, offer, newHirelingName) else - return addPlayerEvent(sendRequestPurchaseData, 250, playerId, offerId, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE) + return addPlayerEvent(sendRequestPurchaseData, 250, player:getId(), offer.id, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE) end end -function GameStore.processHirelingChangeSexPurchase(player, offer) - local playerId = player:getId() +local function HandleHirelingSexChange(playerId, offer) + local player = Player(playerId) + if not player then + return + end + + local functionCallback = function(playerIdInFunction, data, hireling) + local playerInFunction = Player(playerIdInFunction) + if not playerInFunction then + return + end + + if not hireling then + return playerInFunction:showInfoModal("Error", "Your must select a hireling.") + end + if hireling.active > 0 then + return playerInFunction:showInfoModal("Error", "Your hireling must be inside his/her lamp.") + end + + if not playerInFunction:makeCoinTransaction(data.offer, hireling:getName()) then + return playerInFunction:showInfoModal("Error", "Transaction error") + end + + local changeTo, sexString, lookType + if hireling.sex == HIRELING_SEX.FEMALE then + changeTo = HIRELING_SEX.MALE + sexString = "male" + lookType = HIRELING_OUTFIT_DEFAULT.male + else + changeTo = HIRELING_SEX.FEMALE + sexString = "female" + lookType = HIRELING_OUTFIT_DEFAULT.female + end + + hireling.sex = changeTo + hireling.looktype = lookType + + logger.debug("{} sex was changed to {}", hireling:getName(), sexString) + sendUpdatedStoreBalances(playerIdInFunction) + end + + player:sendHirelingSelectionModal("Choose a Hireling", "Select a hireling below", functionCallback, { offer = offer }) +end + +function GameStore.processHirelingChangeSexPurchase(player, offer) if player:getClient().version < 1200 then return error({ code = 1, @@ -1857,8 +1941,8 @@ function GameStore.processHirelingChangeSexPurchase(player, offer) end local message = "Close the store window to select which hireling should have the sex changed." + local playerId = player:getId() addPlayerEvent(sendStorePurchaseSuccessful, 200, playerId, message) - addPlayerEvent(HandleHirelingSexChange, 550, playerId, offer) end @@ -2154,75 +2238,3 @@ function Player:openStore(serviceName) --exporting the method so other scripts c addPlayerEvent(sendShowStoreOffers, 50, playerId, category) end end - --- Hireling Helpers -function HandleHirelingNameChange(playerId, offer, newHirelingName) - local player = Player(playerId) - - local cb = function(playerId, data, hireling) - local offer = data.offer - local newHirelingName = data.newHirelingName - local player = Player(playerId) - if not hireling then - return player:showInfoModal("Error", "Your must select a hireling.") - end - - if hireling.active > 0 then - return player:showInfoModal("Error", "Your hireling must be inside his/her lamp.") - end - - local oldName = hireling.name - hireling.name = newHirelingName - - if not player:makeCoinTransaction(data.offer, oldName .. " to " .. newHirelingName) then - return player:showInfoModal("Error", "Transaction error") - end - - local lamp = player:findHirelingLamp(hireling:getId()) - if lamp then - lamp:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "This mysterious lamp summons your very own personal hireling.\nThis item cannot be traded.\nThis magic lamp is the home of " .. hireling:getName() .. ".") - end - logger.debug("{} has been renamed to {}", oldName, newHirelingName) - sendUpdatedStoreBalances(playerId) - end - - player:sendHirelingSelectionModal("Choose a Hireling", "Select a hireling below", cb, { offer = offer, newHirelingName = newHirelingName }) -end - -function HandleHirelingSexChange(playerId, offer) - local player = Player(playerId) - - local cb = function(playerId, data, hireling) - local player = Player(playerId) - if not hireling then - return player:showInfoModal("Error", "Your must select a hireling.") - end - - if hireling.active > 0 then - return player:showInfoModal("Error", "Your hireling must be inside his/her lamp.") - end - - if not player:makeCoinTransaction(data.offer, hireling:getName()) then - return player:showInfoModal("Error", "Transaction error") - end - - local changeTo, sexString, lookType - if hireling.sex == HIRELING_SEX.FEMALE then - changeTo = HIRELING_SEX.MALE - sexString = "male" - lookType = HIRELING_OUTFIT_DEFAULT.male - else - changeTo = HIRELING_SEX.FEMALE - sexString = "female" - lookType = HIRELING_OUTFIT_DEFAULT.female - end - - hireling.sex = changeTo - hireling.looktype = lookType - - logger.debug("{} sex was changed to {}", hireling:getName(), sexString) - sendUpdatedStoreBalances(playerId) - end - - player:sendHirelingSelectionModal("Choose a Hireling", "Select a hireling below", cb, { offer = offer }) -end diff --git a/src/game/game.cpp b/src/game/game.cpp index 9310f1c9a20..cac0b596ebf 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -2616,12 +2616,10 @@ std::shared_ptr Game::transformItem(std::shared_ptr item, uint16_t n auto decaying = item->getDecaying(); // If the item is decaying, we need to transform it to the new item - if (decaying > DECAYING_FALSE && item->getDuration() <= 1) { + if (decaying > DECAYING_FALSE && item->getDuration() <= 1 && newType.decayTo) { g_logger().debug("Decay duration old type {}, transformEquipTo {}, transformDeEquipTo {}", curType.decayTo, curType.transformEquipTo, curType.transformDeEquipTo); - g_logger().debug("Decay duration new type, decayTo {}, transformEquipTo {}, transformDeEquipTo {}", newType.decayTo, newType.transformEquipTo, newType.transformDeEquipTo); - if (newType.decayTo) { - itemId = newType.decayTo; - } + g_logger().debug("Decay duration new type decayTo {}, transformEquipTo {}, transformDeEquipTo {}", newType.decayTo, newType.transformEquipTo, newType.transformDeEquipTo); + itemId = newType.decayTo; } else if (curType.id != newType.id) { if (newType.group != curType.group) { item->setDefaultSubtype(); diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 218fc857d3c..0d0d8e79f17 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -5021,7 +5021,7 @@ void ProtocolGame::sendOpenForge() { getForgeInfoMap(item, receiveTierItemMap); } if (itemClassification == 4) { - getForgeInfoMap(item, convergenceItemsMap[item->getSlotPosition()]); + getForgeInfoMap(item, convergenceItemsMap[item->getClassification()]); } } }