From 92db25038c457528a3b950f6b0630de58f93cef0 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 12 Mar 2026 02:35:29 -0700 Subject: [PATCH] Parse SMSG_ARENA_TEAM_STATS and display in character screen PvP tab --- include/game/game_handler.hpp | 16 ++++++++++++++++ src/game/game_handler.cpp | 31 ++++++++++++++++++++++++++++++- src/ui/inventory_screen.cpp | 28 ++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index ce836072..b50204fd 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -1070,6 +1070,18 @@ public: uint32_t getLfgBootTimeLeft() const { return lfgBootTimeLeft_; } uint32_t getLfgBootNeeded() const { return lfgBootNeeded_; } + // ---- Arena Team Stats ---- + struct ArenaTeamStats { + uint32_t teamId = 0; + uint32_t rating = 0; + uint32_t weekGames = 0; + uint32_t weekWins = 0; + uint32_t seasonGames = 0; + uint32_t seasonWins = 0; + uint32_t rank = 0; + }; + const std::vector& getArenaTeamStats() const { return arenaTeamStats_; } + // ---- Phase 5: Loot ---- void lootTarget(uint64_t guid); void lootItem(uint8_t slotIndex); @@ -1774,6 +1786,7 @@ private: void handleArenaTeamQueryResponse(network::Packet& packet); void handleArenaTeamInvite(network::Packet& packet); void handleArenaTeamEvent(network::Packet& packet); + void handleArenaTeamStats(network::Packet& packet); void handleArenaError(network::Packet& packet); // ---- Bank handlers ---- @@ -2127,6 +2140,9 @@ private: // Instance / raid lockouts std::vector instanceLockouts_; + // Arena team stats (indexed by team slot, updated by SMSG_ARENA_TEAM_STATS) + std::vector arenaTeamStats_; + // 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 e792f459..aeb9a2e9 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -4924,7 +4924,7 @@ void GameHandler::handlePacket(network::Packet& packet) { handleArenaTeamEvent(packet); break; case Opcode::SMSG_ARENA_TEAM_STATS: - LOG_INFO("Received SMSG_ARENA_TEAM_STATS"); + handleArenaTeamStats(packet); break; case Opcode::SMSG_ARENA_ERROR: handleArenaError(packet); @@ -13276,6 +13276,35 @@ void GameHandler::handleArenaTeamEvent(network::Packet& packet) { LOG_INFO("Arena team event: ", eventName, " ", param1, " ", param2); } +void GameHandler::handleArenaTeamStats(network::Packet& packet) { + // SMSG_ARENA_TEAM_STATS (WotLK 3.3.5a): + // uint32 teamId, uint32 rating, uint32 weekGames, uint32 weekWins, + // uint32 seasonGames, uint32 seasonWins, uint32 rank + if (packet.getSize() - packet.getReadPos() < 28) return; + + ArenaTeamStats stats; + stats.teamId = packet.readUInt32(); + stats.rating = packet.readUInt32(); + stats.weekGames = packet.readUInt32(); + stats.weekWins = packet.readUInt32(); + stats.seasonGames = packet.readUInt32(); + stats.seasonWins = packet.readUInt32(); + stats.rank = packet.readUInt32(); + + // Update or insert for this team + for (auto& s : arenaTeamStats_) { + if (s.teamId == stats.teamId) { + s = stats; + LOG_INFO("SMSG_ARENA_TEAM_STATS: teamId=", stats.teamId, + " rating=", stats.rating, " rank=", stats.rank); + return; + } + } + arenaTeamStats_.push_back(stats); + LOG_INFO("SMSG_ARENA_TEAM_STATS: teamId=", stats.teamId, + " rating=", stats.rating, " rank=", stats.rank); +} + void GameHandler::handleArenaError(network::Packet& packet) { if (packet.getSize() - packet.getReadPos() < 4) return; uint32_t error = packet.readUInt32(); diff --git a/src/ui/inventory_screen.cpp b/src/ui/inventory_screen.cpp index bcc3d0e0..f2b40f7c 100644 --- a/src/ui/inventory_screen.cpp +++ b/src/ui/inventory_screen.cpp @@ -1270,6 +1270,34 @@ void InventoryScreen::renderCharacterScreen(game::GameHandler& gameHandler) { ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("PvP")) { + const auto& arenaStats = gameHandler.getArenaTeamStats(); + if (arenaStats.empty()) { + ImGui::Spacing(); + ImGui::TextDisabled("Not a member of any Arena team."); + } else { + for (const auto& team : arenaStats) { + ImGui::PushID(static_cast(team.teamId)); + char header[64]; + snprintf(header, sizeof(header), "Team ID %u (Rating: %u)", team.teamId, team.rating); + if (ImGui::CollapsingHeader(header, ImGuiTreeNodeFlags_DefaultOpen)) { + ImGui::Columns(2, "##arenacols", false); + ImGui::Text("Rating:"); ImGui::NextColumn(); + ImGui::Text("%u", team.rating); ImGui::NextColumn(); + ImGui::Text("Rank:"); ImGui::NextColumn(); + ImGui::Text("#%u", team.rank); ImGui::NextColumn(); + ImGui::Text("This week:"); ImGui::NextColumn(); + ImGui::Text("%u / %u (W/G)", team.weekWins, team.weekGames); ImGui::NextColumn(); + ImGui::Text("Season:"); ImGui::NextColumn(); + ImGui::Text("%u / %u (W/G)", team.seasonWins, team.seasonGames); ImGui::NextColumn(); + ImGui::Columns(1); + } + ImGui::PopID(); + } + } + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); }