diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 478b4349..e4baed7d 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -368,6 +368,7 @@ public: void sellItemBySlot(int backpackIndex); void autoEquipItemBySlot(int backpackIndex); void useItemBySlot(int backpackIndex); + void useItemById(uint32_t itemId); bool isVendorWindowOpen() const { return vendorWindowOpen; } const ListInventoryData& getVendorItems() const { return currentVendorItems; } const ItemQueryResponseData* getItemInfo(uint32_t itemId) const { diff --git a/include/ui/inventory_screen.hpp b/include/ui/inventory_screen.hpp index 9ff3fa7b..d979dc9e 100644 --- a/include/ui/inventory_screen.hpp +++ b/include/ui/inventory_screen.hpp @@ -134,6 +134,13 @@ private: public: static ImVec4 getQualityColor(game::ItemQuality quality); + + /// Returns true if the user is currently holding an item (pickup cursor). + bool isHoldingItem() const { return holdingItem; } + /// Returns the item being held (only valid when isHoldingItem() is true). + const game::ItemDef& getHeldItem() const { return heldItem; } + /// Cancel the pickup, returning the item to its original slot. + void returnHeldItem(game::Inventory& inv) { cancelPickup(inv); } }; } // namespace ui diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index ed46a03c..b7d60b91 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -4147,6 +4147,17 @@ void GameHandler::useItemBySlot(int backpackIndex) { } } +void GameHandler::useItemById(uint32_t itemId) { + if (itemId == 0) return; + for (int i = 0; i < inventory.getBackpackSize(); i++) { + const auto& slot = inventory.getBackpackSlot(i); + if (!slot.empty() && slot.item.itemId == itemId) { + useItemBySlot(i); + return; + } + } +} + void GameHandler::handleLootResponse(network::Packet& packet) { if (!LootResponseParser::parse(packet, currentLoot)) return; lootWindowOpen = true; diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index dff0d079..e5a3a137 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -519,6 +519,8 @@ void GameScreen::processTargetInput(game::GameHandler& gameHandler) { if (bar[i].type == game::ActionBarSlot::SPELL && bar[i].isReady()) { uint64_t target = gameHandler.hasTarget() ? gameHandler.getTargetGuid() : 0; gameHandler.castSpell(bar[i].id, target); + } else if (bar[i].type == game::ActionBarSlot::ITEM && bar[i].id != 0) { + gameHandler.useItemById(bar[i].id); } } } @@ -1435,8 +1437,22 @@ void GameScreen::renderActionBar(game::GameHandler& gameHandler) { // Try to get icon texture for this slot GLuint iconTex = 0; + const game::ItemDef* barItemDef = nullptr; if (slot.type == game::ActionBarSlot::SPELL && slot.id != 0) { iconTex = getSpellIcon(slot.id, assetMgr); + } else if (slot.type == game::ActionBarSlot::ITEM && slot.id != 0) { + // Look up item in inventory for icon and name + auto& inv = gameHandler.getInventory(); + for (int bi = 0; bi < inv.getBackpackSize(); bi++) { + const auto& bs = inv.getBackpackSlot(bi); + if (!bs.empty() && bs.item.itemId == slot.id) { + barItemDef = &bs.item; + break; + } + } + if (barItemDef && barItemDef->displayInfoId != 0) { + iconTex = inventoryScreen.getItemIcon(barItemDef->displayInfoId); + } } bool clicked = false; @@ -1468,6 +1484,10 @@ void GameScreen::renderActionBar(game::GameHandler& gameHandler) { std::string spellName = getSpellName(slot.id); if (spellName.size() > 6) spellName = spellName.substr(0, 6); snprintf(label, sizeof(label), "%s", spellName.c_str()); + } else if (slot.type == game::ActionBarSlot::ITEM && barItemDef) { + std::string itemName = barItemDef->name; + if (itemName.size() > 6) itemName = itemName.substr(0, 6); + snprintf(label, sizeof(label), "%s", itemName.c_str()); } else if (slot.type == game::ActionBarSlot::ITEM) { snprintf(label, sizeof(label), "Item"); } else if (slot.type == game::ActionBarSlot::MACRO) { @@ -1480,19 +1500,41 @@ void GameScreen::renderActionBar(game::GameHandler& gameHandler) { ImGui::PopStyleColor(); } - if (clicked) { + // Drop held item from inventory onto action bar + if (clicked && inventoryScreen.isHoldingItem()) { + const auto& held = inventoryScreen.getHeldItem(); + gameHandler.setActionBarSlot(i, game::ActionBarSlot::ITEM, held.itemId); + inventoryScreen.returnHeldItem(gameHandler.getInventory()); + } else if (clicked) { if (slot.type == game::ActionBarSlot::SPELL && slot.isReady()) { uint64_t target = gameHandler.hasTarget() ? gameHandler.getTargetGuid() : 0; gameHandler.castSpell(slot.id, target); + } else if (slot.type == game::ActionBarSlot::ITEM && slot.id != 0) { + gameHandler.useItemById(slot.id); } } + // Right-click to clear slot + if (ImGui::IsItemClicked(ImGuiMouseButton_Right) && !slot.isEmpty()) { + gameHandler.setActionBarSlot(i, game::ActionBarSlot::EMPTY, 0); + } + + // Tooltip if (ImGui::IsItemHovered() && slot.type == game::ActionBarSlot::SPELL && slot.id != 0) { std::string fullName = getSpellName(slot.id); ImGui::BeginTooltip(); ImGui::Text("%s", fullName.c_str()); ImGui::TextDisabled("Spell ID: %u", slot.id); ImGui::EndTooltip(); + } else if (ImGui::IsItemHovered() && slot.type == game::ActionBarSlot::ITEM && slot.id != 0) { + ImGui::BeginTooltip(); + if (barItemDef) { + ImGui::Text("%s", barItemDef->name.c_str()); + } else { + ImGui::Text("Item #%u", slot.id); + } + ImGui::TextDisabled("(Right-click to remove)"); + ImGui::EndTooltip(); } // Cooldown overlay