From a731223e47f7f68894053dbeeb97146ce3b2c805 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 17 Mar 2026 21:38:08 -0700 Subject: [PATCH] fix: right-clicking a quest-starting item now opens the quest offer dialog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Items with startQuestId != 0 were calling useItemBySlot()/useItemInBag() which sends CMSG_USE_ITEM — but quest-starting items have no on-use spell, so the server silently ignored the packet and no quest dialog appeared. Fix: - offerQuestFromItem(itemGuid, questId): sends CMSG_QUESTGIVER_QUERY_QUEST with the item's own GUID as the questgiver GUID. The server responds with SMSG_QUESTGIVER_QUEST_DETAILS which handleQuestDetails() already picks up and opens the Accept/Decline dialog with full rewards/description. - getBagItemGuid(bagIndex, slotIndex): resolves the per-slot item GUID from the bag's containerContents_ map (mirrors the logic inside useItemInBag). - inventory_screen.cpp right-click handler: checks item.startQuestId != 0 before the equip/use branch; if set, resolves item GUID and calls offerQuestFromItem. Works for both backpack slots and bag slots. --- include/game/game_handler.hpp | 3 +++ src/game/game_handler.cpp | 28 ++++++++++++++++++++++++++++ src/ui/inventory_screen.cpp | 16 ++++++++++++---- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index ff253b96..e75fedb5 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -1460,6 +1460,9 @@ public: void acceptQuest(); void declineQuest(); void closeGossip(); + // Quest-starting items: right-click triggers quest offer dialog via questgiver protocol + void offerQuestFromItem(uint64_t itemGuid, uint32_t questId); + uint64_t getBagItemGuid(int bagIndex, int slotIndex) const; bool isGossipWindowOpen() const { return gossipWindowOpen; } const GossipMessageData& getCurrentGossip() const { return currentGossip; } bool isQuestDetailsOpen() { diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 40c861d3..c5fe5c5e 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -20552,6 +20552,34 @@ void GameHandler::closeGossip() { currentGossip = GossipMessageData{}; } +void GameHandler::offerQuestFromItem(uint64_t itemGuid, uint32_t questId) { + if (state != WorldState::IN_WORLD || !socket) return; + if (itemGuid == 0 || questId == 0) { + addSystemChatMessage("Cannot start quest right now."); + return; + } + // Send CMSG_QUESTGIVER_QUERY_QUEST with the item GUID as the "questgiver." + // The server responds with SMSG_QUESTGIVER_QUEST_DETAILS which handleQuestDetails() + // picks up and opens the Accept/Decline dialog. + auto queryPkt = packetParsers_ + ? packetParsers_->buildQueryQuestPacket(itemGuid, questId) + : QuestgiverQueryQuestPacket::build(itemGuid, questId); + socket->send(queryPkt); + LOG_INFO("offerQuestFromItem: itemGuid=0x", std::hex, itemGuid, std::dec, + " questId=", questId); +} + +uint64_t GameHandler::getBagItemGuid(int bagIndex, int slotIndex) const { + if (bagIndex < 0 || bagIndex >= inventory.NUM_BAG_SLOTS) return 0; + if (slotIndex < 0) return 0; + uint64_t bagGuid = equipSlotGuids_[19 + bagIndex]; + if (bagGuid == 0) return 0; + auto it = containerContents_.find(bagGuid); + if (it == containerContents_.end()) return 0; + if (slotIndex >= static_cast(it->second.numSlots)) return 0; + return it->second.slotGuids[slotIndex]; +} + void GameHandler::openVendor(uint64_t npcGuid) { if (state != WorldState::IN_WORLD || !socket) return; buybackItems_.clear(); diff --git a/src/ui/inventory_screen.cpp b/src/ui/inventory_screen.cpp index 42c03e8e..083096a7 100644 --- a/src/ui/inventory_screen.cpp +++ b/src/ui/inventory_screen.cpp @@ -2335,8 +2335,12 @@ void InventoryScreen::renderItemSlot(game::Inventory& inventory, const game::Ite } else if (kind == SlotKind::BACKPACK && backpackIndex >= 0) { LOG_INFO("Right-click backpack item: name='", item.name, "' inventoryType=", (int)item.inventoryType, - " itemId=", item.itemId); - if (item.inventoryType > 0) { + " itemId=", item.itemId, + " startQuestId=", item.startQuestId); + if (item.startQuestId != 0) { + uint64_t iGuid = gameHandler_->getBackpackItemGuid(backpackIndex); + gameHandler_->offerQuestFromItem(iGuid, item.startQuestId); + } else if (item.inventoryType > 0) { gameHandler_->autoEquipItemBySlot(backpackIndex); } else { gameHandler_->useItemBySlot(backpackIndex); @@ -2344,8 +2348,12 @@ void InventoryScreen::renderItemSlot(game::Inventory& inventory, const game::Ite } else if (kind == SlotKind::BACKPACK && isBagSlot) { LOG_INFO("Right-click bag item: name='", item.name, "' inventoryType=", (int)item.inventoryType, - " bagIndex=", bagIndex, " slotIndex=", bagSlotIndex); - if (item.inventoryType > 0) { + " bagIndex=", bagIndex, " slotIndex=", bagSlotIndex, + " startQuestId=", item.startQuestId); + if (item.startQuestId != 0) { + uint64_t iGuid = gameHandler_->getBagItemGuid(bagIndex, bagSlotIndex); + gameHandler_->offerQuestFromItem(iGuid, item.startQuestId); + } else if (item.inventoryType > 0) { gameHandler_->autoEquipItemInBag(bagIndex, bagSlotIndex); } else { gameHandler_->useItemInBag(bagIndex, bagSlotIndex);