Add quest details dialog, fix vendor UI, suppress both-button clicks

Parse SMSG_QUESTGIVER_QUEST_DETAILS and show quest text with Accept/
Decline buttons. Vendor window now shows item names with quality colors,
stat tooltips on hover, player money, and closes properly via X button.
Suppress left/right-click targeting and interaction when the other mouse
button is held (both-button run forward).
This commit is contained in:
Kelsi 2026-02-06 11:59:51 -08:00
parent 60be428250
commit 67a3da3bae
6 changed files with 227 additions and 5 deletions

View file

@ -1092,6 +1092,8 @@ void GameHandler::handlePacket(network::Packet& packet) {
case Opcode::MSG_RAID_TARGET_UPDATE:
case Opcode::SMSG_QUESTGIVER_STATUS:
case Opcode::SMSG_QUESTGIVER_QUEST_DETAILS:
handleQuestDetails(packet);
break;
case Opcode::SMSG_QUESTGIVER_REQUEST_ITEMS:
case Opcode::SMSG_QUESTGIVER_OFFER_REWARD:
case Opcode::SMSG_QUESTGIVER_QUEST_COMPLETE:
@ -3538,6 +3540,31 @@ void GameHandler::selectGossipQuest(uint32_t questId) {
gossipWindowOpen = false;
}
void GameHandler::handleQuestDetails(network::Packet& packet) {
QuestDetailsData data;
if (!QuestDetailsParser::parse(packet, data)) {
LOG_WARNING("Failed to parse SMSG_QUESTGIVER_QUEST_DETAILS");
return;
}
currentQuestDetails = data;
questDetailsOpen = true;
gossipWindowOpen = false;
}
void GameHandler::acceptQuest() {
if (!questDetailsOpen || state != WorldState::IN_WORLD || !socket) return;
auto packet = QuestgiverAcceptQuestPacket::build(
currentQuestDetails.npcGuid, currentQuestDetails.questId);
socket->send(packet);
questDetailsOpen = false;
currentQuestDetails = QuestDetailsData{};
}
void GameHandler::declineQuest() {
questDetailsOpen = false;
currentQuestDetails = QuestDetailsData{};
}
void GameHandler::closeGossip() {
gossipWindowOpen = false;
currentGossip = GossipMessageData{};
@ -3549,6 +3576,11 @@ void GameHandler::openVendor(uint64_t npcGuid) {
socket->send(packet);
}
void GameHandler::closeVendor() {
vendorWindowOpen = false;
currentVendorItems = ListInventoryData{};
}
void GameHandler::buyItem(uint64_t vendorGuid, uint32_t itemId, uint32_t slot, uint8_t count) {
if (state != WorldState::IN_WORLD || !socket) return;
auto packet = BuyItemPacket::build(vendorGuid, itemId, slot, count);
@ -3605,6 +3637,11 @@ void GameHandler::handleListInventory(network::Packet& packet) {
if (!ListInventoryParser::parse(packet, currentVendorItems)) return;
vendorWindowOpen = true;
gossipWindowOpen = false; // Close gossip if vendor opens
// Query item info for all vendor items so we can show names
for (const auto& item : currentVendorItems.items) {
queryItemInfo(item.itemId, 0);
}
}
// ============================================================

View file

@ -1835,6 +1835,44 @@ network::Packet QuestgiverAcceptQuestPacket::build(uint64_t npcGuid, uint32_t qu
return packet;
}
bool QuestDetailsParser::parse(network::Packet& packet, QuestDetailsData& data) {
if (packet.getSize() < 20) return false;
data.npcGuid = packet.readUInt64();
/*informUnit*/ packet.readUInt64();
data.questId = packet.readUInt32();
data.title = packet.readString();
data.details = packet.readString();
data.objectives = packet.readString();
/*activateAccept*/ packet.readUInt8();
/*flags*/ packet.readUInt32();
data.suggestedPlayers = packet.readUInt32();
/*isFinished*/ packet.readUInt8();
// Reward choice items
uint32_t choiceCount = packet.readUInt32();
for (uint32_t i = 0; i < choiceCount && i < 6; i++) {
packet.readUInt32(); // itemId
packet.readUInt32(); // count
packet.readUInt32(); // displayInfo
}
// Reward items
uint32_t rewardCount = packet.readUInt32();
for (uint32_t i = 0; i < rewardCount && i < 4; i++) {
packet.readUInt32(); // itemId
packet.readUInt32(); // count
packet.readUInt32(); // displayInfo
}
data.rewardMoney = packet.readUInt32();
if (packet.getReadPos() < packet.getSize()) {
data.rewardXp = packet.readUInt32();
}
LOG_INFO("Quest details: id=", data.questId, " title='", data.title, "'");
return true;
}
bool GossipMessageParser::parse(network::Packet& packet, GossipMessageData& data) {
data.npcGuid = packet.readUInt64();
data.menuId = packet.readUInt32();