mirror of
https://github.com/brues-code/SuperCleveRoidMacros.git
synced 2026-09-16 03:38:00 +00:00
optimize buff and debuff checks and update functions to nampower v2_18_0
This commit is contained in:
+104
-48
@@ -1388,6 +1388,74 @@ function CleveRoids.GetPlayerAura(index, isbuff)
|
|||||||
return GetPlayerBuffTexture(bid), GetPlayerBuffApplications(bid), spellID, GetPlayerBuffTimeLeft(bid)
|
return GetPlayerBuffTexture(bid), GetPlayerBuffApplications(bid), spellID, GetPlayerBuffTimeLeft(bid)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- PERFORMANCE: Local function refs and reusable pattern for buff checking
|
||||||
|
local _string_lower = string.lower
|
||||||
|
local _string_gsub = string.gsub
|
||||||
|
local _RANK_PATTERN = "%s*%(%s*Rank%s+%d+%s*%)"
|
||||||
|
|
||||||
|
-- PERFORMANCE: Simple cache for lowercase spell names (cleared periodically)
|
||||||
|
local _spellNameCache = {}
|
||||||
|
local _spellNameCacheSize = 0
|
||||||
|
local _MAX_SPELL_CACHE = 200
|
||||||
|
|
||||||
|
-- PERFORMANCE: Cache for base spell names (rank stripped, not lowercased)
|
||||||
|
local _baseNameCache = {}
|
||||||
|
local _baseNameCacheSize = 0
|
||||||
|
|
||||||
|
local function GetLowercaseSpellName(spellID)
|
||||||
|
local cached = _spellNameCache[spellID]
|
||||||
|
if cached then return cached end
|
||||||
|
|
||||||
|
local name = SpellInfo(spellID)
|
||||||
|
if not name then return nil end
|
||||||
|
|
||||||
|
-- Strip rank and lowercase
|
||||||
|
local baseName = _string_gsub(name, _RANK_PATTERN, "")
|
||||||
|
local lowerName = _string_lower(baseName)
|
||||||
|
|
||||||
|
-- Cache if not too large
|
||||||
|
if _spellNameCacheSize < _MAX_SPELL_CACHE then
|
||||||
|
_spellNameCache[spellID] = lowerName
|
||||||
|
_spellNameCacheSize = _spellNameCacheSize + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return lowerName
|
||||||
|
end
|
||||||
|
|
||||||
|
-- PERFORMANCE: Get base spell name (rank stripped) and full name - cached
|
||||||
|
local function GetSpellNames(spellID)
|
||||||
|
local cached = _baseNameCache[spellID]
|
||||||
|
if cached then
|
||||||
|
return cached.base, cached.full
|
||||||
|
end
|
||||||
|
|
||||||
|
local fullName = SpellInfo(spellID)
|
||||||
|
if not fullName then return nil, nil end
|
||||||
|
|
||||||
|
local baseName = _string_gsub(fullName, _RANK_PATTERN, "")
|
||||||
|
|
||||||
|
-- Cache if not too large
|
||||||
|
if _baseNameCacheSize < _MAX_SPELL_CACHE then
|
||||||
|
_baseNameCache[spellID] = { base = baseName, full = fullName }
|
||||||
|
_baseNameCacheSize = _baseNameCacheSize + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return baseName, fullName
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Clear spell name caches (called periodically from Core.lua cleanup)
|
||||||
|
function CleveRoids.ClearSpellNameCaches()
|
||||||
|
for k in pairs(_spellNameCache) do
|
||||||
|
_spellNameCache[k] = nil
|
||||||
|
end
|
||||||
|
_spellNameCacheSize = 0
|
||||||
|
|
||||||
|
for k in pairs(_baseNameCache) do
|
||||||
|
_baseNameCache[k] = nil
|
||||||
|
end
|
||||||
|
_baseNameCacheSize = 0
|
||||||
|
end
|
||||||
|
|
||||||
function CleveRoids.ValidateAura(unit, args, isbuff)
|
function CleveRoids.ValidateAura(unit, args, isbuff)
|
||||||
if not args or not UnitExists(unit) then return false end
|
if not args or not UnitExists(unit) then return false end
|
||||||
|
|
||||||
@@ -1405,7 +1473,7 @@ function CleveRoids.ValidateAura(unit, args, isbuff)
|
|||||||
local i = isPlayer and 0 or 1
|
local i = isPlayer and 0 or 1
|
||||||
|
|
||||||
-- PERFORMANCE: Cache lowercased search name to avoid repeated string.lower calls
|
-- PERFORMANCE: Cache lowercased search name to avoid repeated string.lower calls
|
||||||
local searchName = args.name and string.lower(args.name)
|
local searchName = args.name and _string_lower(args.name)
|
||||||
|
|
||||||
-- Primary search: BUFFS if isbuff==true, DEBUFFS if isbuff==false
|
-- Primary search: BUFFS if isbuff==true, DEBUFFS if isbuff==false
|
||||||
while true do
|
while true do
|
||||||
@@ -1429,14 +1497,11 @@ function CleveRoids.ValidateAura(unit, args, isbuff)
|
|||||||
if not texture then break end
|
if not texture then break end
|
||||||
|
|
||||||
if current_spellID and searchName then
|
if current_spellID and searchName then
|
||||||
local auraName = SpellInfo(current_spellID)
|
-- PERFORMANCE: Use cached lowercase spell name lookup
|
||||||
if auraName then
|
local lowerName = GetLowercaseSpellName(current_spellID)
|
||||||
-- Strip rank for comparison (handles "Moonfire (Rank 7)" vs "moonfire")
|
if lowerName and lowerName == searchName then
|
||||||
local baseName = string.gsub(auraName, "%s*%(%s*Rank%s+%d+%s*%)", "")
|
found = true
|
||||||
if string.lower(baseName) == searchName or string.lower(auraName) == searchName then
|
break
|
||||||
found = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1455,14 +1520,11 @@ function CleveRoids.ValidateAura(unit, args, isbuff)
|
|||||||
if not texture then break end
|
if not texture then break end
|
||||||
|
|
||||||
if current_spellID then
|
if current_spellID then
|
||||||
local auraName = SpellInfo(current_spellID)
|
-- PERFORMANCE: Use cached lowercase spell name lookup
|
||||||
if auraName then
|
local lowerName = GetLowercaseSpellName(current_spellID)
|
||||||
-- Strip rank for comparison
|
if lowerName and lowerName == searchName then
|
||||||
local baseName = string.gsub(auraName, "%s*%(%s*Rank%s+%d+%s*%)", "")
|
found = true
|
||||||
if string.lower(baseName) == searchName or string.lower(auraName) == searchName then
|
break
|
||||||
found = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1635,18 +1697,16 @@ function CleveRoids.ValidateUnitDebuff(unit, args)
|
|||||||
if not tex then break end
|
if not tex then break end
|
||||||
|
|
||||||
if debuffSpellID then
|
if debuffSpellID then
|
||||||
local fullName = SpellInfo(debuffSpellID)
|
-- PERFORMANCE: Use cached spell name lookup
|
||||||
if fullName then
|
local baseName, fullName = GetSpellNames(debuffSpellID)
|
||||||
local baseName = string.gsub(fullName, "%s*%(%s*Rank%s+%d+%s*%)", "")
|
if baseName and (baseName == args.name or fullName == args.name) then
|
||||||
if baseName == args.name or fullName == args.name then
|
found = true
|
||||||
found = true
|
texture = tex
|
||||||
texture = tex
|
stacks = debuffStacks or 0
|
||||||
stacks = debuffStacks or 0
|
spellID = debuffSpellID
|
||||||
spellID = debuffSpellID
|
-- No duration tracking for shared debuffs
|
||||||
-- No duration tracking for shared debuffs
|
remaining = nil
|
||||||
remaining = nil
|
break
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1658,17 +1718,15 @@ function CleveRoids.ValidateUnitDebuff(unit, args)
|
|||||||
if not tex then break end
|
if not tex then break end
|
||||||
|
|
||||||
if buffSpellID then
|
if buffSpellID then
|
||||||
local fullName = SpellInfo(buffSpellID)
|
-- PERFORMANCE: Use cached spell name lookup
|
||||||
if fullName then
|
local baseName, fullName = GetSpellNames(buffSpellID)
|
||||||
local baseName = string.gsub(fullName, "%s*%(%s*Rank%s+%d+%s*%)", "")
|
if baseName and (baseName == args.name or fullName == args.name) then
|
||||||
if baseName == args.name or fullName == args.name then
|
found = true
|
||||||
found = true
|
texture = tex
|
||||||
texture = tex
|
stacks = buffStacks or 0
|
||||||
stacks = buffStacks or 0
|
spellID = buffSpellID
|
||||||
spellID = buffSpellID
|
remaining = nil
|
||||||
remaining = nil
|
break
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1683,10 +1741,9 @@ function CleveRoids.ValidateUnitDebuff(unit, args)
|
|||||||
if not texture then break end
|
if not texture then break end
|
||||||
|
|
||||||
if CleveRoids.hasSuperwow then
|
if CleveRoids.hasSuperwow then
|
||||||
local fullName = SpellInfo(spellID)
|
-- PERFORMANCE: Use cached spell name lookup
|
||||||
-- Strip rank for comparison (handles "Moonfire (Rank 7)" vs "Moonfire")
|
local baseName, fullName = GetSpellNames(spellID)
|
||||||
local baseName = string.gsub(fullName or "", "%s*%(%s*Rank%s+%d+%s*%)", "")
|
if baseName and (baseName == args.name or fullName == args.name) then
|
||||||
if baseName == args.name or fullName == args.name then
|
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
@@ -1705,10 +1762,9 @@ function CleveRoids.ValidateUnitDebuff(unit, args)
|
|||||||
if not texture then break end
|
if not texture then break end
|
||||||
|
|
||||||
if CleveRoids.hasSuperwow then
|
if CleveRoids.hasSuperwow then
|
||||||
local fullName = SpellInfo(spellID)
|
-- PERFORMANCE: Use cached spell name lookup
|
||||||
-- Strip rank for comparison
|
local baseName, fullName = GetSpellNames(spellID)
|
||||||
local baseName = string.gsub(fullName or "", "%s*%(%s*Rank%s+%d+%s*%)", "")
|
if baseName and (baseName == args.name or fullName == args.name) then
|
||||||
if baseName == args.name or fullName == args.name then
|
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -827,14 +827,6 @@ function CleveRoids.TestForActiveAction(actions)
|
|||||||
local previousOom = actions.active.oom
|
local previousOom = actions.active.oom
|
||||||
local previousInRange = actions.active.inRange
|
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
|
if actions.active.spell then
|
||||||
-- Default to -1 (unknown) so we fall through to proxy slot or original function
|
-- Default to -1 (unknown) so we fall through to proxy slot or original function
|
||||||
-- Only set to 1/0 when we have a definitive answer from IsSpellInRange
|
-- Only set to 1/0 when we have a definitive answer from IsSpellInRange
|
||||||
@@ -2522,11 +2514,37 @@ function CleveRoids.DoUse(msg)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Try to interpret as item ID (numbers > 19)
|
-- Try to interpret as item ID (numbers > 19)
|
||||||
-- Resolve item ID to item name using available APIs
|
-- v2.18+: Use FindPlayerItemSlot directly for item IDs (no name resolution needed)
|
||||||
if slotId and slotId > 19 then
|
if slotId and slotId > 19 then
|
||||||
local itemName = nil
|
|
||||||
-- Try Nampower API first (most reliable)
|
|
||||||
local API = CleveRoids.NampowerAPI
|
local API = CleveRoids.NampowerAPI
|
||||||
|
-- v2.18+: Native lookup can find item directly by ID
|
||||||
|
if API and API.features and API.features.hasFindPlayerItemSlot then
|
||||||
|
local itemInfo = API.FindItemFast(slotId)
|
||||||
|
if itemInfo then
|
||||||
|
ClearCursor()
|
||||||
|
if itemInfo.inventoryID then
|
||||||
|
if CleveRoids.equipDebugLog then
|
||||||
|
CleveRoids.Print("|cff888888[UseLog] /use " .. slotId .. " via UseInventoryItem(" .. itemInfo.inventoryID .. ") [v2.18 ID lookup]|r")
|
||||||
|
end
|
||||||
|
UseInventoryItem(itemInfo.inventoryID)
|
||||||
|
return
|
||||||
|
elseif itemInfo.bagID and itemInfo.slot then
|
||||||
|
if CleveRoids.equipDebugLog then
|
||||||
|
CleveRoids.Print("|cff888888[UseLog] /use " .. slotId .. " via UseContainerItem(" .. itemInfo.bagID .. "," .. itemInfo.slot .. ") [v2.18 ID lookup]|r")
|
||||||
|
end
|
||||||
|
UseContainerItem(itemInfo.bagID, itemInfo.slot)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Item not found by ID - fail
|
||||||
|
if CleveRoids.equipDebugLog then
|
||||||
|
CleveRoids.Print("|cffff8800[UseLog] Item ID " .. slotId .. " not found in inventory [v2.18]|r")
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fallback: Resolve item ID to name for legacy lookup
|
||||||
|
local itemName = nil
|
||||||
if API and API.GetItemName then
|
if API and API.GetItemName then
|
||||||
itemName = API.GetItemName(slotId)
|
itemName = API.GetItemName(slotId)
|
||||||
end
|
end
|
||||||
@@ -2548,6 +2566,30 @@ function CleveRoids.DoUse(msg)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- v2.18+: Use native fast lookup (much faster than Lua cache + scan)
|
||||||
|
local API = CleveRoids.NampowerAPI
|
||||||
|
if API and API.features and API.features.hasFindPlayerItemSlot then
|
||||||
|
local itemInfo = API.FindItemFast(msg)
|
||||||
|
if itemInfo then
|
||||||
|
ClearCursor()
|
||||||
|
if itemInfo.inventoryID then
|
||||||
|
if CleveRoids.equipDebugLog then
|
||||||
|
CleveRoids.Print("|cff888888[UseLog] /use " .. msg .. " via UseInventoryItem(" .. itemInfo.inventoryID .. ") [v2.18 native]|r")
|
||||||
|
end
|
||||||
|
UseInventoryItem(itemInfo.inventoryID)
|
||||||
|
return
|
||||||
|
elseif itemInfo.bagID and itemInfo.slot then
|
||||||
|
if CleveRoids.equipDebugLog then
|
||||||
|
CleveRoids.Print("|cff888888[UseLog] /use " .. msg .. " via UseContainerItem(" .. itemInfo.bagID .. "," .. itemInfo.slot .. ") [v2.18 native]|r")
|
||||||
|
end
|
||||||
|
UseContainerItem(itemInfo.bagID, itemInfo.slot)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- v2.18 lookup didn't find item - fall through to legacy path
|
||||||
|
-- (might be partial match or different case that native doesn't handle)
|
||||||
|
end
|
||||||
|
|
||||||
-- PERFORMANCE: Try cache lookup first (O(1) instead of O(n) scan)
|
-- PERFORMANCE: Try cache lookup first (O(1) instead of O(n) scan)
|
||||||
-- IMPORTANT: Validate cache hits to prevent stale data during combat
|
-- IMPORTANT: Validate cache hits to prevent stale data during combat
|
||||||
-- (IndexItems() is skipped during combat, so cache may have old bag/slot locations)
|
-- (IndexItems() is skipped during combat, so cache may have old bag/slot locations)
|
||||||
@@ -2652,11 +2694,56 @@ function CleveRoids.EquipBagItem(msg, offhand)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local invslot = offhand and 17 or 16
|
||||||
|
local API = CleveRoids.NampowerAPI
|
||||||
|
|
||||||
|
-- v2.18+: Use native fast lookup for item ID or name
|
||||||
|
if API and API.features and API.features.hasFindPlayerItemSlot then
|
||||||
|
local searchTerm = msg
|
||||||
|
local itemId = tonumber(msg)
|
||||||
|
|
||||||
|
-- Check if already equipped in target slot
|
||||||
|
if API.IsItemInSlot(searchTerm, invslot) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Find the item (works with both ID and name)
|
||||||
|
local itemInfo = API.FindItemFast(searchTerm)
|
||||||
|
if itemInfo then
|
||||||
|
-- Already equipped in different slot - need to swap
|
||||||
|
if itemInfo.inventoryID then
|
||||||
|
if itemInfo.inventoryID == invslot then
|
||||||
|
return true -- Already in correct slot
|
||||||
|
end
|
||||||
|
-- Pick up from current slot and equip to target
|
||||||
|
ClearCursor()
|
||||||
|
PickupInventoryItem(itemInfo.inventoryID)
|
||||||
|
if CursorHasItem and CursorHasItem() then
|
||||||
|
EquipCursorItem(invslot)
|
||||||
|
ClearCursor()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
elseif itemInfo.bagID and itemInfo.slot then
|
||||||
|
-- In bag - equip to target slot
|
||||||
|
ClearCursor()
|
||||||
|
PickupContainerItem(itemInfo.bagID, itemInfo.slot)
|
||||||
|
if CursorHasItem and CursorHasItem() then
|
||||||
|
EquipCursorItem(invslot)
|
||||||
|
ClearCursor()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Item not found via v2.18 lookup
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fallback for older Nampower versions
|
||||||
-- Try to interpret as item ID (numbers > 19)
|
-- Try to interpret as item ID (numbers > 19)
|
||||||
local itemId = tonumber(msg)
|
local itemId = tonumber(msg)
|
||||||
if itemId and itemId > 19 then
|
if itemId and itemId > 19 then
|
||||||
local itemName = nil
|
local itemName = nil
|
||||||
local API = CleveRoids.NampowerAPI
|
|
||||||
if API and API.GetItemName then
|
if API and API.GetItemName then
|
||||||
itemName = API.GetItemName(itemId)
|
itemName = API.GetItemName(itemId)
|
||||||
end
|
end
|
||||||
@@ -2670,8 +2757,6 @@ function CleveRoids.EquipBagItem(msg, offhand)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local invslot = offhand and 17 or 16
|
|
||||||
|
|
||||||
-- PERFORMANCE: Fast check if already equipped (single function call)
|
-- PERFORMANCE: Fast check if already equipped (single function call)
|
||||||
if CleveRoids.IsItemEquipped and CleveRoids.IsItemEquipped(msg, invslot) then
|
if CleveRoids.IsItemEquipped and CleveRoids.IsItemEquipped(msg, invslot) then
|
||||||
return true
|
return true
|
||||||
@@ -3106,6 +3191,15 @@ function CleveRoids.OnUpdate(self)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- MEMORY: Clear spell name caches every 60 seconds (12 cleanup cycles)
|
||||||
|
CleveRoids._spellCacheCleanupCounter = (CleveRoids._spellCacheCleanupCounter or 0) + 1
|
||||||
|
if CleveRoids._spellCacheCleanupCounter >= 12 then
|
||||||
|
CleveRoids._spellCacheCleanupCounter = 0
|
||||||
|
if CleveRoids.ClearSpellNameCaches then
|
||||||
|
CleveRoids.ClearSpellNameCaches()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -681,9 +681,31 @@ end
|
|||||||
-- Only scans until item is found, then stops.
|
-- Only scans until item is found, then stops.
|
||||||
-- Optimized: extracts name from link directly instead of calling GetItemInfo.
|
-- Optimized: extracts name from link directly instead of calling GetItemInfo.
|
||||||
-- Uses cache with VALIDATION - checks if item is actually at cached location before trusting it.
|
-- Uses cache with VALIDATION - checks if item is actually at cached location before trusting it.
|
||||||
|
-- v2.18+: Uses native FindPlayerItemSlot for O(1) lookup when available.
|
||||||
function CleveRoids.FindItemQuick(text)
|
function CleveRoids.FindItemQuick(text)
|
||||||
if not text or text == "" then return nil end
|
if not text or text == "" then return nil end
|
||||||
|
|
||||||
|
-- v2.18+: Try native fast lookup first (much faster than Lua scanning)
|
||||||
|
local API = CleveRoids.NampowerAPI
|
||||||
|
if API and API.features and API.features.hasFindPlayerItemSlot then
|
||||||
|
local itemInfo = API.FindItemFast(text)
|
||||||
|
if itemInfo then
|
||||||
|
-- Update cache with the found item for future lookups
|
||||||
|
local Items = CleveRoids.Items or {}
|
||||||
|
if itemInfo.name then
|
||||||
|
Items[itemInfo.name] = itemInfo
|
||||||
|
Items[string_lower(itemInfo.name)] = itemInfo.name
|
||||||
|
if itemInfo.itemId then
|
||||||
|
Items[itemInfo.itemId] = itemInfo.name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return itemInfo
|
||||||
|
end
|
||||||
|
-- Item not found via native lookup - no need to scan
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fallback: no v2.18 API, use existing cache + scan logic
|
||||||
local Items = CleveRoids.Items or {}
|
local Items = CleveRoids.Items or {}
|
||||||
local qid = tonumber(text)
|
local qid = tonumber(text)
|
||||||
-- Only compute lowercase name if we're NOT searching by ID
|
-- Only compute lowercase name if we're NOT searching by ID
|
||||||
|
|||||||
+639
@@ -76,6 +76,17 @@ API.features = {
|
|||||||
|
|
||||||
-- Enhanced spell functions (accept name/"spellId:number")
|
-- Enhanced spell functions (accept name/"spellId:number")
|
||||||
hasEnhancedSpellFunctions = false, -- Detected at runtime
|
hasEnhancedSpellFunctions = false, -- Detected at runtime
|
||||||
|
|
||||||
|
-- v2.18+ APIs (inventory/equipment/cast info)
|
||||||
|
hasFindPlayerItemSlot = (FindPlayerItemSlot ~= nil),
|
||||||
|
hasGetEquippedItems = (GetEquippedItems ~= nil),
|
||||||
|
hasGetEquippedItem = (GetEquippedItem ~= nil),
|
||||||
|
hasGetBagItems = (GetBagItems ~= nil),
|
||||||
|
hasGetBagItem = (GetBagItem ~= nil),
|
||||||
|
hasGetCastInfo = (GetCastInfo ~= nil),
|
||||||
|
hasGetSpellIdCooldown = (GetSpellIdCooldown ~= nil),
|
||||||
|
hasGetItemIdCooldown = (GetItemIdCooldown ~= nil),
|
||||||
|
hasChannelStopCastingNextTick = (ChannelStopCastingNextTick ~= nil),
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Detect if enhanced spell functions are available (accept name/spellId:number)
|
-- Detect if enhanced spell functions are available (accept name/spellId:number)
|
||||||
@@ -121,6 +132,7 @@ API.defaultSettings = {
|
|||||||
NP_MaxBufferIncreaseMs = "30",
|
NP_MaxBufferIncreaseMs = "30",
|
||||||
NP_RetryServerRejectedSpells = "1",
|
NP_RetryServerRejectedSpells = "1",
|
||||||
NP_QuickcastTargetingSpells = "0",
|
NP_QuickcastTargetingSpells = "0",
|
||||||
|
NP_QuickcastOnDoubleCast = "0", -- v2.18+: cast targeting spells by double-casting
|
||||||
NP_ReplaceMatchingNonGcdCategory = "0",
|
NP_ReplaceMatchingNonGcdCategory = "0",
|
||||||
NP_OptimizeBufferUsingPacketTimings = "0",
|
NP_OptimizeBufferUsingPacketTimings = "0",
|
||||||
NP_PreventRightClickTargetChange = "0",
|
NP_PreventRightClickTargetChange = "0",
|
||||||
@@ -545,6 +557,633 @@ function API.UnitHasAura(unitToken, spellId)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- INVENTORY & EQUIPMENT API (v2.18+)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Find an item in player's inventory by ID or name
|
||||||
|
-- Returns: bagIndex, slot (see wiki for bag index meanings)
|
||||||
|
-- bagIndex nil + slot = equipped item (slot is equipment slot 0-18)
|
||||||
|
-- bagIndex + slot = item in bag
|
||||||
|
-- nil, nil = item not found
|
||||||
|
function API.FindPlayerItemSlot(itemIdOrName)
|
||||||
|
if not itemIdOrName then return nil, nil end
|
||||||
|
|
||||||
|
-- Use native function if available (v2.18+)
|
||||||
|
if FindPlayerItemSlot then
|
||||||
|
return FindPlayerItemSlot(itemIdOrName)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fallback: manual search for older versions
|
||||||
|
-- This is much slower but works without Nampower 2.18
|
||||||
|
local itemName = itemIdOrName
|
||||||
|
if type(itemIdOrName) == "number" then
|
||||||
|
-- Try to get item name from ID
|
||||||
|
itemName = API.GetItemName(itemIdOrName)
|
||||||
|
if not itemName then
|
||||||
|
-- Try GetItemInfo as fallback (only works for cached items)
|
||||||
|
itemName = GetItemInfo(itemIdOrName)
|
||||||
|
end
|
||||||
|
if not itemName then
|
||||||
|
return nil, nil -- Can't resolve item ID
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Search equipped items first (slots 0-18, but WoW uses 1-19 in API)
|
||||||
|
for slot = 0, 18 do
|
||||||
|
local link = GetInventoryItemLink("player", slot + 1) -- GetInventoryItemLink is 1-indexed
|
||||||
|
if link then
|
||||||
|
local name = GetItemInfo(link)
|
||||||
|
if name and string.lower(name) == string.lower(itemName) then
|
||||||
|
return nil, slot -- Equipped
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Search bags (0 = backpack, 1-4 = bags)
|
||||||
|
for bag = 0, 4 do
|
||||||
|
local numSlots = GetContainerNumSlots(bag)
|
||||||
|
for slot = 1, numSlots do
|
||||||
|
local link = GetContainerItemLink(bag, slot)
|
||||||
|
if link then
|
||||||
|
local name = GetItemInfo(link)
|
||||||
|
if name and string.lower(name) == string.lower(itemName) then
|
||||||
|
return bag, slot
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil, nil -- Not found
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get all equipped items for a unit
|
||||||
|
-- Returns table with slot indices (0-18) as keys, item info tables as values
|
||||||
|
function API.GetEquippedItems(unitToken)
|
||||||
|
unitToken = unitToken or "player"
|
||||||
|
|
||||||
|
-- Use native function if available (v2.18+)
|
||||||
|
if GetEquippedItems then
|
||||||
|
return GetEquippedItems(unitToken)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fallback: manual lookup for player only
|
||||||
|
if unitToken ~= "player" then
|
||||||
|
return nil -- Can't inspect other units without native API
|
||||||
|
end
|
||||||
|
|
||||||
|
local items = {}
|
||||||
|
for slot = 0, 18 do
|
||||||
|
local link = GetInventoryItemLink("player", slot + 1)
|
||||||
|
if link then
|
||||||
|
local _, _, itemId = string.find(link, "item:(%d+)")
|
||||||
|
if itemId then
|
||||||
|
items[slot] = {
|
||||||
|
itemId = tonumber(itemId),
|
||||||
|
-- Other fields not available without native API
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return items
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get equipped item info for a specific slot
|
||||||
|
-- Slot numbers: 1=Head, 2=Neck, 3=Shoulder... 16=MainHand, 17=OffHand, 18=Ranged
|
||||||
|
function API.GetEquippedItem(unitToken, slot)
|
||||||
|
unitToken = unitToken or "player"
|
||||||
|
|
||||||
|
-- Use native function if available (v2.18+)
|
||||||
|
if GetEquippedItem then
|
||||||
|
return GetEquippedItem(unitToken, slot)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fallback: manual lookup for player
|
||||||
|
if unitToken ~= "player" then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local link = GetInventoryItemLink("player", slot + 1) -- 1-indexed
|
||||||
|
if link then
|
||||||
|
local _, _, itemId = string.find(link, "item:(%d+)")
|
||||||
|
if itemId then
|
||||||
|
return {
|
||||||
|
itemId = tonumber(itemId),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get all items in all bags
|
||||||
|
-- Returns nested table: bagIndex -> { slot -> itemInfo }
|
||||||
|
function API.GetBagItems()
|
||||||
|
-- Use native function if available (v2.18+)
|
||||||
|
if GetBagItems then
|
||||||
|
return GetBagItems()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fallback: manual enumeration
|
||||||
|
local bags = {}
|
||||||
|
for bag = 0, 4 do
|
||||||
|
local numSlots = GetContainerNumSlots(bag)
|
||||||
|
if numSlots > 0 then
|
||||||
|
bags[bag] = {}
|
||||||
|
for slot = 1, numSlots do
|
||||||
|
local link = GetContainerItemLink(bag, slot)
|
||||||
|
if link then
|
||||||
|
local _, _, itemId = string.find(link, "item:(%d+)")
|
||||||
|
local _, count = GetContainerItemInfo(bag, slot)
|
||||||
|
if itemId then
|
||||||
|
bags[bag][slot] = {
|
||||||
|
itemId = tonumber(itemId),
|
||||||
|
stackCount = count or 1,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return bags
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get item info for a specific bag slot
|
||||||
|
function API.GetBagItem(bagIndex, slot)
|
||||||
|
-- Use native function if available (v2.18+)
|
||||||
|
if GetBagItem then
|
||||||
|
return GetBagItem(bagIndex, slot)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fallback: manual lookup
|
||||||
|
local link = GetContainerItemLink(bagIndex, slot)
|
||||||
|
if link then
|
||||||
|
local _, _, itemId = string.find(link, "item:(%d+)")
|
||||||
|
local _, count = GetContainerItemInfo(bagIndex, slot)
|
||||||
|
if itemId then
|
||||||
|
return {
|
||||||
|
itemId = tonumber(itemId),
|
||||||
|
stackCount = count or 1,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- OPTIMIZED ITEM FINDING (v2.18+)
|
||||||
|
-- These functions provide fast item lookup compatible with CleveRoids.Items format
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Fast item lookup using v2.18 FindPlayerItemSlot
|
||||||
|
-- Returns item info table compatible with CleveRoids.Items format:
|
||||||
|
-- { name, itemId, inventoryID } for equipped items
|
||||||
|
-- { name, itemId, bagID, slot } for bag items
|
||||||
|
-- Returns nil if item not found
|
||||||
|
function API.FindItemFast(itemIdOrName)
|
||||||
|
if not itemIdOrName then return nil end
|
||||||
|
|
||||||
|
-- Use native v2.18 function if available (much faster than Lua scan)
|
||||||
|
if FindPlayerItemSlot then
|
||||||
|
local bag, slot = FindPlayerItemSlot(itemIdOrName)
|
||||||
|
|
||||||
|
if slot then
|
||||||
|
local itemInfo = {}
|
||||||
|
|
||||||
|
if bag == nil then
|
||||||
|
-- Equipped item (slot is equipment slot 0-18)
|
||||||
|
-- Nampower uses 0-indexed, WoW API uses 1-indexed for GetInventoryItemLink
|
||||||
|
local invSlot = slot + 1
|
||||||
|
local link = GetInventoryItemLink("player", invSlot)
|
||||||
|
if link then
|
||||||
|
local _, _, itemId = string.find(link, "item:(%d+)")
|
||||||
|
local _, _, name = string.find(link, "|h%[(.-)%]|h")
|
||||||
|
itemInfo.inventoryID = invSlot
|
||||||
|
itemInfo.itemId = tonumber(itemId)
|
||||||
|
itemInfo.name = name
|
||||||
|
itemInfo._source = "nampower_equipped"
|
||||||
|
return itemInfo
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Bag item
|
||||||
|
local link = GetContainerItemLink(bag, slot)
|
||||||
|
if link then
|
||||||
|
local _, _, itemId = string.find(link, "item:(%d+)")
|
||||||
|
local _, _, name = string.find(link, "|h%[(.-)%]|h")
|
||||||
|
local _, count = GetContainerItemInfo(bag, slot)
|
||||||
|
itemInfo.bagID = bag
|
||||||
|
itemInfo.slot = slot
|
||||||
|
itemInfo.itemId = tonumber(itemId)
|
||||||
|
itemInfo.name = name
|
||||||
|
itemInfo.count = count or 1
|
||||||
|
itemInfo._source = "nampower_bag"
|
||||||
|
return itemInfo
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil -- Not found via native lookup
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fallback: no v2.18 API, return nil (caller should use existing methods)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if an item is equipped in a specific slot (fast)
|
||||||
|
-- Returns true if item matches by ID or name
|
||||||
|
function API.IsItemInSlot(itemIdOrName, inventorySlot)
|
||||||
|
if not itemIdOrName or not inventorySlot then return false end
|
||||||
|
|
||||||
|
-- Use native v2.18 GetEquippedItem if available
|
||||||
|
if GetEquippedItem then
|
||||||
|
local slotInfo = GetEquippedItem("player", inventorySlot - 1) -- 0-indexed
|
||||||
|
if slotInfo and slotInfo.itemId then
|
||||||
|
local checkId = tonumber(itemIdOrName)
|
||||||
|
if checkId then
|
||||||
|
return slotInfo.itemId == checkId
|
||||||
|
else
|
||||||
|
-- Check by name
|
||||||
|
local itemName = API.GetItemName(slotInfo.itemId)
|
||||||
|
if itemName then
|
||||||
|
return string.lower(itemName) == string.lower(itemIdOrName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fallback: use GetInventoryItemLink
|
||||||
|
local link = GetInventoryItemLink("player", inventorySlot)
|
||||||
|
if not link then return false end
|
||||||
|
|
||||||
|
local checkId = tonumber(itemIdOrName)
|
||||||
|
if checkId then
|
||||||
|
local _, _, currentId = string.find(link, "item:(%d+)")
|
||||||
|
return currentId and tonumber(currentId) == checkId
|
||||||
|
else
|
||||||
|
local _, _, currentName = string.find(link, "|h%[(.-)%]|h")
|
||||||
|
return currentName and string.lower(currentName) == string.lower(itemIdOrName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if item is equipped anywhere (fast)
|
||||||
|
-- Returns inventorySlot if equipped, nil otherwise
|
||||||
|
function API.FindEquippedItem(itemIdOrName)
|
||||||
|
if not itemIdOrName then return nil end
|
||||||
|
|
||||||
|
-- Use native v2.18 FindPlayerItemSlot if available
|
||||||
|
if FindPlayerItemSlot then
|
||||||
|
local bag, slot = FindPlayerItemSlot(itemIdOrName)
|
||||||
|
if bag == nil and slot then
|
||||||
|
return slot + 1 -- Convert to 1-indexed WoW API slot
|
||||||
|
end
|
||||||
|
return nil -- Not equipped (might be in bag or not found)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fallback: scan equipped slots
|
||||||
|
local checkId = tonumber(itemIdOrName)
|
||||||
|
local checkName = (not checkId) and string.lower(itemIdOrName) or nil
|
||||||
|
|
||||||
|
for invSlot = 1, 19 do
|
||||||
|
local link = GetInventoryItemLink("player", invSlot)
|
||||||
|
if link then
|
||||||
|
if checkId then
|
||||||
|
local _, _, currentId = string.find(link, "item:(%d+)")
|
||||||
|
if currentId and tonumber(currentId) == checkId then
|
||||||
|
return invSlot
|
||||||
|
end
|
||||||
|
elseif checkName then
|
||||||
|
local _, _, currentName = string.find(link, "|h%[(.-)%]|h")
|
||||||
|
if currentName and string.lower(currentName) == checkName then
|
||||||
|
return invSlot
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Find item in bags only (not equipped) - fast
|
||||||
|
-- Returns bag, slot if found, nil otherwise
|
||||||
|
function API.FindBagItem(itemIdOrName)
|
||||||
|
if not itemIdOrName then return nil, nil end
|
||||||
|
|
||||||
|
-- Use native v2.18 FindPlayerItemSlot if available
|
||||||
|
if FindPlayerItemSlot then
|
||||||
|
local bag, slot = FindPlayerItemSlot(itemIdOrName)
|
||||||
|
if bag ~= nil and slot then
|
||||||
|
return bag, slot
|
||||||
|
end
|
||||||
|
return nil, nil -- Not in bags (might be equipped or not found)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fallback: scan bags
|
||||||
|
local checkId = tonumber(itemIdOrName)
|
||||||
|
local checkName = (not checkId) and string.lower(itemIdOrName) or nil
|
||||||
|
|
||||||
|
for bag = 0, 4 do
|
||||||
|
local numSlots = GetContainerNumSlots(bag) or 0
|
||||||
|
for slot = 1, numSlots do
|
||||||
|
local link = GetContainerItemLink(bag, slot)
|
||||||
|
if link then
|
||||||
|
if checkId then
|
||||||
|
local _, _, currentId = string.find(link, "item:(%d+)")
|
||||||
|
if currentId and tonumber(currentId) == checkId then
|
||||||
|
return bag, slot
|
||||||
|
end
|
||||||
|
elseif checkName then
|
||||||
|
local _, _, currentName = string.find(link, "|h%[(.-)%]|h")
|
||||||
|
if currentName and string.lower(currentName) == checkName then
|
||||||
|
return bag, slot
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Use an item by ID or name (fast lookup)
|
||||||
|
-- Returns true if item was used, false otherwise
|
||||||
|
function API.UseItem(itemIdOrName)
|
||||||
|
if not itemIdOrName then return false end
|
||||||
|
|
||||||
|
-- Try native v2.18 lookup first
|
||||||
|
local itemInfo = API.FindItemFast(itemIdOrName)
|
||||||
|
if itemInfo then
|
||||||
|
ClearCursor()
|
||||||
|
if itemInfo.inventoryID then
|
||||||
|
UseInventoryItem(itemInfo.inventoryID)
|
||||||
|
return true
|
||||||
|
elseif itemInfo.bagID and itemInfo.slot then
|
||||||
|
UseContainerItem(itemInfo.bagID, itemInfo.slot)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- No v2.18 API or item not found via native lookup
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Equip an item from bags to a specific slot (fast lookup)
|
||||||
|
-- Returns true if equip was attempted, false if item not found
|
||||||
|
function API.EquipItem(itemIdOrName, targetSlot)
|
||||||
|
if not itemIdOrName then return false end
|
||||||
|
|
||||||
|
-- Check if already equipped in target slot
|
||||||
|
if targetSlot and API.IsItemInSlot(itemIdOrName, targetSlot) then
|
||||||
|
return true -- Already equipped
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Find the item
|
||||||
|
local itemInfo = API.FindItemFast(itemIdOrName)
|
||||||
|
if not itemInfo then return false end
|
||||||
|
|
||||||
|
-- If already equipped (but not in target slot), need to swap
|
||||||
|
if itemInfo.inventoryID then
|
||||||
|
if targetSlot and itemInfo.inventoryID ~= targetSlot then
|
||||||
|
-- Item equipped in wrong slot - pick up and move
|
||||||
|
ClearCursor()
|
||||||
|
PickupInventoryItem(itemInfo.inventoryID)
|
||||||
|
if CursorHasItem and CursorHasItem() then
|
||||||
|
EquipCursorItem(targetSlot)
|
||||||
|
ClearCursor()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true -- Already equipped (and no target slot specified)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Item in bag - equip it
|
||||||
|
if itemInfo.bagID and itemInfo.slot then
|
||||||
|
ClearCursor()
|
||||||
|
PickupContainerItem(itemInfo.bagID, itemInfo.slot)
|
||||||
|
if CursorHasItem and CursorHasItem() then
|
||||||
|
if targetSlot then
|
||||||
|
EquipCursorItem(targetSlot)
|
||||||
|
else
|
||||||
|
-- Auto-equip to appropriate slot
|
||||||
|
AutoEquipCursorItem()
|
||||||
|
end
|
||||||
|
ClearCursor()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- CAST INFO API (v2.18+)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Get detailed information about current cast/channel
|
||||||
|
-- Returns table with: castId, spellId, guid, castType, castStartS, castEndS,
|
||||||
|
-- castRemainingMs, castDurationMs, gcdEndS, gcdRemainingMs
|
||||||
|
-- Returns nil if no active cast
|
||||||
|
function API.GetCastInfo()
|
||||||
|
-- Use native function if available (v2.18+)
|
||||||
|
if GetCastInfo then
|
||||||
|
return GetCastInfo()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fallback: use GetCurrentCastingInfo (less detailed)
|
||||||
|
if GetCurrentCastingInfo then
|
||||||
|
local castId, visId, autoId, casting, channeling, onswing, autoattack = GetCurrentCastingInfo()
|
||||||
|
if castId and castId > 0 then
|
||||||
|
return {
|
||||||
|
spellId = castId,
|
||||||
|
castType = channeling == 1 and 3 or 0, -- 3 = CHANNEL, 0 = NORMAL
|
||||||
|
-- Timing info not available without native GetCastInfo
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if currently casting (convenience wrapper)
|
||||||
|
function API.IsCasting()
|
||||||
|
local info = API.GetCastInfo()
|
||||||
|
return info ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get GCD remaining in milliseconds
|
||||||
|
function API.GetGCDRemainingMs()
|
||||||
|
local info = API.GetCastInfo()
|
||||||
|
if info and info.gcdRemainingMs then
|
||||||
|
return info.gcdRemainingMs
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get cast remaining in milliseconds
|
||||||
|
function API.GetCastRemainingMs()
|
||||||
|
local info = API.GetCastInfo()
|
||||||
|
if info and info.castRemainingMs then
|
||||||
|
return info.castRemainingMs
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- COOLDOWN API (v2.18+)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Get detailed cooldown information for a spell
|
||||||
|
-- Returns table with: isOnCooldown, cooldownRemainingMs,
|
||||||
|
-- individual cooldown: individualStartS, individualDurationMs, individualRemainingMs, isOnIndividualCooldown
|
||||||
|
-- category cooldown: categoryId, categoryStartS, categoryDurationMs, categoryRemainingMs, isOnCategoryCooldown
|
||||||
|
-- GCD: gcdCategoryId, gcdCategoryStartS, gcdCategoryDurationMs, gcdCategoryRemainingMs, isOnGcdCategoryCooldown
|
||||||
|
function API.GetSpellCooldownInfo(spellId)
|
||||||
|
if not spellId or spellId == 0 then return nil end
|
||||||
|
|
||||||
|
-- Use native function if available (v2.18+)
|
||||||
|
if GetSpellIdCooldown then
|
||||||
|
return GetSpellIdCooldown(spellId)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fallback: use GetSpellCooldown (less detailed)
|
||||||
|
local spellName = API.GetSpellNameById(spellId)
|
||||||
|
if spellName then
|
||||||
|
local start, duration = GetSpellCooldown(spellName, BOOKTYPE_SPELL)
|
||||||
|
if start and start > 0 then
|
||||||
|
local remaining = (start + duration) - GetTime()
|
||||||
|
if remaining > 0 then
|
||||||
|
return {
|
||||||
|
isOnCooldown = 1,
|
||||||
|
cooldownRemainingMs = remaining * 1000,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return { isOnCooldown = 0, cooldownRemainingMs = 0 }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get detailed cooldown information for an item
|
||||||
|
function API.GetItemCooldownInfo(itemId)
|
||||||
|
if not itemId or itemId == 0 then return nil end
|
||||||
|
|
||||||
|
-- Use native function if available (v2.18+)
|
||||||
|
if GetItemIdCooldown then
|
||||||
|
return GetItemIdCooldown(itemId)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fallback: use GetItemCooldown (less detailed)
|
||||||
|
local start, duration = GetItemCooldown(itemId)
|
||||||
|
if start and start > 0 then
|
||||||
|
local remaining = (start + duration) - GetTime()
|
||||||
|
if remaining > 0 then
|
||||||
|
return {
|
||||||
|
isOnCooldown = 1,
|
||||||
|
cooldownRemainingMs = remaining * 1000,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return { isOnCooldown = 0, cooldownRemainingMs = 0 }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if a spell is on cooldown (ignoring GCD)
|
||||||
|
function API.IsSpellOnCooldown(spellId, ignoreGCD)
|
||||||
|
local info = API.GetSpellCooldownInfo(spellId)
|
||||||
|
if not info then return false end
|
||||||
|
|
||||||
|
if ignoreGCD then
|
||||||
|
-- Only check individual and category cooldowns
|
||||||
|
return (info.isOnIndividualCooldown == 1) or (info.isOnCategoryCooldown == 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
return info.isOnCooldown == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get remaining cooldown for a spell in seconds
|
||||||
|
function API.GetSpellCooldownRemaining(spellId)
|
||||||
|
local info = API.GetSpellCooldownInfo(spellId)
|
||||||
|
if info and info.cooldownRemainingMs then
|
||||||
|
return info.cooldownRemainingMs / 1000
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- CHANNEL CONTROL API (v2.18+)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Stop channeling early on the next tick
|
||||||
|
-- Only works if NP_QueueChannelingSpells is enabled
|
||||||
|
function API.StopChannelNextTick()
|
||||||
|
if ChannelStopCastingNextTick then
|
||||||
|
ChannelStopCastingNextTick()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- EVENT CONSTANTS (v2.18+)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Spell queue event codes
|
||||||
|
API.QUEUE_EVENT = {
|
||||||
|
ON_SWING_QUEUED = 0,
|
||||||
|
ON_SWING_QUEUE_POPPED = 1,
|
||||||
|
NORMAL_QUEUED = 2,
|
||||||
|
NORMAL_QUEUE_POPPED = 3,
|
||||||
|
NON_GCD_QUEUED = 4,
|
||||||
|
NON_GCD_QUEUE_POPPED = 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Spell cast event types
|
||||||
|
API.CAST_TYPE = {
|
||||||
|
NORMAL = 1,
|
||||||
|
NON_GCD = 2,
|
||||||
|
ON_SWING = 3,
|
||||||
|
CHANNEL = 4,
|
||||||
|
TARGETING = 5,
|
||||||
|
TARGETING_NON_GCD = 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Buff/debuff event names (v2.18+)
|
||||||
|
API.AURA_EVENTS = {
|
||||||
|
"BUFF_ADDED_SELF",
|
||||||
|
"BUFF_REMOVED_SELF",
|
||||||
|
"BUFF_ADDED_OTHER",
|
||||||
|
"BUFF_REMOVED_OTHER",
|
||||||
|
"DEBUFF_ADDED_SELF",
|
||||||
|
"DEBUFF_REMOVED_SELF",
|
||||||
|
"DEBUFF_ADDED_OTHER",
|
||||||
|
"DEBUFF_REMOVED_OTHER",
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Spell school constants (for reference)
|
||||||
|
API.SPELL_SCHOOL = {
|
||||||
|
PHYSICAL = 0,
|
||||||
|
HOLY = 1,
|
||||||
|
FIRE = 2,
|
||||||
|
NATURE = 3,
|
||||||
|
FROST = 4,
|
||||||
|
SHADOW = 5,
|
||||||
|
ARCANE = 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Resistance indices (1-indexed for Lua tables)
|
||||||
|
API.RESISTANCE_INDEX = {
|
||||||
|
ARMOR = 1,
|
||||||
|
HOLY = 2,
|
||||||
|
FIRE = 3,
|
||||||
|
NATURE = 4,
|
||||||
|
FROST = 5,
|
||||||
|
SHADOW = 6,
|
||||||
|
ARCANE = 7,
|
||||||
|
}
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- SPELL MODIFIERS API (GetSpellModifiers)
|
-- SPELL MODIFIERS API (GetSpellModifiers)
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
## Author: DWG, _brain, Torio, yani9o
|
## Author: DWG, _brain, Torio, yani9o
|
||||||
## Title: [Super]CleveRoidMacros
|
## Title: [Super]CleveRoidMacros
|
||||||
## Notes: /cleveroid for settings
|
## Notes: /cleveroid for settings
|
||||||
## Version: 1.5
|
## Version: 1.6
|
||||||
## OptionalDeps: ClassicFocus, FocusFrame, pfUI, SuperMacro, Bongos_ActionBar
|
## OptionalDeps: ClassicFocus, FocusFrame, pfUI, SuperMacro, Bongos_ActionBar
|
||||||
## SavedVariables: CleveRoidMacros, CleveRoids_LearnedDurations, CleveRoids_AuraTextures, CleveRoids_ImmunityData, CleveRoids_ComboDurations, CleveRoids_SpellSchools
|
## SavedVariables: CleveRoidMacros, CleveRoids_LearnedDurations, CleveRoids_AuraTextures, CleveRoids_ImmunityData, CleveRoids_ComboDurations, CleveRoids_SpellSchools
|
||||||
Localization.lua
|
Localization.lua
|
||||||
|
|||||||
Reference in New Issue
Block a user