From 3daacfded91c1a143e18e044b4ed08395738ebd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo?= Date: Thu, 16 Oct 2025 14:16:41 -0300 Subject: [PATCH 1/6] fix npc sell loot drop gold on ground This PR fixes an issue where players selling items without enough capacity or backpack space causes the gold to drop on the ground. --- src/creatures/npcs/npc.cpp | 55 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index 1ff6f34d5..347d591bd 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -559,6 +559,61 @@ void Npc::onPlayerSellItem(const std::shared_ptr &player, uint16_t itemI return; } + // Pre-check: compute how many items can be sold in this call (eligible) without removing yet + uint32_t eligibleCount = 0; + for (const auto &item : player->getInventoryItemsFromId(itemId, ignore)) { + if (!item || item->getTier() > 0 || item->hasImbuements()) { + continue; + } + if (const auto &container = item->getContainer()) { + if (container->size() > 0) { + continue; + } + } + if (parent && item->getParent() != parent) { + continue; + } + if (!item->hasMarketAttributes()) { + continue; + } + eligibleCount += item->getItemCount(); + if (eligibleCount >= amount) { + break; + } + } + + const uint32_t willRemove = std::min(amount, eligibleCount); + if (willRemove == 0) { + return; + } + + // Capacity and backpack space check for gold payouts (when not using autobank) + if (getCurrency() == ITEM_GOLD_COIN && !g_configManager().getBoolean(AUTOBANK)) { + const uint64_t prospectiveTotal = static_cast(sellPrice) * static_cast(willRemove); + uint32_t crystalCoins = static_cast(prospectiveTotal / 10000); + uint32_t remainder = static_cast(prospectiveTotal % 10000); + uint32_t platinumCoins = remainder / 100; + uint32_t goldCoins = remainder % 100; + + // Number of stacks that will be created (each stack up to 100) + auto stacksNeeded = static_cast((crystalCoins + 99) / 100 + (platinumCoins + 99) / 100 + (goldCoins + 99) / 100); + const uint16_t freeSlots = player->getFreeBackpackSlots(); + if (stacksNeeded > 0 && freeSlots < stacksNeeded) { + player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM); + return; + } + + // Capacity check (approximate by coin unit weights) + const uint32_t goldWeight = Item::items[ITEM_GOLD_COIN].weight; + const uint32_t platWeight = Item::items[ITEM_PLATINUM_COIN].weight; + const uint32_t crysWeight = Item::items[ITEM_CRYSTAL_COIN].weight; + const uint64_t totalWeight = static_cast(goldCoins) * goldWeight + static_cast(platinumCoins) * platWeight + static_cast(crystalCoins) * crysWeight; + if (player->getFreeCapacity() < totalWeight) { + player->sendCancelMessage(RETURNVALUE_NOTENOUGHCAPACITY); + return; + } + } + auto toRemove = amount; for (const auto &item : player->getInventoryItemsFromId(itemId, ignore)) { if (!item || item->getTier() > 0 || item->hasImbuements()) { From 8cafcdcc6fbc5de51104d49da0e977b2a2ef24d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo?= Date: Sun, 26 Oct 2025 11:47:27 -0300 Subject: [PATCH 2/6] fix: update loot message --- src/creatures/npcs/npc.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index 347d591bd..c9b7f7fd4 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -516,11 +516,16 @@ void Npc::onPlayerSellAllLoot(uint32_t playerId, uint16_t itemId, bool ignore, u auto ss = std::stringstream(); if (!madeProgress) { if (totalPrice == 0) { - ss << "You have no sellable items in your loot pouch."; - player->sendTextMessage(MESSAGE_FAILURE, ss.str()); + if (preSize > 0) { + ss << "You don't have enough space. Free up space in your bag."; + player->sendTextMessage(MESSAGE_FAILURE, ss.str()); + } else { + ss << "You have no sellable items in your loot pouch."; + player->sendTextMessage(MESSAGE_FAILURE, ss.str()); + } } else { ss << "Finished selling. Some items in your loot pouch could not be sold."; - player->sendTextMessage(MESSAGE_LOOK, ss.str()); + player->sendTextMessage(MESSAGE_ADMINISTRATOR, ss.str()); } } else { if (totalPrice == 0) { From 8c47a5313620527d87f26d7b31d6bc3957e05dc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo?= Date: Sun, 26 Oct 2025 13:27:07 -0300 Subject: [PATCH 3/6] fix: mesage --- src/creatures/npcs/npc.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index c9b7f7fd4..00bf7f0e3 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -524,8 +524,11 @@ void Npc::onPlayerSellAllLoot(uint32_t playerId, uint16_t itemId, bool ignore, u player->sendTextMessage(MESSAGE_FAILURE, ss.str()); } } else { - ss << "Finished selling. Some items in your loot pouch could not be sold."; + ss << "Sale stopped. Some items in your loot bag could not be sold. Make sure you have enough space in your bag."; player->sendTextMessage(MESSAGE_ADMINISTRATOR, ss.str()); + ss.clear(); + ss << "You sold all of the items from your loot pouch for " << totalPrice << " gold."; + player->sendTextMessage(MESSAGE_LOOK, ss.str()); } } else { if (totalPrice == 0) { From a4e633bda5d2b8394d041526af2cff4d997e7618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo?= Date: Sun, 26 Oct 2025 13:29:30 -0300 Subject: [PATCH 4/6] Update npc.cpp --- src/creatures/npcs/npc.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index 00bf7f0e3..3a33cb613 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -526,6 +526,7 @@ void Npc::onPlayerSellAllLoot(uint32_t playerId, uint16_t itemId, bool ignore, u } else { ss << "Sale stopped. Some items in your loot bag could not be sold. Make sure you have enough space in your bag."; player->sendTextMessage(MESSAGE_ADMINISTRATOR, ss.str()); + ss.str(""); ss.clear(); ss << "You sold all of the items from your loot pouch for " << totalPrice << " gold."; player->sendTextMessage(MESSAGE_LOOK, ss.str()); From 1cee8f415af7ecdabee1806aad0bac9991b538a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo?= Date: Tue, 4 Nov 2025 18:54:22 -0300 Subject: [PATCH 5/6] fix: loot messages --- src/creatures/npcs/npc.cpp | 44 ++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index 3a33cb613..34ba86f1b 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -458,6 +458,39 @@ void Npc::onPlayerSellAllLoot(uint32_t playerId, uint16_t itemId, bool ignore, u const auto preSize = container->size(); const uint64_t preTotal = totalPrice; + bool hasSellable = false; + const auto &shopVector = getShopItemVector(player->getGUID()); + for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) { + const auto &item = *it; + if (!item) { + continue; + } + uint32_t sellPriceCandidate = 0; + const ItemType &itemType = Item::items[item->getID()]; + for (const ShopBlock &shopBlock : shopVector) { + if (itemType.id == shopBlock.itemId && shopBlock.itemSellPrice != 0) { + sellPriceCandidate = shopBlock.itemSellPrice; + break; + } + } + if (sellPriceCandidate == 0) { + continue; + } + if (item->getTier() > 0 || item->hasImbuements()) { + continue; + } + if (const auto &child = item->getContainer()) { + if (child->size() > 0) { + continue; + } + } + if (!item->hasMarketAttributes()) { + continue; + } + hasSellable = true; + break; + } + phmap::flat_hash_map toSell; uint32_t MAX_BATCH_SIZE = 10; uint32_t processedCount = 0; @@ -516,12 +549,15 @@ void Npc::onPlayerSellAllLoot(uint32_t playerId, uint16_t itemId, bool ignore, u auto ss = std::stringstream(); if (!madeProgress) { if (totalPrice == 0) { - if (preSize > 0) { - ss << "You don't have enough space. Free up space in your bag."; + if (preSize == 0) { + ss << "You have no items in your loot pouch."; player->sendTextMessage(MESSAGE_FAILURE, ss.str()); - } else { + } else if (!hasSellable) { ss << "You have no sellable items in your loot pouch."; player->sendTextMessage(MESSAGE_FAILURE, ss.str()); + } else { + ss << "You don't have enough space. Free up space in your bag."; + player->sendTextMessage(MESSAGE_FAILURE, ss.str()); } } else { ss << "Sale stopped. Some items in your loot bag could not be sold. Make sure you have enough space in your bag."; @@ -536,7 +572,7 @@ void Npc::onPlayerSellAllLoot(uint32_t playerId, uint16_t itemId, bool ignore, u ss << "You have no items in your loot pouch."; player->sendTextMessage(MESSAGE_FAILURE, ss.str()); } else { - ss << "You sold all of the items from your loot pouch for " << totalPrice << " gold."; + ss << "Congratulations. You sold all of the items from your loot pouch for " << totalPrice << " gold."; player->sendTextMessage(MESSAGE_LOOK, ss.str()); } } From 37ecc19f55f926d86379ce4d613d004e90c8d788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo?= Date: Tue, 4 Nov 2025 21:12:23 -0300 Subject: [PATCH 6/6] Update npc.cpp --- src/creatures/npcs/npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index 34ba86f1b..20e352da0 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -572,7 +572,7 @@ void Npc::onPlayerSellAllLoot(uint32_t playerId, uint16_t itemId, bool ignore, u ss << "You have no items in your loot pouch."; player->sendTextMessage(MESSAGE_FAILURE, ss.str()); } else { - ss << "Congratulations. You sold all of the items from your loot pouch for " << totalPrice << " gold."; + ss << "You sold all of the items from your loot pouch for " << totalPrice << " gold."; player->sendTextMessage(MESSAGE_LOOK, ss.str()); } }