optimize buff and debuff checks and update functions to nampower v2_18_0

This commit is contained in:
Jrc13245
2025-12-17 16:29:32 -05:00
parent ed67c38b0f
commit eede643540
5 changed files with 874 additions and 63 deletions
+104 -48
View File
@@ -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
+108 -14
View File
@@ -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
+22
View File
@@ -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
View File
@@ -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)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
+1 -1
View File
@@ -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