From acde6070cff199db20d65e124fb562a15b257dec Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 9 Mar 2026 14:14:15 -0700 Subject: [PATCH] Handle SMSG_QUEST_CONFIRM_ACCEPT (shared quest) with accept/decline popup - Parse SMSG_QUEST_CONFIRM_ACCEPT (questId + title + sharerGuid), show chat notification with quest title and sharer name - acceptSharedQuest() sends CMSG_QUEST_CONFIRM_ACCEPT with questId - renderSharedQuestPopup(): shows sharer name, gold quest title, Accept/Decline buttons (stacked below other social popups) --- include/game/game_handler.hpp | 16 +++++++++++ include/ui/game_screen.hpp | 1 + src/game/game_handler.cpp | 51 +++++++++++++++++++++++++++++++++++ src/ui/game_screen.cpp | 26 ++++++++++++++++++ 4 files changed, 94 insertions(+) diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index bec28993..b2ff31a8 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -707,6 +707,14 @@ public: bool hasPendingGroupInvite() const { return pendingGroupInvite; } const std::string& getPendingInviterName() const { return pendingInviterName; } + // ---- Shared Quest ---- + bool hasPendingSharedQuest() const { return pendingSharedQuest_; } + uint32_t getSharedQuestId() const { return sharedQuestId_; } + const std::string& getSharedQuestTitle() const { return sharedQuestTitle_; } + const std::string& getSharedQuestSharerName() const { return sharedQuestSharerName_; } + void acceptSharedQuest(); + void declineSharedQuest(); + // ---- Summon ---- bool hasPendingSummonRequest() const { return pendingSummonRequest_; } const std::string& getSummonerName() const { return summonerName_; } @@ -1295,6 +1303,7 @@ private: // ---- Instance lockout handler ---- void handleRaidInstanceInfo(network::Packet& packet); + void handleQuestConfirmAccept(network::Packet& packet); void handleSummonRequest(network::Packet& packet); void handleTradeStatus(network::Packet& packet); void handleDuelRequested(network::Packet& packet); @@ -1654,6 +1663,13 @@ private: bool pendingGroupInvite = false; std::string pendingInviterName; + // Shared quest state + bool pendingSharedQuest_ = false; + uint32_t sharedQuestId_ = 0; + std::string sharedQuestTitle_; + std::string sharedQuestSharerName_; + uint64_t sharedQuestSharerGuid_ = 0; + // Summon state bool pendingSummonRequest_ = false; uint64_t summonerGuid_ = 0; diff --git a/include/ui/game_screen.hpp b/include/ui/game_screen.hpp index 23b8e363..b4d5a735 100644 --- a/include/ui/game_screen.hpp +++ b/include/ui/game_screen.hpp @@ -208,6 +208,7 @@ private: void renderLootRollPopup(game::GameHandler& gameHandler); void renderTradeRequestPopup(game::GameHandler& gameHandler); void renderSummonRequestPopup(game::GameHandler& gameHandler); + void renderSharedQuestPopup(game::GameHandler& gameHandler); void renderBuffBar(game::GameHandler& gameHandler); void renderLootWindow(game::GameHandler& gameHandler); void renderGossipWindow(game::GameHandler& gameHandler); diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index cf9f289c..5734cb93 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -1994,6 +1994,9 @@ void GameHandler::handlePacket(network::Packet& packet) { case Opcode::SMSG_LOOT_REMOVED: handleLootRemoved(packet); break; + case Opcode::SMSG_QUEST_CONFIRM_ACCEPT: + handleQuestConfirmAccept(packet); + break; case Opcode::SMSG_SUMMON_REQUEST: handleSummonRequest(packet); break; @@ -15043,6 +15046,54 @@ void GameHandler::handleAuctionCommandResult(network::Packet& packet) { " error=", result.errorCode); } +// --------------------------------------------------------------------------- +// SMSG_QUEST_CONFIRM_ACCEPT (shared quest from group member) +// uint32 questId + string questTitle + uint64 sharerGuid +// --------------------------------------------------------------------------- + +void GameHandler::handleQuestConfirmAccept(network::Packet& packet) { + size_t rem = packet.getSize() - packet.getReadPos(); + if (rem < 4) return; + + sharedQuestId_ = packet.readUInt32(); + sharedQuestTitle_ = packet.readString(); + if (packet.getSize() - packet.getReadPos() >= 8) { + sharedQuestSharerGuid_ = packet.readUInt64(); + } + + sharedQuestSharerName_.clear(); + auto entity = entityManager.getEntity(sharedQuestSharerGuid_); + if (auto* unit = dynamic_cast(entity.get())) { + sharedQuestSharerName_ = unit->getName(); + } + if (sharedQuestSharerName_.empty()) { + char tmp[32]; + std::snprintf(tmp, sizeof(tmp), "0x%llX", + static_cast(sharedQuestSharerGuid_)); + sharedQuestSharerName_ = tmp; + } + + pendingSharedQuest_ = true; + addSystemChatMessage(sharedQuestSharerName_ + " has shared the quest \"" + + sharedQuestTitle_ + "\" with you."); + LOG_INFO("SMSG_QUEST_CONFIRM_ACCEPT: questId=", sharedQuestId_, + " title=", sharedQuestTitle_, " sharer=", sharedQuestSharerName_); +} + +void GameHandler::acceptSharedQuest() { + if (!pendingSharedQuest_ || !socket) return; + pendingSharedQuest_ = false; + network::Packet pkt(wireOpcode(Opcode::CMSG_QUEST_CONFIRM_ACCEPT)); + pkt.writeUInt32(sharedQuestId_); + socket->send(pkt); + addSystemChatMessage("Accepted: " + sharedQuestTitle_); +} + +void GameHandler::declineSharedQuest() { + pendingSharedQuest_ = false; + // No response packet needed — just dismiss the UI +} + // --------------------------------------------------------------------------- // SMSG_SUMMON_REQUEST // uint64 summonerGuid + uint32 zoneId + uint32 timeoutMs diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 1905f5af..6ba93447 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -400,6 +400,7 @@ void GameScreen::render(game::GameHandler& gameHandler) { renderLootRollPopup(gameHandler); renderTradeRequestPopup(gameHandler); renderSummonRequestPopup(gameHandler); + renderSharedQuestPopup(gameHandler); renderGuildInvitePopup(gameHandler); renderGuildRoster(gameHandler); renderBuffBar(gameHandler); @@ -4404,6 +4405,31 @@ void GameScreen::renderDuelRequestPopup(game::GameHandler& gameHandler) { ImGui::End(); } +void GameScreen::renderSharedQuestPopup(game::GameHandler& gameHandler) { + if (!gameHandler.hasPendingSharedQuest()) return; + + auto* window = core::Application::getInstance().getWindow(); + float screenW = window ? static_cast(window->getWidth()) : 1280.0f; + + ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 175, 490), ImGuiCond_Always); + ImGui::SetNextWindowSize(ImVec2(350, 0), ImGuiCond_Always); + + if (ImGui::Begin("Shared Quest", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize)) { + ImGui::Text("%s has shared a quest with you:", gameHandler.getSharedQuestSharerName().c_str()); + ImGui::TextColored(ImVec4(1.0f, 0.85f, 0.0f, 1.0f), "\"%s\"", gameHandler.getSharedQuestTitle().c_str()); + ImGui::Spacing(); + + if (ImGui::Button("Accept", ImVec2(130, 30))) { + gameHandler.acceptSharedQuest(); + } + ImGui::SameLine(); + if (ImGui::Button("Decline", ImVec2(130, 30))) { + gameHandler.declineSharedQuest(); + } + } + ImGui::End(); +} + void GameScreen::renderSummonRequestPopup(game::GameHandler& gameHandler) { if (!gameHandler.hasPendingSummonRequest()) return;