From 52507b1f74ba252e0be443a96ddb291f8bf7bf67 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 9 Mar 2026 16:49:50 -0700 Subject: [PATCH] Add target-of-target (ToT) mini frame below target frame Shows the name and health bar of whoever your current target is targeting. Reads UNIT_FIELD_TARGET_LO/HI update fields which are populated from SMSG_UPDATE_OBJECT. Frame is positioned below and right-aligned with the main target frame. --- src/ui/game_screen.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index c2ac1c25..14fc2324 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -2034,6 +2034,62 @@ void GameScreen::renderTargetFrame(game::GameHandler& gameHandler) { ImGui::PopStyleColor(2); ImGui::PopStyleVar(); + + // ---- Target-of-Target (ToT) mini frame ---- + // Read target's current target from UNIT_FIELD_TARGET_LO/HI update fields + if (target) { + const auto& fields = target->getFields(); + uint64_t totGuid = 0; + auto loIt = fields.find(game::fieldIndex(game::UF::UNIT_FIELD_TARGET_LO)); + if (loIt != fields.end()) { + totGuid = loIt->second; + auto hiIt = fields.find(game::fieldIndex(game::UF::UNIT_FIELD_TARGET_HI)); + if (hiIt != fields.end()) + totGuid |= (static_cast(hiIt->second) << 32); + } + + if (totGuid != 0) { + auto totEntity = gameHandler.getEntityManager().getEntity(totGuid); + if (totEntity) { + // Position ToT frame just below and right-aligned with the target frame + float totW = 160.0f; + float totX = (screenW - totW) / 2.0f + (frameW - totW); + ImGui::SetNextWindowPos(ImVec2(totX, 30.0f + 130.0f), ImGuiCond_Always); + ImGui::SetNextWindowSize(ImVec2(totW, 0.0f), ImGuiCond_Always); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 3.0f); + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.08f, 0.08f, 0.08f, 0.80f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.4f, 0.4f, 0.4f, 0.7f)); + + if (ImGui::Begin("##ToTFrame", nullptr, + ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar)) { + std::string totName = getEntityName(totEntity); + ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.8f, 1.0f), "%s", totName.c_str()); + + if (totEntity->getType() == game::ObjectType::UNIT || + totEntity->getType() == game::ObjectType::PLAYER) { + auto totUnit = std::static_pointer_cast(totEntity); + uint32_t hp = totUnit->getHealth(); + uint32_t maxHp = totUnit->getMaxHealth(); + if (maxHp > 0) { + float pct = static_cast(hp) / static_cast(maxHp); + ImGui::PushStyleColor(ImGuiCol_PlotHistogram, + pct > 0.5f ? ImVec4(0.2f, 0.7f, 0.2f, 1.0f) : + pct > 0.2f ? ImVec4(0.7f, 0.7f, 0.2f, 1.0f) : + ImVec4(0.7f, 0.2f, 0.2f, 1.0f)); + ImGui::ProgressBar(pct, ImVec2(-1, 10), ""); + ImGui::PopStyleColor(); + } + } + } + ImGui::End(); + ImGui::PopStyleColor(2); + ImGui::PopStyleVar(); + } + } + } } void GameScreen::sendChatMessage(game::GameHandler& gameHandler) {