From 7967bfdcb106e5aba827682dc11f010c6a9ca07c Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 18 Mar 2026 03:09:43 -0700 Subject: [PATCH] feat: implement [target=mouseover] macro conditional via nameplate/raid hover - Adds mouseoverGuid_ to GameHandler (set/cleared each frame by UI) - renderNameplates() sets mouseoverGuid when the cursor is inside a nameplate's hit region; resets to 0 at frame start - Raid frame cells set mouseoverGuid while hovered (IsItemHovered) - evaluateMacroConditionals() resolves @mouseover / target=mouseover to the hover GUID; returns false (skip alternative) when no unit is hovered This enables common healer macros like: /cast [target=mouseover,help,nodead] Renew; Renew --- include/game/game_handler.hpp | 5 +++++ src/ui/game_screen.cpp | 33 +++++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 33cd0f85..9090a1fa 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -375,6 +375,10 @@ public: std::shared_ptr getFocus() const; bool hasFocus() const { return focusGuid != 0; } + // Mouseover targeting — set each frame by the nameplate renderer + void setMouseoverGuid(uint64_t guid) { mouseoverGuid_ = guid; } + uint64_t getMouseoverGuid() const { return mouseoverGuid_; } + // Advanced targeting void targetLastTarget(); void targetEnemy(bool reverse = false); @@ -2537,6 +2541,7 @@ private: uint64_t targetGuid = 0; uint64_t focusGuid = 0; // Focus target uint64_t lastTargetGuid = 0; // Previous target + uint64_t mouseoverGuid_ = 0; // Set each frame by nameplate renderer std::vector tabCycleList; int tabCycleIndex = -1; bool tabCycleStale = true; diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 9bc77728..0fa503bf 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -5322,21 +5322,30 @@ static std::string evaluateMacroConditionals(const std::string& rawArg, size_t e = c.find_last_not_of(" \t"); if (e != std::string::npos) c.resize(e + 1); if (c.empty()) return true; - // @target specifiers: @player, @focus, @mouseover (mouseover → skip, no tracking) + // @target specifiers: @player, @focus, @mouseover, @target if (!c.empty() && c[0] == '@') { std::string spec = c.substr(1); - if (spec == "player") tgt = gameHandler.getPlayerGuid(); - else if (spec == "focus") tgt = gameHandler.getFocusGuid(); - else if (spec == "target") tgt = gameHandler.getTargetGuid(); - // mouseover: no tracking yet — treat as "use current target" + if (spec == "player") tgt = gameHandler.getPlayerGuid(); + else if (spec == "focus") tgt = gameHandler.getFocusGuid(); + else if (spec == "target") tgt = gameHandler.getTargetGuid(); + else if (spec == "mouseover") { + uint64_t mo = gameHandler.getMouseoverGuid(); + if (mo != 0) tgt = mo; + else return false; // no mouseover — skip this alternative + } return true; } // target=X specifiers if (c.rfind("target=", 0) == 0) { std::string spec = c.substr(7); - if (spec == "player") tgt = gameHandler.getPlayerGuid(); - else if (spec == "focus") tgt = gameHandler.getFocusGuid(); - else if (spec == "target") tgt = gameHandler.getTargetGuid(); + if (spec == "player") tgt = gameHandler.getPlayerGuid(); + else if (spec == "focus") tgt = gameHandler.getFocusGuid(); + else if (spec == "target") tgt = gameHandler.getTargetGuid(); + else if (spec == "mouseover") { + uint64_t mo = gameHandler.getMouseoverGuid(); + if (mo != 0) tgt = mo; + else return false; // no mouseover — skip this alternative + } return true; } @@ -10042,6 +10051,9 @@ void GameScreen::renderDPSMeter(game::GameHandler& gameHandler) { void GameScreen::renderNameplates(game::GameHandler& gameHandler) { if (gameHandler.getState() != game::WorldState::IN_WORLD) return; + // Reset mouseover each frame; we'll set it below when the cursor is over a nameplate + gameHandler.setMouseoverGuid(0); + auto* appRenderer = core::Application::getInstance().getRenderer(); if (!appRenderer) return; rendering::Camera* camera = appRenderer->getCamera(); @@ -10416,6 +10428,8 @@ void GameScreen::renderNameplates(game::GameHandler& gameHandler) { float nx1 = nameX + textSize.x + 2.0f; float ny1 = sy + barH + 2.0f; if (mouse.x >= nx0 && mouse.x <= nx1 && mouse.y >= ny0 && mouse.y <= ny1) { + // Track mouseover for [target=mouseover] macro conditionals + gameHandler.setMouseoverGuid(guid); if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { gameHandler.setTarget(guid); } else if (ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { @@ -10749,6 +10763,9 @@ void GameScreen::renderPartyFrames(game::GameHandler& gameHandler) { if (ImGui::InvisibleButton("raidCell", ImVec2(CELL_W, CELL_H))) { gameHandler.setTarget(m.guid); } + if (ImGui::IsItemHovered()) { + gameHandler.setMouseoverGuid(m.guid); + } if (ImGui::BeginPopupContextItem("RaidMemberCtx")) { ImGui::TextDisabled("%s", m.name.c_str()); ImGui::Separator();