From b18f615ddcb731c208bf56e1b807c44d7920079b Mon Sep 17 00:00:00 2001 From: agatho Date: Thu, 12 Feb 2026 22:12:40 +0100 Subject: [PATCH] Core/Items: Implement Sell All Junk button at vendors --- src/server/game/Handlers/AuthHandler.cpp | 1 + src/server/game/Handlers/ItemHandler.cpp | 75 +++++++++++++++++++ .../game/Server/Packets/ItemPackets.cpp | 5 ++ src/server/game/Server/Packets/ItemPackets.h | 10 +++ src/server/game/Server/Protocol/Opcodes.cpp | 2 +- src/server/game/Server/WorldSession.h | 2 + 6 files changed, 94 insertions(+), 1 deletion(-) diff --git a/src/server/game/Handlers/AuthHandler.cpp b/src/server/game/Handlers/AuthHandler.cpp index bd671c558a..dbc8880af6 100644 --- a/src/server/game/Handlers/AuthHandler.cpp +++ b/src/server/game/Handlers/AuthHandler.cpp @@ -145,6 +145,7 @@ void WorldSession::SendFeatureSystemStatusGlueScreen() WorldPackets::System::MirrorVarSingle vars[] = { { "raidLockoutExtendEnabled"sv, "1"sv }, + { "sellAllJunkEnabled"sv, "1"sv }, { "bypassItemLevelScalingCode"sv, "0"sv }, { "shop2Enabled"sv, "0"sv }, { "bpayStoreEnable"sv, "0"sv }, diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index daec396e03..127f7b0496 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -427,6 +427,81 @@ void WorldSession::HandleSellItemOpcode(WorldPackets::Item::SellItem const& sell _player->SendSellError(*sellResult, creature, sellItem.ItemGUID); } +void WorldSession::HandleSellAllJunkItems(WorldPackets::Item::SellAllJunkItems const& sellAllJunkItems) +{ + Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(sellAllJunkItems.VendorGUID, UNIT_NPC_FLAG_VENDOR, UNIT_NPC_FLAG_2_NONE); + if (!creature) + { + _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, nullptr, ObjectGuid::Empty); + return; + } + + if ((creature->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_SELL_VENDOR) != 0) + { + _player->SendSellError(SELL_ERR_CANT_SELL_TO_THIS_MERCHANT, creature, ObjectGuid::Empty); + return; + } + + // collect junk items first - can't modify inventory while iterating + std::vector junkItems; + _player->ForEachItem(ItemSearchLocation::Inventory, [this, &junkItems](Item* item) + { + if (item->GetQuality() != ITEM_QUALITY_POOR) + return ItemSearchCallbackResult::Continue; + + if (item->GetSellPrice(_player) == 0) + return ItemSearchCallbackResult::Continue; + + if (item->IsRefundable()) + return ItemSearchCallbackResult::Continue; + + if (_player->GetLootGUID() == item->GetGUID()) + return ItemSearchCallbackResult::Continue; + + if (item->IsNotEmptyBag()) + return ItemSearchCallbackResult::Continue; + + // check per-bag junk sell exclusion + if (item->GetBagSlot() == INVENTORY_SLOT_BAG_0) + { + if (_player->IsBackpackSellJunkDisabled()) + return ItemSearchCallbackResult::Continue; + } + else + { + uint32 bagIndex = item->GetBagSlot() - INVENTORY_SLOT_BAG_START; + if (bagIndex < _player->m_activePlayerData->BagSlotFlags.size() + && _player->GetBagSlotFlags(bagIndex).HasFlag(BagSlotFlags::ExcludeJunkSell)) + return ItemSearchCallbackResult::Continue; + } + + junkItems.push_back(item); + return ItemSearchCallbackResult::Continue; + }); + + for (Item* item : junkItems) + { + uint32 sellPrice = item->GetSellPrice(_player); + + uint64 money = uint64(sellPrice) * item->GetCount(); + + using BuybackStorageType = std::remove_cvref_tm_activePlayerData->BuybackPrice[0])>; + if (money > std::numeric_limits::max()) + continue; + + if (!_player->ModifyMoney(money)) + continue; + + _player->UpdateCriteria(CriteriaType::MoneyEarnedFromSales, money); + _player->UpdateCriteria(CriteriaType::SellItemsToVendors, 1); + + _player->RemoveItem(item->GetBagSlot(), item->GetSlot(), true); + _player->ItemRemovedQuestCheck(item->GetEntry(), item->GetCount()); + RemoveItemFromUpdateQueueOf(item, _player); + _player->AddItemToBuyBackSlot(item); + } +} + void WorldSession::HandleBuybackItem(WorldPackets::Item::BuyBackItem& packet) { TC_LOG_DEBUG("network", "WORLD: Received CMSG_BUYBACK_ITEM: Vendor {}, Slot: {}", packet.VendorGUID.ToString(), packet.Slot); diff --git a/src/server/game/Server/Packets/ItemPackets.cpp b/src/server/game/Server/Packets/ItemPackets.cpp index 26b2b6c2d0..e03789fb7e 100644 --- a/src/server/game/Server/Packets/ItemPackets.cpp +++ b/src/server/game/Server/Packets/ItemPackets.cpp @@ -137,6 +137,11 @@ void SellItem::Read() _worldPacket >> Amount; } +void SellAllJunkItems::Read() +{ + _worldPacket >> VendorGUID; +} + WorldPacket const* ItemTimeUpdate::Write() { _worldPacket << ItemGuid; diff --git a/src/server/game/Server/Packets/ItemPackets.h b/src/server/game/Server/Packets/ItemPackets.h index 2375360cea..d3b1b90ac3 100644 --- a/src/server/game/Server/Packets/ItemPackets.h +++ b/src/server/game/Server/Packets/ItemPackets.h @@ -184,6 +184,16 @@ namespace WorldPackets uint32 Amount = 0; }; + class SellAllJunkItems final : public ClientPacket + { + public: + explicit SellAllJunkItems(WorldPacket&& packet) : ClientPacket(CMSG_SELL_ALL_JUNK_ITEMS, std::move(packet)) { } + + void Read() override; + + ObjectGuid VendorGUID; + }; + class ItemTimeUpdate final : public ServerPacket { public: diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 3693240a4f..bb90a71f0a 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -990,7 +990,7 @@ void OpcodeTable::InitializeClientOpcodes() DEFINE_HANDLER(CMSG_SEAMLESS_TRANSFER_COMPLETE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_SELECT_WOW_LABS_AREA, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_SELF_RES, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSelfResOpcode); - DEFINE_HANDLER(CMSG_SELL_ALL_JUNK_ITEMS, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL); + DEFINE_HANDLER(CMSG_SELL_ALL_JUNK_ITEMS, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleSellAllJunkItems); DEFINE_HANDLER(CMSG_SELL_ITEM, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleSellItemOpcode); DEFINE_HANDLER(CMSG_SEND_CHARACTER_CLUB_INVITATION, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_SEND_CONTACT_LIST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleContactListOpcode); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 5aa394ad93..4781d7df8f 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -448,6 +448,7 @@ namespace WorldPackets class RepairItem; class ReadItem; class SellItem; + class SellAllJunkItems; class SplitItem; class SwapInvItem; class SwapItem; @@ -1536,6 +1537,7 @@ class TC_GAME_API WorldSession void HandleDestroyItemOpcode(WorldPackets::Item::DestroyItem& destroyItem); void HandleAutoEquipItemOpcode(WorldPackets::Item::AutoEquipItem& autoEquipItem); void HandleSellItemOpcode(WorldPackets::Item::SellItem const& sellItem); + void HandleSellAllJunkItems(WorldPackets::Item::SellAllJunkItems const& sellAllJunkItems); void HandleBuyItemOpcode(WorldPackets::Item::BuyItem& packet); void HandleListInventoryOpcode(WorldPackets::NPC::Hello& packet); void HandleAutoStoreBagItemOpcode(WorldPackets::Item::AutoStoreBagItem& packet);