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
This commit is contained in:
Kelsi 2026-03-18 03:09:43 -07:00
parent d2b2a25393
commit 7967bfdcb1
2 changed files with 30 additions and 8 deletions

View file

@ -375,6 +375,10 @@ public:
std::shared_ptr<Entity> 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<uint64_t> tabCycleList;
int tabCycleIndex = -1;
bool tabCycleStale = true;

View file

@ -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();