From 4e13a344e8ae2ce5d898c99b241f24f3d7e513ba Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 18 Mar 2026 05:20:15 -0700 Subject: [PATCH] feat: add buff:/nobuff:/debuff:/nodebuff: macro conditionals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Macro conditions now support checking aura presence: [buff:Power Word: Fortitude] — player has the named buff [nobuff:Frost Armor] — player does NOT have the named buff [debuff:Faerie Fire] — target has the named debuff [nodebuff:Hunter's Mark] — target does NOT have the named debuff Name matching is case-insensitive. When a target override (@target etc.) is active the check uses that unit's aura list instead of the player's. --- src/ui/game_screen.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 06827659..299464a5 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -5534,6 +5534,41 @@ static std::string evaluateMacroConditionals(const std::string& rawArg, return true; } + // buff:SpellName / nobuff:SpellName — check if the effective target (or player + // if no target specified) has a buff with the given name. + // debuff:SpellName / nodebuff:SpellName — same for debuffs (harmful auras). + auto checkAuraByName = [&](const std::string& spellName, bool wantDebuff, + bool negate) -> bool { + // Determine which aura list to check: effective target or player + const std::vector* auras = nullptr; + if (tgt != static_cast(-1) && tgt != 0 && tgt != gameHandler.getPlayerGuid()) { + // Check target's auras + auras = &gameHandler.getTargetAuras(); + } else { + auras = &gameHandler.getPlayerAuras(); + } + std::string nameLow = spellName; + for (char& ch : nameLow) ch = static_cast(std::tolower(static_cast(ch))); + for (const auto& a : *auras) { + if (a.isEmpty() || a.spellId == 0) continue; + // Filter: debuffs have the HARMFUL flag (0x80) or spell has a dispel type + bool isDebuff = (a.flags & 0x80) != 0; + if (wantDebuff ? !isDebuff : isDebuff) continue; + std::string sn = gameHandler.getSpellName(a.spellId); + for (char& ch : sn) ch = static_cast(std::tolower(static_cast(ch))); + if (sn == nameLow) return !negate; + } + return negate; + }; + if (c.rfind("buff:", 0) == 0 && c.size() > 5) + return checkAuraByName(c.substr(5), false, false); + if (c.rfind("nobuff:", 0) == 0 && c.size() > 7) + return checkAuraByName(c.substr(7), false, true); + if (c.rfind("debuff:", 0) == 0 && c.size() > 7) + return checkAuraByName(c.substr(7), true, false); + if (c.rfind("nodebuff:", 0) == 0 && c.size() > 9) + return checkAuraByName(c.substr(9), true, true); + // Unknown → permissive (don't block) return true; };