mirror of
https://github.com/brues-code/SuperCleveRoidMacros.git
synced 2026-09-16 03:38:00 +00:00
fix equip with conditionals and dont pollute runmacro global namespace
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
- Intercepts CRM-owned slashcommands (including {…}, […], !, ?, ~)
|
||||
- Forwards to CRM's SlashCmdList handlers; preserves /castsequence
|
||||
- Leaves SuperMacro unmodified; safe fallback to original RunLine
|
||||
- Redirects global RunMacro to SuperMacro_RunMacro so RunLine hook can intercept
|
||||
]]
|
||||
|
||||
do
|
||||
@@ -14,12 +15,14 @@ do
|
||||
CRM.Hooks = CRM.Hooks or {}
|
||||
|
||||
-- Commands owned by CRM (keep in sync with Console.lua registrations)
|
||||
-- NOTE: /runmacro is NOT intercepted here - we let SuperMacro's RunLine handle it
|
||||
-- naturally via SuperMacro_RunMacro. This avoids conflicts with SuperMacro's keybinding system.
|
||||
local INTERCEPT = {
|
||||
cast=true, castsequence=true, use=true,
|
||||
startattack=true, stopattack=true, stopcasting=true,
|
||||
target=true, retarget=true, cancelaura=true, unbuff=true,
|
||||
unqueue=true, unshift=true, equip=true, equipmh=true, equipoh=true,
|
||||
runmacro=true,
|
||||
-- runmacro intentionally NOT included - see note above
|
||||
-- pet
|
||||
petattack=true, petfollow=true, petpassive=true,
|
||||
petaggressive=true, petdefensive=true, petwait=true,
|
||||
@@ -114,6 +117,17 @@ do
|
||||
end
|
||||
|
||||
CRM.SM_RunLineHooked = true
|
||||
|
||||
-- Redirect global RunMacro to SuperMacro_RunMacro so all macro execution
|
||||
-- goes through RunLine (where our hook intercepts CRM commands).
|
||||
-- This ensures both SuperMacro and CleveRoids features work together.
|
||||
if type(_G.SuperMacro_RunMacro) == "function" then
|
||||
_G.RunMacro = function(index)
|
||||
return _G.SuperMacro_RunMacro(index)
|
||||
end
|
||||
-- Also update the Macro alias
|
||||
_G.Macro = _G.SuperMacro_RunMacro
|
||||
end
|
||||
end
|
||||
|
||||
-- ===== Order-agnostic loader: install only when BOTH addons are present =====
|
||||
@@ -133,7 +147,8 @@ do
|
||||
local function both_loaded()
|
||||
if SM_LOADED and CRM_LOADED then return true end
|
||||
-- heuristic fallback in case this file loads late:
|
||||
local sm_ok = type(_G.RunLine) == "function"
|
||||
-- Check for SuperMacro's namespaced internal function (preferred) or RunLine
|
||||
local sm_ok = type(_G.SuperMacro_RunMacro) == "function" or type(_G.RunLine) == "function"
|
||||
local crm_ok = _G.CleveRoids and _G.SlashCmdList and type(_G.SlashCmdList.CAST) == "function"
|
||||
return sm_ok and crm_ok
|
||||
end
|
||||
@@ -142,7 +157,9 @@ do
|
||||
if both_loaded() then install_supermacro_hook() end
|
||||
end
|
||||
|
||||
f:SetScript("OnEvent", function(_, evt, addon)
|
||||
f:SetScript("OnEvent", function()
|
||||
local evt = event
|
||||
local addon = arg1
|
||||
if evt == "ADDON_LOADED" then
|
||||
if type(addon) == "string" then note_loaded(addon) end
|
||||
try_install()
|
||||
|
||||
@@ -271,6 +271,15 @@ SlashCmdList.RUNMACRO = function(msg)
|
||||
return CleveRoids.ExecuteMacroByName(CleveRoids.Trim(msg))
|
||||
end
|
||||
|
||||
-- Global RunMacro wrapper for user convenience (delegates to namespaced internal function)
|
||||
-- This pattern ensures internal logic uses CleveRoids.ExecuteMacroByName and won't break
|
||||
-- if another addon overwrites the global RunMacro
|
||||
-- NOTE: When SuperMacro is also loaded, Compatibility/SuperMacro.lua redirects this to
|
||||
-- SuperMacro_RunMacro so macros go through RunLine (where CRM commands are intercepted)
|
||||
function RunMacro(name)
|
||||
return CleveRoids.ExecuteMacroByName(name)
|
||||
end
|
||||
|
||||
SLASH_RETARGET1 = "/retarget"
|
||||
SlashCmdList.RETARGET = function(msg)
|
||||
CleveRoids.DoRetarget()
|
||||
|
||||
@@ -119,8 +119,21 @@ local function GetInventoryIdFromSlot(slotName)
|
||||
return SLOT_TO_INVID[slotName] or GetInventorySlotInfo(slotName)
|
||||
end
|
||||
|
||||
-- Check if slot 18 is a relic (no GCD) for current player class
|
||||
local function IsRelicSlot(slot)
|
||||
if slot ~= 18 then return false end
|
||||
local playerClass = CleveRoids.playerClass
|
||||
return playerClass == "PALADIN" or playerClass == "DRUID" or playerClass == "SHAMAN"
|
||||
end
|
||||
|
||||
-- PERFORMANCE: Cache GetTime() result for cooldown checks within same frame
|
||||
-- Slot 18 (relic/idol/libram/totem) has no GCD for Paladin/Druid/Shaman
|
||||
local function IsSlotOnCooldown(slot, now)
|
||||
-- Relics have no GCD - skip cooldown check entirely
|
||||
if IsRelicSlot(slot) then
|
||||
return false
|
||||
end
|
||||
|
||||
now = now or GetTime()
|
||||
local slotTime = CleveRoids.lastEquipTime[slot] or 0
|
||||
local globalTime = CleveRoids.lastGlobalEquipTime or 0
|
||||
@@ -265,8 +278,11 @@ function CleveRoids.QueueEquipItem(item, slotName)
|
||||
local success = PerformEquipSwap(item, inventoryId, true)
|
||||
|
||||
if success then
|
||||
CleveRoids.lastEquipTime[inventoryId] = now
|
||||
CleveRoids.lastGlobalEquipTime = now
|
||||
-- Don't set cooldowns for relic slots (no GCD)
|
||||
if not IsRelicSlot(inventoryId) then
|
||||
CleveRoids.lastEquipTime[inventoryId] = now
|
||||
CleveRoids.lastGlobalEquipTime = now
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -313,8 +329,11 @@ function CleveRoids.ProcessEquipmentQueue()
|
||||
local success = PerformEquipSwap(queued.item, queued.inventoryId, true)
|
||||
|
||||
if success then
|
||||
CleveRoids.lastEquipTime[queued.inventoryId] = now
|
||||
CleveRoids.lastGlobalEquipTime = now
|
||||
-- Don't set cooldowns for relic slots (no GCD)
|
||||
if not IsRelicSlot(queued.inventoryId) then
|
||||
CleveRoids.lastEquipTime[queued.inventoryId] = now
|
||||
CleveRoids.lastGlobalEquipTime = now
|
||||
end
|
||||
RemoveQueueEntry(i)
|
||||
else
|
||||
queued.retries = queued.retries + 1
|
||||
@@ -1146,40 +1165,54 @@ function CleveRoids.GetMacroBody(name)
|
||||
return macro and macro.body
|
||||
end
|
||||
|
||||
-- Attempts to execute a macro by the given name (Blizzard or Super tab)
|
||||
-- Attempts to execute a macro by the given name or index (Blizzard or Super tab)
|
||||
-- Returns: true if something was executed, false otherwise
|
||||
-- Supports: macro name (string), macro index (number or numeric string like "19")
|
||||
-- Uses CleveRoids' own ExecuteMacroBody for processing (supports enhanced syntax)
|
||||
function CleveRoids.ExecuteMacroByName(name)
|
||||
if not name or name == "" then return false end
|
||||
|
||||
local body
|
||||
|
||||
-- 1) Blizzard macro runner by name/ID
|
||||
local id = GetMacroIndexByName(name)
|
||||
if id and id ~= 0 and type(RunMacro) == "function" then
|
||||
if pcall(RunMacro, name) then return true end
|
||||
if pcall(RunMacro, id) then return true end
|
||||
local _n, _tex, b2 = GetMacroInfo(id)
|
||||
if b2 and b2 ~= "" then body = body or b2 end
|
||||
end
|
||||
-- Check if input is a numeric index (e.g., "19" or 19)
|
||||
local numericIndex = tonumber(name)
|
||||
|
||||
-- 2) SuperMacro runner by name
|
||||
if type(GetSuperMacroInfo) == "function" and type(RunSuperMacro) == "function" then
|
||||
local _n2, _t2, b3 = GetSuperMacroInfo(name)
|
||||
if b3 and b3 ~= "" then
|
||||
if pcall(RunSuperMacro, name) then return true end
|
||||
body = body or b3
|
||||
-- 1) Try Blizzard macro (by index or name)
|
||||
if numericIndex then
|
||||
-- Numeric index - get body directly (supports character macros 19-36)
|
||||
local _n, _tex, b = GetMacroInfo(numericIndex)
|
||||
if b and b ~= "" then body = b end
|
||||
else
|
||||
-- String name - look up by name
|
||||
local id = GetMacroIndexByName(name)
|
||||
if id and id ~= 0 then
|
||||
local _n, _tex, b = GetMacroInfo(id)
|
||||
if b and b ~= "" then body = b end
|
||||
end
|
||||
end
|
||||
|
||||
-- 3) CRM cache fallback
|
||||
if not body and type(CleveRoids.GetMacro) == "function" then
|
||||
local m = CleveRoids.GetMacro(name)
|
||||
if m and m.body and m.body ~= "" then
|
||||
body = m.body
|
||||
-- 2) SuperMacro's Super macros (by name only, not index)
|
||||
-- These are the 7000-char extended macros stored in SM_SUPER
|
||||
if not body and not numericIndex then
|
||||
if type(GetSuperMacroInfo) == "function" then
|
||||
local _n2, _t2, b2 = GetSuperMacroInfo(name)
|
||||
if b2 and b2 ~= "" then body = b2 end
|
||||
end
|
||||
end
|
||||
|
||||
-- 3) CRM cache fallback (by name only)
|
||||
if not body and not numericIndex then
|
||||
if type(CleveRoids.GetMacro) == "function" then
|
||||
local m = CleveRoids.GetMacro(name)
|
||||
if m and m.body and m.body ~= "" then
|
||||
body = m.body
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not body or body == "" then return false end
|
||||
|
||||
-- Execute using CleveRoids' processor (supports conditionals, extended syntax)
|
||||
return CleveRoids.ExecuteMacroBody(body)
|
||||
end
|
||||
|
||||
@@ -1938,7 +1971,14 @@ function CleveRoids.DoWithConditionals(msg, hook, fixEmptyTargetFunc, targetBefo
|
||||
|
||||
for k, v in pairs(conditionals) do
|
||||
if not CleveRoids.ignoreKeywords[k] then
|
||||
if not CleveRoids.Keywords[k] or not CleveRoids.Keywords[k](conditionals) then
|
||||
local result = CleveRoids.Keywords[k] and CleveRoids.Keywords[k](conditionals)
|
||||
-- Debug logging for equipped conditional when equipDebugLog is enabled
|
||||
if CleveRoids.equipDebugLog and (k == "equipped" or k == "noequipped") then
|
||||
local valStr = type(v) == "table" and table.concat(v, ", ") or tostring(v)
|
||||
CleveRoids.Print("|cff888888[EquipLog] Conditional [" .. k .. ":" .. valStr .. "] = " ..
|
||||
(result and "|cff00ff00PASS|r" or "|cffff0000FAIL|r") .. "|r")
|
||||
end
|
||||
if not result then
|
||||
if needRetarget then
|
||||
TargetLastTarget()
|
||||
needRetarget = false
|
||||
@@ -2397,82 +2437,29 @@ function CleveRoids.DoUse(msg)
|
||||
return
|
||||
end
|
||||
|
||||
-- PERFORMANCE: Try fast lookup methods first, fall back to full scan
|
||||
-- Full scan is now optimized with GetNameFromLink() instead of GetItemInfo()
|
||||
local item
|
||||
-- Simple bag scan and equip - no cache, no Nampower API (causes crashes)
|
||||
local qname = string_lower(msg)
|
||||
|
||||
-- Try fast cache lookup first (no scanning)
|
||||
if CleveRoids.GetItemFast then
|
||||
item = CleveRoids.GetItemFast(msg)
|
||||
end
|
||||
|
||||
-- Try quick targeted scan (stops when found)
|
||||
if not item and CleveRoids.FindItemQuick then
|
||||
item = CleveRoids.FindItemQuick(msg)
|
||||
end
|
||||
|
||||
-- Full scan fallback (now optimized, safe during combat)
|
||||
if not item then
|
||||
item = CleveRoids.GetItem(msg)
|
||||
end
|
||||
|
||||
if not item then return end
|
||||
|
||||
-- If it's an equipped item (trinket etc.), use it directly
|
||||
if item.inventoryID then
|
||||
ClearCursor()
|
||||
UseInventoryItem(item.inventoryID)
|
||||
-- Invalidate cache - item location may have changed (e.g., trinket swap)
|
||||
if item.name and CleveRoids.Items then
|
||||
CleveRoids.Items[item.name] = nil
|
||||
CleveRoids.Items[string_lower(item.name)] = nil
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Otherwise, use the bag item
|
||||
if item.bagID and item.slot then
|
||||
-- If we tracked multiple stacks, advance politely
|
||||
CleveRoids.GetNextBagSlotForUse(item, msg)
|
||||
|
||||
-- Before equipping, note what's in commonly swapped slots
|
||||
-- so we can invalidate cache entries for displaced items
|
||||
-- Slots: 13/14 (trinkets), 16/17 (weapons), 18 (relic/idol/libram)
|
||||
local swapSlots = {13, 14, 16, 17, 18}
|
||||
local oldSlotItems = {}
|
||||
for _, slotId in ipairs(swapSlots) do
|
||||
local link = GetInventoryItemLink("player", slotId)
|
||||
for bag = 0, 4 do
|
||||
local numSlots = GetContainerNumSlots(bag) or 0
|
||||
for bagSlot = 1, numSlots do
|
||||
local link = GetContainerItemLink(bag, bagSlot)
|
||||
if link then
|
||||
local _, _, name = string_find(link, "|h%[(.-)%]|h")
|
||||
if name then oldSlotItems[slotId] = name end
|
||||
end
|
||||
end
|
||||
|
||||
ClearCursor()
|
||||
UseContainerItem(item.bagID, item.slot)
|
||||
|
||||
-- Invalidate cache for the item we just used
|
||||
if item.name and CleveRoids.Items then
|
||||
CleveRoids.Items[item.name] = nil
|
||||
CleveRoids.Items[string_lower(item.name)] = nil
|
||||
end
|
||||
|
||||
-- Invalidate cache for any displaced items in swap slots
|
||||
if CleveRoids.Items then
|
||||
for slotId, oldName in pairs(oldSlotItems) do
|
||||
local newLink = GetInventoryItemLink("player", slotId)
|
||||
local newName = nil
|
||||
if newLink then
|
||||
local _, _, name = string_find(newLink, "|h%[(.-)%]|h")
|
||||
newName = name
|
||||
end
|
||||
if newName ~= oldName then
|
||||
CleveRoids.Items[oldName] = nil
|
||||
CleveRoids.Items[string_lower(oldName)] = nil
|
||||
local _, _, nm = string_find(link, "|h%[(.-)%]|h")
|
||||
if nm and string_lower(nm) == qname then
|
||||
if CleveRoids.equipDebugLog then
|
||||
CleveRoids.Print("|cff888888[EquipLog] /equip " .. msg .. " via UseContainerItem(" .. bag .. "," .. bagSlot .. ")|r")
|
||||
end
|
||||
ClearCursor()
|
||||
UseContainerItem(bag, bagSlot)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if CleveRoids.equipDebugLog then
|
||||
CleveRoids.Print("|cff888888[EquipLog] /equip " .. msg .. " - not found in bags (already equipped?)|r")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4238,6 +4225,8 @@ SlashCmdList["CLEVEROID"] = function(msg)
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid diagnosetalent <spellID> - Diagnose talent modifier issues')
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Equipment Modifiers:|r")
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid testequip <spellID> - Test equipment modifier for a spell')
|
||||
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid equipdebug <item name> - 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 <spell> [duration] - Manually set proc (testing)')
|
||||
@@ -4951,6 +4940,101 @@ SlashCmdList["CLEVEROID"] = function(msg)
|
||||
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 <item name>")
|
||||
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 ===")
|
||||
|
||||
@@ -628,18 +628,63 @@ end
|
||||
-- PERFORMANCE: Quick scan for a single item by name (doesn't update full cache)
|
||||
-- Only scans until item is found, then stops.
|
||||
-- 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.
|
||||
function CleveRoids.FindItemQuick(text)
|
||||
if not text or text == "" then return nil end
|
||||
|
||||
-- First try fast cache lookup
|
||||
local cached = CleveRoids.GetItemFast(text)
|
||||
if cached then return cached end
|
||||
|
||||
local Items = CleveRoids.Items or {}
|
||||
local qid = tonumber(text)
|
||||
-- Only compute lowercase name if we're NOT searching by ID
|
||||
local qname = (not qid) and string_lower(text) or nil
|
||||
|
||||
-- Try cache first, but VALIDATE the cached location is still correct
|
||||
local cached = CleveRoids.GetItemFast(text)
|
||||
if cached then
|
||||
-- Validate: check if item is actually at the cached location
|
||||
if cached.inventoryID then
|
||||
local link = GetInventoryItemLink("player", cached.inventoryID)
|
||||
if link then
|
||||
local nm = GetNameFromLink(link)
|
||||
if nm and qname and string_lower(nm) == qname then
|
||||
cached._validated = true
|
||||
return cached -- Cache is valid
|
||||
elseif qid then
|
||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
||||
if itemID and tonumber(itemID) == qid then
|
||||
cached._validated = true
|
||||
return cached -- Cache is valid
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Cache is stale - item not at cached equipped slot, invalidate
|
||||
if cached.name then
|
||||
Items[cached.name] = nil
|
||||
Items[string_lower(cached.name)] = nil
|
||||
end
|
||||
elseif cached.bagID and cached.slot then
|
||||
local link = GetContainerItemLink(cached.bagID, cached.slot)
|
||||
if link then
|
||||
local nm = GetNameFromLink(link)
|
||||
if nm and qname and string_lower(nm) == qname then
|
||||
cached._validated = true
|
||||
return cached -- Cache is valid
|
||||
elseif qid then
|
||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
||||
if itemID and tonumber(itemID) == qid then
|
||||
cached._validated = true
|
||||
return cached -- Cache is valid
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Cache is stale - item not at cached bag slot, invalidate
|
||||
if cached.name then
|
||||
Items[cached.name] = nil
|
||||
Items[string_lower(cached.name)] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Cache miss or stale - do a fresh scan
|
||||
-- Quick scan equipped items first (only 19 slots)
|
||||
for inv = 1, 19 do
|
||||
local link = GetInventoryItemLink("player", inv)
|
||||
|
||||
+59
-3
@@ -342,10 +342,10 @@ function API.GetItemRecord(itemId)
|
||||
return API.itemStatsCache[itemId]
|
||||
end
|
||||
|
||||
-- Use native function if available
|
||||
-- Use native function if available (wrapped in pcall for safety)
|
||||
if GetItemStats then
|
||||
local stats = GetItemStats(itemId)
|
||||
if stats then
|
||||
local ok, stats = pcall(GetItemStats, itemId)
|
||||
if ok and stats then
|
||||
API.itemStatsCache[itemId] = stats
|
||||
return stats
|
||||
end
|
||||
@@ -406,6 +406,62 @@ function API.GetWeaponSpeed(itemId)
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Get item inventory type (equipment slot)
|
||||
-- Returns: inventoryType number, or nil if not equippable
|
||||
-- Common types: 0=Non-equip, 1=Head, 2=Neck, 3=Shoulder, 4=Shirt, 5=Chest,
|
||||
-- 6=Waist, 7=Legs, 8=Feet, 9=Wrist, 10=Hands, 11=Finger, 12=Trinket,
|
||||
-- 13=One-Hand, 14=Shield, 15=Ranged, 16=Back, 17=Two-Hand,
|
||||
-- 18=Bag, 20=Robe, 21=Main Hand, 22=Off Hand, 23=Holdable,
|
||||
-- 24=Ammo, 25=Thrown, 26=Ranged Right, 28=Relic
|
||||
function API.GetItemInventoryType(itemId)
|
||||
local ok, result = pcall(API.GetItemField, itemId, "inventoryType")
|
||||
if ok then return result end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Convert inventoryType to equipment slot ID(s)
|
||||
-- Returns slotId, altSlotId (for rings/trinkets)
|
||||
API.inventoryTypeToSlot = {
|
||||
[1] = 1, -- Head -> HeadSlot
|
||||
[2] = 2, -- Neck -> NeckSlot
|
||||
[3] = 3, -- Shoulder -> ShoulderSlot
|
||||
[4] = 4, -- Shirt -> ShirtSlot
|
||||
[5] = 5, -- Chest -> ChestSlot
|
||||
[6] = 6, -- Waist -> WaistSlot
|
||||
[7] = 7, -- Legs -> LegsSlot
|
||||
[8] = 8, -- Feet -> FeetSlot
|
||||
[9] = 9, -- Wrist -> WristSlot
|
||||
[10] = 10, -- Hands -> HandsSlot
|
||||
[11] = {11, 12}, -- Finger -> Finger0Slot or Finger1Slot
|
||||
[12] = {13, 14}, -- Trinket -> Trinket0Slot or Trinket1Slot
|
||||
[13] = 16, -- One-Hand -> MainHandSlot (can also go off-hand)
|
||||
[14] = 17, -- Shield -> SecondaryHandSlot
|
||||
[15] = 18, -- Ranged -> RangedSlot
|
||||
[16] = 15, -- Back -> BackSlot
|
||||
[17] = 16, -- Two-Hand -> MainHandSlot
|
||||
[20] = 5, -- Robe -> ChestSlot
|
||||
[21] = 16, -- Main Hand -> MainHandSlot
|
||||
[22] = 17, -- Off Hand -> SecondaryHandSlot
|
||||
[23] = 17, -- Holdable -> SecondaryHandSlot
|
||||
[24] = 0, -- Ammo -> AmmoSlot
|
||||
[25] = 18, -- Thrown -> RangedSlot
|
||||
[26] = 18, -- Ranged Right -> RangedSlot
|
||||
[28] = 18, -- Relic -> RangedSlot
|
||||
}
|
||||
|
||||
-- Get equipment slot ID for an item
|
||||
-- Returns slotId, altSlotId for items that can go in multiple slots
|
||||
function API.GetItemEquipSlot(itemId)
|
||||
local ok, invType = pcall(API.GetItemInventoryType, itemId)
|
||||
if not ok or not invType or invType == 0 then return nil end
|
||||
|
||||
local slotInfo = API.inventoryTypeToSlot[invType]
|
||||
if type(slotInfo) == "table" then
|
||||
return slotInfo[1], slotInfo[2] -- Primary and alt slot
|
||||
end
|
||||
return slotInfo, nil
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- UNIT DATA API (GetUnitData / GetUnitField)
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user