diff --git a/Conditionals.lua b/Conditionals.lua index fa11346..105bf17 100644 --- a/Conditionals.lua +++ b/Conditionals.lua @@ -2023,6 +2023,100 @@ function CleveRoids.ValidateTTE(operator, amount) return false end +-- ============================================================================ +-- MONKEYSPEED ADDON INTEGRATION +-- ============================================================================ +-- Integrates with MonkeySpeed addon for accurate movement speed detection +-- MonkeySpeed uses SuperWoW's UnitPosition to calculate actual movement speed + +--- Check if MonkeySpeed addon is available and loaded +--- @return boolean True if MonkeySpeed is available with speed data +function CleveRoids.HasMonkeySpeed() + return type(MonkeySpeed) == "table" and + MonkeySpeed.m_bLoaded == true and + MonkeySpeed.m_fSpeed ~= nil +end + +--- Require MonkeySpeed for a feature, warn once if missing +--- @param feature string Feature name for warning message +--- @return boolean True if MonkeySpeed is available +function CleveRoids.RequireMonkeySpeed(feature) + if CleveRoids.HasMonkeySpeed() then + return true + end + if not CleveRoids._monkeySpeedErrorShown then + DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[SuperCleveRoidMacros]|r The [moving] speed conditional requires the MonkeySpeed addon.", 1, 0.5, 0.5) + CleveRoids._monkeySpeedErrorShown = true + end + return false +end + +--- Get current player movement speed as percentage (100 = normal run speed) +--- @return number|nil Speed percentage, or nil if MonkeySpeed not available +function CleveRoids.GetPlayerSpeed() + if not CleveRoids.HasMonkeySpeed() then + return nil + end + return MonkeySpeed.m_fSpeed or 0 +end + +--- Check if player is currently moving (speed > 0) +--- Falls back to basic position checking if MonkeySpeed isn't available +--- @return boolean True if player is moving +function CleveRoids.IsPlayerMoving() + -- If MonkeySpeed is available, use it for accurate detection + if CleveRoids.HasMonkeySpeed() then + return (MonkeySpeed.m_fSpeed or 0) > 0 + end + + -- Fallback: use SuperWoW's UnitPosition for basic moving detection + -- We store last position and compare + if UnitPosition then + local x, y = UnitPosition("player") + if x and y then + if CleveRoids._lastPlayerPos then + local dx = x - CleveRoids._lastPlayerPos.x + local dy = y - CleveRoids._lastPlayerPos.y + local dist = math.sqrt(dx * dx + dy * dy) + CleveRoids._lastPlayerPos = { x = x, y = y, time = GetTime() } + -- Moving if distance > small threshold (account for floating point) + return dist > 0.001 + else + CleveRoids._lastPlayerPos = { x = x, y = y, time = GetTime() } + return false -- Can't determine on first call + end + end + end + + return false +end + +--- Validates a movement speed conditional. +--- Usage: [moving:>100] [moving:<50] [moving:=0] +--- operator: Comparison operator (>, <, =, >=, <=, ~=) +--- amount: Speed percentage (100 = normal run speed) +--- returns: True if speed [operator] amount +function CleveRoids.ValidateMovingSpeed(operator, amount) + if not operator or not amount then return false end + + -- Check if MonkeySpeed is available for speed comparisons + if not CleveRoids.RequireMonkeySpeed("moving speed comparison") then + return false + end + + local speed = CleveRoids.GetPlayerSpeed() + if speed == nil then + return false + end + + -- Compare speed against threshold + if CleveRoids.operators[operator] then + return CleveRoids.comparators[operator](speed, amount) + end + + return false +end + -- ============================================================================ -- CURSIVE ADDON INTEGRATION -- ============================================================================ @@ -6268,6 +6362,81 @@ CleveRoids.Keywords = { nobuffcapped = function(conditionals) local target = conditionals.target or "target" return not CleveRoids.IsTargetBuffCapped(target) + end, + + -- ========================================================================= + -- MOVEMENT SPEED CONDITIONALS (MonkeySpeed integration) + -- ========================================================================= + -- [moving] - Player is moving (speed > 0) + -- [moving:>100] - Player speed is above 100% (faster than normal run) + -- [moving:<50] - Player speed is below 50% (slower than half speed) + -- Requires MonkeySpeed addon for speed comparisons (basic moving check uses fallback) + moving = function(conditionals) + -- Boolean form [moving] - just check if moving at all + if conditionals.moving == true then + return CleveRoids.IsPlayerMoving() + end + + -- Operator form [moving:>100] - check speed percentage + return Multi(conditionals.moving, function(args) + if type(args) ~= "table" then return false end + + -- Handle multi-comparison (e.g., >50&<150) + if args.comparisons and type(args.comparisons) == "table" then + local speed = CleveRoids.GetPlayerSpeed() + if speed == nil then + CleveRoids.RequireMonkeySpeed("moving speed comparison") + return false + end + + -- ALL comparisons must pass (AND logic) + for _, comp in ipairs(args.comparisons) do + if not CleveRoids.operators[comp.operator] then + return false + end + if not CleveRoids.comparators[comp.operator](speed, comp.amount) then + return false + end + end + return true + end + + return CleveRoids.ValidateMovingSpeed(args.operator, args.amount) + end, conditionals, "moving") + end, + + -- [nomoving] - Player is NOT moving (speed == 0) + -- [nomoving:>100] - Player speed is NOT above 100% (at or below normal run) + nomoving = function(conditionals) + -- Boolean form [nomoving] - check if NOT moving + if conditionals.nomoving == true then + return not CleveRoids.IsPlayerMoving() + end + + -- Operator form [nomoving:>100] - negate speed comparison + return NegatedMulti(conditionals.nomoving, function(args) + if type(args) ~= "table" then return false end + + if args.comparisons and type(args.comparisons) == "table" then + local speed = CleveRoids.GetPlayerSpeed() + if speed == nil then + -- If MonkeySpeed not available, negated returns true (fail-safe) + return true + end + + for _, comp in ipairs(args.comparisons) do + if not CleveRoids.operators[comp.operator] then + return true + end + if not CleveRoids.comparators[comp.operator](speed, comp.amount) then + return true + end + end + return false + end + + return not CleveRoids.ValidateMovingSpeed(args.operator, args.amount) + end, conditionals, "nomoving") end } diff --git a/Core.lua b/Core.lua index 2e61ab3..4c326f9 100644 --- a/Core.lua +++ b/Core.lua @@ -96,6 +96,8 @@ local BOOLEAN_CONDITIONALS = { noohimbue = true, group = true, nogroup = true, + moving = true, + nomoving = true, } local requirementCheckFrame = CreateFrame("Frame") @@ -5285,11 +5287,7 @@ SlashCmdList["CLEVEROID"] = function(msg) DEFAULT_CHAT_FRAME:AddMessage("/cleveroid forget - Forget learned duration(s)") DEFAULT_CHAT_FRAME:AddMessage("/cleveroid debug [0|1] - Toggle learning debug messages") end - DEFAULT_CHAT_FRAME:AddMessage("/cleveroid macrodebug - Toggle macro length warning debug") - DEFAULT_CHAT_FRAME:AddMessage("/cleveroid macrorefdebug - Toggle macro reference {Name} execution debug") - DEFAULT_CHAT_FRAME:AddMessage("/cleveroid macrostatus - Check macro length warning status") - DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Spell School Detection:|r") - DEFAULT_CHAT_FRAME:AddMessage('/cleveroid schooltest - Test spell school detection') + DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Spell Schools:|r") DEFAULT_CHAT_FRAME:AddMessage('/cleveroid listschools - List all learned spell schools') DEFAULT_CHAT_FRAME:AddMessage('/cleveroid clearschools - Clear learned spell school data') DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Immunity Tracking:|r") @@ -5312,24 +5310,6 @@ SlashCmdList["CLEVEROID"] = function(msg) DEFAULT_CHAT_FRAME:AddMessage('/cleveroid listtab - List all talents in a tab with their IDs') DEFAULT_CHAT_FRAME:AddMessage('/cleveroid talents - Show current talent ranks') DEFAULT_CHAT_FRAME:AddMessage('/cleveroid testtalent - Test talent modifier for a spell') - DEFAULT_CHAT_FRAME:AddMessage('/cleveroid diagnosetalent - Diagnose talent modifier issues') - DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Equipment Modifiers:|r") - DEFAULT_CHAT_FRAME:AddMessage('/cleveroid testequip - Test equipment modifier for a spell') - DEFAULT_CHAT_FRAME:AddMessage('/cleveroid equipdebug - Debug item lookup for /equip') - DEFAULT_CHAT_FRAME:AddMessage('/cleveroid equiplog - Toggle real-time equip command logging') - DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Reactive Proc Tracking:|r") - DEFAULT_CHAT_FRAME:AddMessage('/cleveroid listprocs - Show active reactive ability procs') - DEFAULT_CHAT_FRAME:AddMessage('/cleveroid setproc [duration] - Manually set proc (testing)') - DEFAULT_CHAT_FRAME:AddMessage('/cleveroid clearproc [spell|all] - Clear reactive proc(s)') - DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Casting Detection:|r") - DEFAULT_CHAT_FRAME:AddMessage('/cleveroid testcasting - Test [selfcasting]/[noselfcasting] conditionals') - DEFAULT_CHAT_FRAME:AddMessage('/cleveroid testchannel - Test [channeltime] conditional tracking') - DEFAULT_CHAT_FRAME:AddMessage('/cleveroid channeldebug - Toggle [channeltime] conditional debug output') - DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Action Slot Debug:|r") - DEFAULT_CHAT_FRAME:AddMessage('/cleveroid slotdebug - Debug action slot state (tooltip/range/mana)') - DEFAULT_CHAT_FRAME:AddMessage('/cleveroid rangedebug - Debug spell range checking (channeled spells)') - DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Slam Rotation (Warrior):|r") - DEFAULT_CHAT_FRAME:AddMessage('/cleveroid slamdebug - Show Slam cast time and clip window calculations') DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Debuff Tracking Debug:|r") DEFAULT_CHAT_FRAME:AddMessage('/cleveroid debuffdebug [spell] - Debug debuff tracking on target') return @@ -5422,342 +5402,6 @@ SlashCmdList["CLEVEROID"] = function(msg) return end - -- macrodebug (toggle macro length warning debug messages) - if cmd == "macrodebug" then - CleveRoids.MacroLengthDebug = not CleveRoids.MacroLengthDebug - CleveRoids.Print("Macro length warning debug " .. (CleveRoids.MacroLengthDebug and "enabled" or "disabled")) - return - end - - -- macrorefdebug (toggle macro reference execution debug messages) - if cmd == "macrorefdebug" or cmd == "refdebug" then - CleveRoids.macroRefDebug = not CleveRoids.macroRefDebug - CleveRoids.Print("Macro reference debug " .. (CleveRoids.macroRefDebug and "enabled" or "disabled")) - CleveRoids.Print("Use /cast [cond] {MacroName} and watch for [MacroRef] messages") - return - end - - -- cooldowndebug (toggle cooldown update debug messages) - if cmd == "cooldowndebug" or cmd == "cddebug" then - CleveRoids.cooldownDebug = not CleveRoids.cooldownDebug - -- Clear the logged cache when enabling to get fresh output - if CleveRoids.cooldownDebug then - CleveRoids.cooldownDebugLogged = {} - end - CleveRoids.Print("Cooldown debug " .. (CleveRoids.cooldownDebug and "enabled" or "disabled")) - if CleveRoids.cooldownDebug then - CleveRoids.Print("Cast spells with broken cooldowns - issues will be logged once per slot.") - CleveRoids.Print("Use /cleveroid slotdebug for detailed slot info.") - local handlerCount = CleveRoids.actionEventHandlers and table.getn(CleveRoids.actionEventHandlers) or 0 - CleveRoids.Print("Action event handlers: " .. handlerCount .. " (0 = Blizzard UI only)") - end - return - end - - -- slotdebug - Debug action slot state for tooltip/range/mana issues - if cmd == "slotdebug" then - local slot = tonumber(val) - if not slot then - CleveRoids.Print("Usage: /cleveroid slotdebug - Debug action slot (1-120)") - return - end - local actions = CleveRoids.GetAction(slot) - CleveRoids.Print("|cff00ff00=== Slot " .. slot .. " Debug ===|r") - if not actions then - CleveRoids.Print("No actions data for slot " .. slot) - CleveRoids.Print("GetActionText: " .. tostring(GetActionText(slot))) - return - end - CleveRoids.Print("actions.tooltip: " .. tostring(actions.tooltip ~= nil)) - CleveRoids.Print("actions.explicitTooltip: " .. tostring(actions.explicitTooltip)) - CleveRoids.Print("actions.list count: " .. tostring(actions.list and table.getn(actions.list) or 0)) - if actions.active then - CleveRoids.Print("|cff00ffffActive action:|r") - CleveRoids.Print(" .action: " .. tostring(actions.active.action)) - CleveRoids.Print(" .type: " .. tostring(actions.active.type)) - CleveRoids.Print(" .spell: " .. tostring(actions.active.spell ~= nil)) - if actions.active.spell then - CleveRoids.Print(" .spell.name: " .. tostring(actions.active.spell.name)) - CleveRoids.Print(" .spell.cost: " .. tostring(actions.active.spell.cost)) - end - CleveRoids.Print(" .usable: " .. tostring(actions.active.usable)) - CleveRoids.Print(" .oom: " .. tostring(actions.active.oom)) - CleveRoids.Print(" .inRange: " .. tostring(actions.active.inRange)) - CleveRoids.Print(" .conditionals: " .. tostring(actions.active.conditionals ~= nil)) - if actions.active.conditionals then - CleveRoids.Print(" .target: " .. tostring(actions.active.conditionals.target)) - end - else - CleveRoids.Print("|cffff0000No active action|r") - end - CleveRoids.Print("|cffaaaa00Hook conditions:|r") - local hasTooltip = actions.tooltip ~= nil - local hasActive = actions.active ~= nil - local typeIsSpell = actions.active and actions.active.type == "spell" - CleveRoids.Print(" actions.tooltip: " .. tostring(hasTooltip)) - CleveRoids.Print(" actions.active: " .. tostring(hasActive)) - CleveRoids.Print(" .type == 'spell': " .. tostring(typeIsSpell)) - CleveRoids.Print(" Range hook would: " .. (hasTooltip and hasActive and typeIsSpell and "USE CUSTOM" or "FALLBACK")) - return - end - - -- macrostatus (check macro length warning status) - if cmd == "macrostatus" then - CleveRoids.Print("|cff00ff00=== MacroLengthWarn Status ===|r") - - -- Check if file loaded - if CleveRoids.MacroLengthWarnLoaded then - CleveRoids.Print("File: |cff00ff00LOADED|r") - else - CleveRoids.Print("File: |cffff0000NOT LOADED|r - Check for Lua errors!") - end - - -- Check if extension is registered - local ext = CleveRoids.Extensions and CleveRoids.Extensions["MacroLengthWarn"] - if ext then - CleveRoids.Print("Extension: |cff00ff00REGISTERED|r") - - -- Check if OnLoad was called - if ext.ShowMessages then - CleveRoids.Print("OnLoad: |cff00ff00CALLED|r") - - -- Call the status function - ext.ShowMessages() - else - CleveRoids.Print("OnLoad: |cffff0000NOT CALLED|r") - end - else - CleveRoids.Print("Extension: |cffff0000NOT REGISTERED|r") - CleveRoids.Print("The MacroLengthWarn extension failed to register!") - end - - -- Check key functions - CleveRoids.Print(" ") - CleveRoids.Print("Function Status:") - CleveRoids.Print(" EditMacro: " .. (EditMacro and "|cff00ff00EXISTS|r" or "|cffff0000MISSING|r")) - CleveRoids.Print(" MacroFrame_SaveMacro: " .. (MacroFrame_SaveMacro and "|cff00ff00EXISTS|r" or "|cffffff00NOT LOADED YET|r")) - - return - end - - -- channeldebug (toggle channeltime conditional debug) - if cmd == "channeldebug" then - CleveRoids.ChannelTimeDebug = not CleveRoids.ChannelTimeDebug - CleveRoids.Print("Channel time debug " .. (CleveRoids.ChannelTimeDebug and "enabled" or "disabled")) - if CleveRoids.ChannelTimeDebug then - CleveRoids.Print("You will see messages every time [channeltime] is evaluated") - CleveRoids.Print("This shows if your macro is re-evaluating during the channel") - end - return - end - - -- testchannel (debug channel time detection) - if cmd == "testchannel" or cmd == "channeltest" then - CleveRoids.Print("|cff00ff00=== Channel Time Test ===|r") - - -- Check spell tracking - local playerCast = CleveRoids.spell_tracking[CleveRoids.playerGuid] - if not playerCast then - CleveRoids.Print("|cffffff00No spell tracking data for player|r") - CleveRoids.Print("You must be casting or channeling a spell for this to show data") - else - CleveRoids.Print("Spell tracking found:") - CleveRoids.Print(" Type: " .. tostring(playerCast.type)) - CleveRoids.Print(" Spell ID: " .. tostring(playerCast.spell_id)) - if playerCast.spell_id and SpellInfo then - local spellName = SpellInfo(playerCast.spell_id) - CleveRoids.Print(" Spell Name: " .. tostring(spellName)) - end - if playerCast.expires then - local timeLeft = playerCast.expires - GetTime() - CleveRoids.Print(" Expires at: " .. tostring(playerCast.expires)) - CleveRoids.Print(" Time left: " .. string.format("%.2f", timeLeft) .. "s") - - -- Test conditionals - CleveRoids.Print(" ") - CleveRoids.Print("Conditional tests:") - CleveRoids.Print(" [channeltime:<0.5]: " .. (timeLeft < 0.5 and "|cff00ff00TRUE|r" or "|cffff0000FALSE|r")) - CleveRoids.Print(" [channeltime:<1.0]: " .. (timeLeft < 1.0 and "|cff00ff00TRUE|r" or "|cffff0000FALSE|r")) - CleveRoids.Print(" [channeltime:>2.0]: " .. (timeLeft > 2.0 and "|cff00ff00TRUE|r" or "|cffff0000FALSE|r")) - else - CleveRoids.Print(" Expires: NOT SET") - end - end - - CleveRoids.Print(" ") - CleveRoids.Print("|cff00ffffInstructions:|r") - CleveRoids.Print("1. Start channeling Arcane Missiles") - CleveRoids.Print("2. Run /cleveroid testchannel while channeling") - CleveRoids.Print("3. Check if spell tracking is working") - - CleveRoids.Print("|cff00ff00=== End Test ===|r") - return - end - - -- rangedebug - Debug range checking for a spell - if cmd == "rangedebug" or cmd == "testrange" then - -- Combine val and val2 for multi-word spell names - local spellName = val - if val2 and val2 ~= "" then - spellName = val .. " " .. val2 - end - if not spellName or spellName == "" then - CleveRoids.Print("Usage: /cleveroid rangedebug ") - CleveRoids.Print("Example: /cleveroid rangedebug Arcane Missiles") - return - end - - CleveRoids.Print("|cff00ff00=== Range Debug: " .. spellName .. " ===|r") - - -- Get spell ID - local spellId = nil - if GetSpellIdForName then - spellId = GetSpellIdForName(spellName) - CleveRoids.Print("Spell ID: " .. (spellId and tostring(spellId) or "|cffff0000NOT FOUND|r")) - else - CleveRoids.Print("GetSpellIdForName: |cffff0000NOT AVAILABLE|r") - end - - if spellId and spellId > 0 then - -- Check native IsSpellInRange - if IsSpellInRange then - local target = UnitExists("target") and "target" or nil - if target then - local result = IsSpellInRange(spellId, target) - CleveRoids.Print("Native IsSpellInRange: " .. tostring(result)) - if result == -1 then - CleveRoids.Print(" |cffffff00(-1 means non-unit-targeted spell, using fallback)|r") - elseif result == nil then - CleveRoids.Print(" |cffffff00(nil means error or unknown spell)|r") - end - else - CleveRoids.Print("Native IsSpellInRange: |cffffff00No target selected|r") - end - end - - -- Check GetSpellRec fields - local API = CleveRoids.NampowerAPI - if API then - -- Try rangeMax (may not exist) - local rangeMax = API.GetSpellField(spellId, "rangeMax") - CleveRoids.Print("rangeMax field: " .. (rangeMax and tostring(rangeMax) or "|cffffff00nil|r")) - - -- Try rangeIndex - local rangeIndex = API.GetSpellField(spellId, "rangeIndex") - CleveRoids.Print("rangeIndex field: " .. (rangeIndex and tostring(rangeIndex) or "|cffffff00nil|r")) - - if rangeIndex and API.SpellRangeTable then - local lookupRange = API.SpellRangeTable[rangeIndex] - CleveRoids.Print("SpellRangeTable[" .. rangeIndex .. "]: " .. (lookupRange and (tostring(lookupRange) .. " yards") or "|cffffff00not found|r")) - end - - -- Final GetSpellRange result - local finalRange = API.GetSpellRange(spellId) - CleveRoids.Print("API.GetSpellRange: " .. (finalRange and (tostring(finalRange) .. " yards") or "|cffff0000nil|r")) - - -- UnitXP distance check - if CleveRoids.hasUnitXP and UnitExists("target") then - local distance = UnitXP("distanceBetween", "player", "target") - CleveRoids.Print("Distance to target: " .. (distance and (string.format("%.1f", distance) .. " yards") or "|cffffff00nil|r")) - - if finalRange and distance then - local inRange = distance <= finalRange - CleveRoids.Print("In range (distance <= spellRange): " .. (inRange and "|cff00ff00YES|r" or "|cffff0000NO|r")) - end - elseif not CleveRoids.hasUnitXP then - CleveRoids.Print("UnitXP: |cffff0000NOT INSTALLED (required for fallback)|r") - else - CleveRoids.Print("Distance check: |cffffff00No target selected|r") - end - - -- Final API.IsSpellInRange result - if UnitExists("target") then - local finalResult = API.IsSpellInRange(spellId, "target") - CleveRoids.Print("API.IsSpellInRange: " .. tostring(finalResult)) - else - -- Test without target - local finalResult = API.IsSpellInRange(spellId, "player") - CleveRoids.Print("API.IsSpellInRange (no target, using player): " .. tostring(finalResult)) - end - - -- Target type detection - local targetA = API.GetSpellField(spellId, "effectImplicitTargetA") - local targetB = API.GetSpellField(spellId, "effectImplicitTargetB") - - -- Format target data (handle tables) - local function formatTarget(t) - if not t then return "|cffffff00nil|r" end - if type(t) == "table" then - local parts = {} - for i = 1, 3 do - table.insert(parts, tostring(t[i] or 0)) - end - return "{" .. table.concat(parts, ", ") .. "}" - end - return tostring(t) - end - - CleveRoids.Print("effectImplicitTargetA: " .. formatTarget(targetA)) - CleveRoids.Print("effectImplicitTargetB: " .. formatTarget(targetB)) - - local isUnitTargeted = API.IsUnitTargetedSpell(spellId) - CleveRoids.Print("Is unit-targeted spell: " .. (isUnitTargeted == true and "|cff00ff00YES|r" or isUnitTargeted == false and "|cffff0000NO|r" or "|cffffff00UNKNOWN|r")) - - -- Self-cast/ground-targeted detection summary - local isSelfCast = (isUnitTargeted == false) - CleveRoids.Print("Detected as self/ground-targeted: " .. (isSelfCast and "|cff00ff00YES|r" or "|cffff0000NO|r")) - end - end - - CleveRoids.Print("|cff00ff00=== End Range Debug ===|r") - return - end - - -- testcasting (debug casting state detection) - if cmd == "testcasting" or cmd == "casttest" then - CleveRoids.Print("|cff00ff00=== Casting State Test ===|r") - - -- Check GetCurrentCastingInfo availability - if not GetCurrentCastingInfo then - CleveRoids.Print("|cffff0000ERROR: GetCurrentCastingInfo not available!|r") - CleveRoids.Print("This requires Nampower to be installed.") - return - end - - -- Get current casting info - local castId, visId, autoId, casting, channeling, onswing, autoattack = GetCurrentCastingInfo() - - CleveRoids.Print("GetCurrentCastingInfo() values:") - CleveRoids.Print(" castId: " .. tostring(castId)) - CleveRoids.Print(" visId: " .. tostring(visId)) - CleveRoids.Print(" autoId: " .. tostring(autoId)) - CleveRoids.Print(" casting: " .. tostring(casting)) - CleveRoids.Print(" channeling: " .. tostring(channeling)) - CleveRoids.Print(" onswing: " .. tostring(onswing)) - CleveRoids.Print(" autoattack: " .. tostring(autoattack)) - - -- Test conditionals - local isCasting = (casting and casting == 1) or false - local isChanneling = (channeling and channeling == 1) or false - - CleveRoids.Print(" ") - CleveRoids.Print("Conditional results:") - CleveRoids.Print(" [selfcasting]: " .. tostring(isCasting or isChanneling)) - CleveRoids.Print(" [noselfcasting]: " .. tostring(not isCasting and not isChanneling)) - - CleveRoids.Print(" ") - if isCasting then - CleveRoids.Print("|cffff00ffYou ARE currently CASTING|r") - elseif isChanneling then - CleveRoids.Print("|cffff00ffYou ARE currently CHANNELING|r") - else - CleveRoids.Print("|cff00ff00You are NOT casting or channeling|r") - end - - CleveRoids.Print("|cff00ff00=== End Test ===|r") - return - end - -- listimmune (list immunity data) if cmd == "listimmune" or cmd == "immunelist" then CleveRoids.ListImmunities(val ~= "" and val or nil) @@ -5880,43 +5524,6 @@ SlashCmdList["CLEVEROID"] = function(msg) return end - -- schooltest (test spell school detection) - if cmd == "schooltest" or cmd == "testschool" then - local input = val - if input == "" then - CleveRoids.Print("Usage: /cleveroid schooltest ") - CleveRoids.Print("Example: /cleveroid schooltest 133") - CleveRoids.Print("Example: /cleveroid schooltest Fireball") - return - end - - local spellID = tonumber(input) - local spellName = nil - local school = nil - - if spellID then - -- Input is a spell ID - spellName = SpellInfo and SpellInfo(spellID) - school = CleveRoids.GetSpellSchoolByID(spellID) - else - -- Input is a spell name - spellName = input - spellID = CleveRoids.GetSpellIdForName and CleveRoids.GetSpellIdForName(spellName) - school = CleveRoids.GetSpellSchool(spellName, spellID) - end - - CleveRoids.Print("|cff88ff88=== Spell School Test ===|r") - CleveRoids.Print("Spell: " .. (spellName or "Unknown") .. " (ID:" .. (spellID or "Unknown") .. ")") - CleveRoids.Print("School: " .. (school or "|cffff0000Unknown|r")) - - if spellID and CleveRoids_SpellSchools[spellID] then - CleveRoids.Print("|cff00ff00Source:|r Learned from Nampower damage events") - elseif school then - CleveRoids.Print("|cffffaa00Source:|r Tooltip/pattern matching") - end - return - end - -- listschools (show learned spell schools) if cmd == "listschools" or cmd == "schools" then CleveRoids.Print("|cff88ff88=== Learned Spell Schools ===|r") @@ -6069,74 +5676,6 @@ SlashCmdList["CLEVEROID"] = function(msg) return end - -- diagnosetalent (comprehensive diagnostic for talent modifier) - if cmd == "diagnosetalent" or cmd == "diagtalent" then - local spellID = tonumber(val) - if not spellID then - CleveRoids.Print("Usage: /cleveroid diagnosetalent ") - CleveRoids.Print("Example: /cleveroid diagnosetalent 1943 (Rupture Rank 1)") - return - end - - -- Inline diagnostic - local baseDuration = 10 - DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00=== Talent Modifier Diagnostic ===|r") - - local spellName = SpellInfo(spellID) - if not spellName then - DEFAULT_CHAT_FRAME:AddMessage("|cffff0000ERROR: Invalid spell ID " .. tostring(spellID) .. "|r") - return - end - DEFAULT_CHAT_FRAME:AddMessage("Spell: " .. spellName .. " (ID: " .. spellID .. ")") - - local modifier = CleveRoids.talentModifiers and CleveRoids.talentModifiers[spellID] - if not modifier then - DEFAULT_CHAT_FRAME:AddMessage("|cffff0000No talent modifier registered for this spell|r") - return - end - - local talentDesc = modifier.talent or ("Tab " .. tostring(modifier.tab) .. " ID " .. tostring(modifier.id)) - DEFAULT_CHAT_FRAME:AddMessage("Modifier registered: " .. talentDesc) - - -- Test position-based lookup - local talentRank = 0 - local lookupMethod = "none" - - if modifier.tab and modifier.id then - local _, name, _, _, rank = GetTalentInfo(modifier.tab, modifier.id) - talentRank = tonumber(rank) or 0 - lookupMethod = "position" - DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00Position lookup (Tab " .. modifier.tab .. ", ID " .. modifier.id .. "):|r") - DEFAULT_CHAT_FRAME:AddMessage(" Talent name: " .. tostring(name)) - DEFAULT_CHAT_FRAME:AddMessage(" Rank: " .. talentRank) - end - - if modifier.talent and CleveRoids.GetTalentRank then - local nameRank = CleveRoids.GetTalentRank(modifier.talent) - DEFAULT_CHAT_FRAME:AddMessage("|cffaaaa00Name lookup (" .. modifier.talent .. "):|r") - DEFAULT_CHAT_FRAME:AddMessage(" Rank: " .. nameRank) - if talentRank == 0 and nameRank > 0 then - talentRank = nameRank - lookupMethod = "name" - end - end - - DEFAULT_CHAT_FRAME:AddMessage("Final rank: " .. talentRank .. " (via " .. lookupMethod .. ")") - - if talentRank > 0 then - local modifiedDuration = modifier.modifier(baseDuration, talentRank) - DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00Test calculation:|r") - DEFAULT_CHAT_FRAME:AddMessage(" Base: " .. baseDuration .. "s") - DEFAULT_CHAT_FRAME:AddMessage(" Modified: " .. modifiedDuration .. "s") - DEFAULT_CHAT_FRAME:AddMessage(" Bonus: +" .. (modifiedDuration - baseDuration) .. "s") - else - DEFAULT_CHAT_FRAME:AddMessage("|cffff0000You don't have " .. talentDesc .. "!|r") - end - - DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00=== End Diagnostic ===|r") - return - end - -- testtalent (test talent modifier for a spell) if cmd == "testtalent" or cmd == "talenttest" then local spellID = tonumber(val) @@ -6190,349 +5729,6 @@ SlashCmdList["CLEVEROID"] = function(msg) return end - -- testequip (test equipment modifier for a spell) - if cmd == "testequip" or cmd == "equiptest" then - local spellID = tonumber(val) - if not spellID then - CleveRoids.Print("Usage: /cleveroid testequip ") - CleveRoids.Print("Example: /cleveroid testequip 1079 (Rip Rank 1)") - return - end - - local spellName = SpellInfo(spellID) or ("Spell " .. spellID) - local modifier = CleveRoids.equipmentModifiers and CleveRoids.equipmentModifiers[spellID] - - if not modifier then - CleveRoids.Print(spellName .. " (ID:" .. spellID .. ") has no equipment modifier configured") - return - end - - local itemID = CleveRoids.GetEquippedItemID and CleveRoids.GetEquippedItemID(modifier.slot) - local itemName = "None" - if itemID then - local itemLink = GetInventoryItemLink("player", modifier.slot) - if itemLink then - itemName = string.match(itemLink, "%[(.-)%]") or ("Item " .. itemID) - end - end - - CleveRoids.Print("=== Equipment Modifier Test ===") - CleveRoids.Print("Spell: " .. spellName .. " (ID:" .. spellID .. ")") - CleveRoids.Print("Slot: " .. modifier.slot .. " (Ranged/Relic)") - CleveRoids.Print("Equipped: " .. itemName .. (itemID and (" [" .. itemID .. "]") or "")) - - if not itemID then - CleveRoids.Print("|cffff0000No item equipped in this slot!|r") - else - -- Test with a base duration (use 10s as example) - local baseDur = 10 - local modDur = modifier.modifier(baseDur, itemID) - if modDur ~= baseDur then - CleveRoids.Print("Example: 10s base -> " .. modDur .. "s modified") - if modDur < baseDur then - CleveRoids.Print("|cffff0000Duration reduced by " .. (baseDur - modDur) .. "s|r") - else - CleveRoids.Print("|cff00ff00Duration increased by " .. (modDur - baseDur) .. "s|r") - end - else - CleveRoids.Print("|cffffaa00This item has no effect on this spell|r") - end - end - return - end - - -- equiplog (toggle real-time equip logging) - if cmd == "equiplog" then - CleveRoids.equipDebugLog = not CleveRoids.equipDebugLog - CleveRoids.Print("Equip command logging " .. (CleveRoids.equipDebugLog and "|cff00ff00ENABLED|r" or "|cffff0000DISABLED|r")) - if CleveRoids.equipDebugLog then - CleveRoids.Print("Use /equip commands to see detailed lookup info") - end - return - end - - -- equipdebug (debug item lookup for /equip command) - if cmd == "equipdebug" then - local itemName = val - if val2 and val2 ~= "" then - itemName = val .. " " .. val2 - end - if not itemName or itemName == "" then - CleveRoids.Print("Usage: /cleveroid equipdebug ") - CleveRoids.Print('Example: /cleveroid equipdebug Idol of Ferocity') - return - end - -- Strip quotes if present - itemName = string.gsub(itemName, '"', "") - itemName = string.gsub(itemName, "_", " ") - - CleveRoids.Print("|cff00ff00=== Equipment Debug: " .. itemName .. " ===|r") - - -- Check equipped slot 18 (relic/idol) - local slot18Link = GetInventoryItemLink("player", 18) - if slot18Link then - local _, _, slot18Name = string.find(slot18Link, "|h%[(.-)%]|h") - CleveRoids.Print("Slot 18 equipped: " .. (slot18Name or "unknown")) - else - CleveRoids.Print("Slot 18 equipped: (empty)") - end - - -- Check cache - local Items = CleveRoids.Items or {} - local cached = Items[itemName] - CleveRoids.Print("|cffffaa00Cache lookup:|r") - if cached then - if type(cached) == "table" then - if cached.inventoryID then - CleveRoids.Print(" Found in cache: EQUIPPED (inventoryID=" .. cached.inventoryID .. ")") - elseif cached.bagID then - CleveRoids.Print(" Found in cache: BAG (bag=" .. cached.bagID .. ", slot=" .. cached.slot .. ")") - else - CleveRoids.Print(" Found in cache: (type unknown)") - end - elseif type(cached) == "string" then - CleveRoids.Print(" Found in cache: -> " .. cached .. " (canonical name)") - end - else - CleveRoids.Print(" Not in cache") - end - - -- Try GetItemFast - CleveRoids.Print("|cffffaa00GetItemFast:|r") - local fastItem = CleveRoids.GetItemFast and CleveRoids.GetItemFast(itemName) - if fastItem then - if fastItem.inventoryID then - CleveRoids.Print(" Returns: EQUIPPED (inventoryID=" .. fastItem.inventoryID .. ")") - elseif fastItem.bagID then - CleveRoids.Print(" Returns: BAG (bag=" .. fastItem.bagID .. ", slot=" .. fastItem.slot .. ")") - end - else - CleveRoids.Print(" Returns: nil") - end - - -- Try FindItemQuick - CleveRoids.Print("|cffffaa00FindItemQuick (scans equipped then bags):|r") - local quickItem = CleveRoids.FindItemQuick and CleveRoids.FindItemQuick(itemName) - if quickItem then - if quickItem.inventoryID then - CleveRoids.Print(" Returns: EQUIPPED (inventoryID=" .. quickItem.inventoryID .. ")") - elseif quickItem.bagID then - CleveRoids.Print(" Returns: BAG (bag=" .. quickItem.bagID .. ", slot=" .. quickItem.slot .. ")") - end - else - CleveRoids.Print(" Returns: nil (item not found)") - end - - -- Check HasGearEquipped (used by [equipped] conditional) - CleveRoids.Print("|cffffaa00HasGearEquipped (conditional check):|r") - local hasEquipped = CleveRoids.HasGearEquipped and CleveRoids.HasGearEquipped(itemName) - CleveRoids.Print(" [equipped:" .. itemName .. "] = " .. (hasEquipped and "|cff00ff00TRUE|r" or "|cffff0000FALSE|r")) - - -- Combat state - CleveRoids.Print("|cffffaa00Combat state:|r") - CleveRoids.Print(" UnitAffectingCombat: " .. (UnitAffectingCombat("player") and "IN COMBAT" or "not in combat")) - CleveRoids.Print(" playerClass: " .. (CleveRoids.playerClass or "unknown")) - - return - end - - -- listprocs (show active reactive procs) - if cmd == "listprocs" or cmd == "procs" or cmd == "reactive" then - CleveRoids.Print("=== Active Reactive Procs ===") - local found = false - local now = GetTime() - local _, currentTargetGUID = UnitExists("target") - - if CleveRoids.reactiveProcs then - for spellName, procData in pairs(CleveRoids.reactiveProcs) do - if procData and procData.expiry and procData.expiry > now then - local remaining = procData.expiry - now - local guidInfo = "" - - if procData.targetGUID then - local matches = currentTargetGUID and (currentTargetGUID == procData.targetGUID) - if matches then - guidInfo = " |cff00ff00[Current Target]|r" - else - local targetName = UnitName("target") - if targetName then - guidInfo = " |cffff0000[Wrong Target: " .. targetName .. "]|r" - else - guidInfo = " |cffff0000[No Target]|r" - end - end - end - - CleveRoids.Print(spellName .. ": " .. string.format("%.1fs", remaining) .. " remaining" .. guidInfo) - found = true - end - end - end - - if not found then - CleveRoids.Print("|cffffaa00No active reactive procs|r") - end - return - end - - -- setproc (manually set a reactive proc for testing) - if cmd == "setproc" or cmd == "procset" then - if val == "" then - CleveRoids.Print("Usage: /cleveroid setproc [duration]") - CleveRoids.Print("Example: /cleveroid setproc Overpower 5") - CleveRoids.Print("Note: Uses current target's GUID for target-specific procs") - return - end - - local duration = tonumber(val2) or 5.0 - local _, targetGUID = UnitExists("target") - - if CleveRoids.SetReactiveProc then - CleveRoids.SetReactiveProc(val, duration, targetGUID) - local guidMsg = targetGUID and (" for target [" .. (UnitName("target") or "Unknown") .. "]") or "" - CleveRoids.Print("Set " .. val .. " proc for " .. duration .. " seconds" .. guidMsg) - else - CleveRoids.Print("|cffff0000Reactive proc system not loaded!|r") - end - return - end - - -- clearproc (clear a reactive proc) - if cmd == "clearproc" or cmd == "procclear" then - if val == "" or val == "all" then - CleveRoids.reactiveProcs = {} - CleveRoids.Print("Cleared all reactive procs") - else - if CleveRoids.ClearReactiveProc then - CleveRoids.ClearReactiveProc(val) - CleveRoids.Print("Cleared " .. val .. " proc") - else - CleveRoids.Print("|cffff0000Reactive proc system not loaded!|r") - end - end - CleveRoids.QueueActionUpdate() - return - end - - -- slamdebug (show Slam cast time and window calculations for Warrior rotation) - if cmd == "slamdebug" or cmd == "slam" or cmd == "slamtime" then - CleveRoids.Print("|cff88ff88=== Slam Clip Window Debug ===|r") - - -- Get swing timer info - local attackSpeed = UnitAttackSpeed("player") - if not attackSpeed or attackSpeed <= 0 then - CleveRoids.Print("|cffff0000No attack speed available!|r") - CleveRoids.Print("You need to be in combat or have a weapon equipped.") - return - end - - -- Check SP_SwingTimer - if st_timer == nil then - CleveRoids.Print("|cffff0000SP_SwingTimer not detected!|r") - CleveRoids.Print("The [noslamclip] conditionals require SP_SwingTimer addon.") - CleveRoids.Print("Get it at: https://github.com/jrc13245/SP_SwingTimer") - else - local timeElapsed = attackSpeed - st_timer - local percentElapsed = (timeElapsed / attackSpeed) * 100 - CleveRoids.Print("|cffffaa00SP_SwingTimer:|r st_timer=" .. string.format("%.3f", st_timer) .. "s") - CleveRoids.Print("Swing elapsed: " .. string.format("%.2f", percentElapsed) .. "%") - end - - CleveRoids.Print(" ") - CleveRoids.Print("|cffffaa00Swing Timer:|r " .. string.format("%.3f", attackSpeed) .. "s") - - -- Get Slam cast time from tooltip scanning - local slamCastTime = CleveRoids.GetSlamCastTime() - local isDefault = (slamCastTime == 1.5) - - CleveRoids.Print("|cffffaa00Slam Cast Time:|r " .. string.format("%.3f", slamCastTime) .. "s" .. (isDefault and " (default)" or " (from tooltip)")) - - -- Show tooltip scanning diagnostic info - if CleveRoids.GetSlamSpellSlot then - local slot = CleveRoids.GetSlamSpellSlot() - if slot then - CleveRoids.Print(" Slam found in spellbook slot: " .. slot) - else - CleveRoids.Print(" |cffff0000Slam not found in spellbook!|r Are you a Warrior?") - end - end - - if isDefault then - CleveRoids.Print(" Note: Could not read from tooltip, using default 1.5s") - end - - -- Calculate windows - CleveRoids.Print(" ") - local slamWindowTime = attackSpeed - slamCastTime - local slamWindow = 0 - if slamWindowTime > 0 then - slamWindow = (slamWindowTime / attackSpeed) * 100 - end - - local instantWindowTime = (2 * attackSpeed) - slamCastTime - 1.5 - local instantWindow = 0 - if instantWindowTime > 0 then - instantWindow = (instantWindowTime / attackSpeed) * 100 - end - - CleveRoids.Print("|cff00ff00=== Window Calculations ===|r") - CleveRoids.Print("|cffffaa00Slam Window:|r " .. string.format("%.2f", slamWindow) .. "%") - CleveRoids.Print(" Formula: (SwingTimer - SlamCast) / SwingTimer") - CleveRoids.Print(" (" .. string.format("%.3f", attackSpeed) .. " - " .. string.format("%.3f", slamCastTime) .. ") / " .. string.format("%.3f", attackSpeed) .. " = " .. string.format("%.2f", slamWindow) .. "%") - CleveRoids.Print(" Max time to cast Slam: " .. string.format("%.3f", slamWindowTime) .. "s into swing") - - CleveRoids.Print(" ") - CleveRoids.Print("|cffffaa00Instant Window:|r " .. string.format("%.2f", instantWindow) .. "%") - CleveRoids.Print(" Formula: (2×SwingTimer - SlamCast - GCD) / SwingTimer") - CleveRoids.Print(" (2×" .. string.format("%.3f", attackSpeed) .. " - " .. string.format("%.3f", slamCastTime) .. " - 1.5) / " .. string.format("%.3f", attackSpeed) .. " = " .. string.format("%.2f", instantWindow) .. "%") - CleveRoids.Print(" Max time to cast instant: " .. string.format("%.3f", instantWindowTime) .. "s into swing") - - -- Show current status - if st_timer ~= nil then - CleveRoids.Print(" ") - CleveRoids.Print("|cff00ff00=== Current Status ===|r") - local timeElapsed = attackSpeed - st_timer - local percentElapsed = (timeElapsed / attackSpeed) * 100 - local inSlamWindow = percentElapsed <= slamWindow - local inInstantWindow = percentElapsed <= instantWindow - CleveRoids.Print("[noslamclip]: " .. (inSlamWindow and "|cff00ff00TRUE|r (safe to Slam)" or "|cffff0000FALSE|r (would clip)")) - CleveRoids.Print("[nonextslamclip]: " .. (inInstantWindow and "|cff00ff00TRUE|r (safe to instant)" or "|cffff0000FALSE|r (would clip next Slam)")) - end - - return - end - - -- formdebug (debug shapeshift form detection) - if cmd == "formdebug" or cmd == "form" or cmd == "shapeshiftdebug" then - CleveRoids.Print("|cff88ff88=== Shapeshift Form Debug ===|r") - CleveRoids.Print("Player class: " .. tostring(CleveRoids.playerClass)) - - local numForms = GetNumShapeshiftForms() - CleveRoids.Print("Number of forms: " .. tostring(numForms)) - - local currentIndex = CleveRoids.GetCurrentShapeshiftIndex() - CleveRoids.Print("Current form index: " .. tostring(currentIndex)) - - CleveRoids.Print(" ") - CleveRoids.Print("|cff00ff00=== Form Details ===|r") - for i = 1, numForms do - local icon, name, isActive, isCastable = GetShapeshiftFormInfo(i) - local activeStr = isActive and "|cff00ff00ACTIVE|r" or "|cff888888inactive|r" - local castableStr = isCastable and "castable" or "not castable" - CleveRoids.Print(string.format("Form %d: %s - %s (%s)", i, tostring(name), activeStr, castableStr)) - end - - CleveRoids.Print(" ") - CleveRoids.Print("|cff00ff00=== ValidatePlayerBuff Tests ===|r") - local testForms = {"Cat Form", "Bear Form", "Dire Bear Form", "Travel Form", "Aquatic Form", "Moonkin Form"} - for _, formName in ipairs(testForms) do - local result = CleveRoids.ValidatePlayerBuff(formName) - local resultStr = result and "|cff00ff00true|r" or "|cffff0000false|r" - CleveRoids.Print(" ValidatePlayerBuff('" .. formName .. "') = " .. resultStr) - end - return - end - -- debuffdebug (debug debuff tracking on target) if cmd == "debuffdebug" or cmd == "debuff" or cmd == "trackdebug" then local searchName = val @@ -6687,85 +5883,6 @@ SlashCmdList["CLEVEROID"] = function(msg) return end - -- tooltipdebug (debug spell tooltip scanning) - if cmd == "tooltipdebug" or cmd == "ttdebug" or cmd == "spelltooltip" then - local spellName = val - if val2 and val2 ~= "" then - spellName = val .. " " .. val2 - end - if not spellName or spellName == "" then - spellName = "Arcane Missiles" -- Default to Arcane Missiles - end - -- Strip underscores - spellName = string.gsub(spellName, "_", " ") - - CleveRoids.Print("|cff88ff88=== Tooltip Scan Debug: " .. spellName .. " ===|r") - - -- Find highest rank spell in spellbook (same logic as GetSpellSlotByName) - local slot, bookType = nil, nil - local foundRank = nil - local i = 1 - while true do - local name, rank = GetSpellName(i, BOOKTYPE_SPELL) - if not name then break end - if name == spellName then - -- Keep updating to find the last (highest) rank - slot = i - bookType = BOOKTYPE_SPELL - foundRank = rank - end - i = i + 1 - end - - if not slot then - CleveRoids.Print("|cffff0000Spell not found in spellbook!|r") - return - end - - CleveRoids.Print("Found in spellbook slot: " .. slot .. " (rank: " .. (foundRank or "none") .. ")") - - -- Create tooltip if needed - if not CleveRoidsDebugTooltip then - CreateFrame("GameTooltip", "CleveRoidsDebugTooltip", nil, "GameTooltipTemplate") - CleveRoidsDebugTooltip:SetOwner(WorldFrame, "ANCHOR_NONE") - end - - CleveRoidsDebugTooltip:ClearLines() - CleveRoidsDebugTooltip:SetSpell(slot, bookType) - - CleveRoids.Print("Tooltip lines (" .. CleveRoidsDebugTooltip:NumLines() .. "):") - for lineNum = 1, CleveRoidsDebugTooltip:NumLines() do - local leftText = getglobal("CleveRoidsDebugTooltipTextLeft" .. lineNum) - local rightText = getglobal("CleveRoidsDebugTooltipTextRight" .. lineNum) - - local leftStr = leftText and leftText:GetText() or "" - local rightStr = rightText and rightText:GetText() or "" - - if leftStr and leftStr ~= "" then - CleveRoids.Print(" L" .. lineNum .. ": " .. leftStr) - -- Check for duration pattern - local duration = string.match(leftStr, "for (%d+%.?%d*) sec") - if duration then - CleveRoids.Print(" |cff00ff00^ Found 'for X sec': " .. duration .. "s|r") - end - local duration2 = string.match(leftStr, "over (%d+%.?%d*) sec") - if duration2 then - CleveRoids.Print(" |cff00ff00^ Found 'over X sec': " .. duration2 .. "s|r") - end - end - if rightStr and rightStr ~= "" then - CleveRoids.Print(" R" .. lineNum .. ": " .. rightStr) - end - end - - -- Show what GetSpellDurationFromTooltip returns - local cachedDuration = CleveRoids.GetSpellDurationFromTooltip and CleveRoids.GetSpellDurationFromTooltip(spellName) - CleveRoids.Print(" ") - CleveRoids.Print("GetSpellDurationFromTooltip result: " .. (cachedDuration and (cachedDuration .. "s") or "|cffff0000nil|r")) - - return - end - -- Unknown command fallback CleveRoids.Print("Usage:") DEFAULT_CHAT_FRAME:AddMessage("/cleveroid - Show current settings") diff --git a/README.md b/README.md index 6e1929c..a350cdf 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ All conditionals support negation with `no` prefix (e.g., `[nocombat]`, `[nobuff | `group` | `[group]` `[group:party/raid]` | Player in group type | | `resting` | `[resting]` | In rest area | | `swimming` | `[swimming]` | Can use aquatic form | +| `moving` | `[moving]` `[moving:>100]` | Moving / speed % (MonkeySpeed) | | `zone` | `[zone:"Ironforge"]` | Current zone name | ### Resources @@ -200,6 +201,7 @@ All conditionals support negation with `no` prefix (e.g., `[nocombat]`, `[nobuff | `ttk` | TimeToKill | `[ttk:<10]` | Time to kill (seconds) | | `tte` | TimeToKill | `[tte:<5]` | Time to execute (20% HP) | | `cursive` | Cursive | `[cursive:Rake>3]` | GUID debuff tracking | +| `moving` | MonkeySpeed | `[moving:>100]` | Speed % (100=normal run) | ### Warrior Slam Conditionals For optimizing Slam rotations without clipping auto-attacks: @@ -552,6 +554,39 @@ The addon checks debuffs on the target: - Works at debuff cap - More accurate timing +### MonkeySpeed Integration (`[moving]`) + +[MonkeySpeed](https://github.com/jrc13245/MonkeySpeed) provides accurate movement speed detection via SuperWoW's `UnitPosition` API: + +```lua +-- Only cast if standing still +/cast [nomoving] Aimed Shot + +-- Cast instant ability while moving +/cast [moving] Arcane Shot + +-- Check speed percentage (100 = normal run speed) +/cast [moving:>100] Sprint -- Already faster than normal +/cast [moving:<50] Escape Artist -- Currently slowed + +-- Combined with other conditionals +/cast [nomoving, harm] Aimed Shot +/cast [moving, harm] Arcane Shot +``` + +**Speed Reference:** +| Speed | Description | +|-------|-------------| +| `0` | Standing still | +| `100` | Normal run speed | +| `160` | Level 40 mount | +| `200` | Epic mount | + +**Notes:** +- Basic `[moving]`/`[nomoving]` works without MonkeySpeed (position fallback) +- Speed comparisons (`[moving:>100]`) require MonkeySpeed addon +- Speed values vary with buffs/debuffs affecting movement + ### Recommended DoT Macro Patterns **Druid Feral (with Cursive):** @@ -612,7 +647,7 @@ The addon checks debuffs on the target: **Action Bars:** Blizzard, [pfUI](https://github.com/jrc13245/pfUI), Bongos, Discord Action Bars -**Integrations:** SP_SwingTimer, TWThreat, TimeToKill, QuickHeal, Cursive (with mouseover support for DoT timer bars), ClassicFocus, SuperMacro +**Integrations:** SP_SwingTimer, TWThreat, TimeToKill, QuickHeal, Cursive (with mouseover support for DoT timer bars), ClassicFocus, SuperMacro, MonkeySpeed > **Note:** For pfUI users, the [jrc13245/pfUI fork](https://github.com/jrc13245/pfUI) includes native SuperCleveRoidMacros integration for proper cooldown, icon, and tooltip display on conditional macros.