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()]);
}
}
}