From ff6ec25c06801aaa4346cd81fb79400c1c88e147 Mon Sep 17 00:00:00 2001 From: Jrc13245 Date: Thu, 27 Nov 2025 09:21:41 -0500 Subject: [PATCH] bleed immunity, fix icons and their tooltips with no available actions, reformat readme --- ComboPointTracker.lua | 21 +- Compatibility/pfUI.lua | 11 +- Conditionals.lua | 12 +- Core.lua | 215 ++++++-- README.md | 1168 +++++++++++++++++----------------------- Utility.lua | 52 +- 6 files changed, 726 insertions(+), 753 deletions(-) diff --git a/ComboPointTracker.lua b/ComboPointTracker.lua index c280bfa..0079e39 100644 --- a/ComboPointTracker.lua +++ b/ComboPointTracker.lua @@ -535,12 +535,7 @@ if _G.UseAction then if currentCP and currentCP > 0 then CleveRoids.lastComboPoints = currentCP CleveRoids.lastComboPointsTime = GetTime() - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cff888888[UseAction Hook]|r Captured %d CP before slot %d (spell:%s)", - currentCP, slot or 0, spellName or "nil") - ) - end + -- Debug message removed to reduce spam -- If it's a combo finisher, pre-populate tracking if spellName and CleveRoids.IsComboScalingSpell(spellName) then @@ -562,12 +557,8 @@ if _G.UseAction then end end end - elseif CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cff666666[UseAction Hook]|r slot %d (spell:%s), CP=0", - slot or 0, spellName or "nil") - ) end + -- Debug message removed to reduce spam return originalUseAction(slot, target, button) end DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00ComboPointTracker: UseAction hook installed!|r") @@ -578,13 +569,7 @@ if CastSpellByName then local originalCastSpellByName = CastSpellByName CastSpellByName = function(spellName, onSelf) local currentCP = CleveRoids.GetComboPoints() - - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff8800[CastSpellByName Hook]|r spell='%s' CP=%d", - spellName or "nil", currentCP or 0) - ) - end + -- Debug message removed to reduce spam if spellName and CleveRoids.IsComboScalingSpell(spellName) then if currentCP and currentCP > 0 then diff --git a/Compatibility/pfUI.lua b/Compatibility/pfUI.lua index 5f4b72e..93dcb98 100644 --- a/Compatibility/pfUI.lua +++ b/Compatibility/pfUI.lua @@ -311,18 +311,15 @@ function Extension.RegisterPfUIActionEventHandler() if CleveRoids.RegisterActionEventHandler then CleveRoids.RegisterActionEventHandler(function(slot, event, ...) -- Trigger pfUI's button update for this slot - -- pfUI exposes bars.update (the updatecache table) and bars.buttons -- Mark the slot for update in pfUI's cache, which will be processed on next OnUpdate - if pfUI.bars and pfUI.bars.update then - -- Mark slot for update in pfUI's update cache pfUI.bars.update[slot] = true end - -- If there's a button at this slot, we can also directly update it - if pfUI.bars and pfUI.bars.buttons and pfUI.bars.buttons[slot] and pfUI.bars.ButtonFullUpdate then - -- Direct update the button for immediate visual feedback - pfUI.bars.ButtonFullUpdate(pfUI.bars.buttons[slot]) + -- Also directly call ButtonFullUpdate if the button exists + local button = pfUI.bars and pfUI.bars.buttons and pfUI.bars.buttons[slot] + if button and pfUI.bars.ButtonFullUpdate then + pfUI.bars.ButtonFullUpdate(button) end end) diff --git a/Conditionals.lua b/Conditionals.lua index a49c889..3957a09 100644 --- a/Conditionals.lua +++ b/Conditionals.lua @@ -1278,11 +1278,17 @@ function CleveRoids.GetActionButtonInfo(slot) end function CleveRoids.IsReactiveUsable(spellName) - -- First check combat log-based proc tracking (works across stances) - if CleveRoids.HasReactiveProc and CleveRoids.HasReactiveProc(spellName) then - return 1 + -- For Overpower, Revenge, and Riposte: ONLY use combat log tracking + -- These spells have specific proc conditions tracked via combat log + if spellName == "Overpower" or spellName == "Revenge" or spellName == "Riposte" then + if CleveRoids.HasReactiveProc and CleveRoids.HasReactiveProc(spellName) then + return 1 + else + return nil + end end + -- For other reactive spells, use fallback methods -- Use Nampower's IsSpellUsable if available (more accurate) if IsSpellUsable then local usable, oom = IsSpellUsable(spellName) diff --git a/Core.lua b/Core.lua index 9702618..7b87a32 100644 --- a/Core.lua +++ b/Core.lua @@ -302,6 +302,12 @@ end) function CleveRoids.QueueActionUpdate() if CleveRoidMacros.realtime == 0 then CleveRoids.isActionUpdateQueued = true + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cffff00ff[QueueActionUpdate]|r Queued, isActionUpdateQueued = %s", + tostring(CleveRoids.isActionUpdateQueued)) + ) + end end end @@ -538,6 +544,14 @@ function CleveRoids.TestForActiveAction(actions) local previousOom = actions.active.oom local previousInRange = actions.active.inRange + -- Debug: Log when we're checking a reactive ability + if CleveRoids.debug and actions.active.isReactive then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff00ffff[TestForActiveAction]|r Checking reactive spell: %s", + tostring(actions.active.action)) + ) + end + if actions.active.spell then actions.active.inRange = 1 @@ -594,30 +608,70 @@ function CleveRoids.TestForActiveAction(actions) local onCooldown = (start > 0 and duration > 0) if actions.active.isReactive then - -- Check combat log-based proc tracking first (stance-independent) - if CleveRoids.HasReactiveProc and CleveRoids.HasReactiveProc(actions.active.action) then - -- Proc is active, show as usable if in range and have enough rage/mana - if actions.active.inRange ~= 0 and not actions.active.oom then - actions.active.usable = 1 - elseif pfUI and pfUI.bars and actions.active.oom then - actions.active.usable = 2 -- pfUI: out of mana/rage - else - actions.active.usable = nil + -- For Overpower, Revenge, Riposte: ONLY use combat log tracking + local spellName = actions.active.action + local useCombatLogOnly = (spellName == "Overpower" or spellName == "Revenge" or spellName == "Riposte") + + if useCombatLogOnly then + -- Only trust HasReactiveProc for these spells + local hasProc = CleveRoids.HasReactiveProc and CleveRoids.HasReactiveProc(spellName) + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff00ff00[UPDATE USABLE]|r %s: hasProc=%s, previousUsable=%s, inRange=%s, oom=%s", + spellName, tostring(hasProc), tostring(previousUsable), tostring(actions.active.inRange), tostring(actions.active.oom)) + ) end - -- Use Nampower's IsSpellUsable if available (stance-aware fallback) - elseif IsSpellUsable then - local usable, oom = IsSpellUsable(actions.active.action) - if usable == 1 and oom ~= 1 then - actions.active.usable = (pfUI and pfUI.bars) and nil or 1 + if hasProc then + -- Proc is active, show as usable if in range and have enough rage/mana + if actions.active.inRange ~= 0 and not actions.active.oom then + actions.active.usable = 1 + elseif pfUI and pfUI.bars and actions.active.oom then + actions.active.usable = 2 -- pfUI: out of mana/rage + else + actions.active.usable = nil + end + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff00ff00[UPDATE USABLE]|r %s: SET usable=%s (proc active)", + spellName, tostring(actions.active.usable)) + ) + end else + -- No proc = not usable actions.active.usable = nil + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff00ff00[UPDATE USABLE]|r %s: SET usable=nil (no proc)", spellName) + ) + end end - actions.active.oom = false - elseif not CleveRoids.IsReactiveUsable(actions.active.action) then - actions.active.oom = false - actions.active.usable = nil else - actions.active.usable = (pfUI and pfUI.bars) and nil or 1 + -- For other reactive spells, use the original fallback logic + -- Check combat log-based proc tracking first (stance-independent) + if CleveRoids.HasReactiveProc and CleveRoids.HasReactiveProc(actions.active.action) then + -- Proc is active, show as usable if in range and have enough rage/mana + if actions.active.inRange ~= 0 and not actions.active.oom then + actions.active.usable = 1 + elseif pfUI and pfUI.bars and actions.active.oom then + actions.active.usable = 2 -- pfUI: out of mana/rage + else + actions.active.usable = nil + end + -- Use Nampower's IsSpellUsable if available (stance-aware fallback) + elseif IsSpellUsable then + local usable, oom = IsSpellUsable(actions.active.action) + if usable == 1 and oom ~= 1 then + actions.active.usable = (pfUI and pfUI.bars) and nil or 1 + else + actions.active.usable = nil + end + actions.active.oom = false + elseif not CleveRoids.IsReactiveUsable(actions.active.action) then + actions.active.oom = false + actions.active.usable = nil + else + actions.active.usable = (pfUI and pfUI.bars) and nil or 1 + end end elseif actions.active.inRange ~= 0 and not actions.active.oom then actions.active.usable = 1 @@ -636,6 +690,12 @@ function CleveRoids.TestForActiveAction(actions) actions.active.oom ~= previousOom or actions.active.inRange ~= previousInRange then changed = true + if CleveRoids.debug and actions.active.isReactive then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cffff00ff[STATE CHANGED]|r %s: usable %s->%s, will send ACTIONBAR_SLOT_CHANGED", + actions.active.action, tostring(previousUsable), tostring(actions.active.usable)) + ) + end end end return changed @@ -2297,8 +2357,49 @@ function CleveRoids.OnUpdate(self) end if not CleveRoids.ready then return end + -- Check for expired reactive procs BEFORE throttle to ensure immediate icon updates + if CleveRoids.reactiveProcs then + local hasExpiredProc = false + for spellName, procData in pairs(CleveRoids.reactiveProcs) do + if procData and procData.expiry and time >= procData.expiry then + -- DEBUG: Show proc expiration + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cffff9900[REACTIVE PROC]|r %s expired at time=%.3f", spellName, time) + ) + end + CleveRoids.reactiveProcs[spellName] = nil + hasExpiredProc = true + end + end + -- If any proc expired, immediately update all actions to refresh icon states + if hasExpiredProc then + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage("|cffff9900[REACTIVE PROC]|r Forcing immediate TestForAllActiveActions()") + -- Debug: Show how many actions we're checking + local actionCount = 0 + for _ in pairs(CleveRoids.Actions) do + actionCount = actionCount + 1 + end + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cffff9900[REACTIVE PROC]|r Checking %d action slots", actionCount) + ) + end + -- Force immediate update (don't just queue it) + CleveRoids.TestForAllActiveActions() + CleveRoids.isActionUpdateQueued = false -- Clear queue flag since we just processed + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage("|cffff9900[REACTIVE PROC]|r TestForAllActiveActions() completed") + end + end + end + -- Throttle the update loop to avoid excessive CPU usage. - if (time - CleveRoids.lastUpdate) < refreshRate then return end + -- HOWEVER: If an action update is queued, allow it to bypass throttle for reactive abilities + local bypassThrottle = CleveRoids.isActionUpdateQueued and CleveRoidMacros.realtime == 0 + if not bypassThrottle and (time - CleveRoids.lastUpdate) < refreshRate then + return + end CleveRoids.lastUpdate = time -- Check the saved variable to decide which update mode to use. if CleveRoidMacros.realtime == 1 then @@ -2307,8 +2408,14 @@ function CleveRoids.OnUpdate(self) else -- Event-Driven Mode (Default): Only update if a relevant game event has queued it. if CleveRoids.isActionUpdateQueued then + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage("|cffff00ff[OnUpdate]|r Processing queued action update") + end CleveRoids.TestForAllActiveActions() CleveRoids.isActionUpdateQueued = false -- Reset the flag after updating + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage("|cffff00ff[OnUpdate]|r Action update complete, flag reset") + end end end @@ -2330,27 +2437,6 @@ function CleveRoids.OnUpdate(self) end end - -- Check for expired reactive procs and update icons - if CleveRoids.reactiveProcs then - local hasExpiredProc = false - for spellName, procData in pairs(CleveRoids.reactiveProcs) do - if procData and procData.expiry and time >= procData.expiry then - -- DEBUG: Show proc expiration - if CleveRoids.debug then - DEFAULT_CHAT_FRAME:AddMessage( - string.format("|cffff9900[REACTIVE PROC]|r %s expired", spellName) - ) - end - CleveRoids.reactiveProcs[spellName] = nil - hasExpiredProc = true - end - end - -- If any proc expired, queue an action update to refresh icons - if hasExpiredProc then - CleveRoids.QueueActionUpdate() - end - end - -- PERFORMANCE OPTIMIZATION: Run memory cleanup less frequently (every 5 seconds instead of every frame) -- This reduces CPU usage while maintaining effective memory management if (time - CleveRoids.lastCleanupTime) >= CleveRoids.CLEANUP_INTERVAL then @@ -2386,12 +2472,21 @@ CleveRoids.Hooks.GameTooltip.SetAction = GameTooltip.SetAction function GameTooltip.SetAction(self, slot) local actions = CleveRoids.GetAction(slot) + -- If this is our macro but has no active action, show just the macro name + if actions and not actions.active then + local macroName = GetActionText(slot) + if macroName then + GameTooltip:SetText(macroName) + GameTooltip:Show() + return + end + end + local action_to_display_info = nil if actions then + -- Only show spell/item tooltip when there's an active action if actions.active then action_to_display_info = actions.active - elseif actions.tooltip then - action_to_display_info = actions.tooltip end end @@ -2485,10 +2580,10 @@ end CleveRoids.Hooks.ActionHasRange = ActionHasRange function ActionHasRange(slot) local actions = CleveRoids.GetAction(slot) - -- Use the same priority as GetActionTexture: active first, then tooltip - local actionToCheck = (actions and actions.active) or (actions and actions.tooltip) - if actionToCheck then - return (1 and actionToCheck.inRange ~= -1 or nil) + -- Only check active action for range (not tooltip) + -- When there's no active action, the macro is unusable so range is irrelevant + if actions and actions.active then + return (1 and actions.active.inRange ~= -1 or nil) else return CleveRoids.Hooks.ActionHasRange(slot) end @@ -2497,10 +2592,10 @@ end CleveRoids.Hooks.IsActionInRange = IsActionInRange function IsActionInRange(slot, unit) local actions = CleveRoids.GetAction(slot) - -- Use the same priority as GetActionTexture: active first, then tooltip - local actionToCheck = (actions and actions.active) or (actions and actions.tooltip) - if actionToCheck and actionToCheck.type == "spell" then - return actionToCheck.inRange + -- Only check active action for range (not tooltip) + -- When there's no active action, the macro is unusable so range is irrelevant + if actions and actions.active and actions.active.type == "spell" then + return actions.active.inRange else return CleveRoids.Hooks.IsActionInRange(slot, unit) end @@ -2510,11 +2605,21 @@ CleveRoids.Hooks.OriginalIsUsableAction = IsUsableAction CleveRoids.Hooks.IsUsableAction = IsUsableAction function IsUsableAction(slot, unit) local actions = CleveRoids.GetAction(slot) - -- Use the same priority as GetActionTexture: active first, then tooltip - local actionToCheck = (actions and actions.active) or (actions and actions.tooltip) - if actionToCheck then - return actionToCheck.usable, actionToCheck.oom + + -- If this is one of our macros + if actions then + -- IMPORTANT: Only use active action for usability checks + -- Tooltip is for icon/texture display (#showtooltip), not for determining usability + if actions.active then + -- We have an active action - return its usable state + return actions.active.usable, actions.active.oom + else + -- This is our macro but no action is active (all conditionals failed) + -- Return nil to make the icon dark + return nil, nil + end else + -- Not our macro - use game's default behavior return CleveRoids.Hooks.IsUsableAction(slot, unit) end end diff --git a/README.md b/README.md index 412ca5a..b0bafa5 100644 --- a/README.md +++ b/README.md @@ -1,681 +1,578 @@ # SuperCleveRoid Macros -This was originally an effort to bring the dynamic tooltip and cast sequence functionality of [CleverMacro](https://github.com/DanielAdolfsson/CleverMacro) into [Roid-Macros](https://github.com/MarcelineVQ/Roid-Macros). It has since expanded after some additional changes I wanted along with feedback from others. The majority of credit goes to the [original addon authors](#original-addons--authors). Still a work in progress. -# REQUIRED DLLS -[SuperWoW](https://github.com/balakethelock/SuperWoW) -[Nampower](https://github.com/pepopo978/nampower) -[UnitXP_SP3](https://codeberg.org/konaka/UnitXP_SP3) -# ALL 3 ARE REQUIRED!!! -Check slash command and all conditional lists for new usages! +An enhanced macro addon for World of Warcraft 1.12.1 (Vanilla) that provides dynamic tooltips, conditional execution, and extended syntax. Originally based on [CleverMacro](https://github.com/DanielAdolfsson/CleverMacro) and [Roid-Macros](https://github.com/MarcelineVQ/Roid-Macros) with significant expansions. + +## Required DLL Mods + +**ALL THREE ARE MANDATORY:** +- [SuperWoW](https://github.com/balakethelock/SuperWoW) - Extended API functions +- [Nampower](https://github.com/pepopo978/nampower) - Spell queueing and range checking +- [UnitXP_SP3](https://codeberg.org/konaka/UnitXP_SP3) - Distance/positioning checks + +The addon will not function without all three DLL mods installed. --- ## Installation -### Manual -1. Download a zip from the green Code button or by clicking [here](https://github.com/jrc13245/SuperCleveRoidMacros/archive/refs/heads/main.zip) -2. Unzip the file and place the folder into your `Interface/Addons` folder. -3. Rename the `SuperCleveRoidMacros-main` folder to `SuperCleveRoidMacros` -4. Check that it is enabled in your addon list in-game. -5. Make sure you don't have other macro addons that may interfere. -6. SUPERWOW ,NAMPOWER, and UNITXP_SP3 are REQUIRED! -7. Disable Macrotweak module in pfui, and disable macrotweak in shagutweaks! -8. Disable Scan Macros for spells in the actionbar section of pfui! -* [SuperWoW](https://github.com/balakethelock/SuperWoW) dll mod is required -* [Nampower](https://github.com/pepopo978/nampower) dll mod is required -* [UnitXP_SP3](https://codeberg.org/konaka/UnitXP_SP3) dll mod is required +1. Download from the green Code button or [click here](https://github.com/jrc13245/SuperCleveRoidMacros/archive/refs/heads/main.zip) +2. Unzip and place folder into `Interface/Addons` +3. Rename `SuperCleveRoidMacros-main` to `SuperCleveRoidMacros` +4. Verify it's enabled in your addon list in-game +5. **Disable conflicts:** + - Disable MacroTweak module in pfUI + - Disable MacroTweak in ShaguTweaks + - Disable "Scan Macros for spells" in pfUI actionbar settings -### SuperCleveRoidMacros Settings -* `/cleveroid` - View current settings -* `/cleveroid realtime 0 or 1` - Force realtime updates rather than event based updates (Default: 0. 1 = on, increases CPU load) -* `/cleveroid refresh X` - Set refresh rate (1 to 10 updates per second. Default: 5) -* `/cleveroid learn ` - Manually set spell duration in seconds -* `/cleveroid forget ` - Forget learned spell duration(s) -* `/cleveroid debug [0|1]` - Toggle learning debug messages -* `/cleveroid listimmune [school]` - List all or specific school immunities -* `/cleveroid addimmune "" [buff]` - Add manual immunity -* `/cleveroid removeimmune "" ` - Remove immunity -* `/cleveroid clearimmune [school]` - Clear immunity data +--- ---- +## Settings & Commands + +### Basic Settings +```lua +/cleveroid -- View current settings +/cleveroid realtime 0|1 -- Toggle realtime updates (Default: 0, increases CPU load) +/cleveroid refresh 1-10 -- Set refresh rate in Hz (Default: 5) +``` + +### Debuff Duration Learning +```lua +/cleveroid learn -- Manually set spell duration +/cleveroid forget -- Forget learned duration(s) +/cleveroid debug 0|1 -- Toggle learning debug messages +``` + +### Immunity Tracking +```lua +/cleveroid listimmune [school] -- List immunities +/cleveroid addimmune "" [buff] -- Add immunity +/cleveroid removeimmune "" -- Remove immunity +/cleveroid clearimmune [school] -- Clear immunity data +``` + +### Talent & Equipment Testing +```lua +/cleveroid talenttabs -- Show talent tab IDs +/cleveroid listtab -- List talents in a tab +/cleveroid talents -- Show current talent ranks +/cleveroid testtalent -- Test talent modifier +/cleveroid diagnosetalent -- Diagnose talent issues +/cleveroid testequip -- Test equipment modifier +``` + +### Combo Point Tracking +```lua +/combotrack show -- Display tracking data +/combotrack clear -- Clear all tracking data +/combotrack debug -- Toggle debug output +``` + +--- ## Known Issues -* By default, conditional checks to update icons are event based with realtime set to 0. This means icons using some conditionals that arent triggered by events are slow to change. example: @unitid mod etc. Enable realtime setting, but beware increase in cpu usage and lag. -* ALL macros must be given unique names, no blank names or muliple of the same name or using spell names. -* If any of your macros have syntax errors, it will affect all macros when it comes to errors. -* Spells with parenthesis ie: Faerie Fire (Feral) or Barkskin (Feral) MUST be written using ranks, becomming `Faerie Fire (Feral)(Rank X)` -* Minor: If you improperly create a macro, in some certain cases it can cause strange UI issues including not displaying any icons, other macros not working or lua errors. Fix the macro or remove it from your actionbar and it should go back to normal automatically. Depending on the cause, you may need to reload your UI (/rl). If you find one of these and can reproduce the issue, let me know. -* Minor: HealComm support. See [below](#healcomm-support) -* I have not tested all possible combinations of conditionals or ways to break things. Find me on Discord or open an issue if you find any bugs or have feedback. + +- **Event-based updates:** By default (realtime=0), conditionals like `@unitid` and `mod` update on events. Enable realtime for instant updates (increases CPU usage). +- **Unique macro names:** All macros must have unique names - no blanks, duplicates, or spell names or special characters. +- **Parenthesis in spell names:** Must include rank - `Faerie Fire (Feral)(Rank 4)` NOT `Faerie Fire (Feral)`. +- **Reactive abilities:** Revenge, Overpower, etc. must be on action bars for detection. +- **HealComm support:** See [HealComm Support](#healcomm-support) section. --- # Macro Syntax Guide -## Basic Syntax Rules +## Basic Rules -### ***Important! Spells, Items, conditionals, etc are case sensitive. Barring a bug, if there is an issue, it's almost always because something is typed incorrectly.*** +### **Spells/items are case sensitive!** ### Spell and Item Names -* **Spaces**: Use underscores (`_`) OR enclose in quotes (`"`) - * `Mark_of_the_Wild` OR `"Mark of the Wild"` -* **Ranks**: Must include full rank syntax with parentheses - * `Faerie Fire (Feral)(Rank 4)` NOT `Faerie Fire (Feral)` +- **Spaces:** Use underscores OR quotes + - `Mark_of_the_Wild` OR `"Mark of the Wild"` +- **Ranks:** Must include full rank syntax + - ✅ `Faerie Fire (Feral)(Rank 4)` + - ❌ `Faerie Fire (Feral)` ### Conditional Structure -* **Format**: `[conditional1 conditional2] Action` -* **Evaluation**: Left to right, top to bottom -* **First match wins**: First action where ALL conditionals are true executes -* **Separators**: Space or comma between conditionals - * `[mod:alt harm alive]` OR `[mod:alt,harm,alive]` +- **Format:** `[conditional1 conditional2] Action` +- **Evaluation:** Left to right, top to bottom +- **First match wins:** First action where ALL conditionals pass +- **Separators:** Space or comma + - `[mod:alt harm alive]` = `[mod:alt,harm,alive]` + +### Conditional Arguments +- **Colon required:** Use `:` to pass arguments to conditionals + - ✅ `[mod:alt]` - Correct (argument provided) + - ✅ `[hp:>50]` - Correct (argument provided) + - ✅ `[harm]` - Correct (no argument needed) + - ❌ `[mod alt]` - Wrong (missing colon) + - ❌ `[hp>50]` - Wrong (missing colon) ### Line Formats -Both single-line and multi-line formats work identically: +Both formats work identically: -**Multi-line** (easier to read): -``` +**Multi-line:** +```lua #showtooltip /cast [mod:alt] Frostbolt /cast [mod:ctrl] Fire Blast /cast Blink ``` -**Single-line** (semicolon separated): -``` +**Single-line:** +```lua #showtooltip /cast [mod:alt] Frostbolt; [mod:ctrl] Fire Blast; Blink ``` ### Target Specification -* **Syntax**: `[@unitid]` at the start of conditionals -* **Example**: `/cast [@party1 combat] Intervene` - * Targets party1, checks if player is in combat -* **Default**: Most conditionals default to `@target` if not specified +```lua +/cast [@party1 combat] Intervene -- Targets party1, checks if player in combat +``` +- **Syntax:** `[@unitid]` at start of conditionals +- **Default:** Most conditionals default to `@target` ### Multi-Value Conditionals -Some conditionals accept multiple values with operators: #### OR Operator (`/`) -* **Any match wins**: `[zone:Stormwind/Ironforge]` = in Stormwind OR Ironforge -* **Works with negation**: `[nozone:Stormwind_City/Ironforge]` = NOT in Stormwind OR NOT in Ironforge - * True if you're outside either zone (or both) - * Example: `[nomypower:>10/<20]` = power is not >10 OR not <20 (true if power is 5-10 or ≥20) +```lua +[zone:Stormwind_City/Ironforge] -- In Stormwind OR Ironforge +[nozone:Stormwind_City/Ironforge] -- NOT in Stormwind OR NOT in Ironforge +[nomypower:>10/<20] -- Power NOT >10 OR NOT <20 +``` #### AND Operator (`&`) -* **All must match**: `[zone:Stormwind_City&Elwynn_Forest]` = in both zones (subzone & zone check) -* **Works with negation**: `[nozone:Stormwind_City&Ironforge]` = NOT in Stormwind AND NOT in Ironforge - * True only if you're in neither zone - * Example: `[nomybuff:Mark_of_the_Wild&Thorns]` = missing both Mark AND Thorns +```lua +[zone:Stormwind_City&Elwynn_Forest] -- In both zones (subzone & zone) +[nozone:Stormwind_City&Ironforge] -- NOT in Stormwind AND NOT in Ironforge +[nomybuff:Mark_of_the_Wild&Thorns] -- Missing both buffs +``` -**Important**: The `/` operator ALWAYS means OR, even for negated conditionals. Use `&` for AND logic. +**Important:** `/` always means OR, even for negated conditionals. Use `&` for AND logic. -* **Marked as "Multi"** in the conditionals table below - -### Negation (No-able Conditionals) -Prefix with `no` to negate: -* **Marked as "Noable"** in the conditionals table below -* **Operators apply**: Use `/` for OR logic, `&` for AND logic (see above) +### Negation +Prefix with `no` to negate (marked as "Noable" in tables): +```lua +[nobuff] -- Does NOT have buff +[nomod:alt] -- Alt is NOT pressed +``` ### Numerical Comparisons -For hp, power, cooldown, etc: -* **Operators**: `<`, `>`, `=`, `<=`, `>=`, `~=` -* **Format**: `[condition:>50]` or `[condition:"Name">50]` -* **Stacks vs Time**: Use `>#` for stacks/rank, no `#` for time - * `[buff:"Mark_of_the_Wild">#5]` = 5+ stacks - * `[mybuff:"Renew"<4]` = less than 4 seconds remaining +```lua +[hp:>50] -- HP above 50% +[cooldown:"Spell Name"<5] -- Less than 5 seconds remaining +[buff:"Mark of the Wild">#5] -- 5+ stacks (use ># for stacks) +[mybuff:"Renew"<4] -- Less than 4 seconds remaining +``` +- **Operators:** `<`, `>`, `=`, `<=`, `>=`, `~=` ### Omitting Values -If the conditional value matches the action, you can omit it: -* `[debuff:"Sunder_Armor"<#5] Sunder Armor` = `[debuff:<#5] Sunder Armor` -* `[nobuff:"Mark_of_the_Wild"] Mark of the Wild` = `[nobuff] Mark of the Wild` +If conditional value matches the action: +```lua +[debuff:<#5] Sunder Armor -- Same as [debuff:"Sunder Armor"<#5] +[nobuff] Mark of the Wild -- Same as [nobuff:"Mark of the Wild"] +``` ### Item IDs -Use item IDs instead of names to avoid cache issues: -``` +Use IDs to avoid cache issues: +```lua #showtooltip -/use [nomod] 5350 +/use [nomod] 5350 -- Conjured Water /cast [mod] Conjure Water ``` ### Equipment Slots -Use slot numbers (1-19) with `/use`: -``` +Use slot numbers (1-19): +```lua #showtooltip -/use [combat hp:<=20] 13 +/use [combat hp:<=20] 13 -- Use trinket in slot 13 +/use 16 -- Use main hand weapon ``` ## Special Prefixes | Prefix | Example | Description | |:------:|---------|-------------| -| `!` | `!Attack`
`!Cat Form` | Only use if not already active
Shorthand for `[nomybuff]` | -| `?` | `?[equipped:Swords]`
`?Presence of Mind` | Hides icon/tooltip
Must be first character | -| `~` | `~Slow Fall` | Toggle buff on/off
Casts or cancels if possible | +| `!` | `!Attack`
`!Cat Form` | Only use if not active
Shorthand for `[nomybuff]` | +| `?` | `?[equipped:Swords] Ability` | Hides icon/tooltip
Must be first character | +| `~` | `~Slow Fall` | Toggle buff on/off | ## Common Pitfalls -❌ **Cascading conditionals** (Retail/Classic style) - NOT SUPPORTED: -``` +❌ **Cascading conditionals (NOT SUPPORTED):** +```lua /use [@mouseover alive help hp:<70][@target alive help hp:<70][@player] Bandage ``` -✅ **Correct format** - Separate lines or semicolons: -``` +✅ **Correct format:** +```lua /use [@mouseover alive help hp:<70] Bandage /use [@target alive help hp:<70] Bandage /use [@player] Bandage ``` -❌ **Missing rank on spells with parentheses**: -``` -/cast Faerie Fire (Feral) -- WRONG -``` - -✅ **Include full rank**: -``` -/cast Faerie Fire (Feral)(Rank 4) -- CORRECT -``` - -### Multi-Value Operator Examples - -**OR Operator (`/`)** - Any value matches: -``` -# Cast if in Stormwind OR Ironforge -/cast [zone:Stormwind_City/Ironforge] Hearthstone - -# Cast if NOT in Stormwind OR NOT in Ironforge (true if outside either/both) -/cast [nozone:Stormwind_City/Ironforge] Mount - -# Cast if power is NOT >10 OR NOT <20 (true if power is 5-10 or ≥20) -/cast [nomypower:>10/<20] Spell -``` - -**AND Operator (`&`)** - All values must match: -``` -# Cast if missing both Mark of the Wild AND Thorns -/cast [nobuff:Mark_of_the_Wild&Thorns] Mark of the Wild - -# Cast if NOT alt AND NOT ctrl (only true if pressing neither) -/cast [nomod:alt&ctrl] Fireball - -# Cast if target has neither Corruption AND Curse of Agony -/cast [nodebuff:Corruption&Curse_of_Agony] Corruption -``` - -**Practical Comparison**: -``` -# OR: Reapply if missing EITHER buff -/cast [nomybuff:Mark_of_the_Wild/Thorns] Mark of the Wild - -# AND: Only apply if missing BOTH buffs -/cast [nomybuff:Mark_of_the_Wild&Thorns] Mark of the Wild -``` - --- -# What's Different +# Features ## Dynamic Icons and Tooltips -* The icon and tooltip for a macro will automatically update to the first true condition's action. Left to right, top to bottom. -* Consumables and certain other item types will now show a count on the action bar. -* Spells with reagent costs will now show a count of how many uses you have. -* Full `#showtooltip` support with dynamic icons and tooltips - * A macro must start with `#showtooltip` or `#showtooltip spell/item/itemid`. The icons and tooltips will update on your bars as the conditions are met. - ```lua - #showtooltip - /cast [mod:alt] Frostbolt; [mod:ctrl] Fire Blast; [mod:shift] {MyMacro}; Blink - ``` - ```lua - #showtooltip - /cast [stance:1/3, nocooldown, notargeting:player] Mocking Blow - /cast [stance:2/3, nocooldown, notargeting:player] Taunt - /cast Shield Slam - ``` - - One line or separate lines are valid, sometimes one is better than the other +Icons and tooltips automatically update to the first action with passing conditionals. - ``` - #showtooltip - /cast [zone:Ahn'Qiraj] Yellow Qiraji Battle Tank;[nozone:Orgrimmar/Undercity/"Thunder Bluff" nomybuff:"Water Walking"] Red Goblin Shredder;Plainsrunning - ``` - Is the same as - ``` - #showtooltip - /cast [zone:Ahn'Qiraj] Yellow Qiraji Battle Tank - /cast [nozone:Orgrimmar/Undercity/"Thunder Bluff" nomybuff:"Water Walking"] Red Goblin Shredder - /cast Plainsrunning - ``` - - -* Updated `/castsequence` support. See [below](#cast-sequence) -* Added support for updating to the icon/tooltip of the {macro} named in a another macro - * Macro 1 calls Macro 2 and will update the icon when out of combat with Arcane Missiles from Macro2's `#showtooltip` - - ***Note: This will only use the icon/tooltip of either the referenced `#showtooltip ` or the icon you set for the macro.*** - - Macro 1: - ```lua - #showtooltip - /cast [nocombat] {Macro2}; Fireball - ``` - Macro 2 - ```lua - #showtooltip Arcane Missiles - /cast [mod] Conjure Food - /use 2288 - ``` -* Added support for itemid lookup instead of the item's name. Using this will show the icon/tooltip even if you don't have the item cached or in your inventory so you don't see ? icons. e.g. No Mage food/water on login - - ```lua - #showtooltip - /use [nomod] 5350 - /cast [mod] Conjure Water - ``` -*Added support of using equipped items by slot number - - ```lua - #showtooltip - /use 16 - ``` ---- - -## Debuff Timer System (SuperWoW) BY: yani9o -SuperCleveRoidMacros includes a built-in **debuff timer tracking system** that accurately tracks your debuff durations using SuperWoW's advanced features: - -### Features -* ✅ **Auto-Learning** - Automatically learns debuff durations as you cast them -* ✅ **Accurate Tracking** - Only tracks debuffs that successfully land (no false timers on misses) -* ✅ **335+ Debuffs Pre-configured** - Extensive database of vanilla 1.12.1 spell IDs -* ✅ **Per-Caster Storage** - Handles talent variations by tracking per player GUID -* ✅ **GUID-Based** - Correctly handles multiple mobs with the same name -* ✅ **No pfUI Required** - Fully independent debuff tracking system - -### How It Works -1. When you cast a debuff, the system records the timestamp -2. When the debuff fades, it calculates the actual duration -3. Duration is saved and used for all future casts of that spell -4. You can check debuff timers in macros using conditionals like `[debuff:Sunder Armor>25]` - -### Manual Control +### #showtooltip Support ```lua -/cleveroid learn 11597 30 -- Set Sunder Armor (ID 11597) to 30 seconds -/cleveroid forget 11597 -- Forget Sunder Armor duration -/cleveroid forget all -- Clear all learned durations -/cleveroid debug 1 -- Enable learning messages in chat +#showtooltip +/cast [mod:alt] Frostbolt +/cast [mod:ctrl] Fire Blast +/cast Blink ``` -### Macro Examples +### With Explicit Icon ```lua --- Maintain Sunder Armor with 25s buffer +#showtooltip Fireball +/cast [mod] Conjure Food +/use 2288 +``` + +### Macro References +Macro 1 shows Macro 2's icon when out of combat: +```lua +-- Macro 1 +#showtooltip +/cast [nocombat] {Macro2} +/cast Fireball + +-- Macro 2 +#showtooltip Arcane Missiles +/cast [mod] Conjure Food +/use 2288 +``` + +### Item Counts +- Consumables show stack count +- Spells with reagents show use count +- Item IDs work even when uncached (no ? icons) + +--- + +## Debuff Timer System + +Built-in debuff tracking using SuperWoW's advanced features. + +### Features +- ✅ Auto-learning from successful casts +- ✅ 335+ debuffs pre-configured +- ✅ Per-caster storage (handles talent variations) +- ✅ GUID-based tracking +- ✅ Works independently of pfUI + +### How It Works +1. Cast debuff → timestamp recorded +2. Debuff fades → duration calculated +3. Duration saved for future casts +4. Use in conditionals: `[debuff:Sunder_Armor>25]` + +### Examples +```lua +-- Maintain Sunder with 25s buffer /cast [debuff:Sunder_Armor>25] Heroic Strike; Sunder Armor --- Refresh Corruption when <4s remain +-- Refresh Corruption when <4s /cast [debuff:Corruption<4] Corruption; Shadow Bolt --- Apply Serpent Sting if missing or <5s -/cast [nodebuff:Serpent_Sting] Serpent Sting; [debuff:Serpent_Sting<5] Serpent Sting; Steady Shot - --- Multi-DoT priority system +-- Multi-DoT priority /cast [nodebuff:Moonfire] Moonfire /cast [debuff:Moonfire<4] Moonfire /cast [nodebuff:Insect_Swarm] Insect Swarm /cast Wrath ``` -### Supported Debuffs -The system includes pre-configured durations for 335+ debuffs across all classes: -- **Warrior:** Sunder Armor, Rend, Hamstring, Thunder Clap, Demoralizing Shout, etc. -- **Rogue:** Rupture, Garrote, Expose Armor, Poisons, etc. -- **Hunter:** Serpent Sting, Hunter's Mark, Wing Clip, Wyvern Sting, etc. -- **Druid:** Rip, Rake, Moonfire, Insect Swarm, Faerie Fire, Entangling Roots, etc. -- **Warlock:** Corruption, Curses, Immolate, Siphon Life, etc. -- **Mage:** Polymorph (all variants), Slow effects, etc. -- **Priest:** Shadow Word: Pain, Devouring Plague, Mind Flay, etc. -- **Paladin:** Judgement of the Crusader (all 6 ranks), Hammer of Justice, etc. -- **Shaman:** Flame Shock, Frost Shock, etc. +### Supported Classes +- **Warrior:** Sunder, Rend, Hamstring, Thunder Clap, Demo Shout +- **Rogue:** Rupture, Garrote, Expose Armor, Poisons +- **Hunter:** Serpent Sting, Hunter's Mark, Wing Clip, Wyvern Sting +- **Druid:** Rip, Rake, Moonfire, Insect Swarm, Faerie Fire, Roots +- **Warlock:** Corruption, Curses, Immolate, Siphon Life +- **Mage:** Polymorph (all variants), Slow effects +- **Priest:** Shadow Word: Pain, Devouring Plague, Mind Flay +- **Paladin:** Judgement of the Crusader (all ranks), Hammer of Justice +- **Shaman:** Flame Shock, Frost Shock -### Combo Point Scaling -The addon automatically tracks combo point finishers that scale duration with combo points: -- **Rogue Rupture** (all ranks): 8s base + 2s per combo point (8s @ 1 CP, 16s @ 5 CP) -- **Rogue Kidney Shot**: Rank 1: 1s + 1s per CP, Rank 2: 2s + 1s per CP -- **Druid Rip** (all ranks): 10s base + 2s per combo point (10s @ 1 CP, 18s @ 5 CP) +--- -The system automatically detects combo points at cast time and calculates the correct duration **regardless of how you cast the spell**: -- ✅ Clicking spell in spellbook -- ✅ Clicking ability on action bars -- ✅ Using macros (`/cast Rip`) -- ✅ Direct Lua calls (`CastSpellByName("Rip")`) +## Combo Point Tracking +Automatically tracks combo finisher durations regardless of casting method. + +### Supported Spells +- **Rogue Rupture:** 8s + 2s per CP (8-16s) +- **Rogue Kidney Shot:** Rank 1: 1s + 1s per CP, Rank 2: 2s + 1s per CP +- **Druid Rip:** 10s + 2s per CP (10-18s) + +### Universal Tracking +Works with ALL casting methods: +- ✅ Spellbook clicks +- ✅ Action bar clicks +- ✅ Macros (`/cast Rip`) +- ✅ Direct Lua calls + +### Examples ```lua --- Will show accurate duration based on combo points used +-- Refresh Rip when low /cast [nodebuff:Rip] Rip /cast [debuff:Rip<4] Rip --- Rupture duration tracking with combo point awareness +-- Rupture tracking /cast [nodebuff:Rupture] Rupture /cast [debuff:Rupture<2] Rupture ``` -Use `/combotrack show` to see recent combo finisher casts and their calculated durations. +--- -### Talent Modifiers -The addon automatically applies talent-based duration modifications to debuffs. Talent modifiers layer on top of combo point calculations for finisher abilities. -**IF YOUR TALENT DEBUFF DURATION MODIFIER IS MISSING, PLEASE MAKE A BUG REPORT!!!** +## Talent Modifiers -**Calculation Order:** `final_duration = (base + combo_points) + talent_modifier` +Automatically applies talent-based duration modifications. -**Supported Talent Modifiers:** +**Calculation:** `final = (base + combo_points) + talent_modifier` + +### Supported Talents - **Warrior:** - - **Booming Voice** (5 ranks): +12% per rank to Demoralizing Shout duration (60% at rank 5) + - Booming Voice (5 ranks): +12% per rank to Demo Shout - **Rogue:** - - **Taste for Blood** (3 ranks): +2s per rank to Rupture duration - - **Improved Gouge** (3 ranks): +0.5s per rank to Gouge duration + - Taste for Blood (3 ranks): +2s per rank to Rupture + - Improved Gouge (3 ranks): +0.5s per rank to Gouge - **Priest:** - - **Improved Shadow Word: Pain** (2 ranks): +3s per rank to SW:P duration + - Improved Shadow Word: Pain (2 ranks): +3s per rank - **Druid:** - - **Brutal Impact** (2 ranks): +0.5s per rank to Bash and Pounce stun duration + - Brutal Impact (2 ranks): +0.5s per rank to Bash/Pounce stun -**Example:** Rupture with 5 CP and Taste for Blood 3/3: -1. Base duration: 8s -2. Combo point modifier: 8s + (5-1)×2 = 16s -3. Talent modifier: 16s + (3×2) = **22s final** +### Example +Rupture with 5 CP and Taste for Blood 3/3: +1. Base: 8s +2. Combo points: 8s + (5-1)×2 = 16s +3. Talent: 16s + (3×2) = **22s final** -### Special Talent Mechanics - -#### Carnage (Druid - TWoW Custom Talent) -The **Carnage** talent (Feral Combat tree, 2/2 required) causes **Ferocious Bite at 5 combo points** to refresh Rip and Rake back to their original duration. - -**How it works:** -1. Cast Rip at 5 CP → 18 second duration (with Idol of Savagery: 16.2s) -2. Cast Ferocious Bite at 5 CP → Rip refreshes back to 18 seconds (or 16.2s with idol) - -**Features:** -- ✅ **Duration preservation** - Remembers the original cast duration (including combo points, talents, and equipment modifiers) -- ✅ **pfUI integration** - Refreshed duration displays correctly on pfUI's target debuff timers -- ✅ **Multiple DoT support** - Both Rip and Rake refresh simultaneously and display correctly +### Carnage (Druid - TWoW Custom) +Ferocious Bite at 5 CP refreshes Rip/Rake to original duration. **Requirements:** -- Carnage talent at rank 2 or higher -- Ferocious Bite must be cast with exactly 5 combo points -- Rip/Rake must already be active on the target - -### Equipment Modifiers -The addon supports item-based duration modifications. -**IF YOUR EQUIPMENT DEBUFF DURATION MODIFIER IS MISSING, PLEASE MAKE A BUG REPORT!!!** - -**Calculation Order:** `final_duration = (((base + combo_points) + talent_modifier) × equipment_modifier) + set_bonus` - -**Supported Equipment Modifiers:** -- **Idol of Savagery** (Item ID: 61699, Druid Idol): Reduces the time between periodic ticks and the duration of Rake and Rip by 10% (×0.9 multiplier) - -**Example:** Rip with 5 CP and Idol of Savagery equipped: -1. Base duration: 10s -2. Combo point modifier: 10s + (5-1)×2 = 18s -3. Talent modifier: 18s + 0 = 18s (if no talent) -4. Equipment modifier: 18s × 0.9 = **16.2s final** - -### Set Bonus Modifiers -The addon supports set bonus duration modifications by counting equipped set pieces. -**IF YOUR SET BONUS DEBUFF DURATION MODIFIER IS MISSING, PLEASE MAKE A BUG REPORT!!!** - -**How It Works:** -The system counts how many pieces from a set are equipped and applies the modifier when the threshold is met. - -**Supported Set Bonus Modifiers:** -- **Druid:** - - **Dreamwalker Regalia (4/9)**: +3s to Moonfire duration, +2s to Insect Swarm duration - - Item IDs: 47372, 47373, 47374, 47375, 47376, 47377, 47378, 47379, 47380 - - **Haruspex's Garb (3/5)**: +5s to Faerie Fire duration (non-feral only) - - Item IDs: 19613, 19955, 19840, 19839, 19838 - -**Example:** Moonfire with Dreamwalker Regalia 4-set: -1. Base duration: 18s -2. Set bonus modifier: 18s + 3s = **21s final** - -**Example:** Insect Swarm with Dreamwalker Regalia 4-set: -1. Base duration: 18s -2. Set bonus modifier: 18s + 2s = **20s final** - -### Immunity Tracking System -The addon automatically learns and tracks NPC immunities from combat log messages: - -**Features:** -- ✅ **Auto-learning** from combat log: "X's Spell fails. Y is immune." -- ✅ **Damage school tracking**: fire, frost, nature, shadow, arcane, holy, physical -- ✅ **Buff-based immunity detection**: Tracks temporary immunities (e.g., boss immune during shield phase) -- ✅ **Automatic spell school detection** via tooltip scanning - -**Usage in Macros:** -```lua --- Check immunity before casting -/cast [noimmune:fire] Fireball; Frostbolt -/cast [noimmune:"Flame Shock"] Flame Shock; Lightning Bolt - --- Check if target is immune (any school) -/cast [immune] Shoot; Arcane Shot -``` - -**Manual Control:** -```lua -/cleveroid listimmune [school] -- List all or specific immunities -/cleveroid addimmune "NPC Name" fire -- Add manual immunity -/cleveroid removeimmune "NPC Name" fire -- Remove immunity -/cleveroid clearimmune [school] -- Clear immunity data -``` - -### Technical Details -- **Combo point tracking:** - - Hooks `CastSpell`, `UseAction`, and `CastSpellByName` at addon load time - - Pre-captures combo points before spell execution - - Works universally across all casting methods (spellbook, action bars, macros, Lua calls) - - Integrates with pfUI's debuff timer display -- **Debuff duration tracking:** - - Uses **UNIT_CASTEVENT** for precise cast detection (only tracks successful hits) - - Uses **RAW_COMBATLOG** to detect when debuffs fade - - Stores durations in **SavedVariables** (persists between sessions) - - Falls back to static database for unknown spells -- **No dependency on pfUI or other addons** (but integrates with pfUI if present) +- Carnage talent rank 2+ +- Ferocious Bite with exactly 5 CP +- Rip/Rake active on target --- -# Usage -## Slash Commands -| Command | Conditionals Supported | Purpose | -|-----------------------| :-: |---------| -| /cleveroid | | show update settings. /cleveroid realtime 0/1 : /cleveroid refresh 1-10 | -| /target | * | Targets a unit that matchets the conditionals. Requires friendly nameplates for friendly non-party/raid units or their pets. Requires enemy nameplates for non-friendly units. | -| /retarget | | Clears your target if it doesn't exist, has 0 hp or if you can't attack it and then targets the nearest enemy. | -| /startattack | * | Starts auto-attacking. | -| /stopattack | * | Stops auto-attacking. | -| /stopcasting | * | Stops casting. | -| /unqueue | * | Stops casting. | -| /petattack | * | Command pet to Attack. | -| /petfollow | * | Command pet to Follow. | -| /petwait | * | Command pet to Wait (Stay). | -| /petaggressive | * | Set pet mode to Aggressive. | -| /petdefensive | * | Set pet mode to Defensive. | -| /petpassive | * | Set pet mode to Passive. | -| /castpet | | Casts a pet ability by name. | -| /castsequence | * | Performs a cast sequence. See [below](#cast-sequence) for more infomation. | -| /equip | * | Equips an item by name or itemid. | -| /equipmh | * | Equips an item by name or itemid into your mainhand slot. | -| /equipoh | * | Equips an item by name or itemid into your offhand slot. | -| /unshift | * | Cancels your current shapeshift form. | -| /cancelaura, /unbuff | | Cancels a valid buff/aura. | -| /runmacro | | Runs a macro. Use /runmacro {macroname} | -| /use | * | Uses an item by name or id | -| /cast | * | Casts a spell by name | -| /stopmacro | * | prevent any lines under /stopmacro from being run unless conditionals are met | + +## Equipment Modifiers + +Item-based duration modifications. + +**Calculation:** `final = (base + combo + talent) × equipment` + +### Supported Items +- **Idol of Savagery (61699):** -10% to Rip/Rake duration (×0.9) + +### Example +Rip with 5 CP and Idol of Savagery: +1. Base: 10s +2. Combo: 10s + (5-1)×2 = 18s +3. Talent: 18s + 0 = 18s +4. Equipment: 18s × 0.9 = **16.2s final** + +--- + +## Set Bonus Modifiers + +Set piece bonuses affect durations. + +### Supported Sets +- **Dreamwalker Regalia (4/9):** +3s Moonfire, +2s Insect Swarm +- **Haruspex's Garb (3/5):** +5s Faerie Fire (non-feral) + +--- + +## Immunity Tracking + +Auto-learns NPC immunities from combat log. + +### Features +- ✅ Auto-learning: "X's Spell fails. Y is immune." +- ✅ Damage schools: fire, frost, nature, shadow, arcane, holy, physical, bleed +- ✅ Buff-based immunities (temporary) +- ✅ Split damage spell support (Rake, Pounce, Garrote) + +### Examples +```lua +-- Check by damage school +/cast [noimmune:fire] Fireball; Frostbolt +/cast [noimmune:bleed] Rip; Shred + +-- Check by spell name +/cast [noimmune:"Flame Shock"] Flame Shock; Lightning Bolt + +-- Split damage spells +/cast [noimmune] Rake -- Checks bleed (DoT) +/cast [noimmune:physical] Rake -- Checks physical (initial) +/cast [noimmune noimmune:physical] Rake -- Checks BOTH +``` + +### Split Damage Spells +- **Rake:** Physical initial + Bleed DoT +- **Pounce:** Physical initial + Bleed DoT +- **Garrote:** Physical initial + Bleed DoT + +Default `[noimmune]` checks the debuff school (bleed). + +--- + +# Slash Commands + +| Command | Conditionals | Purpose | +|---------|:------------:|---------| +| /target | * | Target unit matching conditionals | +| /retarget | | Clear invalid target, target nearest enemy | +| /startattack | * | Start auto-attacking | +| /stopattack | * | Stop auto-attacking | +| /stopcasting | * | Stop casting | +| /unqueue | * | Clear spell queue | +| /petattack | * | Pet attack | +| /petfollow | * | Pet follow | +| /petwait | * | Pet stay | +| /petaggressive | * | Set pet aggressive | +| /petdefensive | * | Set pet defensive | +| /petpassive | * | Set pet passive | +| /castpet | | Cast pet ability by name | +| /castsequence | * | Cast sequence (see below) | +| /equip | * | Equip item by name/ID | +| /equipmh | * | Equip main hand | +| /equipoh | * | Equip off-hand | +| /unshift | * | Cancel shapeshift form | +| /cancelaura | | Cancel buff/aura | +| /unbuff | | Alias for cancelaura | +| /runmacro | | Run macro by name | +| /use | * | Use item by name/ID | +| /cast | * | Cast spell by name | +| /stopmacro | * | Stop macro execution | --- ## Cast Sequence - ```lua - #showtooltip - /castsequence reset=3 Fireball, Frostbolt, Arcane Explosion, Fire Blast - ``` -* Dynamic icons and tooltips on each sequence with range/mana/usable checks -* Supports reset=#/mod/target/combat -* Supports conditionals for the entire sequence, not individual actions within the sequence. - ```lua - #showtooltip - /castsequence [group:party/raid] reset=3/target Sweaty Spell 1, Sweaty Spell 2, Sweaty Spell 3 - /castsequence reset=target/combat Casual Spell 1, Casual Spell 2, Casual Spell 3 - ``` -## Conditionals Reference - -See the **[Macro Syntax Guide](#macro-syntax-guide)** above for detailed syntax rules. - -### Example Macros - -**startattack, stopattack, stopcasting:** -``` +```lua #showtooltip -/startattack [harm alive] - -#showtooltip -/stopattack [form:0/1/2/3] - -#showtooltip -/stopcasting [hp:<=20] +/castsequence reset=3 Fireball, Frostbolt, Arcane Explosion, Fire Blast ``` -**Item Slots (1-19) with /use:** -``` +- Dynamic icons/tooltips with range/mana/usable checks +- **Reset options:** `reset=#/mod/target/combat` +- Conditionals apply to entire sequence + +```lua #showtooltip -/use [combat hp:<=20] 13 +/castsequence [group:party/raid] reset=3/target Spell1, Spell2, Spell3 +/castsequence reset=target/combat CasualSpell1, CasualSpell2 ``` -**Bandage priority example:** -``` -#showtooltip -/use [@mouseover alive help hp:<70 nodebuff:"Recently Bandaged"] Runecloth Bandage -/use [@target alive help hp:<70 nodebuff:Recently_Bandaged] Runecloth Bandage -/use [@player] Runecloth Bandage -``` - -**Using selfcasting conditional:** -``` -#showtooltip -/stopcasting [selfcasting mod:shift] -/cast [mod:shift] Polymorph - --- Stop casting and cast Frostbolt if you're currently casting/channeling -#showtooltip -/stopcasting [selfcasting] -/cast Frostbolt - --- Only cast if not already casting -#showtooltip -/cast [noselfcasting] Fireball -``` - -### Additional Notes - -* **Focus targeting**: @focus can be used as a unitid, but requires a compatible addon (pfUI or similar) since Vanilla doesn't support focus natively -* **Time-based buff/debuff checks**: Only the player's buffs/debuffs can be checked for remaining time -* **DLL requirements**: [SuperWoW](https://github.com/balakethelock/SuperWoW), [Nampower](https://github.com/pepopo978/nampower), and [UnitXP_SP3](https://codeberg.org/konaka/UnitXP_SP3) are all required - --- -## Conditional Keywords +# Conditionals Reference -### Key Modifiers -| Conditional | Syntax Examples | Multi | Noable | Tests For | -|----------------|---------------| :-: | :-: |-----------| -| mod | [mod]
[mod:ctrl/alt/shift] | * | * | If any mod key is pressed.
If one of the listed modifier keys are pressed. | +## Key Modifiers +| Conditional | Syntax | Multi | Noable | Tests For | +|-------------|--------|:-----:|:------:|-----------| +| mod | [mod]
[mod:ctrl/alt/shift] | * | * | Any mod key pressed
Specific mod key(s) pressed | -### Player Only -| Conditional | Syntax Examples | Multi | Noable | Tests For | -|----------------|---------------| :-: | :-: |-----------| -| cdgcd | [cdgcd]
[cdgcd:"Name"]
[cdgcd:"Name">X] | * | * | If the Spell or Item is on cooldown and optionally if the amount of time left is >= or <= than X seconds. **GCD NOT IGNORED** | -| channeled | [channeled] | | * | If the player is currently channeling a spell. | -| checkchanneled | [checkchanneled] | | | Prevents a spell from being cast if you are already channeling it. | -| combo | [combo:>#3]
[combo:#2]
[combo:<#5] | * | * | If the player has the specified number of combo points. | -| cooldown | [cooldown]
[cooldown:"Name"]
[cooldown:"Name"= or <= than X seconds. **GCD (if exatly 1.5 sec) IGNORED** | -| druidmana | [druidmana:>=X]
[druidmana:>=X/<=Y] | * | | The druid's mana compared to X. **ONLY FOR DRUIDS** **Works in Cat or Bear form.** | -| equipped | [equipped:"Name"]
[equipped:Shields]
[equipped:Daggers2] | * | * | If the player has an item name/id or item type equipped. See [below](#weapon-types) for a list of valid Weapon Types. | -| form | [form:0/1/2/3/4/5] | * | * | Alias of `stance` | -| group | [group]
[group:party/raid] | * | | If the player is in any group or specific group type. | -| known | [known]
[known:"Name"]
[known:"Name">#2] | * | * | If the player knows a spell or talent. Can optionally check the rank. | -| mybuff | [mybuff]
[mybuff:"Name"]
[mybuff:"Name">#X]
[mybuff:Optionally compared to X number of stacks.
Optionally compared to X time remaining. | -| mybuffcount | [mybuffcount:>=X]
[mybuffcount:<=X] | * | | If the player has more or less auras present than X.| -| mydebuff | [mydebuff]
[mydebuff:"Name"]
[mydebuff:"Name">#X]
[mydebuff:Optionally compared to X number of stacks.
Optionally compared to X time remaining. | -| myhp | [myhp:<=X]
[myhp:>=X/<=Y] | * | | The player's health **PERCENT** compared to X. | -| myhplost | [myhplost:>=X]
[myhplost:>=X/<=Y] | * | | The player's lost health compared to X. | -| mylevel | [mylevel:>=X]
[mylevel:<=Y] | * | | The player's level compared to X. | -| mypower | [mypower:>=X]
[mypower:>=X/<=Y] | * | | The player's power (mana/rage/energy) **PERCENT** compared to X. | -| mypowerlost | [mypowerlost:>=X]
[mypowerlost:>=X/<=Y] | * | | The player's lost power (mana/rage/energy) compared to X. | -| myrawhp | [myrawhp:>=X]
[myrawhp:>=X/<=Y] | * | | The player's health compared to X. | -| myrawpower | [myrawpower:>=X]
[myrawpower:>=X/<=Y] | * | | The player's power (mana/rage/energy) compared to X. | -| mhimbue | [mhimbue:Flametongue] | | * | If the player has weapon imbue on main hand. | -| ohimbue | [ohimbue] | | * | If the player has weapon imbue on off-hand. | -| onswingpending | [onswingpending] | | * | If the player has a on swing spell pending.| -| pet | [pet]
[pet:Voidwalker]
[pet:Imp/Felhunter] | * | * | If the player has a pet summoned and optionally if it matches the specified pet type(s). Works for Warlock demons and Hunter pets. | -| queuedspell | [queuedspell]
[queuedspell:X] | * | * | If the player has any or a specific spell queued with nampower. | -| reactive | [reactive]
[reactive:Overpower] | * | * | If the player has the reactive ability (Revenge, Overpower, Riposte, etc.) available to use.

**NOTE: Currently requires the reactive ability to be somewhere on your actionbars in addition to any macros you're using it in. A planned future update will remove this requirement if using [Nampower](https://github.com/pepopo978/nampower).** | -| resting | [resting] | | * | If the player is resting (in an inn/capital city/etc.) | -| selfcasting | [selfcasting] | | * | If the player is currently casting or channeling any spell. More reliable than `[casting @player]` | -| stance | [stance:0/1/2/3/4/5] | * | * | If the player is in stance #.
Supports Shadowform and Stealth as stance 1.| -| stat | [stat:stat>=x/<=y] | * | | Check if one of the players statistics is greater or less than a specific number. Available Stats: str/strength, agi/agility, stam/stamina, int/intellect, spi/spirit, ap/attackpower, rap/rangedattackpower, healing/healingpower, arcane_power, fire_power, frost_power, nature_power, shadow_power, armor, defense, arcane_res, fire_res, frost_res, nature_res, shadow_res. | -| stealth | [stealth] | | * | If the player is in Stealth or Prowl. | -| swingtimer | [swingtimer:<15] | * | * | If a percentage of swing time has elapsed. `<15` = early in swing, `>80` = late in swing. Requires [SP_SwingTimer](https://github.com/jrc13245/SP_SwingTimer) addon. See [below](#swing-timer-integration) for details.| -| stimer | [stimer:<15] | * | * | Alias for `swingtimer` | -| swimming | [swimming] | | * | Druid only, works like reactive but for aquatic form, must have aquatic form on one of your non-stance actionbars. | -| usable | [usable]
[usable:"Spell Name"] | * | * | If the spell or item is usable (not on cooldown, have reagents, etc.). | -| zone | [zone:"Zone"]
[zone:"Zone"/"Another Zone"] | * | * | If the player is in one or more zones of the given name. | +## Player Only -### Unit Based -### The default @unitid is usually @target if you don't specify one -### The only conditionals that take conditional:unitid are combat/nocombat and targeting/notargeting -| Conditional | Syntax | Multi | Noable | Tests For | -|----------------|---------------| :-: | :-: |-----------| -| alive | [alive] | | * | If the @unitid is NOT dead or a ghost. | -| behind | [behind] | | * | If the player is behind the target.| -| buff | [buff]
[buff:"Name"]
[buff:"Name">#X]
[buff:"Name"= or <= than X number of stacks or X time remaining. | -| casting | [casting]
[casting:"Spell Name"] | * | * | If the @unitid is casting any or one or more specific spells. | -| class | [class:classname1/classname2]
[class:Warrior/Priest] | * | * | The target is a player of the specified class/classes. | -| combat | [combat]
[combat:target] | * | * | If the unitid (default is player) is in combat. | -| dead | [dead] | | * | If the @unitid is dead or a ghost. | -| debuff | [debuff]
[debuff:"Name"]
[debuff:"Name">#X]
[debuff:= or <= than X number of stacks or X time remaining. | -| distance | [distance:>X]
[distance:=X]
[hp:>=X/<=Y] | * | | The @unitid health **PERCENT** compared to X. | -| hplost | [hplost:>=X]
[hplost:>=X/<=Y] | * | | The @unitid health lost compared to X. | -| immune | [immune:fire]
[immune:Flame_Shock] | * | * | If the npc has immunities to a damage type or spell. Check slash commands section for more information. | -| inrange | [inrange]
[inrange:"Name"] | * | * | If the specified @unitid is in range of the spell. | -| insight | [insight] | | * | If the player is in line of sight of the target. | -| isnpc | [isnpc] | | | If the @unitid is an npc.
See this [article](https://wowpedia.fandom.com/wiki/UnitId) for a list of unitids.
Not all units are valid in vanilla. | -| isplayer | [isplayer] | | | If the @unitid is a player.
See this [article](https://wowpedia.fandom.com/wiki/UnitId) for a list of unitids.
Not all units are valid in vanilla. | -| level | [level:>=X]
[level:<=Y] | * | | The @unitid level compared to X. | -| meleerange | [meleerange] | | * | If the player is melee range of the target.| -| member | [member] | * | | If the @unitid is in your party OR raid. | -| outrange | [outrange]
[outrange:"Name"] | | | If the specified @unitid is out of range of the spell. | -| party | [party] | | * | If the @unitid is in your party. | -| power | [power:>=X]
[power:>=X/<=Y] | * | | The @unitid power (mana/rage/energy) **PERCENT** compared to X. | -| powerlost | [powerlost:>=X]
[powerlost:>=X/<=Y] | * | | The @unitid power (mana/rage/energy) lost compared to X. | -| powertype | [powertype:mana]
[powertype:rage]
[powertype:energy]
[powertype:mana/rage] | * | * | If the @unitid uses the specified power type(s). Valid types: mana, rage, energy, focus. Useful for checking if NPCs use mana or for player class detection. | -| raid | [raid] | | * | If the @unitid is in your raid. | -| rawhp | [rawhp:>=X]
[rawhp:>=X/<=Y] | * | | The @unitid health compared to X. | -| rawpower | [rawpower:>=X]
[rawpower:>=X/<=Y] | * | | The @unitid power (mana/rage/energy) compared to X. | -| targeting | [targeting:unitid] | * | * | If the @unitid is targeting the specified unitid.
See this [article](https://wowpedia.fandom.com/wiki/UnitId) for a list of unitids.
Not all units are valid in vanilla. | -| type | [type:"Creature Type"] | * | * | If the @unitid is the specified creature type. See [below](#creature-types) for a list of valid Creature Types. | -| @unitid | [@mouseover] | | | The @unitid is a valid target. | +| Conditional | Syntax | Multi | Noable | Tests For | +|-------------|--------|:-----:|:------:|-----------| +| cdgcd | [cdgcd:"Name">X] | * | * | Cooldown **INCLUDING** GCD | +| channeled | [channeled] | | * | Player channeling | +| checkchanneled | [checkchanneled] | | | Prevent recasting channel | +| combo | [combo:>#3] | * | * | Combo points | +| cooldown | [cooldown:"Name"=X] | * | | Druid mana in form | +| equipped | [equipped:Daggers2] | * | * | Item/type equipped | +| form | [form:0/1/2] | * | * | Shapeshift form | +| group | [group:party/raid] | * | | In group type | +| known | [known:"Name">#2] | * | * | Spell/talent known | +| mybuff | [mybuff:"Name"=X] | * | | Total buff count | +| mydebuff | [mydebuff:"Name"=X] | * | | Player HP lost | +| mylevel | [mylevel:>=60] | * | | Player level | +| mypower | [mypower:>=50] | * | | Player power % | +| mypowerlost | [mypowerlost:>=X] | * | | Player power lost | +| myrawhp | [myrawhp:>=1000] | * | | Player HP raw | +| myrawpower | [myrawpower:>=500] | * | | Player power raw | +| mhimbue | [mhimbue:Flametongue] | | * | Main hand imbue | +| ohimbue | [ohimbue] | | * | Off-hand imbue | +| onswingpending | [onswingpending] | | * | On-swing spell queued | +| pet | [pet:Voidwalker] | * | * | Pet summoned | +| queuedspell | [queuedspell:X] | * | * | Spell queued | +| reactive | [reactive:Overpower] | * | * | Reactive ability available | +| resting | [resting] | | * | In rest area | +| selfcasting | [selfcasting] | | * | Player casting/channeling | +| stance | [stance:1/2/3] | * | * | In stance # | +| stat | [stat:str>=100] | * | | Player stat check | +| stealth | [stealth] | | * | Stealth/Prowl | +| swingtimer | [swingtimer:<15] | * | * | Swing % elapsed | +| stimer | [stimer:>80] | * | * | Alias for swingtimer | +| swimming | [swimming] | | * | Aquatic form available | +| usable | [usable:"Name"] | * | * | Spell/item usable | +| zone | [zone:"Ironforge"] | * | * | In zone | -### Unitids -| Name (N=party/raid slot number) | -|------| +## Unit Based + +Default `@unitid` is usually `@target` if not specified. + +| Conditional | Syntax | Multi | Noable | Tests For | +|-------------|--------|:-----:|:------:|-----------| +| alive | [alive] | | * | NOT dead/ghost | +| behind | [behind] | | * | Player behind target | +| buff | [buff:"Name"=50] | * | | Unit HP % | +| hplost | [hplost:>=X] | * | | Unit HP lost | +| immune | [immune:fire] | * | * | Immune to school/spell | +| inrange | [inrange:"Name"] | * | * | In spell range | +| insight | [insight] | | * | In line of sight | +| isnpc | [isnpc] | | | Is NPC | +| isplayer | [isplayer] | | | Is player | +| level | [level:>=60] | * | | Unit level | +| meleerange | [meleerange] | | * | In melee range | +| member | [member] | * | | In party OR raid | +| outrange | [outrange:"Name"] | | | Out of spell range | +| party | [party] | | * | In party | +| power | [power:>=50] | * | | Unit power % | +| powerlost | [powerlost:>=X] | * | | Unit power lost | +| powertype | [powertype:mana/rage] | * | * | Power type | +| raid | [raid] | | * | In raid | +| rawhp | [rawhp:>=1000] | * | | Unit HP raw | +| rawpower | [rawpower:>=500] | * | | Unit power raw | +| targeting | [targeting:player] | * | * | Unit targeting X | +| type | [type:Undead] | * | * | Creature type | +| @unitid | [@mouseover] | | | Valid unitid | + +## UnitIDs + +| UnitID | +|--------| | player | | target | | pet | | mouseover | -| partyN | +| partyN (N=1-4) | | partypetN | -| raidN | +| raidN (N=1-40) | | raidpetN | | targettarget | | playertarget | @@ -683,179 +580,120 @@ See the **[Macro Syntax Guide](#macro-syntax-guide)** above for detailed syntax | partyNtarget | | raidNtarget | | targettargettarget | -| focus (requires pfui or another addon) | +| focus (requires pfUI) | -### Classes -| Class | -|------| -| Warrior | -| Paladin | -| Hunter | -| Shaman | -| Druid | -| Rogue | -| Mage | -| Warlock | -| Priest | +## Classes -### Weapon Types -| Name | Slot | +Warrior, Paladin, Hunter, Shaman, Druid, Rogue, Mage, Warlock, Priest + +## Weapon Types + +| Type | Slot | |------|------| -| Axes, Axes2 | Main Hand, Off Hand | +| Axes, Axes2 | Main/Off Hand | | Bows | Ranged | -| Daggers, Daggers2 | Main Hand, Off Hand | | Crossbows | Ranged | -| Fists, Fists2 | Main Hand, Off Hand | +| Daggers, Daggers2 | Main/Off Hand | +| Fists, Fists2 | Main/Off Hand | | Guns | Ranged | -| Maces, Maces2 | Main Hand, Off Hand | +| Maces, Maces2 | Main/Off Hand | | Polearms | Main Hand | | Shields | Off Hand | | Staves | Main Hand | -| Swords, Swords2 | Main Hand, Off Hand | +| Swords, Swords2 | Main/Off Hand | | Thrown | Ranged | | Wands | Ranged | +## Creature Types -### Creature Types -| Name | -|------| -| Boss | -| Worldboss | -| Beast | -| Dragonkin | -| Demon | -| Elemental | -| Giant | -| Undead | -| Humanoid | -| Critter | -| Mechanical | -| Not specified | -| Totem | -| Non-combat Pet | -| Gas Cloud | +Boss, Worldboss, Beast, Dragonkin, Demon, Elemental, Giant, Undead, Humanoid, Critter, Mechanical, Not specified, Totem, Non-combat Pet, Gas Cloud -### Reactive Spells (twow) -| Name | -|---------------| -| Revenge | -| Overpower | -| Riposte | -| Lacerate | -| Baited Shot | -| Counterattack | -| Arcane Surge | +## Reactive Spells (TWoW) -### UnitFrames -* agUnitFrames -* Blizzard's UnitFrames -* CT_RaidAssist -* CT_UnitFrames -* DiscordUnitFrames -* FocusFrame -* Grid -* LunaUnitFrames -* NotGrid -* PerfectRaid -* pfUI -* sRaidFrames -* UnitFramesImproved_Vanilla -* XPerl +Revenge, Overpower, Riposte, Lacerate, Baited Shot, Counterattack, Arcane Surge + +## Available Stats (stat conditional) + +str/strength, agi/agility, stam/stamina, int/intellect, spi/spirit, ap/attackpower, rap/rangedattackpower, healing/healingpower, arcane_power, fire_power, frost_power, nature_power, shadow_power, armor, defense, arcane_res, fire_res, frost_res, nature_res, shadow_res + +--- + +# Supported Addons + +### Unit Frames +agUnitFrames, Blizzard, CT_RaidAssist, CT_UnitFrames, DiscordUnitFrames, FocusFrame, Grid, LunaUnitFrames, NotGrid, PerfectRaid, pfUI, sRaidFrames, UnitFramesImproved_Vanilla, XPerl ### Action Bars -* Blizzard's Action Bars -* Bongos -* Discord Action Bars -* pfUI +Blizzard, Bongos, Discord Action Bars, pfUI +### Other +ClassicFocus/FocusFrame, SuperMacro, ShaguTweaks, SP_SwingTimer -### Supported Addons -* ClassicFocus / FocusFrame -* SuperMacro -* ShaguTweaks +--- +# Swing Timer Integration -### Swing Timer Integration -The addon integrates with [SP_SwingTimer](https://github.com/jrc13245/SP_SwingTimer) to provide swing timer conditionals for abilities like Slam that benefit from being cast at specific points in your swing cycle. - -**Requirements:** -- [SP_SwingTimer](https://github.com/jrc13245/SP_SwingTimer) addon must be installed -- If SP_SwingTimer is not loaded, the conditional will return false and display an error message once +Requires [SP_SwingTimer](https://github.com/jrc13245/SP_SwingTimer). **How It Works:** -The conditional checks what percentage of your swing time has **elapsed** (not remaining). - -`[swingtimer:<15]` checks if less than 15% of your swing has elapsed (early in swing). - -This allows you to cast abilities like Slam at the optimal time in your swing cycle. +Checks what percentage of swing time has **elapsed** (not remaining). **Syntax:** -- `[swingtimer:X]` - True if more than X% of swing has elapsed (late in swing) -- `[swingtimer:>=X]` - True if X% or more of swing has elapsed -- `[swingtimer:<=X]` - True if X% or less of swing has elapsed -- `[stimer:80]` - Alias for noswingtimer - -**Example Macros:** - ```lua --- Cast Slam early in the swing (first 15% of swing time) --- This is optimal for Slam to avoid delaying your next auto-attack +[swingtimer:<15] -- Less than 15% elapsed (early) +[swingtimer:>80] -- More than 80% elapsed (late) +[stimer:70] Heroic Strike -``` -```lua --- Complex rotation with swing timer awareness +-- Complex rotation #showtooltip /cast [swingtimer:<15] Slam /cast [swingtimer:>80] Heroic Strike /cast Bloodthirst ``` -**Understanding the Percentage:** -- 0% = Just attacked (swing just started) -- 15% = Early in swing (optimal for Slam) -- 50% = Half of swing time has elapsed -- 80% = Late in swing (close to next auto-attack) +**Understanding Percentage:** +- 0% = Swing just started +- 15% = Early (optimal for Slam) +- 50% = Half elapsed +- 80% = Late (close to next swing) - 100% = About to swing -For a warrior with a 3.5 second weapon: -- `[swingtimer:<15]` = True when less than 0.525 seconds have elapsed (early) -- `[swingtimer:>80]` = True when more than 2.8 seconds have elapsed (late) +For 3.5s weapon: +- `<15` = less than 0.525s elapsed +- `>80` = more than 2.8s elapsed --- -### HealComm Support -This addon uses [SuperWoW](https://github.com/balakethelock/SuperWoW)'s CastSpellByName which is not compatible with the standard HealComm-1.0 library. MarcelineVQ has an updated version of [LunaUnitFrames](https://github.com/MarcelineVQ/LunaUnitFrames) where they added [SuperWoW](https://github.com/balakethelock/SuperWoW) support to the HealComm-1.0 library. If you use [SuperWoW](https://github.com/balakethelock/SuperWoW) and want proper HealComm support with your macros, you need to do one or both of the following: +# HealComm Support -* Contact the author of whichever addon and ask that they update their HealComm to support [SuperWoW](https://github.com/balakethelock/SuperWoW) and link them to MarcelineVQ's updated [LunaUnitFrames](https://github.com/MarcelineVQ/LunaUnitFrames). +This addon uses SuperWoW's CastSpellByName which is incompatible with standard HealComm-1.0. -* Manually update any addon that is using the `HealComm-1.0` library by deleting the `HealComm-1.0` folder and replacing it with the `libs/HealComm-1.0` folder from MarcelineVQ's version of LunaUnitFrames. +**Solution:** +Use MarcelineVQ's updated [LunaUnitFrames](https://github.com/MarcelineVQ/LunaUnitFrames) which includes SuperWoW support in HealComm-1.0. -Example for the standalone HealComm addon: -1. Exit WoW completely. -2. Download a copy of the updated [LunaUnitFrames](https://github.com/MarcelineVQ/LunaUnitFrames). -3. Delete the `/Interface/AddOns/HealComm/libs/HealComm-1.0` folder. -4. From the [LunaUnitFrames](https://github.com/MarcelineVQ/LunaUnitFrames) download, copy the `/libs/HealComm-1.0` folder into your `/Interface/AddOns/HealComm/libs/` folder. +**Manual Update:** +1. Exit WoW +2. Download MarcelineVQ's [LunaUnitFrames](https://github.com/MarcelineVQ/LunaUnitFrames) +3. Delete `/Interface/AddOns/HealComm/libs/HealComm-1.0` +4. Copy `/libs/HealComm-1.0` from LunaUnitFrames into `/Interface/AddOns/HealComm/libs/` +--- ----- -### Original Addons & Authors -* [Roid-Macros by DennisWG (DWG)](https://github.com/DennisWG/Roid-Macros) -* [CleverMacro by DanielAdolfsson (_brain)](https://github.com/DanielAdolfsson/CleverMacro) ----- -License ----- +## Original Addons & Authors +- [Roid-Macros](https://github.com/DennisWG/Roid-Macros) by DennisWG (DWG) +- [CleverMacro](https://github.com/DanielAdolfsson/CleverMacro) by DanielAdolfsson (_brain) + +## License MIT diff --git a/Utility.lua b/Utility.lua index 3a2a143..aa9876c 100644 --- a/Utility.lua +++ b/Utility.lua @@ -2155,6 +2155,16 @@ local IMMUNITY_SCHOOLS = { frost = 5, shadow = 6, arcane = 7, + bleed = 8, +} + +-- Spells with split damage types (initial hit vs DoT/debuff) +-- GetSpellSchool() returns the debuff school by default for these spells +-- Users can explicitly check the initial damage school with [noimmune:physical] +local SPLIT_DAMAGE_SPELLS = { + ["Rake"] = { initial = "physical", debuff = "bleed" }, + ["Pounce"] = { initial = "physical", debuff = "bleed" }, + ["Garrote"] = { initial = "physical", debuff = "bleed" }, } -- Cache for spell school lookups @@ -2172,6 +2182,13 @@ local function GetSpellSchool(spellName) return spellSchoolCache[baseName] end + -- Check if this is a split damage spell (return debuff school by default) + if SPLIT_DAMAGE_SPELLS[baseName] then + local school = SPLIT_DAMAGE_SPELLS[baseName].debuff + spellSchoolCache[baseName] = school + return school + end + -- Try to find spell in player's spellbook local spell = CleveRoids.GetSpell(baseName) if not spell then @@ -2194,7 +2211,10 @@ local function GetSpellSchool(spellName) if line then local text = string.lower(line:GetText() or "") - if string.find(text, "fire") or string.find(text, "flame") then + if string.find(text, "bleed") then + school = "bleed" + break + elseif string.find(text, "fire") or string.find(text, "flame") then school = "fire" break elseif string.find(text, "frost") or string.find(text, "ice") then @@ -2219,7 +2239,11 @@ local function GetSpellSchool(spellName) -- Fallback: Use spell name patterns for common spells if not school then local lower = string.lower(baseName) - if string.find(lower, "fire") or string.find(lower, "flame") or string.find(lower, "immolat") or string.find(lower, "scorch") then + if string.find(lower, "rip") or string.find(lower, "rake") or string.find(lower, "rupture") or + string.find(lower, "garrote") or string.find(lower, "rend") or string.find(lower, "deep wound") or + string.find(lower, "hemorrhage") or string.find(lower, "pounce") then + school = "bleed" + elseif string.find(lower, "fire") or string.find(lower, "flame") or string.find(lower, "immolat") or string.find(lower, "scorch") then school = "fire" elseif string.find(lower, "frost") or string.find(lower, "ice") or string.find(lower, "blizzard") then school = "frost" @@ -2489,13 +2513,13 @@ end function CleveRoids.AddImmunity(npcName, school, buffName) if not npcName or not school then CleveRoids.Print("Usage: /cleveroid addimmune [buff name]") - CleveRoids.Print("Schools: fire, frost, nature, shadow, arcane, holy, physical") + CleveRoids.Print("Schools: fire, frost, nature, shadow, arcane, holy, physical, bleed") return end school = string.lower(school) if not IMMUNITY_SCHOOLS[school] then - CleveRoids.Print("Invalid school. Use: fire, frost, nature, shadow, arcane, holy, physical") + CleveRoids.Print("Invalid school. Use: fire, frost, nature, shadow, arcane, holy, physical, bleed") return end @@ -2546,6 +2570,10 @@ end) -- Structure: { spellName = { expiry = time, targetGUID = guid } } CleveRoids.reactiveProcs = CleveRoids.reactiveProcs or {} +-- Table to track which reactive spells we've seen proc at least once +-- This helps us decide whether to use combat log tracking vs fallback methods +CleveRoids.reactiveProcsEverSeen = CleveRoids.reactiveProcsEverSeen or {} + -- Proc durations (in seconds) -- Overpower and Revenge: 4 seconds -- Riposte: 5 seconds (keeping at 5 for safety, can be adjusted) @@ -2627,19 +2655,33 @@ function CleveRoids.SetReactiveProc(spellName, duration, targetGUID) expiry = GetTime() + duration, targetGUID = targetGUID } + -- Mark that we've seen this reactive spell proc at least once + CleveRoids.reactiveProcsEverSeen[spellName] = true end -- Check if a reactive proc is active (with optional GUID check) function CleveRoids.HasReactiveProc(spellName) local procData = CleveRoids.reactiveProcs[spellName] - if not procData or not procData.expiry then return false end + if not procData or not procData.expiry then + return false + end local now = GetTime() if now >= procData.expiry then -- Expired, clear it + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cffff9900[HasReactiveProc]|r %s: EXPIRED - clearing and queuing update", spellName) + ) + end CleveRoids.reactiveProcs[spellName] = nil -- Queue action update to refresh icon state CleveRoids.QueueActionUpdate() + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cffff9900[HasReactiveProc]|r Action update queued, isActionUpdateQueued = %s", tostring(CleveRoids.isActionUpdateQueued)) + ) + end return false end