From bcd984c1c50f0709ba94de95769d4887440a450f Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 12 Mar 2026 06:08:26 -0700 Subject: [PATCH] feat: sort buff/debuff icons by remaining duration Both the player buff bar and target frame aura display now sort auras so that shorter-duration (more urgent) buffs/debuffs appear first. Permanent auras (no duration) sort to the end. In the target frame, debuffs are sorted before buffs. In the player buff bar, the existing buffs-first / debuffs-second pass ordering is preserved, with ascending duration sort within each group. --- src/ui/game_screen.cpp | 50 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index b6b9e119..db6402c8 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -2937,8 +2937,31 @@ void GameScreen::renderTargetFrame(game::GameHandler& gameHandler) { ImGui::Separator(); + // Build sorted index list: debuffs before buffs, shorter duration first + uint64_t tNowSort = static_cast( + std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count()); + std::vector sortedIdx; + sortedIdx.reserve(targetAuras.size()); + for (size_t i = 0; i < targetAuras.size(); ++i) + if (!targetAuras[i].isEmpty()) sortedIdx.push_back(i); + std::sort(sortedIdx.begin(), sortedIdx.end(), [&](size_t a, size_t b) { + const auto& aa = targetAuras[a]; const auto& ab = targetAuras[b]; + bool aDebuff = (aa.flags & 0x80) != 0; + bool bDebuff = (ab.flags & 0x80) != 0; + if (aDebuff != bDebuff) return aDebuff > bDebuff; // debuffs first + int32_t ra = aa.getRemainingMs(tNowSort); + int32_t rb = ab.getRemainingMs(tNowSort); + // Permanent (-1) goes last; shorter remaining goes first + if (ra < 0 && rb < 0) return false; + if (ra < 0) return false; + if (rb < 0) return true; + return ra < rb; + }); + int shown = 0; - for (size_t i = 0; i < targetAuras.size() && shown < 16; ++i) { + for (size_t si = 0; si < sortedIdx.size() && shown < 16; ++si) { + size_t i = sortedIdx[si]; const auto& aura = targetAuras[i]; if (aura.isEmpty()) continue; @@ -9236,12 +9259,33 @@ void GameScreen::renderBuffBar(game::GameHandler& gameHandler) { ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2.0f, 2.0f)); if (ImGui::Begin("##BuffBar", nullptr, flags)) { - // Separate buffs and debuffs; show buffs first, then debuffs with a visual gap + // Pre-sort auras: buffs first, then debuffs; within each group, shorter remaining first + uint64_t buffNowMs = static_cast( + std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count()); + std::vector buffSortedIdx; + buffSortedIdx.reserve(auras.size()); + for (size_t i = 0; i < auras.size(); ++i) + if (!auras[i].isEmpty()) buffSortedIdx.push_back(i); + std::sort(buffSortedIdx.begin(), buffSortedIdx.end(), [&](size_t a, size_t b) { + const auto& aa = auras[a]; const auto& ab = auras[b]; + bool aDebuff = (aa.flags & 0x80) != 0; + bool bDebuff = (ab.flags & 0x80) != 0; + if (aDebuff != bDebuff) return aDebuff < bDebuff; // buffs (0) first + int32_t ra = aa.getRemainingMs(buffNowMs); + int32_t rb = ab.getRemainingMs(buffNowMs); + if (ra < 0 && rb < 0) return false; + if (ra < 0) return false; + if (rb < 0) return true; + return ra < rb; + }); + // Render one pass for buffs, one for debuffs for (int pass = 0; pass < 2; ++pass) { bool wantBuff = (pass == 0); int shown = 0; - for (size_t i = 0; i < auras.size() && shown < 40; ++i) { + for (size_t si = 0; si < buffSortedIdx.size() && shown < 40; ++si) { + size_t i = buffSortedIdx[si]; const auto& aura = auras[i]; if (aura.isEmpty()) continue;