diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index c8b20983..c18f234c 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -1394,6 +1394,8 @@ public: const LootResponseData& getCurrentLoot() const { return currentLoot; } void setAutoLoot(bool enabled) { autoLoot_ = enabled; } bool isAutoLoot() const { return autoLoot_; } + void setAutoSellGrey(bool enabled) { autoSellGrey_ = enabled; } + bool isAutoSellGrey() const { return autoSellGrey_; } // Master loot candidates (from SMSG_LOOT_MASTER_LIST) const std::vector& getMasterLootCandidates() const { return masterLootCandidates_; } @@ -2879,6 +2881,7 @@ private: // ---- Phase 5: Loot ---- bool lootWindowOpen = false; bool autoLoot_ = false; + bool autoSellGrey_ = false; LootResponseData currentLoot; std::vector masterLootCandidates_; // from SMSG_LOOT_MASTER_LIST diff --git a/include/ui/game_screen.hpp b/include/ui/game_screen.hpp index 3acd13b0..4f6e0f84 100644 --- a/include/ui/game_screen.hpp +++ b/include/ui/game_screen.hpp @@ -195,6 +195,7 @@ private: bool pendingSeparateBags = true; bool pendingShowKeyring = true; bool pendingAutoLoot = false; + bool pendingAutoSellGrey = false; // Keybinding customization int pendingRebindAction = -1; // -1 = not rebinding, otherwise action index diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index aee5d91e..7b354ddd 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -21197,6 +21197,68 @@ void GameHandler::handleListInventory(network::Packet& packet) { vendorWindowOpen = true; gossipWindowOpen = false; // Close gossip if vendor opens + // Auto-sell grey items if enabled + if (autoSellGrey_ && currentVendorItems.vendorGuid != 0) { + uint32_t totalSellPrice = 0; + int itemsSold = 0; + + // Helper lambda to attempt selling a poor-quality slot + auto tryAutoSell = [&](const ItemSlot& slot, uint64_t itemGuid) { + if (slot.empty()) return; + if (slot.item.quality != ItemQuality::POOR) return; + // Determine sell price (slot cache first, then item info fallback) + uint32_t sp = slot.item.sellPrice; + if (sp == 0) { + if (auto* info = getItemInfo(slot.item.itemId); info && info->valid) + sp = info->sellPrice; + } + if (sp == 0 || itemGuid == 0) return; + BuybackItem sold; + sold.itemGuid = itemGuid; + sold.item = slot.item; + sold.count = 1; + buybackItems_.push_front(sold); + if (buybackItems_.size() > 12) buybackItems_.pop_back(); + pendingSellToBuyback_[itemGuid] = sold; + sellItem(currentVendorItems.vendorGuid, itemGuid, 1); + totalSellPrice += sp; + ++itemsSold; + }; + + // Backpack slots + for (int i = 0; i < inventory.getBackpackSize(); ++i) { + uint64_t guid = backpackSlotGuids_[i]; + if (guid == 0) guid = resolveOnlineItemGuid(inventory.getBackpackSlot(i).item.itemId); + tryAutoSell(inventory.getBackpackSlot(i), guid); + } + + // Extra bag slots + for (int b = 0; b < inventory.NUM_BAG_SLOTS; ++b) { + uint64_t bagGuid = equipSlotGuids_[19 + b]; + for (int s = 0; s < inventory.getBagSize(b); ++s) { + uint64_t guid = 0; + if (bagGuid != 0) { + auto it = containerContents_.find(bagGuid); + if (it != containerContents_.end() && s < static_cast(it->second.numSlots)) + guid = it->second.slotGuids[s]; + } + if (guid == 0) guid = resolveOnlineItemGuid(inventory.getBagSlot(b, s).item.itemId); + tryAutoSell(inventory.getBagSlot(b, s), guid); + } + } + + if (itemsSold > 0) { + uint32_t gold = totalSellPrice / 10000; + uint32_t silver = (totalSellPrice % 10000) / 100; + uint32_t copper = totalSellPrice % 100; + char buf[128]; + std::snprintf(buf, sizeof(buf), + "|cffaaaaaaAuto-sold %d grey item%s for %ug %us %uc.|r", + itemsSold, itemsSold == 1 ? "" : "s", gold, silver, copper); + addSystemChatMessage(buf); + } + } + // Play vendor sound if (npcVendorCallback_ && currentVendorItems.vendorGuid != 0) { auto entity = entityManager.getEntity(currentVendorItems.vendorGuid); diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 9c1d402b..729c4ea7 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -592,8 +592,9 @@ void GameScreen::render(game::GameHandler& gameHandler) { } } - // Apply auto-loot setting to GameHandler every frame (cheap bool sync) + // Apply auto-loot / auto-sell settings to GameHandler every frame (cheap bool sync) gameHandler.setAutoLoot(pendingAutoLoot); + gameHandler.setAutoSellGrey(pendingAutoSellGrey); // Zone entry detection — fire a toast when the renderer's zone name changes if (auto* rend = core::Application::getInstance().getRenderer()) { @@ -16328,6 +16329,11 @@ void GameScreen::renderSettingsWindow() { } if (ImGui::IsItemHovered()) ImGui::SetTooltip("Automatically pick up all items when looting"); + if (ImGui::Checkbox("Auto Sell Greys", &pendingAutoSellGrey)) { + saveSettings(); + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Automatically sell all grey (poor quality) items when opening a vendor"); ImGui::Spacing(); ImGui::Text("Bags"); @@ -18328,6 +18334,7 @@ void GameScreen::saveSettings() { // Gameplay out << "auto_loot=" << (pendingAutoLoot ? 1 : 0) << "\n"; + out << "auto_sell_grey=" << (pendingAutoSellGrey ? 1 : 0) << "\n"; out << "graphics_preset=" << static_cast(currentGraphicsPreset) << "\n"; out << "ground_clutter_density=" << pendingGroundClutterDensity << "\n"; out << "shadows=" << (pendingShadows ? 1 : 0) << "\n"; @@ -18469,6 +18476,7 @@ void GameScreen::loadSettings() { else if (key == "activity_volume") pendingActivityVolume = std::clamp(std::stoi(val), 0, 100); // Gameplay else if (key == "auto_loot") pendingAutoLoot = (std::stoi(val) != 0); + else if (key == "auto_sell_grey") pendingAutoSellGrey = (std::stoi(val) != 0); else if (key == "graphics_preset") { int presetVal = std::clamp(std::stoi(val), 0, 4); currentGraphicsPreset = static_cast(presetVal);