diff --git a/include/ui/game_screen.hpp b/include/ui/game_screen.hpp index fd30476e..17327216 100644 --- a/include/ui/game_screen.hpp +++ b/include/ui/game_screen.hpp @@ -283,6 +283,7 @@ private: * Render pet frame (below player frame when player has an active pet) */ void renderPetFrame(game::GameHandler& gameHandler); + void renderTotemFrame(game::GameHandler& gameHandler); /** * Process targeting input (Tab, Escape, click) diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 35907642..be4fe73e 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -516,6 +516,11 @@ void GameScreen::render(game::GameHandler& gameHandler) { renderPetFrame(gameHandler); } + // Totem frame (Shaman only, when any totem is active) + if (gameHandler.getPlayerClass() == 7) { + renderTotemFrame(gameHandler); + } + // Target frame (only when we have a target) if (gameHandler.hasTarget()) { renderTargetFrame(gameHandler); @@ -3032,6 +3037,87 @@ void GameScreen::renderPetFrame(game::GameHandler& gameHandler) { ImGui::PopStyleVar(); } +// ============================================================ +// Totem Frame (Shaman — below pet frame / player frame) +// ============================================================ + +void GameScreen::renderTotemFrame(game::GameHandler& gameHandler) { + // Only show if at least one totem is active + bool anyActive = false; + for (int i = 0; i < game::GameHandler::NUM_TOTEM_SLOTS; ++i) { + if (gameHandler.getTotemSlot(i).active()) { anyActive = true; break; } + } + if (!anyActive) return; + + static const struct { const char* name; ImU32 color; } kTotemInfo[4] = { + { "Earth", IM_COL32(139, 90, 43, 255) }, // brown + { "Fire", IM_COL32(220, 80, 30, 255) }, // red-orange + { "Water", IM_COL32( 30,120, 220, 255) }, // blue + { "Air", IM_COL32(180,220, 255, 255) }, // light blue + }; + + // Position: below pet frame / player frame, left side + // Pet frame is at ~y=200 if active, player frame is at y=20; totem frame near y=300 + // We anchor relative to screen left edge like pet frame + ImGui::SetNextWindowPos(ImVec2(8.0f, 300.0f), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize(ImVec2(130.0f, 0.0f), ImGuiCond_Always); + + ImGuiWindowFlags flags = ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | + ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoTitleBar; + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.1f, 0.08f, 0.06f, 0.88f)); + + if (ImGui::Begin("##TotemFrame", nullptr, flags)) { + ImGui::TextColored(ImVec4(0.9f, 0.75f, 0.3f, 1.0f), "Totems"); + ImGui::Separator(); + + for (int i = 0; i < game::GameHandler::NUM_TOTEM_SLOTS; ++i) { + const auto& slot = gameHandler.getTotemSlot(i); + if (!slot.active()) continue; + + ImGui::PushID(i); + + // Colored element dot + ImVec2 dotPos = ImGui::GetCursorScreenPos(); + dotPos.x += 4.0f; dotPos.y += 6.0f; + ImGui::GetWindowDrawList()->AddCircleFilled( + ImVec2(dotPos.x + 4.0f, dotPos.y + 4.0f), 4.0f, kTotemInfo[i].color); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 14.0f); + + // Totem name or spell name + const std::string& spellName = gameHandler.getSpellName(slot.spellId); + const char* displayName = spellName.empty() ? kTotemInfo[i].name : spellName.c_str(); + ImGui::Text("%s", displayName); + + // Duration countdown bar + float remMs = slot.remainingMs(); + float totMs = static_cast(slot.durationMs); + float frac = (totMs > 0.0f) ? std::min(remMs / totMs, 1.0f) : 0.0f; + float remSec = remMs / 1000.0f; + + // Color bar with totem element tint + ImVec4 barCol( + static_cast((kTotemInfo[i].color >> IM_COL32_R_SHIFT) & 0xFF) / 255.0f, + static_cast((kTotemInfo[i].color >> IM_COL32_G_SHIFT) & 0xFF) / 255.0f, + static_cast((kTotemInfo[i].color >> IM_COL32_B_SHIFT) & 0xFF) / 255.0f, + 0.9f); + ImGui::PushStyleColor(ImGuiCol_PlotHistogram, barCol); + char timeBuf[16]; + snprintf(timeBuf, sizeof(timeBuf), "%.0fs", remSec); + ImGui::ProgressBar(frac, ImVec2(-1, 8), timeBuf); + ImGui::PopStyleColor(); + + ImGui::PopID(); + } + } + ImGui::End(); + + ImGui::PopStyleColor(); + ImGui::PopStyleVar(); +} + void GameScreen::renderTargetFrame(game::GameHandler& gameHandler) { auto target = gameHandler.getTarget(); if (!target) return;