From ae8f900410f83a61523cc056169c14f903ee5e56 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 11 Mar 2026 23:00:03 -0700 Subject: [PATCH] Add Ctrl+click minimap ping sending Ctrl+clicking on the minimap converts screen position to world coordinates and sends MSG_MINIMAP_PING to the server. A local ping is also added immediately so the sender sees their own ping. --- include/game/game_handler.hpp | 3 +++ src/game/game_handler.cpp | 23 +++++++++++++++++++++++ src/ui/game_screen.cpp | 22 ++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index b2901486..870e774c 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -388,6 +388,9 @@ public: // PvP void togglePvp(); + // Minimap ping (Ctrl+click on minimap; wowX/wowY in canonical WoW coords) + void sendMinimapPing(float wowX, float wowY); + // Guild commands void requestGuildInfo(); void requestGuildRoster(); diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index b267f67b..dc33651f 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -7710,6 +7710,29 @@ void GameHandler::sendPing() { socket->send(packet); } +void GameHandler::sendMinimapPing(float wowX, float wowY) { + if (state != WorldState::IN_WORLD) return; + + // MSG_MINIMAP_PING (CMSG direction): float posX + float posY + // Server convention: posX = east/west axis = canonical Y (west) + // posY = north/south axis = canonical X (north) + const float serverX = wowY; // canonical Y (west) → server posX + const float serverY = wowX; // canonical X (north) → server posY + + network::Packet pkt(wireOpcode(Opcode::MSG_MINIMAP_PING)); + pkt.writeFloat(serverX); + pkt.writeFloat(serverY); + socket->send(pkt); + + // Add ping locally so the sender sees their own ping immediately + MinimapPing localPing; + localPing.senderGuid = activeCharacterGuid_; + localPing.wowX = wowX; + localPing.wowY = wowY; + localPing.age = 0.0f; + minimapPings_.push_back(localPing); +} + void GameHandler::handlePong(network::Packet& packet) { LOG_DEBUG("Handling SMSG_PONG"); diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index ad110cc3..deefa228 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -10654,6 +10654,28 @@ void GameScreen::renderMinimapMarkers(game::GameHandler& gameHandler) { } } + // Ctrl+click on minimap → send minimap ping to party + if (ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyCtrl) { + ImVec2 mouse = ImGui::GetMousePos(); + float mdx = mouse.x - centerX; + float mdy = mouse.y - centerY; + float distSq = mdx * mdx + mdy * mdy; + if (distSq <= mapRadius * mapRadius) { + // Invert projectToMinimap: px=mdx, py=mdy → rx=px*viewRadius/mapRadius + float rx = mdx * viewRadius / mapRadius; + float ry = mdy * viewRadius / mapRadius; + // rx/ry are in rotated frame; unrotate to get world dx/dy + // rx = -(dx*cosB + dy*sinB), ry = dx*sinB - dy*cosB + // Solving: dx = -(rx*cosB - ry*sinB), dy = -(rx*sinB + ry*cosB) + float wdx = -(rx * cosB - ry * sinB); + float wdy = -(rx * sinB + ry * cosB); + // playerRender is in render coords; add delta to get render position then convert to canonical + glm::vec3 clickRender = playerRender + glm::vec3(wdx, wdy, 0.0f); + glm::vec3 clickCanon = core::coords::renderToCanonical(clickRender); + gameHandler.sendMinimapPing(clickCanon.x, clickCanon.y); + } + } + auto applyMuteState = [&]() { auto* activeRenderer = core::Application::getInstance().getRenderer(); float masterScale = soundMuted_ ? 0.0f : static_cast(pendingMasterVolume) / 100.0f;