mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-23 15:50:20 +00:00
feat: add BG scoreboard (MSG_PVP_LOG_DATA) and fix TBC aura cache for party frames
- Parse MSG_PVP_LOG_DATA to populate BgScoreboardData (players, KB, deaths, HKs, honor, BG-specific stats, winner) - Add /score command to request the scorecard while in a battleground - Render sortable per-player table with team color-coding and self-highlight - Refresh button re-requests live data from server - Fix TBC SMSG_INIT/SET_EXTRA_AURA_INFO_OBSOLETE to populate unitAurasCache_ for all GUIDs (not just player/target), mirroring WotLK aura update behavior so party frame debuff dots work on TBC servers
This commit is contained in:
parent
a4c23b7fa2
commit
79c0887db2
4 changed files with 249 additions and 2 deletions
|
|
@ -611,6 +611,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
|||
renderGmTicketWindow(gameHandler);
|
||||
renderInspectWindow(gameHandler);
|
||||
renderThreatWindow(gameHandler);
|
||||
renderBgScoreboard(gameHandler);
|
||||
renderObjectiveTracker(gameHandler);
|
||||
// renderQuestMarkers(gameHandler); // Disabled - using 3D billboard markers now
|
||||
if (showMinimap_) {
|
||||
|
|
@ -3979,6 +3980,14 @@ void GameScreen::sendChatMessage(game::GameHandler& gameHandler) {
|
|||
return;
|
||||
}
|
||||
|
||||
// /score command — BG scoreboard
|
||||
if (cmdLower == "score") {
|
||||
gameHandler.requestPvpLog();
|
||||
showBgScoreboard_ = true;
|
||||
chatInputBuffer[0] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
// /time command
|
||||
if (cmdLower == "time") {
|
||||
gameHandler.queryServerTime();
|
||||
|
|
@ -4065,7 +4074,7 @@ void GameScreen::sendChatMessage(game::GameHandler& gameHandler) {
|
|||
"Movement: /sit /stand /kneel /dismount",
|
||||
"Misc: /played /time /zone /afk [msg] /dnd [msg] /inspect",
|
||||
" /helm /cloak /trade /join <channel> /leave <channel>",
|
||||
" /unstuck /logout /ticket /help",
|
||||
" /score /unstuck /logout /ticket /help",
|
||||
};
|
||||
for (const char* line : kHelpLines) {
|
||||
game::MessageChatData helpMsg;
|
||||
|
|
@ -17738,6 +17747,139 @@ void GameScreen::renderThreatWindow(game::GameHandler& gameHandler) {
|
|||
ImGui::End();
|
||||
}
|
||||
|
||||
// ─── BG Scoreboard ────────────────────────────────────────────────────────────
|
||||
void GameScreen::renderBgScoreboard(game::GameHandler& gameHandler) {
|
||||
if (!showBgScoreboard_) return;
|
||||
|
||||
const game::GameHandler::BgScoreboardData* data = gameHandler.getBgScoreboard();
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_FirstUseEver);
|
||||
ImGui::SetNextWindowPos(ImVec2(150, 100), ImGuiCond_FirstUseEver);
|
||||
|
||||
const char* title = "Battleground Score###BgScore";
|
||||
if (!ImGui::Begin(title, &showBgScoreboard_, ImGuiWindowFlags_NoCollapse)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
ImGui::TextDisabled("No score data yet.");
|
||||
ImGui::TextDisabled("Use /score to request the scoreboard while in a battleground.");
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
// Winner banner
|
||||
if (data->hasWinner) {
|
||||
const char* winnerStr = (data->winner == 1) ? "Alliance" : "Horde";
|
||||
ImVec4 winnerColor = (data->winner == 1) ? ImVec4(0.4f, 0.6f, 1.0f, 1.0f)
|
||||
: ImVec4(1.0f, 0.35f, 0.35f, 1.0f);
|
||||
float textW = ImGui::CalcTextSize(winnerStr).x + ImGui::CalcTextSize(" Victory!").x;
|
||||
ImGui::SetCursorPosX((ImGui::GetContentRegionAvail().x - textW) * 0.5f);
|
||||
ImGui::TextColored(winnerColor, "%s", winnerStr);
|
||||
ImGui::SameLine(0, 4);
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.85f, 0.0f, 1.0f), "Victory!");
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
// Refresh button
|
||||
if (ImGui::SmallButton("Refresh")) {
|
||||
gameHandler.requestPvpLog();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("%zu players", data->players.size());
|
||||
|
||||
// Score table
|
||||
constexpr ImGuiTableFlags kTableFlags =
|
||||
ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV |
|
||||
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Sortable;
|
||||
|
||||
// Build dynamic column count based on what BG-specific stats are present
|
||||
int numBgCols = 0;
|
||||
std::vector<std::string> bgColNames;
|
||||
for (const auto& ps : data->players) {
|
||||
for (const auto& [fieldName, val] : ps.bgStats) {
|
||||
// Extract short name after last '.' (e.g. "BattlegroundAB.AbFlagCaptures" → "Caps")
|
||||
std::string shortName = fieldName;
|
||||
auto dotPos = fieldName.rfind('.');
|
||||
if (dotPos != std::string::npos) shortName = fieldName.substr(dotPos + 1);
|
||||
bool found = false;
|
||||
for (const auto& n : bgColNames) { if (n == shortName) { found = true; break; } }
|
||||
if (!found) bgColNames.push_back(shortName);
|
||||
}
|
||||
}
|
||||
numBgCols = static_cast<int>(bgColNames.size());
|
||||
|
||||
// Fixed cols: Team | Name | KB | Deaths | HKs | Honor; then BG-specific
|
||||
int totalCols = 6 + numBgCols;
|
||||
float tableH = ImGui::GetContentRegionAvail().y;
|
||||
if (ImGui::BeginTable("##BgScoreTable", totalCols, kTableFlags, ImVec2(0.0f, tableH))) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("Team", ImGuiTableColumnFlags_WidthFixed, 56.0f);
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("KB", ImGuiTableColumnFlags_WidthFixed, 38.0f);
|
||||
ImGui::TableSetupColumn("Deaths", ImGuiTableColumnFlags_WidthFixed, 52.0f);
|
||||
ImGui::TableSetupColumn("HKs", ImGuiTableColumnFlags_WidthFixed, 38.0f);
|
||||
ImGui::TableSetupColumn("Honor", ImGuiTableColumnFlags_WidthFixed, 52.0f);
|
||||
for (const auto& col : bgColNames)
|
||||
ImGui::TableSetupColumn(col.c_str(), ImGuiTableColumnFlags_WidthFixed, 52.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
// Sort: Alliance first, then Horde; within each team by KB desc
|
||||
std::vector<const game::GameHandler::BgPlayerScore*> sorted;
|
||||
sorted.reserve(data->players.size());
|
||||
for (const auto& ps : data->players) sorted.push_back(&ps);
|
||||
std::stable_sort(sorted.begin(), sorted.end(),
|
||||
[](const game::GameHandler::BgPlayerScore* a,
|
||||
const game::GameHandler::BgPlayerScore* b) {
|
||||
if (a->team != b->team) return a->team > b->team; // Alliance(1) first
|
||||
return a->killingBlows > b->killingBlows;
|
||||
});
|
||||
|
||||
uint64_t playerGuid = gameHandler.getPlayerGuid();
|
||||
for (const auto* ps : sorted) {
|
||||
ImGui::TableNextRow();
|
||||
|
||||
// Team
|
||||
ImGui::TableNextColumn();
|
||||
if (ps->team == 1)
|
||||
ImGui::TextColored(ImVec4(0.4f, 0.6f, 1.0f, 1.0f), "Alliance");
|
||||
else
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.35f, 0.35f, 1.0f), "Horde");
|
||||
|
||||
// Name (highlight player's own row)
|
||||
ImGui::TableNextColumn();
|
||||
bool isSelf = (ps->guid == playerGuid);
|
||||
if (isSelf) ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.85f, 0.0f, 1.0f));
|
||||
const char* nameStr = ps->name.empty() ? "Unknown" : ps->name.c_str();
|
||||
ImGui::TextUnformatted(nameStr);
|
||||
if (isSelf) ImGui::PopStyleColor();
|
||||
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", ps->killingBlows);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", ps->deaths);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", ps->honorableKills);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", ps->bonusHonor);
|
||||
|
||||
for (const auto& col : bgColNames) {
|
||||
ImGui::TableNextColumn();
|
||||
uint32_t val = 0;
|
||||
for (const auto& [fieldName, fval] : ps->bgStats) {
|
||||
std::string shortName = fieldName;
|
||||
auto dotPos = fieldName.rfind('.');
|
||||
if (dotPos != std::string::npos) shortName = fieldName.substr(dotPos + 1);
|
||||
if (shortName == col) { val = fval; break; }
|
||||
}
|
||||
if (val > 0) ImGui::Text("%u", val);
|
||||
else ImGui::TextDisabled("-");
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// ─── Quest Objective Tracker ──────────────────────────────────────────────────
|
||||
void GameScreen::renderObjectiveTracker(game::GameHandler& gameHandler) {
|
||||
if (gameHandler.getState() != game::WorldState::IN_WORLD) return;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue