Implement minimap ping: parse MSG_MINIMAP_PING and render animated ping circles

Parse party member minimap pings (packed GUID + posX + posY), store with
5s lifetime, and render as expanding concentric circles on the minimap.
This commit is contained in:
Kelsi 2026-03-09 20:36:20 -07:00
parent 0562139868
commit 95e8fcb88e
3 changed files with 53 additions and 3 deletions

View file

@ -779,6 +779,7 @@ void GameHandler::update(float deltaTime) {
// Update combat text (Phase 2)
updateCombatText(deltaTime);
tickMinimapPings(deltaTime);
// Update taxi landing cooldown
if (taxiLandingCooldown_ > 0.0f) {
@ -2588,10 +2589,21 @@ void GameHandler::handlePacket(network::Packet& packet) {
case Opcode::SMSG_FISH_ESCAPED:
addSystemChatMessage("Your fish escaped!");
break;
case Opcode::MSG_MINIMAP_PING:
// Minimap ping from a party member — consume; no visual support yet.
packet.setReadPos(packet.getSize());
case Opcode::MSG_MINIMAP_PING: {
// SMSG: packed_guid + float posX (canonical WoW Y=west) + float posY (canonical WoW X=north)
if (packet.getSize() - packet.getReadPos() < 1) break;
uint64_t senderGuid = UpdateObjectParser::readPackedGuid(packet);
if (packet.getSize() - packet.getReadPos() < 8) break;
float pingX = packet.readFloat(); // server sends map-coord X (east-west)
float pingY = packet.readFloat(); // server sends map-coord Y (north-south)
MinimapPing ping;
ping.senderGuid = senderGuid;
ping.wowX = pingY; // canonical WoW X = north = server's posY
ping.wowY = pingX; // canonical WoW Y = west = server's posX
ping.age = 0.0f;
minimapPings_.push_back(ping);
break;
}
case Opcode::SMSG_ZONE_UNDER_ATTACK: {
// uint32 areaId
if (packet.getSize() - packet.getReadPos() >= 4) {

View file

@ -8040,6 +8040,25 @@ void GameScreen::renderMinimapMarkers(game::GameHandler& gameHandler) {
}
}
// Minimap pings from party members
for (const auto& ping : gameHandler.getMinimapPings()) {
glm::vec3 pingRender = core::coords::canonicalToRender(glm::vec3(ping.wowX, ping.wowY, 0.0f));
float sx = 0.0f, sy = 0.0f;
if (!projectToMinimap(pingRender, sx, sy)) continue;
float t = ping.age / game::GameHandler::MinimapPing::LIFETIME;
float alpha = 1.0f - t;
float pulse = 1.0f + 1.5f * t; // expands outward as it fades
ImU32 col = IM_COL32(255, 220, 0, static_cast<int>(alpha * 200));
ImU32 col2 = IM_COL32(255, 150, 0, static_cast<int>(alpha * 100));
float r1 = 4.0f * pulse;
float r2 = 8.0f * pulse;
drawList->AddCircle(ImVec2(sx, sy), r1, col, 16, 2.0f);
drawList->AddCircle(ImVec2(sx, sy), r2, col2, 16, 1.0f);
drawList->AddCircleFilled(ImVec2(sx, sy), 2.5f, col);
}
auto applyMuteState = [&]() {
auto* activeRenderer = core::Application::getInstance().getRenderer();
float masterScale = soundMuted_ ? 0.0f : static_cast<float>(pendingMasterVolume) / 100.0f;