From e2f36f6ac5d49a92d270bc042edb69f68ea9c184 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 12 Mar 2026 15:05:52 -0700 Subject: [PATCH] feat: show party member dots on world map with name labels and class colors --- include/rendering/world_map.hpp | 12 ++++++++++++ src/rendering/world_map.cpp | 27 +++++++++++++++++++++++++++ src/ui/game_screen.cpp | 25 +++++++++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/include/rendering/world_map.hpp b/include/rendering/world_map.hpp index 47956b42..77a98ec0 100644 --- a/include/rendering/world_map.hpp +++ b/include/rendering/world_map.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,13 @@ class VkContext; class VkTexture; class VkRenderTarget; +/// Party member dot passed in from the UI layer for world map overlay. +struct WorldMapPartyDot { + glm::vec3 renderPos; ///< Position in render-space coordinates + uint32_t color; ///< RGBA packed color (IM_COL32 format) + std::string name; ///< Member name (shown as tooltip on hover) +}; + struct WorldMapZone { uint32_t wmaID = 0; uint32_t areaID = 0; // 0 = continent level @@ -47,6 +55,7 @@ public: void setMapName(const std::string& name); void setServerExplorationMask(const std::vector& masks, bool hasData); + void setPartyDots(std::vector dots) { partyDots_ = std::move(dots); } bool isOpen() const { return open; } void close() { open = false; } @@ -113,6 +122,9 @@ private: // Texture storage (owns all VkTexture objects for zone tiles) std::vector> zoneTextures; + // Party member dots (set each frame from the UI layer) + std::vector partyDots_; + // Exploration / fog of war std::vector serverExplorationMask; bool hasServerExplorationMask = false; diff --git a/src/rendering/world_map.cpp b/src/rendering/world_map.cpp index 701c5148..138d39db 100644 --- a/src/rendering/world_map.cpp +++ b/src/rendering/world_map.cpp @@ -1017,6 +1017,33 @@ void WorldMap::renderImGuiOverlay(const glm::vec3& playerRenderPos, int screenWi } } + // Party member dots + if (currentIdx >= 0 && viewLevel != ViewLevel::WORLD) { + ImFont* font = ImGui::GetFont(); + for (const auto& dot : partyDots_) { + glm::vec2 uv = renderPosToMapUV(dot.renderPos, currentIdx); + if (uv.x < 0.0f || uv.x > 1.0f || uv.y < 0.0f || uv.y > 1.0f) continue; + float px = imgMin.x + uv.x * displayW; + float py = imgMin.y + uv.y * displayH; + drawList->AddCircleFilled(ImVec2(px, py), 5.0f, dot.color); + drawList->AddCircle(ImVec2(px, py), 5.0f, IM_COL32(0, 0, 0, 200), 0, 1.5f); + // Name tooltip on hover + if (!dot.name.empty()) { + ImVec2 mp = ImGui::GetMousePos(); + float dx = mp.x - px, dy = mp.y - py; + if (dx * dx + dy * dy <= 49.0f) { // radius 7 px hit area + ImGui::SetTooltip("%s", dot.name.c_str()); + } + // Draw name label above the dot + ImVec2 nameSz = font->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, 0.0f, dot.name.c_str()); + float tx = px - nameSz.x * 0.5f; + float ty = py - nameSz.y - 7.0f; + drawList->AddText(ImVec2(tx + 1.0f, ty + 1.0f), IM_COL32(0, 0, 0, 180), dot.name.c_str()); + drawList->AddText(ImVec2(tx, ty), IM_COL32(255, 255, 255, 220), dot.name.c_str()); + } + } + } + // Hover coordinate display — show WoW coordinates under cursor if (currentIdx >= 0 && viewLevel != ViewLevel::WORLD) { auto& io = ImGui::GetIO(); diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 71be9279..ae1a3f41 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -6139,6 +6139,31 @@ void GameScreen::renderWorldMap(game::GameHandler& gameHandler) { gameHandler.getPlayerExploredZoneMasks(), gameHandler.hasPlayerExploredZoneMasks()); + // Party member dots on world map + { + std::vector dots; + if (gameHandler.isInGroup()) { + const auto& partyData = gameHandler.getPartyData(); + for (const auto& member : partyData.members) { + if (!member.isOnline || !member.hasPartyStats) continue; + if (member.posX == 0 && member.posY == 0) continue; + // posY → canonical X (north), posX → canonical Y (west) + float wowX = static_cast(member.posY); + float wowY = static_cast(member.posX); + glm::vec3 rpos = core::coords::canonicalToRender(glm::vec3(wowX, wowY, 0.0f)); + auto ent = gameHandler.getEntityManager().getEntity(member.guid); + uint8_t cid = entityClassId(ent.get()); + ImU32 col = (cid != 0) + ? classColorU32(cid, 230) + : (member.guid == partyData.leaderGuid + ? IM_COL32(255, 210, 0, 230) + : IM_COL32(100, 180, 255, 230)); + dots.push_back({ rpos, col, member.name }); + } + } + wm->setPartyDots(std::move(dots)); + } + glm::vec3 playerPos = renderer->getCharacterPosition(); auto* window = app.getWindow(); int screenW = window ? window->getWidth() : 1280;