From 770ac645d54a9a173ff1ab253b6f3970dab3d665 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 9 Mar 2026 14:07:50 -0700 Subject: [PATCH] Handle SMSG_SUMMON_REQUEST with accept/decline popup - Parse SMSG_SUMMON_REQUEST (summonerGuid + zoneId + timeoutMs), store summoner name from entity list, show chat notification - acceptSummon() sends CMSG_SUMMON_RESPONSE(1), declineSummon() sends CMSG_SUMMON_RESPONSE(0), SMSG_SUMMON_CANCEL clears pending state - renderSummonRequestPopup(): shows summoner name + countdown timer with Accept/Decline buttons --- include/game/game_handler.hpp | 14 +++++++++ include/ui/game_screen.hpp | 1 + src/game/game_handler.cpp | 57 +++++++++++++++++++++++++++++++++++ src/ui/game_screen.cpp | 29 ++++++++++++++++++ 4 files changed, 101 insertions(+) diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index effd9176..c58aca01 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -707,6 +707,13 @@ public: bool hasPendingGroupInvite() const { return pendingGroupInvite; } const std::string& getPendingInviterName() const { return pendingInviterName; } + // ---- Summon ---- + bool hasPendingSummonRequest() const { return pendingSummonRequest_; } + const std::string& getSummonerName() const { return summonerName_; } + float getSummonTimeoutSec() const { return summonTimeoutSec_; } + void acceptSummon(); + void declineSummon(); + // ---- Trade ---- enum class TradeStatus : uint8_t { None = 0, PendingIncoming, Open, Accepted, Complete @@ -1280,6 +1287,7 @@ private: // ---- Instance lockout handler ---- void handleRaidInstanceInfo(network::Packet& packet); + void handleSummonRequest(network::Packet& packet); void handleTradeStatus(network::Packet& packet); void handleDuelRequested(network::Packet& packet); void handleDuelComplete(network::Packet& packet); @@ -1638,6 +1646,12 @@ private: bool pendingGroupInvite = false; std::string pendingInviterName; + // Summon state + bool pendingSummonRequest_ = false; + uint64_t summonerGuid_ = 0; + std::string summonerName_; + float summonTimeoutSec_ = 0.0f; + // Trade state TradeStatus tradeStatus_ = TradeStatus::None; uint64_t tradePeerGuid_= 0; diff --git a/include/ui/game_screen.hpp b/include/ui/game_screen.hpp index 5668e45f..23b8e363 100644 --- a/include/ui/game_screen.hpp +++ b/include/ui/game_screen.hpp @@ -207,6 +207,7 @@ private: void renderDuelRequestPopup(game::GameHandler& gameHandler); void renderLootRollPopup(game::GameHandler& gameHandler); void renderTradeRequestPopup(game::GameHandler& gameHandler); + void renderSummonRequestPopup(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 a29a9950..bf03c72f 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -1953,6 +1953,13 @@ void GameHandler::handlePacket(network::Packet& packet) { case Opcode::SMSG_LOOT_REMOVED: handleLootRemoved(packet); break; + case Opcode::SMSG_SUMMON_REQUEST: + handleSummonRequest(packet); + break; + case Opcode::SMSG_SUMMON_CANCEL: + pendingSummonRequest_ = false; + addSystemChatMessage("Summon cancelled."); + break; case Opcode::SMSG_TRADE_STATUS: case Opcode::SMSG_TRADE_STATUS_EXTENDED: handleTradeStatus(packet); @@ -14995,6 +15002,56 @@ void GameHandler::handleAuctionCommandResult(network::Packet& packet) { " error=", result.errorCode); } +// --------------------------------------------------------------------------- +// SMSG_SUMMON_REQUEST +// uint64 summonerGuid + uint32 zoneId + uint32 timeoutMs +// --------------------------------------------------------------------------- + +void GameHandler::handleSummonRequest(network::Packet& packet) { + if (packet.getSize() - packet.getReadPos() < 16) return; + + summonerGuid_ = packet.readUInt64(); + /*uint32_t zoneId =*/ packet.readUInt32(); + uint32_t timeoutMs = packet.readUInt32(); + summonTimeoutSec_ = timeoutMs / 1000.0f; + pendingSummonRequest_= true; + + summonerName_.clear(); + auto entity = entityManager.getEntity(summonerGuid_); + if (auto* unit = dynamic_cast(entity.get())) { + summonerName_ = unit->getName(); + } + if (summonerName_.empty()) { + char tmp[32]; + std::snprintf(tmp, sizeof(tmp), "0x%llX", + static_cast(summonerGuid_)); + summonerName_ = tmp; + } + + addSystemChatMessage(summonerName_ + " is summoning you."); + LOG_INFO("SMSG_SUMMON_REQUEST: summoner=", summonerName_, + " timeout=", summonTimeoutSec_, "s"); +} + +void GameHandler::acceptSummon() { + if (!pendingSummonRequest_ || !socket) return; + pendingSummonRequest_ = false; + network::Packet pkt(wireOpcode(Opcode::CMSG_SUMMON_RESPONSE)); + pkt.writeUInt8(1); // 1 = accept + socket->send(pkt); + addSystemChatMessage("Accepting summon..."); + LOG_INFO("Accepted summon from ", summonerName_); +} + +void GameHandler::declineSummon() { + if (!socket) return; + pendingSummonRequest_ = false; + network::Packet pkt(wireOpcode(Opcode::CMSG_SUMMON_RESPONSE)); + pkt.writeUInt8(0); // 0 = decline + socket->send(pkt); + addSystemChatMessage("Summon declined."); +} + // --------------------------------------------------------------------------- // Trade (SMSG_TRADE_STATUS / SMSG_TRADE_STATUS_EXTENDED) // WotLK 3.3.5a status values: diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 3d9cec81..e1f6915c 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -399,6 +399,7 @@ void GameScreen::render(game::GameHandler& gameHandler) { renderDuelRequestPopup(gameHandler); renderLootRollPopup(gameHandler); renderTradeRequestPopup(gameHandler); + renderSummonRequestPopup(gameHandler); renderGuildInvitePopup(gameHandler); renderGuildRoster(gameHandler); renderBuffBar(gameHandler); @@ -4403,6 +4404,34 @@ void GameScreen::renderDuelRequestPopup(game::GameHandler& gameHandler) { ImGui::End(); } +void GameScreen::renderSummonRequestPopup(game::GameHandler& gameHandler) { + if (!gameHandler.hasPendingSummonRequest()) return; + + auto* window = core::Application::getInstance().getWindow(); + float screenW = window ? static_cast(window->getWidth()) : 1280.0f; + + ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 175, 430), ImGuiCond_Always); + ImGui::SetNextWindowSize(ImVec2(350, 0), ImGuiCond_Always); + + if (ImGui::Begin("Summon Request", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize)) { + ImGui::Text("%s is summoning you.", gameHandler.getSummonerName().c_str()); + float t = gameHandler.getSummonTimeoutSec(); + if (t > 0.0f) { + ImGui::Text("Time remaining: %.0fs", t); + } + ImGui::Spacing(); + + if (ImGui::Button("Accept", ImVec2(130, 30))) { + gameHandler.acceptSummon(); + } + ImGui::SameLine(); + if (ImGui::Button("Decline", ImVec2(130, 30))) { + gameHandler.declineSummon(); + } + } + ImGui::End(); +} + void GameScreen::renderTradeRequestPopup(game::GameHandler& gameHandler) { if (!gameHandler.hasPendingTradeRequest()) return;