From 65c4bd1a1796028d54384a46d1b270da8f4634f1 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 10 Mar 2026 05:36:59 -0700 Subject: [PATCH] ui: WoW-style clock-sweep cooldown overlay on action bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the plain yellow text cooldown overlay with a proper clock-sweep: - Dark fan spanning the elapsed fraction of the cooldown, sweeping clockwise from 12 o'clock (matches WoW's native cooldown look) - White remaining-time text with drop-shadow centered on the icon - Minutes shown as "Xm" for cooldowns >= 60s, seconds otherwise - Fan radius set to 1.5× the icon half-width to cover corners on the square icon; works for both icon and empty (label-only) slots --- src/ui/game_screen.cpp | 51 +++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 37af08a5..b72363cb 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -4049,23 +4049,48 @@ void GameScreen::renderActionBar(game::GameHandler& gameHandler) { ImGui::EndTooltip(); } - // Cooldown overlay - if (onCooldown && iconTex) { - // Draw cooldown text centered over the icon + // Cooldown overlay: WoW-style clock-sweep + time text + if (onCooldown) { ImVec2 btnMin = ImGui::GetItemRectMin(); ImVec2 btnMax = ImGui::GetItemRectMax(); + float cx = (btnMin.x + btnMax.x) * 0.5f; + float cy = (btnMin.y + btnMax.y) * 0.5f; + float r = (btnMax.x - btnMin.x) * 0.5f; + + auto* dl = ImGui::GetWindowDrawList(); + + // Dark sweep over the elapsed fraction, starting at 12 o'clock + float total = (slot.cooldownTotal > 0.0f) ? slot.cooldownTotal : 1.0f; + float elapsed = total - slot.cooldownRemaining; + float elapsedFrac = std::min(1.0f, std::max(0.0f, elapsed / total)); + + if (elapsedFrac > 0.005f) { + constexpr int N_SEGS = 32; + float startAngle = -IM_PI * 0.5f; + float endAngle = startAngle + elapsedFrac * 2.0f * IM_PI; + float fanR = r * 1.5f; // reach the icon corners + ImVec2 pts[N_SEGS + 2]; + pts[0] = ImVec2(cx, cy); + for (int s = 0; s <= N_SEGS; ++s) { + float a = startAngle + (endAngle - startAngle) * s / static_cast(N_SEGS); + pts[s + 1] = ImVec2(cx + std::cos(a) * fanR, cy + std::sin(a) * fanR); + } + dl->AddConvexPolyFilled(pts, N_SEGS + 2, IM_COL32(0, 0, 0, 170)); + } + + // Remaining-time text: white with drop-shadow char cdText[16]; - snprintf(cdText, sizeof(cdText), "%.0f", slot.cooldownRemaining); + float cd = slot.cooldownRemaining; + if (cd >= 60.0f) { + snprintf(cdText, sizeof(cdText), "%dm", static_cast(cd) / 60); + } else { + snprintf(cdText, sizeof(cdText), "%.0f", cd); + } ImVec2 textSize = ImGui::CalcTextSize(cdText); - float cx = btnMin.x + (btnMax.x - btnMin.x - textSize.x) * 0.5f; - float cy = btnMin.y + (btnMax.y - btnMin.y - textSize.y) * 0.5f; - ImGui::GetWindowDrawList()->AddText(ImVec2(cx, cy), - IM_COL32(255, 255, 0, 255), cdText); - } else if (onCooldown) { - char cdText[16]; - snprintf(cdText, sizeof(cdText), "%.0f", slot.cooldownRemaining); - ImGui::SetCursorPosY(ImGui::GetCursorPosY() - slotSize / 2 - 8); - ImGui::TextColored(ImVec4(1, 1, 0, 1), "%s", cdText); + float tx = cx - textSize.x * 0.5f; + float ty = cy - textSize.y * 0.5f; + dl->AddText(ImVec2(tx + 1.0f, ty + 1.0f), IM_COL32(0, 0, 0, 220), cdText); + dl->AddText(ImVec2(tx, ty), IM_COL32(255, 255, 255, 255), cdText); } // Key label below