From 4a439fb0d1b049a0e400eae9b3237d53dc26bb30 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 17 Mar 2026 19:04:40 -0700 Subject: [PATCH] feat: add clock-sweep arc to buff bar and target aura icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aura icons on the player buff bar and the target frame now display a WoW-style dark fan overlay that sweeps clockwise as the buff/debuff elapses, providing instant visual feedback on remaining duration. The sweep uses AuraSlot::maxDurationMs / getRemainingMs() — the same data that already drives the numeric countdown — so no new state is required. Only temporary auras (maxDurationMs > 0) show a sweep; permanent buffs remain unaffected. --- src/ui/game_screen.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index ea755177..7358a59c 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -4461,6 +4461,32 @@ void GameScreen::renderTargetFrame(game::GameHandler& gameHandler) { std::chrono::steady_clock::now().time_since_epoch()).count()); int32_t tRemainMs = aura.getRemainingMs(tNowMs); + // Clock-sweep overlay (elapsed = dark area, WoW style) + if (tRemainMs > 0 && aura.maxDurationMs > 0) { + ImVec2 tIconMin = ImGui::GetItemRectMin(); + ImVec2 tIconMax = ImGui::GetItemRectMax(); + float tcx = (tIconMin.x + tIconMax.x) * 0.5f; + float tcy = (tIconMin.y + tIconMax.y) * 0.5f; + float tR = (tIconMax.x - tIconMin.x) * 0.5f; + float tTot = static_cast(aura.maxDurationMs); + float tFrac = std::clamp( + 1.0f - static_cast(tRemainMs) / tTot, 0.0f, 1.0f); + if (tFrac > 0.005f) { + constexpr int TSEGS = 24; + float tSa = -IM_PI * 0.5f; + float tEa = tSa + tFrac * 2.0f * IM_PI; + ImVec2 tPts[TSEGS + 2]; + tPts[0] = ImVec2(tcx, tcy); + for (int s = 0; s <= TSEGS; ++s) { + float a = tSa + (tEa - tSa) * s / static_cast(TSEGS); + tPts[s + 1] = ImVec2(tcx + std::cos(a) * tR, + tcy + std::sin(a) * tR); + } + ImGui::GetWindowDrawList()->AddConvexPolyFilled( + tPts, TSEGS + 2, IM_COL32(0, 0, 0, 145)); + } + } + // Duration countdown overlay if (tRemainMs > 0) { ImVec2 iconMin = ImGui::GetItemRectMin(); @@ -13167,6 +13193,32 @@ void GameScreen::renderBuffBar(game::GameHandler& gameHandler) { std::chrono::steady_clock::now().time_since_epoch()).count()); int32_t remainMs = aura.getRemainingMs(nowMs); + // Clock-sweep overlay: dark fan shows elapsed time (WoW style) + if (remainMs > 0 && aura.maxDurationMs > 0) { + ImVec2 iconMin2 = ImGui::GetItemRectMin(); + ImVec2 iconMax2 = ImGui::GetItemRectMax(); + float cx2 = (iconMin2.x + iconMax2.x) * 0.5f; + float cy2 = (iconMin2.y + iconMax2.y) * 0.5f; + float fanR2 = (iconMax2.x - iconMin2.x) * 0.5f; + float total2 = static_cast(aura.maxDurationMs); + float elapsedFrac2 = std::clamp( + 1.0f - static_cast(remainMs) / total2, 0.0f, 1.0f); + if (elapsedFrac2 > 0.005f) { + constexpr int SWEEP_SEGS = 24; + float sa = -IM_PI * 0.5f; + float ea = sa + elapsedFrac2 * 2.0f * IM_PI; + ImVec2 pts[SWEEP_SEGS + 2]; + pts[0] = ImVec2(cx2, cy2); + for (int s = 0; s <= SWEEP_SEGS; ++s) { + float a = sa + (ea - sa) * s / static_cast(SWEEP_SEGS); + pts[s + 1] = ImVec2(cx2 + std::cos(a) * fanR2, + cy2 + std::sin(a) * fanR2); + } + ImGui::GetWindowDrawList()->AddConvexPolyFilled( + pts, SWEEP_SEGS + 2, IM_COL32(0, 0, 0, 145)); + } + } + // Duration countdown overlay — always visible on the icon bottom if (remainMs > 0) { ImVec2 iconMin = ImGui::GetItemRectMin();