From d99fe8de0f929cddf2244473bda83ca5a8670fa7 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 17 Mar 2026 23:29:50 -0700 Subject: [PATCH] feat: add Sort Bags button to backpack window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds Inventory::sortBags() which collects all items from the backpack and equip bags, sorts them client-side by quality descending → item ID ascending → stack count descending, then writes them back. A "Sort Bags" SmallButton is rendered in the backpack footer with a tooltip explaining the sort order. The sort is purely local (no server packets) since the WoW protocol has no sort-bags opcode; it provides an instant, session-persistent visual reorder. --- include/game/inventory.hpp | 4 ++++ src/game/inventory.cpp | 39 +++++++++++++++++++++++++++++++++++++ src/ui/inventory_screen.cpp | 33 +++++++++++++++++++++---------- 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/include/game/inventory.hpp b/include/game/inventory.hpp index ea6f6110..cf092ac4 100644 --- a/include/game/inventory.hpp +++ b/include/game/inventory.hpp @@ -125,6 +125,10 @@ public: int findFreeBackpackSlot() const; bool addItem(const ItemDef& item); + // Sort all bag slots (backpack + equip bags) by quality desc → itemId asc → stackCount desc. + // Purely client-side: reorders the local inventory struct without server interaction. + void sortBags(); + // Test data void populateTestItems(); diff --git a/src/game/inventory.cpp b/src/game/inventory.cpp index 0d694aba..a6de6dcb 100644 --- a/src/game/inventory.cpp +++ b/src/game/inventory.cpp @@ -1,5 +1,6 @@ #include "game/inventory.hpp" #include "core/logger.hpp" +#include namespace wowee { namespace game { @@ -185,6 +186,44 @@ bool Inventory::addItem(const ItemDef& item) { return true; } +void Inventory::sortBags() { + // Collect all items from backpack and equip bags into a flat list. + std::vector items; + items.reserve(BACKPACK_SLOTS + NUM_BAG_SLOTS * MAX_BAG_SIZE); + + for (int i = 0; i < BACKPACK_SLOTS; ++i) { + if (!backpack[i].empty()) + items.push_back(backpack[i].item); + } + for (int b = 0; b < NUM_BAG_SLOTS; ++b) { + for (int s = 0; s < bags[b].size; ++s) { + if (!bags[b].slots[s].empty()) + items.push_back(bags[b].slots[s].item); + } + } + + // Sort: quality descending → itemId ascending → stackCount descending. + std::stable_sort(items.begin(), items.end(), [](const ItemDef& a, const ItemDef& b) { + if (a.quality != b.quality) + return static_cast(a.quality) > static_cast(b.quality); + if (a.itemId != b.itemId) + return a.itemId < b.itemId; + return a.stackCount > b.stackCount; + }); + + // Write sorted items back, filling backpack first then equip bags. + int idx = 0; + int n = static_cast(items.size()); + + for (int i = 0; i < BACKPACK_SLOTS; ++i) + backpack[i].item = (idx < n) ? items[idx++] : ItemDef{}; + + for (int b = 0; b < NUM_BAG_SLOTS; ++b) { + for (int s = 0; s < bags[b].size; ++s) + bags[b].slots[s].item = (idx < n) ? items[idx++] : ItemDef{}; + } +} + void Inventory::populateTestItems() { // Equipment { diff --git a/src/ui/inventory_screen.cpp b/src/ui/inventory_screen.cpp index 083096a7..7b91ef22 100644 --- a/src/ui/inventory_screen.cpp +++ b/src/ui/inventory_screen.cpp @@ -1019,7 +1019,7 @@ void InventoryScreen::renderBagWindow(const char* title, bool& isOpen, float contentH = rows * (slotSize + 4.0f) + 10.0f; if (bagIndex < 0) { int keyringRows = (inventory.getKeyringSize() + columns - 1) / columns; - contentH += 25.0f; // money display for backpack + contentH += 36.0f; // separator + sort button + money display contentH += 30.0f + keyringRows * (slotSize + 4.0f); // keyring header + slots } float gridW = columns * (slotSize + 4.0f) + 30.0f; @@ -1094,16 +1094,29 @@ void InventoryScreen::renderBagWindow(const char* title, bool& isOpen, } } - // Money display at bottom of backpack - if (bagIndex < 0 && moneyCopper > 0) { + // Footer for backpack: sort button + money display + if (bagIndex < 0) { ImGui::Spacing(); - uint64_t gold = moneyCopper / 10000; - uint64_t silver = (moneyCopper / 100) % 100; - uint64_t copper = moneyCopper % 100; - ImGui::TextColored(ImVec4(1.0f, 0.84f, 0.0f, 1.0f), "%llug %llus %lluc", - static_cast(gold), - static_cast(silver), - static_cast(copper)); + ImGui::Separator(); + + // Sort Bags button — client-side reorder by quality/type + if (ImGui::SmallButton("Sort Bags")) { + inventory.sortBags(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Sort all bag slots by quality (highest first),\nthen by item ID, then by stack size."); + } + + if (moneyCopper > 0) { + ImGui::SameLine(); + uint64_t gold = moneyCopper / 10000; + uint64_t silver = (moneyCopper / 100) % 100; + uint64_t copper = moneyCopper % 100; + ImGui::TextColored(ImVec4(1.0f, 0.84f, 0.0f, 1.0f), "%llug %llus %lluc", + static_cast(gold), + static_cast(silver), + static_cast(copper)); + } } ImGui::End();