From cbb41a289d02a55bee10396bd2ed66c6ea8213a4 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 9 Feb 2026 23:22:14 -0800 Subject: [PATCH] Fix quest opcode mapping for WoW 3.3.5a Corrected opcode assignments: - 0x18F = SMSG_QUESTGIVER_QUEST_INVALID (not QUEST_COMPLETE) - 0x191 = SMSG_QUESTGIVER_QUEST_COMPLETE SMSG_QUESTGIVER_QUEST_INVALID payload is uint32 QuestFailedReason: - 0 = Don't have quest - 1 = Quest level too low - 4 = Insufficient money - 5 = Inventory full - 13 = Already on that quest - 18 = Already completed quest - 19 = Can't take any more quests The "quest ID 13" we were seeing was actually failure reason 13 ("Already on that quest"), not a quest ID at all. --- include/game/opcodes.hpp | 3 ++- src/game/game_handler.cpp | 35 +++++++++++++++++++++-------------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/include/game/opcodes.hpp b/include/game/opcodes.hpp index 9127c1c7..347eb545 100644 --- a/include/game/opcodes.hpp +++ b/include/game/opcodes.hpp @@ -220,7 +220,8 @@ enum class Opcode : uint16_t { CMSG_QUESTGIVER_REQUEST_REWARD = 0x18C, SMSG_QUESTGIVER_OFFER_REWARD = 0x18D, CMSG_QUESTGIVER_CHOOSE_REWARD = 0x18E, - SMSG_QUESTGIVER_QUEST_COMPLETE = 0x18F, + SMSG_QUESTGIVER_QUEST_INVALID = 0x18F, + SMSG_QUESTGIVER_QUEST_COMPLETE = 0x191, CMSG_QUESTLOG_REMOVE_QUEST = 0x194, // ---- Phase 5: Vendor ---- diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 7f0d0174..80a175f7 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -836,24 +836,31 @@ void GameHandler::handlePacket(network::Packet& packet) { case Opcode::SMSG_QUESTGIVER_QUEST_DETAILS: handleQuestDetails(packet); break; + case Opcode::SMSG_QUESTGIVER_QUEST_INVALID: { + // Quest query failed - parse failure reason + if (packet.getSize() - packet.getReadPos() >= 4) { + uint32_t failReason = packet.readUInt32(); + const char* reasonStr = "Unknown"; + switch (failReason) { + case 0: reasonStr = "Don't have quest"; break; + case 1: reasonStr = "Quest level too low"; break; + case 4: reasonStr = "Insufficient money"; break; + case 5: reasonStr = "Inventory full"; break; + case 13: reasonStr = "Already on that quest"; break; + case 18: reasonStr = "Already completed quest"; break; + case 19: reasonStr = "Can't take any more quests"; break; + } + LOG_WARNING("Quest invalid: reason=", failReason, " (", reasonStr, ")"); + // Show error to user + addSystemChatMessage(std::string("Quest unavailable: ") + reasonStr); + } + break; + } case Opcode::SMSG_QUESTGIVER_QUEST_COMPLETE: { // Mark quest as complete in local log - size_t packetSize = packet.getSize(); - size_t readPos = packet.getReadPos(); - LOG_INFO("SMSG_QUESTGIVER_QUEST_COMPLETE: size=", packetSize, " readPos=", readPos); - - // Dump packet hex for debugging - std::string hexDump; - for (size_t i = readPos; i < std::min(readPos + 32, packetSize); ++i) { - char buf[4]; - snprintf(buf, sizeof(buf), "%02x ", static_cast(packet.getData()[i])); - hexDump += buf; - } - LOG_INFO(" Packet hex: ", hexDump); - if (packet.getSize() - packet.getReadPos() >= 4) { uint32_t questId = packet.readUInt32(); - LOG_INFO(" Read questId: ", questId); + LOG_INFO("Quest completed: questId=", questId); for (auto it = questLog_.begin(); it != questLog_.end(); ++it) { if (it->questId == questId) { questLog_.erase(it);