From deed8011d752c96367f7566c2b5fb32e4f4308f3 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 9 Mar 2026 14:52:13 -0700 Subject: [PATCH] Add Reputation tab to character screen with colored tier progress bars - Add Reputation tab in character screen tab bar (Equipment/Stats/Reputation/Skills) - Implement renderReputationPanel() showing all tracked factions sorted alphabetically - Progress bars colored per WoW reputation tier: Hated/Hostile/Unfriendly/Neutral/Friendly/Honored/Revered/Exalted - Add public getFactionNamePublic() backed by DBC name cache with lazy load --- include/game/game_handler.hpp | 1 + include/ui/inventory_screen.hpp | 1 + src/game/game_handler.cpp | 8 +++ src/ui/inventory_screen.cpp | 88 +++++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+) diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index fe631337..b9eda115 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -902,6 +902,7 @@ public: }; const std::vector& getInitialFactions() const { return initialFactions_; } const std::unordered_map& getFactionStandings() const { return factionStandings_; } + const std::string& getFactionNamePublic(uint32_t factionId) const; uint32_t getLastContactListMask() const { return lastContactListMask_; } uint32_t getLastContactListCount() const { return lastContactListCount_; } bool isServerMovementAllowed() const { return serverMovementAllowed_; } diff --git a/include/ui/inventory_screen.hpp b/include/ui/inventory_screen.hpp index bc580bde..a0a19386 100644 --- a/include/ui/inventory_screen.hpp +++ b/include/ui/inventory_screen.hpp @@ -148,6 +148,7 @@ private: void renderEquipmentPanel(game::Inventory& inventory); void renderBackpackPanel(game::Inventory& inventory, bool collapseEmptySections = false); void renderStatsPanel(game::Inventory& inventory, uint32_t playerLevel, int32_t serverArmor = 0); + void renderReputationPanel(game::GameHandler& gameHandler); void renderItemSlot(game::Inventory& inventory, const game::ItemSlot& slot, float size, const char* label, diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 02bca1b1..3ebc05a6 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -16016,5 +16016,13 @@ std::string GameHandler::getFactionName(uint32_t factionId) const { return "faction #" + std::to_string(factionId); } +const std::string& GameHandler::getFactionNamePublic(uint32_t factionId) const { + const_cast(this)->loadFactionNameCache(); + auto it = factionNameCache_.find(factionId); + if (it != factionNameCache_.end()) return it->second; + static const std::string empty; + return empty; +} + } // namespace game } // namespace wowee diff --git a/src/ui/inventory_screen.cpp b/src/ui/inventory_screen.cpp index ee4b54a3..320fc316 100644 --- a/src/ui/inventory_screen.cpp +++ b/src/ui/inventory_screen.cpp @@ -1090,6 +1090,11 @@ void InventoryScreen::renderCharacterScreen(game::GameHandler& gameHandler) { ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("Reputation")) { + renderReputationPanel(gameHandler); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Skills")) { const auto& skills = gameHandler.getPlayerSkills(); if (skills.empty()) { @@ -1171,6 +1176,89 @@ void InventoryScreen::renderCharacterScreen(game::GameHandler& gameHandler) { } } +void InventoryScreen::renderReputationPanel(game::GameHandler& gameHandler) { + const auto& standings = gameHandler.getFactionStandings(); + if (standings.empty()) { + ImGui::Spacing(); + ImGui::TextDisabled("No reputation data received yet."); + ImGui::TextDisabled("Reputation updates as you kill enemies and complete quests."); + return; + } + + // WoW reputation tier breakpoints (cumulative from floor -42000) + // Tier name, threshold for next rank, bar color + struct RepTier { + const char* name; + int32_t floor; // raw value where this tier begins + int32_t ceiling; // raw value where the next tier begins + ImVec4 color; + }; + static const RepTier tiers[] = { + { "Hated", -42000, -6001, ImVec4(0.6f, 0.1f, 0.1f, 1.0f) }, + { "Hostile", -6000, -3001, ImVec4(0.8f, 0.2f, 0.1f, 1.0f) }, + { "Unfriendly", -3000, -1, ImVec4(0.9f, 0.5f, 0.1f, 1.0f) }, + { "Neutral", 0, 2999, ImVec4(0.8f, 0.8f, 0.2f, 1.0f) }, + { "Friendly", 3000, 8999, ImVec4(0.2f, 0.7f, 0.2f, 1.0f) }, + { "Honored", 9000, 20999, ImVec4(0.2f, 0.8f, 0.5f, 1.0f) }, + { "Revered", 21000, 41999, ImVec4(0.3f, 0.6f, 1.0f, 1.0f) }, + { "Exalted", 42000, 42000, ImVec4(1.0f, 0.84f, 0.0f, 1.0f) }, + }; + + auto getTier = [&](int32_t val) -> const RepTier& { + for (int i = 6; i >= 0; --i) { + if (val >= tiers[i].floor) return tiers[i]; + } + return tiers[0]; + }; + + ImGui::BeginChild("##ReputationList", ImVec2(0, 0), true); + + // Sort factions alphabetically by name + std::vector> sortedFactions(standings.begin(), standings.end()); + std::sort(sortedFactions.begin(), sortedFactions.end(), + [&](const auto& a, const auto& b) { + const std::string& na = gameHandler.getFactionNamePublic(a.first); + const std::string& nb = gameHandler.getFactionNamePublic(b.first); + return na < nb; + }); + + for (const auto& [factionId, standing] : sortedFactions) { + const RepTier& tier = getTier(standing); + + const std::string& factionName = gameHandler.getFactionNamePublic(factionId); + const char* displayName = factionName.empty() ? "Unknown Faction" : factionName.c_str(); + + // Faction name + tier label on same line + ImGui::TextColored(tier.color, "[%s]", tier.name); + ImGui::SameLine(90.0f); + ImGui::Text("%s", displayName); + + // Progress bar showing position within current tier + float ratio = 0.0f; + char overlay[64] = ""; + if (tier.floor == 42000) { + // Exalted — full bar + ratio = 1.0f; + snprintf(overlay, sizeof(overlay), "Exalted"); + } else { + int32_t tierRange = tier.ceiling - tier.floor + 1; + int32_t inTier = standing - tier.floor; + ratio = static_cast(inTier) / static_cast(tierRange); + ratio = std::max(0.0f, std::min(1.0f, ratio)); + snprintf(overlay, sizeof(overlay), "%d / %d", + inTier < 0 ? 0 : inTier, tierRange); + } + + ImGui::PushStyleColor(ImGuiCol_PlotHistogram, tier.color); + ImGui::SetNextItemWidth(-1.0f); + ImGui::ProgressBar(ratio, ImVec2(0, 12.0f), overlay); + ImGui::PopStyleColor(); + ImGui::Spacing(); + } + + ImGui::EndChild(); +} + void InventoryScreen::renderEquipmentPanel(game::Inventory& inventory) { ImGui::TextColored(ImVec4(1.0f, 0.84f, 0.0f, 1.0f), "Equipment"); ImGui::Separator();