From 113be66314bf293a52aae1aae38b5294710d6e18 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 17 Mar 2026 20:54:59 -0700 Subject: [PATCH] feat: parse MSG_BATTLEGROUND_PLAYER_POSITIONS and show flag carriers on minimap Replaces the silent consume with full packet parsing: reads two lists of (guid, x, y) positions (typically ally and horde flag carriers) and stores them in bgPlayerPositions_. Renders each as a colored diamond on the minimap (blue=group0, red=group1) with a "Flag carrier" tooltip showing the player's name when available. --- include/game/game_handler.hpp | 12 +++++++++ src/game/game_handler.cpp | 18 ++++++++++--- src/ui/game_screen.cpp | 49 +++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 114ee071..027b5de8 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -497,6 +497,15 @@ public: return bgScoreboard_.players.empty() ? nullptr : &bgScoreboard_; } + // BG flag carrier / important player positions (MSG_BATTLEGROUND_PLAYER_POSITIONS) + struct BgPlayerPosition { + uint64_t guid = 0; + float wowX = 0.0f; // canonical WoW X (north) + float wowY = 0.0f; // canonical WoW Y (west) + int group = 0; // 0 = first list (usually ally flag carriers), 1 = second list + }; + const std::vector& getBgPlayerPositions() const { return bgPlayerPositions_; } + // Network latency (milliseconds, updated each PONG response) uint32_t getLatencyMs() const { return lastLatency; } @@ -2780,6 +2789,9 @@ private: // BG scoreboard (MSG_PVP_LOG_DATA) BgScoreboardData bgScoreboard_; + // BG flag carrier / player positions (MSG_BATTLEGROUND_PLAYER_POSITIONS) + std::vector bgPlayerPositions_; + // Instance encounter boss units (slots 0-4 from SMSG_UPDATE_INSTANCE_ENCOUNTER_UNIT) std::array encounterUnitGuids_ = {}; // 0 = empty slot diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 7f1c7b02..8f83e052 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -5587,10 +5587,22 @@ void GameHandler::handlePacket(network::Packet& packet) { addUIError("Battlefield port denied."); addSystemChatMessage("Battlefield port denied."); break; - case Opcode::MSG_BATTLEGROUND_PLAYER_POSITIONS: - // Optional map position updates for BG objectives/players. - packet.setReadPos(packet.getSize()); + case Opcode::MSG_BATTLEGROUND_PLAYER_POSITIONS: { + bgPlayerPositions_.clear(); + for (int grp = 0; grp < 2; ++grp) { + if (packet.getSize() - packet.getReadPos() < 4) break; + uint32_t count = packet.readUInt32(); + for (uint32_t i = 0; i < count && packet.getSize() - packet.getReadPos() >= 16; ++i) { + BgPlayerPosition pos; + pos.guid = packet.readUInt64(); + pos.wowX = packet.readFloat(); + pos.wowY = packet.readFloat(); + pos.group = grp; + bgPlayerPositions_.push_back(pos); + } + } break; + } case Opcode::SMSG_REMOVED_FROM_PVP_QUEUE: addSystemChatMessage("You have been removed from the PvP queue."); break; diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index b7cc3135..6430e1df 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -17391,6 +17391,55 @@ void GameScreen::renderMinimapMarkers(game::GameHandler& gameHandler) { } } + // BG flag carrier / important player positions (MSG_BATTLEGROUND_PLAYER_POSITIONS) + { + const auto& bgPositions = gameHandler.getBgPlayerPositions(); + if (!bgPositions.empty()) { + ImVec2 mouse = ImGui::GetMousePos(); + // group 0 = typically ally-held flag / first list; group 1 = enemy + static const ImU32 kBgGroupColors[2] = { + IM_COL32( 80, 180, 255, 240), // group 0: blue (alliance) + IM_COL32(220, 50, 50, 240), // group 1: red (horde) + }; + for (const auto& bp : bgPositions) { + // Packet coords: wowX=canonical X (north), wowY=canonical Y (west) + glm::vec3 bpRender = core::coords::canonicalToRender(glm::vec3(bp.wowX, bp.wowY, 0.0f)); + float sx = 0.0f, sy = 0.0f; + if (!projectToMinimap(bpRender, sx, sy)) continue; + + ImU32 col = kBgGroupColors[bp.group & 1]; + + // Draw a flag-like diamond icon + const float r = 5.0f; + ImVec2 top (sx, sy - r); + ImVec2 right(sx + r, sy ); + ImVec2 bot (sx, sy + r); + ImVec2 left (sx - r, sy ); + drawList->AddQuadFilled(top, right, bot, left, col); + drawList->AddQuad(top, right, bot, left, IM_COL32(255, 255, 255, 180), 1.0f); + + float mdx = mouse.x - sx, mdy = mouse.y - sy; + if (mdx * mdx + mdy * mdy < 64.0f) { + // Show entity name if available, otherwise guid + auto ent = gameHandler.getEntityManager().getEntity(bp.guid); + if (ent) { + std::string nm; + if (ent->getType() == game::ObjectType::PLAYER) { + auto pl = std::static_pointer_cast(ent); + nm = pl ? pl->getName() : ""; + } + if (!nm.empty()) + ImGui::SetTooltip("Flag carrier: %s", nm.c_str()); + else + ImGui::SetTooltip("Flag carrier"); + } else { + ImGui::SetTooltip("Flag carrier"); + } + } + } + } + } + // Corpse direction indicator — shown when player is a ghost if (gameHandler.isPlayerGhost()) { float corpseCanX = 0.0f, corpseCanY = 0.0f;