mirror of
https://github.com/brues-code/SuperCleveRoidMacros.git
synced 2026-09-16 03:38:00 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d060b04908 | |||
| 6ac69501f5 | |||
| 782c685fba | |||
| 97e898f14c | |||
| b84835a567 | |||
| 40cf2c8010 | |||
| a5a0b9c00a | |||
| 6e57ff01dc | |||
| 9cd79d682d | |||
| f54eda5ae3 | |||
| 653f8db3d7 | |||
| 86fb0254e6 | |||
| fc640abb07 | |||
| 3384765faa | |||
| ceaf7c2c43 |
+41
-69
@@ -273,24 +273,17 @@ local function BuildEquipmentCache()
|
||||
end
|
||||
end
|
||||
|
||||
-- Fallback: manual slot enumeration
|
||||
-- Fallback: manual slot enumeration via ClassicAPI (id + decorated name),
|
||||
-- no link string built. C_Item.GetItemName carries random-suffix decoration
|
||||
-- and falls back to the base name internally, so it replaces the old
|
||||
-- bracket-name / GetItemInfo two-step in a single call.
|
||||
for slot = 1, 19 do
|
||||
local link = GetInventoryItemLink("player", slot)
|
||||
if link then
|
||||
local _, _, id = string_find(link, "item:(%d+)")
|
||||
local _, _, nameInBrackets = string_find(link, "%[(.+)%]")
|
||||
|
||||
if id then
|
||||
_equippedItemIDs[slot] = tonumber(id)
|
||||
end
|
||||
if nameInBrackets then
|
||||
_equippedItemNames[slot] = string_lower(nameInBrackets)
|
||||
elseif id then
|
||||
-- Fallback: resolve via GetItemInfo
|
||||
local itemName = GetItemInfo(tonumber(id))
|
||||
if itemName then
|
||||
_equippedItemNames[slot] = string_lower(itemName)
|
||||
end
|
||||
local id = GetInventoryItemID("player", slot)
|
||||
if id then
|
||||
_equippedItemIDs[slot] = id
|
||||
local name = C_Item.GetItemName({ equipmentSlotIndex = slot })
|
||||
if name then
|
||||
_equippedItemNames[slot] = string_lower(name)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -350,8 +343,7 @@ function CleveRoids.FindItemLocation(item)
|
||||
if numericItem then
|
||||
-- Check if it's an equipment slot (1-19)
|
||||
if numericItem >= 1 and numericItem <= 19 then
|
||||
local link = GetInventoryItemLink("player", numericItem)
|
||||
if link then
|
||||
if GetInventoryItemID("player", numericItem) then
|
||||
return { type = "inventory", inventoryID = numericItem }
|
||||
end
|
||||
return nil
|
||||
@@ -3466,11 +3458,8 @@ function CleveRoids.ValidateCooldown(args, ignoreGCD)
|
||||
-- If this is a numeric slot (1-19), resolve to the equipped item's name
|
||||
local slotNum = tonumber(name)
|
||||
if slotNum and slotNum >= 1 and slotNum <= 19 then
|
||||
local link = GetInventoryItemLink("player", slotNum)
|
||||
if link then
|
||||
local _, _, itemName = string.find(link, "%[(.+)%]")
|
||||
if itemName then name = itemName end
|
||||
end
|
||||
local itemName = C_Item.GetItemName({ equipmentSlotIndex = slotNum })
|
||||
if itemName then name = itemName end
|
||||
end
|
||||
args = {name = name}
|
||||
else
|
||||
@@ -3481,11 +3470,8 @@ function CleveRoids.ValidateCooldown(args, ignoreGCD)
|
||||
-- If this is a numeric slot (1-19), resolve to the equipped item's name
|
||||
local slotNum = tonumber(name)
|
||||
if slotNum and slotNum >= 1 and slotNum <= 19 then
|
||||
local link = GetInventoryItemLink("player", slotNum)
|
||||
if link then
|
||||
local _, _, itemName = string.find(link, "%[(.+)%]")
|
||||
if itemName then name = itemName end
|
||||
end
|
||||
local itemName = C_Item.GetItemName({ equipmentSlotIndex = slotNum })
|
||||
if itemName then name = itemName end
|
||||
end
|
||||
args.name = name
|
||||
else
|
||||
@@ -4869,13 +4855,11 @@ function CleveRoids.HasItem(item)
|
||||
if type(item) == "string" and item ~= "" then
|
||||
local itemLower = string.lower(item)
|
||||
|
||||
-- Check equipped slots for substring match
|
||||
for slot = 0, 19 do
|
||||
local link = GetInventoryItemLink("player", slot)
|
||||
if link then
|
||||
if string.find(string.lower(link), itemLower, 1, true) then
|
||||
return true
|
||||
end
|
||||
-- Check equipped slots for substring match (decorated name, no link build)
|
||||
for slot = 1, 19 do
|
||||
local name = C_Item.GetItemName({ equipmentSlotIndex = slot })
|
||||
if name and string.find(string.lower(name), itemLower, 1, true) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4884,11 +4868,9 @@ function CleveRoids.HasItem(item)
|
||||
local size = GetContainerNumSlots(bag)
|
||||
if size and size > 0 then
|
||||
for slotIndex = 1, size do
|
||||
local link = GetContainerItemLink(bag, slotIndex)
|
||||
if link then
|
||||
if string.find(string.lower(link), itemLower, 1, true) then
|
||||
return true
|
||||
end
|
||||
local name = C_Item.GetItemName({ bagID = bag, slotIndex = slotIndex })
|
||||
if name and string.find(string.lower(name), itemLower, 1, true) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4933,14 +4915,12 @@ function CleveRoids.GetItemCooldown(item)
|
||||
local itemLower = string.lower(item)
|
||||
local start, dur, en
|
||||
|
||||
-- Check equipped slots for substring match
|
||||
for slot = 0, 19 do
|
||||
local link = GetInventoryItemLink("player", slot)
|
||||
if link then
|
||||
if string.find(string.lower(link), itemLower, 1, true) then
|
||||
start, dur, en = GetInventoryItemCooldown("player", slot)
|
||||
return _norm(start, dur, en)
|
||||
end
|
||||
-- Check equipped slots for substring match (decorated name, no link build)
|
||||
for slot = 1, 19 do
|
||||
local name = C_Item.GetItemName({ equipmentSlotIndex = slot })
|
||||
if name and string.find(string.lower(name), itemLower, 1, true) then
|
||||
start, dur, en = GetInventoryItemCooldown("player", slot)
|
||||
return _norm(start, dur, en)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4949,12 +4929,10 @@ function CleveRoids.GetItemCooldown(item)
|
||||
local size = GetContainerNumSlots(bag)
|
||||
if size and size > 0 then
|
||||
for slotIndex = 1, size do
|
||||
local link = GetContainerItemLink(bag, slotIndex)
|
||||
if link then
|
||||
if string.find(string.lower(link), itemLower, 1, true) then
|
||||
start, dur, en = GetContainerItemCooldown(bag, slotIndex)
|
||||
return _norm(start, dur, en)
|
||||
end
|
||||
local name = C_Item.GetItemName({ bagID = bag, slotIndex = slotIndex })
|
||||
if name and string.find(string.lower(name), itemLower, 1, true) then
|
||||
start, dur, en = GetContainerItemCooldown(bag, slotIndex)
|
||||
return _norm(start, dur, en)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -5887,13 +5865,10 @@ CleveRoids.Keywords = {
|
||||
local itemName = name
|
||||
local slotNum = tonumber(name)
|
||||
if slotNum and slotNum >= 1 and slotNum <= 19 then
|
||||
-- Resolve slot number to item name
|
||||
local link = GetInventoryItemLink("player", slotNum)
|
||||
if link then
|
||||
local _, _, extractedName = string.find(link, "%[(.+)%]")
|
||||
if extractedName then
|
||||
itemName = extractedName
|
||||
end
|
||||
-- Resolve slot number to item name (decorated, no link build)
|
||||
local extractedName = C_Item.GetItemName({ equipmentSlotIndex = slotNum })
|
||||
if extractedName then
|
||||
itemName = extractedName
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5925,13 +5900,10 @@ CleveRoids.Keywords = {
|
||||
local itemName = name
|
||||
local slotNum = tonumber(name)
|
||||
if slotNum and slotNum >= 1 and slotNum <= 19 then
|
||||
-- Resolve slot number to item name
|
||||
local link = GetInventoryItemLink("player", slotNum)
|
||||
if link then
|
||||
local _, _, extractedName = string.find(link, "%[(.+)%]")
|
||||
if extractedName then
|
||||
itemName = extractedName
|
||||
end
|
||||
-- Resolve slot number to item name (decorated, no link build)
|
||||
local extractedName = C_Item.GetItemName({ equipmentSlotIndex = slotNum })
|
||||
if extractedName then
|
||||
itemName = extractedName
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -67,6 +67,10 @@ SLASH_UNSHIFT1 = "/unshift"
|
||||
|
||||
SlashCmdList.UNSHIFT = CleveRoids.DoUnshift
|
||||
|
||||
SLASH_CANCELFORM1 = "/cancelform"
|
||||
|
||||
SlashCmdList.CANCELFORM = CleveRoids.DoCancelForm
|
||||
|
||||
SLASH_UNQUEUE1 = "/unqueue"
|
||||
SlashCmdList.UNQUEUE = SpellStopCasting
|
||||
|
||||
|
||||
@@ -46,10 +46,8 @@ CleveRoids.spellNameCache = {}
|
||||
local GetTime = GetTime
|
||||
local UnitExists = UnitExists
|
||||
local UnitAffectingCombat = UnitAffectingCombat
|
||||
local GetContainerItemLink = GetContainerItemLink
|
||||
local GetContainerItemInfo = GetContainerItemInfo
|
||||
local GetContainerNumSlots = GetContainerNumSlots
|
||||
local GetInventoryItemLink = GetInventoryItemLink
|
||||
local GetItemInfo = GetItemInfo
|
||||
local PickupContainerItem = PickupContainerItem
|
||||
local PickupInventoryItem = PickupInventoryItem
|
||||
@@ -63,7 +61,6 @@ local ipairs = ipairs
|
||||
local type = type
|
||||
local tonumber = tonumber
|
||||
local tostring = tostring
|
||||
local string_find = string.find
|
||||
local string_lower = string.lower
|
||||
local string_gsub = string.gsub
|
||||
local table_insert = table.insert
|
||||
@@ -577,118 +574,34 @@ function CleveRoids.QueueActionUpdate()
|
||||
end
|
||||
end
|
||||
|
||||
local function _StripColor(s)
|
||||
if not s then return s end
|
||||
if string.sub(s,1,2) == "|c" then return string.sub(s,11,-3) end
|
||||
return s
|
||||
end
|
||||
-- Count of `itemId` in *bags only* (not bank), matched by itemID throughout --
|
||||
-- locale-independent, no hardcoded item names.
|
||||
-- PERFORMANCE: uses the CleveRoids.Items cache (itemId -> name -> data) for an
|
||||
-- O(1) lookup when available, falling back to a bag scan on a cache miss.
|
||||
function CleveRoids.GetReagentCount(itemId)
|
||||
if not itemId then return 0 end
|
||||
|
||||
local _ReagentBySpell = {
|
||||
["Vanish"] = "Flash Powder", -- 5140
|
||||
["Blind"] = "Blinding Powder", -- 5530
|
||||
}
|
||||
|
||||
-- Expose for use in Generic.lua (IndexSpells fallback)
|
||||
CleveRoids.ReagentBySpell = _ReagentBySpell
|
||||
|
||||
-- Minimal map for rogue reagents; extend as needed
|
||||
local _ReagentIdByName = {
|
||||
["Flash Powder"] = 5140, -- Vanish
|
||||
["Blinding Powder"] = 5530, -- Blind
|
||||
}
|
||||
|
||||
-- Lazy bag-scan tooltip (only if we need to scan a bag slot by name)
|
||||
local function CRM_GetBagScanTip()
|
||||
local tip = _G.CleveRoidsBagScanTip
|
||||
if tip then return tip end
|
||||
local ok, created = pcall(CreateFrame, "GameTooltip", "CleveRoidsBagScanTip", UIParent, "GameTooltipTemplate")
|
||||
if ok and created then
|
||||
tip = created
|
||||
else
|
||||
tip = CreateFrame("GameTooltip", "CleveRoidsBagScanTip", UIParent)
|
||||
local L1 = tip:CreateFontString("$parentTextLeft1", nil, "GameTooltipText")
|
||||
local R1 = tip:CreateFontString("$parentTextRight1", nil, "GameTooltipText")
|
||||
tip:AddFontStrings(L1, R1)
|
||||
for i=2,10 do
|
||||
tip:CreateFontString("$parentTextLeft"..i, nil, "GameTooltipText")
|
||||
tip:CreateFontString("$parentTextRight"..i, nil, "GameTooltipText")
|
||||
end
|
||||
end
|
||||
tip:SetOwner(WorldFrame, "ANCHOR_NONE")
|
||||
_G.CleveRoidsBagScanTip = tip
|
||||
return tip
|
||||
end
|
||||
|
||||
-- Return total in *bags only* (not bank), by id when possible; fallback to name
|
||||
-- PERFORMANCE: Uses CleveRoids.Items cache for O(1) lookup when available
|
||||
function CleveRoids.GetReagentCount(reagentName)
|
||||
if not reagentName or reagentName == "" then return 0 end
|
||||
|
||||
-- Fast path: check cache first
|
||||
local Items = CleveRoids.Items
|
||||
if Items then
|
||||
local wantId = _ReagentIdByName[reagentName]
|
||||
|
||||
-- Try by ID first if we have a mapping
|
||||
if wantId then
|
||||
local itemName = Items[wantId]
|
||||
if itemName then
|
||||
local itemData = Items[itemName]
|
||||
if itemData and itemData.count and not itemData.inventoryID then
|
||||
-- Item is in bags (not equipped), return cached count
|
||||
return itemData.count
|
||||
end
|
||||
local itemName = Items[itemId]
|
||||
if itemName then
|
||||
local itemData = Items[itemName]
|
||||
if itemData and itemData.count and not itemData.inventoryID then
|
||||
-- Item is in bags (not equipped), return cached count
|
||||
return itemData.count
|
||||
end
|
||||
end
|
||||
|
||||
-- Try by name
|
||||
local itemData = Items[reagentName]
|
||||
if type(itemData) == "string" then
|
||||
itemData = Items[itemData] -- Resolve indirection
|
||||
end
|
||||
if itemData and type(itemData) == "table" and itemData.count and not itemData.inventoryID then
|
||||
return itemData.count
|
||||
end
|
||||
|
||||
-- Try lowercase
|
||||
local lowerName = string.lower(reagentName)
|
||||
local resolved = Items[lowerName]
|
||||
if type(resolved) == "string" then
|
||||
itemData = Items[resolved]
|
||||
elseif type(resolved) == "table" then
|
||||
itemData = resolved
|
||||
end
|
||||
if itemData and type(itemData) == "table" and itemData.count and not itemData.inventoryID then
|
||||
return itemData.count
|
||||
end
|
||||
end
|
||||
|
||||
-- Slow path fallback: full bag scan (only when cache miss)
|
||||
local wantId = _ReagentIdByName[reagentName]
|
||||
-- Slow path: scan bags by itemID (only on a cache miss)
|
||||
local total = 0
|
||||
|
||||
for bag = 0, 4 do
|
||||
local slots = GetContainerNumSlots(bag) or 0
|
||||
for slot = 1, slots do
|
||||
local _, count = GetContainerItemInfo(bag, slot)
|
||||
count = count or 0
|
||||
local link = GetContainerItemLink and GetContainerItemLink(bag, slot)
|
||||
if link then
|
||||
local _, _, idstr = string.find(link, "item:(%d+)")
|
||||
local id = idstr and tonumber(idstr) or nil
|
||||
if (wantId and id == wantId) or (not wantId and string.find(link, "%["..reagentName.."%]")) then
|
||||
total = total + count
|
||||
end
|
||||
else
|
||||
-- Fallback: scan bag slot tooltip for the name (expensive, only when no link)
|
||||
local tip = CRM_GetBagScanTip()
|
||||
tip:ClearLines()
|
||||
tip:SetBagItem(bag, slot)
|
||||
local left1 = _G[tip:GetName().."TextLeft1"]
|
||||
local name = left1 and left1:GetText()
|
||||
if name and name == reagentName then
|
||||
total = total + count
|
||||
end
|
||||
-- Base itemID straight off the slot (no link string, no regex).
|
||||
if C_Container.GetContainerItemID(bag, slot) == itemId then
|
||||
local _, count = GetContainerItemInfo(bag, slot)
|
||||
total = total + (count or 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -701,102 +614,55 @@ end
|
||||
function CleveRoids.GetLiveItemCount(itemName)
|
||||
if not itemName or itemName == "" then return 0 end
|
||||
-- Escape Lua pattern special chars in item name to avoid crashes
|
||||
local escaped = string.gsub(itemName, "([%(%)%.%%%+%-%*%?%[%]%^%$])", "%%%1")
|
||||
local pattern = "%[" .. escaped .. "%]"
|
||||
local lowerPattern = string.lower(pattern)
|
||||
local wantLower = string.lower(itemName)
|
||||
local total = 0
|
||||
for bag = 0, 4 do
|
||||
local slots = GetContainerNumSlots(bag) or 0
|
||||
for slot = 1, slots do
|
||||
local link = GetContainerItemLink and GetContainerItemLink(bag, slot)
|
||||
if link and string.find(string.lower(link), lowerPattern) then
|
||||
local _, count = GetContainerItemInfo(bag, slot)
|
||||
total = total + (count or 0)
|
||||
local id = C_Container.GetContainerItemID(bag, slot)
|
||||
if id then
|
||||
local name = C_Item.GetItemNameByID(id)
|
||||
if name and string.lower(name) == wantLower then
|
||||
local _, count = GetContainerItemInfo(bag, slot)
|
||||
total = total + (count or 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return total
|
||||
end
|
||||
|
||||
function CleveRoids.GetSpellCost(spellSlot, bookType)
|
||||
-- Fast path: existing fixed-slot read
|
||||
CleveRoids.Frame:SetOwner(WorldFrame, "ANCHOR_NONE")
|
||||
CleveRoids.Frame:SetSpell(spellSlot, bookType)
|
||||
|
||||
local cost, reagent
|
||||
local costText = CleveRoids.Frame.costFontString:GetText()
|
||||
if costText then
|
||||
_, _, cost = string.find(costText, "^(%d+)%s+[^yYsS]")
|
||||
-- Power cost and (first) reagent for a spellbook slot, read from Spell.dbc via
|
||||
-- ClassicAPI -- no GameTooltip scan, no locale-dependent string parsing. Pass
|
||||
-- spellId to skip the slot->id resolution when the caller already has it.
|
||||
-- cost: C_Spell.GetSpellPowerCost returns the *effective* (talent-modified)
|
||||
-- cost the engine actually charges -- matches the old tooltip value,
|
||||
-- unlike GetSpellInfo().cost which is base-only.
|
||||
-- reagent: C_Spell.GetSpellReagents returns { {itemID, count}, ... }; take the
|
||||
-- first reagent's itemID (Spell.dbc covers reagent spells like Vanish).
|
||||
-- Returns cost, reagentName (localized, for display), reagentId (for counting).
|
||||
function CleveRoids.GetSpellCost(spellSlot, bookType, spellId)
|
||||
if not spellId then
|
||||
spellId = select(10, GetSpellInfo(spellSlot, bookType))
|
||||
end
|
||||
|
||||
local reagentText = CleveRoids.Frame.reagentFontString:GetText()
|
||||
if reagentText then
|
||||
_, _, reagent = string.find(reagentText, "^Reagents?%s*:%s*(.*)")
|
||||
end
|
||||
reagent = _StripColor(reagent)
|
||||
if reagent == "" then reagent = nil end
|
||||
local cost = 0
|
||||
local reagent, reagentId
|
||||
|
||||
-- Fallback: scan all lines on a named tooltip (handles Vanish layout)
|
||||
if not reagent or not cost then
|
||||
local tip = CleveRoidsTooltipScan
|
||||
if not tip then
|
||||
-- belt & suspenders: create if somehow missing
|
||||
local ok,_ = pcall(CreateFrame, "GameTooltip", "CleveRoidsTooltipScan", UIParent, "GameTooltipTemplate")
|
||||
if not ok or not CleveRoidsTooltipScan then
|
||||
CleveRoidsTooltipScan = CreateFrame("GameTooltip", "CleveRoidsTooltipScan", UIParent)
|
||||
local L1 = CleveRoidsTooltipScan:CreateFontString("$parentTextLeft1", nil, "GameTooltipText")
|
||||
local R1 = CleveRoidsTooltipScan:CreateFontString("$parentTextRight1", nil, "GameTooltipText")
|
||||
CleveRoidsTooltipScan:AddFontStrings(L1, R1)
|
||||
for i=2,32 do
|
||||
CleveRoidsTooltipScan:CreateFontString("$parentTextLeft"..i, nil, "GameTooltipText")
|
||||
CleveRoidsTooltipScan:CreateFontString("$parentTextRight"..i, nil, "GameTooltipText")
|
||||
end
|
||||
end
|
||||
CleveRoidsTooltipScan:SetOwner(WorldFrame, "ANCHOR_NONE")
|
||||
tip = CleveRoidsTooltipScan
|
||||
if spellId then
|
||||
local costs = C_Spell.GetSpellPowerCost(spellId)
|
||||
if costs and costs[1] and costs[1].cost then
|
||||
cost = costs[1].cost
|
||||
end
|
||||
|
||||
tip:ClearLines()
|
||||
tip:SetOwner(WorldFrame, "ANCHOR_NONE")
|
||||
tip:SetSpell(spellSlot, bookType)
|
||||
|
||||
local base = tip:GetName() or "CleveRoidsTooltipScan"
|
||||
local maxLines = (tip.NumLines and tip:NumLines()) or 32
|
||||
|
||||
for i = 1, maxLines do
|
||||
local L = _G[base.."TextLeft"..i]
|
||||
local R = _G[base.."TextRight"..i]
|
||||
local lt = L and L:GetText() or ""
|
||||
local rt = R and R:GetText() or ""
|
||||
|
||||
if not reagent then
|
||||
if string.find(lt, "^[Rr]eagents?%s*:") then
|
||||
reagent = _StripColor((rt ~= "" and rt) or (string.gsub(lt, "^[Rr]eagents?%s*:%s*", "")))
|
||||
elseif string.find(rt, "^[Rr]eagents?%s*:") then
|
||||
reagent = _StripColor((lt ~= "" and lt) or (string.gsub(rt, "^[Rr]eagents?%s*:%s*", "")))
|
||||
end
|
||||
if reagent == "" then reagent = nil end
|
||||
end
|
||||
|
||||
if not cost and rt ~= "" then
|
||||
local _, _, num = string.find(rt, "^(%d+)%s+(Mana|Energy|Rage|Focus)")
|
||||
if num then cost = tonumber(num) end
|
||||
end
|
||||
|
||||
if reagent and cost then break end
|
||||
local reagents = C_Spell.GetSpellReagents(spellId)
|
||||
if reagents and reagents[1] and reagents[1].itemID then
|
||||
reagentId = reagents[1].itemID
|
||||
reagent = C_Item.GetItemNameByID(reagentId)
|
||||
end
|
||||
end
|
||||
|
||||
if not reagent or reagent == "" then
|
||||
reagent = nil
|
||||
local name = GetSpellName(spellSlot, bookType)
|
||||
if name then
|
||||
name = CleveRoids.StripRank(name)
|
||||
reagent = _ReagentBySpell[name]
|
||||
end
|
||||
end
|
||||
|
||||
return (cost and tonumber(cost) or 0), (reagent and reagent ~= "" and tostring(reagent) or nil)
|
||||
return cost, (reagent and reagent ~= "" and tostring(reagent) or nil), reagentId
|
||||
end
|
||||
|
||||
function CleveRoids.GetProxyActionSlot(slot)
|
||||
@@ -2689,11 +2555,13 @@ function CleveRoids.DoWithConditionals(msg, hook, fixEmptyTargetFunc, targetBefo
|
||||
CastSpellByName(castMsg)
|
||||
end
|
||||
else
|
||||
-- For other actions like UseContainerItem etc.
|
||||
-- For other actions like item use etc. Pass the resolved unit token so
|
||||
-- item-use can target it directly (DoUse -> C_Item.UseItemByName); action
|
||||
-- closures that only take (msg) simply ignore the extra arg.
|
||||
if CleveRoids.equipDebugLog then
|
||||
CleveRoids.Print("|cff00ff00[EquipLog] Calling action('" .. tostring(msg) .. "')|r")
|
||||
end
|
||||
action(msg)
|
||||
action(msg, conditionals.target)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3100,15 +2968,17 @@ end
|
||||
-- PERFORMANCE: Module-level action to avoid closure allocation per call
|
||||
local function _startAttackAction()
|
||||
if not UnitExists("target") or CleveRoids.IsUnitDead("target") then TargetNearestEnemy() end
|
||||
-- Check both event-based flag AND action bar state for reliable detection
|
||||
-- Ground truth is the real action-bar state, NOT the cached autoAttack flag.
|
||||
-- The flag can drift stale-true (e.g. set optimistically after AttackTarget below,
|
||||
-- or a target dying without PLAYER_LEAVE_COMBAT firing), which would make us wrongly
|
||||
-- believe we're already swinging and skip the attack. Use the ORIGINAL API here:
|
||||
-- the overridden global IsCurrentAction just echoes the cached flag for the attack
|
||||
-- slot, so it can't detect drift. Fall back to the flag only if the slot is unknown.
|
||||
local isAttacking = CleveRoids.CurrentSpell.autoAttack
|
||||
if not isAttacking then
|
||||
-- Fallback: check action bar state via IsCurrentAction
|
||||
local slot = CleveRoids.GetProxyActionSlot(CleveRoids.Localized.Attack)
|
||||
if slot and IsCurrentAction(slot) then
|
||||
CleveRoids.CurrentSpell.autoAttack = true
|
||||
isAttacking = true
|
||||
end
|
||||
local slot = CleveRoids.GetProxyActionSlot(CleveRoids.Localized.Attack)
|
||||
if slot then
|
||||
isAttacking = CleveRoids.Hooks.IsCurrentAction(slot) and true or false
|
||||
CleveRoids.CurrentSpell.autoAttack = isAttacking
|
||||
end
|
||||
if not isAttacking and not CleveRoids.CurrentSpell.autoAttackLock and UnitExists("target") and UnitCanAttack("player", "target") then
|
||||
CleveRoids.CurrentSpell.autoAttackLock = true
|
||||
@@ -3194,6 +3064,36 @@ function CleveRoids.DoConditionalClearTarget(msg)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Resolve an EQUIPPED inventory slot (1-19) holding the item, or nil.
|
||||
-- C_Item.UseItemByName only searches bags, so equipped-only items (trinkets,
|
||||
-- weapons) must be fired via their slot. Prefer nampower's fast lookup; fall
|
||||
-- back to a manual scan for clients without FindPlayerItemSlot (< v2.18).
|
||||
local function FindEquippedItemSlot(msg, itemId)
|
||||
local API = CleveRoids.NampowerAPI
|
||||
if type(API) == "table" and API.FindItemFast then
|
||||
local info = API.FindItemFast(itemId or msg)
|
||||
if info and info.inventoryID then return info.inventoryID end
|
||||
-- Found only in bags (or not at all) -> not an equipped-only item.
|
||||
if info then return nil end
|
||||
end
|
||||
-- Fallback scan of equipped slots by ID or name, read straight from each
|
||||
-- slot via ClassicAPI (GetInventoryItemID / C_Item.GetItemNameByID) -- no
|
||||
-- link string built, no "item:"/"|h[..]|h" parse. Slots are 1-19.
|
||||
local wantLower = (not itemId) and string_lower(msg) or nil
|
||||
for slot = 1, 19 do
|
||||
local id = GetInventoryItemID("player", slot)
|
||||
if id then
|
||||
if itemId then
|
||||
if id == itemId then return slot end
|
||||
else
|
||||
local nm = C_Item.GetItemNameByID(id)
|
||||
if nm and string_lower(nm) == wantLower then return slot end
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Attempts to use or equip an item by a set of conditionals
|
||||
-- Also checks if a condition is a spell so that you can mix item and spell use
|
||||
-- msg: The raw message intercepted from a /use or /equip command
|
||||
@@ -3205,184 +3105,42 @@ function CleveRoids.DoUse(msg)
|
||||
|
||||
local handled = false
|
||||
|
||||
local action = function(msg)
|
||||
local action = function(msg, unit)
|
||||
-- Defensive: make sure we are not in "split stack" mode and nothing is on the cursor
|
||||
if type(CloseStackSplitFrame) == "function" then CloseStackSplitFrame() end
|
||||
if CursorHasItem and CursorHasItem() then ClearCursor() end
|
||||
|
||||
-- Try to interpret the message as a direct inventory slot ID first.
|
||||
-- Only pass a cast target when it actually exists, so a stale @unit doesn't
|
||||
-- waste a consumable. Self-use items (potions/food/hearth) ignore it anyway.
|
||||
local useUnit = (unit and UnitExists(unit)) and unit or nil
|
||||
|
||||
-- Direct equipped inventory slot ID (1-19): use it in place.
|
||||
local slotId = tonumber(msg)
|
||||
if slotId and slotId >= 1 and slotId <= 19 then -- Character slots are 1-19
|
||||
if slotId and slotId >= 1 and slotId <= 19 then
|
||||
ClearCursor() -- extra safety before using equipped items
|
||||
UseInventoryItem(slotId)
|
||||
return
|
||||
end
|
||||
|
||||
-- Try to interpret as item ID (numbers > 19)
|
||||
-- v2.18+: Use FindPlayerItemSlot directly for item IDs (no name resolution needed)
|
||||
if slotId and slotId > 19 then
|
||||
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
|
||||
itemName = API.GetItemName(slotId)
|
||||
end
|
||||
-- Fall back to GetItemInfo
|
||||
if not itemName and GetItemInfo then
|
||||
itemName = GetItemInfo(slotId)
|
||||
end
|
||||
if itemName then
|
||||
if CleveRoids.equipDebugLog then
|
||||
CleveRoids.Print("|cff888888[UseLog] Resolved item ID " .. slotId .. " to '" .. itemName .. "'|r")
|
||||
end
|
||||
msg = itemName -- Replace ID with name for subsequent lookups
|
||||
else
|
||||
if CleveRoids.equipDebugLog then
|
||||
CleveRoids.Print("|cffff8800[UseLog] Could not resolve item ID " .. slotId .. " - item not in cache|r")
|
||||
end
|
||||
-- Item not in client cache - can't resolve without seeing it first
|
||||
return
|
||||
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)
|
||||
-- IMPORTANT: Validate cache hits to prevent stale data during combat
|
||||
-- (IndexItems() is skipped during combat, so cache may have old bag/slot locations)
|
||||
local location = CleveRoids.FindItemLocation(msg)
|
||||
if location then
|
||||
local cacheValid = false
|
||||
local qname = string_lower(msg)
|
||||
|
||||
if location.type == "inventory" then
|
||||
-- Validate: check if this slot actually contains the item we want
|
||||
local link = GetInventoryItemLink("player", location.inventoryID)
|
||||
if link then
|
||||
local _, _, nm = string_find(link, "|h%[(.-)%]|h")
|
||||
if nm and string_lower(nm) == qname then
|
||||
cacheValid = true
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Validate: check if this bag slot actually contains the item we want
|
||||
local link = GetContainerItemLink(location.bag, location.slot)
|
||||
if link then
|
||||
local _, _, nm = string_find(link, "|h%[(.-)%]|h")
|
||||
if nm and string_lower(nm) == qname then
|
||||
cacheValid = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if cacheValid then
|
||||
ClearCursor()
|
||||
if location.type == "inventory" then
|
||||
if CleveRoids.equipDebugLog then
|
||||
CleveRoids.Print("|cff888888[UseLog] /use " .. msg .. " via UseInventoryItem(" .. location.inventoryID .. ") [cached]|r")
|
||||
end
|
||||
UseInventoryItem(location.inventoryID)
|
||||
else
|
||||
if CleveRoids.equipDebugLog then
|
||||
CleveRoids.Print("|cff888888[UseLog] /use " .. msg .. " via UseContainerItem(" .. location.bag .. "," .. location.slot .. ") [cached]|r")
|
||||
end
|
||||
UseContainerItem(location.bag, location.slot)
|
||||
end
|
||||
return
|
||||
elseif CleveRoids.equipDebugLog then
|
||||
CleveRoids.Print("|cff888888[UseLog] /use " .. msg .. " - cache STALE, falling back to scan|r")
|
||||
end
|
||||
end
|
||||
|
||||
-- Slow path fallback: full scan for substring matches or cache miss
|
||||
local qname = string_lower(msg)
|
||||
|
||||
-- Search equipped inventory slots first (for trinkets, etc.)
|
||||
for slot = 0, 19 do
|
||||
local link = GetInventoryItemLink("player", slot)
|
||||
if link then
|
||||
local _, _, nm = string_find(link, "|h%[(.-)%]|h")
|
||||
if nm and string_lower(nm) == qname then
|
||||
if CleveRoids.equipDebugLog then
|
||||
CleveRoids.Print("|cff888888[UseLog] /use " .. msg .. " via UseInventoryItem(" .. slot .. ")|r")
|
||||
end
|
||||
ClearCursor()
|
||||
UseInventoryItem(slot)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Then search bags
|
||||
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 _, _, nm = string_find(link, "|h%[(.-)%]|h")
|
||||
if nm and string_lower(nm) == qname then
|
||||
if CleveRoids.equipDebugLog then
|
||||
CleveRoids.Print("|cff888888[UseLog] /use " .. msg .. " via UseContainerItem(" .. bag .. "," .. bagSlot .. ")|r")
|
||||
end
|
||||
ClearCursor()
|
||||
UseContainerItem(bag, bagSlot)
|
||||
return
|
||||
end
|
||||
end
|
||||
-- Equipped items (trinkets, weapons) live outside bags, so C_Item.UseItemByName
|
||||
-- can't reach them. Fire an equipped match through its slot first.
|
||||
local invSlot = FindEquippedItemSlot(msg, slotId)
|
||||
if invSlot then
|
||||
ClearCursor()
|
||||
if CleveRoids.equipDebugLog then
|
||||
CleveRoids.Print("|cff888888[UseLog] /use " .. msg .. " via UseInventoryItem(" .. invSlot .. ")|r")
|
||||
end
|
||||
UseInventoryItem(invSlot)
|
||||
return
|
||||
end
|
||||
|
||||
-- Bag path: one ClassicAPI call finds the item in bags and dispatches by
|
||||
-- type (potion/food/scroll/on-use), honoring `useUnit` for targeted-spell
|
||||
-- items. itemIDs pass as numbers; names/links pass through unchanged.
|
||||
if CleveRoids.equipDebugLog then
|
||||
CleveRoids.Print("|cff888888[UseLog] /use " .. msg .. " - not found in equipped slots or bags|r")
|
||||
CleveRoids.Print("|cff888888[UseLog] /use " .. msg .. " via C_Item.UseItemByName(" .. tostring(slotId or msg) .. ", " .. tostring(useUnit) .. ")|r")
|
||||
end
|
||||
C_Item.UseItemByName(slotId or msg, useUnit)
|
||||
end
|
||||
|
||||
-- PERFORMANCE: Use numeric iteration to avoid pairs() iterator allocation
|
||||
@@ -3408,12 +3166,9 @@ local function FindItemInBagsByName(itemName)
|
||||
for bag = 0, 4 do
|
||||
local numSlots = GetContainerNumSlots(bag) or 0
|
||||
for slot = 1, numSlots do
|
||||
local link = GetContainerItemLink(bag, slot)
|
||||
if link then
|
||||
local _, _, name = string_find(link, "|h%[(.-)%]|h")
|
||||
if name and string_lower(name) == lowerName then
|
||||
return bag, slot
|
||||
end
|
||||
local name = C_Item.GetItemName({ bagID = bag, slotIndex = slot })
|
||||
if name and string_lower(name) == lowerName then
|
||||
return bag, slot
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3550,12 +3305,7 @@ function CleveRoids.EquipBagItem(msg, slotOrOffhand)
|
||||
end
|
||||
|
||||
-- Note what's currently in the target slot so we can invalidate its cache
|
||||
local oldSlotLink = GetInventoryItemLink("player", invslot)
|
||||
local oldSlotName = nil
|
||||
if oldSlotLink then
|
||||
local _, _, name = string_find(oldSlotLink, "|h%[(.-)%]|h")
|
||||
oldSlotName = name
|
||||
end
|
||||
local oldSlotName = C_Item.GetItemName({ equipmentSlotIndex = invslot })
|
||||
|
||||
-- Helper to invalidate displaced item's cache
|
||||
local function InvalidateDisplacedItem()
|
||||
@@ -3572,10 +3322,9 @@ function CleveRoids.EquipBagItem(msg, slotOrOffhand)
|
||||
local pairedSlots = {[13] = 14, [14] = 13, [16] = 17, [17] = 16, [11] = 12, [12] = 11}
|
||||
local checkSlot = pairedSlots[invslot]
|
||||
if checkSlot then
|
||||
local link = GetInventoryItemLink("player", checkSlot)
|
||||
if link then
|
||||
local _, _, slotItemName = string_find(link, "|h%[(.-)%]|h")
|
||||
if slotItemName and string_lower(slotItemName) == string_lower(msg) then
|
||||
local slotItemName = C_Item.GetItemName({ equipmentSlotIndex = checkSlot })
|
||||
if slotItemName then
|
||||
if string_lower(slotItemName) == string_lower(msg) then
|
||||
-- Item found in paired slot - but prefer a bag copy if one exists
|
||||
local bagCopyBag, bagCopySlot = FindItemInBagsByName(msg)
|
||||
if bagCopyBag then
|
||||
@@ -3627,17 +3376,14 @@ function CleveRoids.EquipBagItem(msg, slotOrOffhand)
|
||||
-- Verify the item actually landed in the target slot
|
||||
-- EquipItemByName may silently no-op for same-named items in paired slots
|
||||
-- (e.g., dual-wielding Scimitar: MH copy found first, "equipped" to same slot)
|
||||
local newLink = GetInventoryItemLink("player", invslot)
|
||||
if newLink then
|
||||
local _, _, newName = string_find(newLink, "|h%[(.-)%]|h")
|
||||
if newName and string_lower(newName) == string_lower(msg) then
|
||||
if CleveRoids.Items then
|
||||
CleveRoids.Items[msg] = nil
|
||||
CleveRoids.Items[string_lower(msg)] = nil
|
||||
end
|
||||
InvalidateDisplacedItem()
|
||||
return true
|
||||
local newName = C_Item.GetItemName({ equipmentSlotIndex = invslot })
|
||||
if newName and string_lower(newName) == string_lower(msg) then
|
||||
if CleveRoids.Items then
|
||||
CleveRoids.Items[msg] = nil
|
||||
CleveRoids.Items[string_lower(msg)] = nil
|
||||
end
|
||||
InvalidateDisplacedItem()
|
||||
return true
|
||||
end
|
||||
-- Verification failed - EquipItemByName didn't place item in target slot
|
||||
if CleveRoids.equipDebugLog then
|
||||
@@ -3679,10 +3425,9 @@ function CleveRoids.EquipBagItem(msg, slotOrOffhand)
|
||||
local ok = pcall(EquipItemByName, item.name, invslot)
|
||||
if ok then
|
||||
-- Verify the item actually landed in the target slot (same guard as above)
|
||||
local newLink = GetInventoryItemLink("player", invslot)
|
||||
if newLink then
|
||||
local _, _, newName = string_find(newLink, "|h%[(.-)%]|h")
|
||||
if newName and string_lower(newName) == string_lower(item.name) then
|
||||
local newName = C_Item.GetItemName({ equipmentSlotIndex = invslot })
|
||||
if newName then
|
||||
if string_lower(newName) == string_lower(item.name) then
|
||||
if CleveRoids.Items then
|
||||
CleveRoids.Items[item.name] = nil
|
||||
CleveRoids.Items[string_lower(item.name)] = nil
|
||||
@@ -3771,11 +3516,8 @@ local function _equipRing2Action(msg)
|
||||
return CleveRoids.EquipBagItem(msg, 12)
|
||||
end
|
||||
|
||||
local function _unshiftAction()
|
||||
local currentShapeshiftIndex = CleveRoids.GetCurrentShapeshiftIndex()
|
||||
if currentShapeshiftIndex ~= 0 then
|
||||
CastShapeshiftForm(currentShapeshiftIndex)
|
||||
end
|
||||
local function _cancelFormAction()
|
||||
CancelShapeshiftForm()
|
||||
end
|
||||
|
||||
function CleveRoids.DoEquipMainhand(msg)
|
||||
@@ -3846,27 +3588,29 @@ function CleveRoids.DoEquipRing2(msg)
|
||||
return false
|
||||
end
|
||||
|
||||
function CleveRoids.DoUnshift(msg)
|
||||
function CleveRoids.DoCancelForm(msg)
|
||||
local handled
|
||||
-- PERFORMANCE: Use numeric iteration to avoid pairs() iterator allocation
|
||||
local parts = CleveRoids.splitStringIgnoringQuotes(msg)
|
||||
for i = 1, table.getn(parts) do
|
||||
handled = false
|
||||
if CleveRoids.DoWithConditionals(parts[i], _unshiftAction, CleveRoids.FixEmptyTarget, false, _unshiftAction) then
|
||||
if CleveRoids.DoWithConditionals(parts[i], _cancelFormAction, CleveRoids.FixEmptyTarget, false, _cancelFormAction) then
|
||||
handled = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if handled == nil then
|
||||
_unshiftAction()
|
||||
_cancelFormAction()
|
||||
end
|
||||
|
||||
return handled
|
||||
end
|
||||
|
||||
CleveRoids.DoUnshift = CleveRoids.DoCancelForm
|
||||
|
||||
function CleveRoids.DoRetarget()
|
||||
if GetUnitName("target") == nil
|
||||
if UnitName("target") == nil
|
||||
or UnitHealth("target") == 0
|
||||
or not UnitCanAttack("player", "target")
|
||||
then
|
||||
@@ -4191,6 +3935,13 @@ function CleveRoids.OnUpdate(self)
|
||||
|
||||
CR.lastUpdate = time
|
||||
|
||||
-- Reclaim the top of the SendChatMessage hook chain if another addon has
|
||||
-- displaced our #showtooltip filter (see EnsureSendChatMessageHook). Cheap
|
||||
-- identity check; only re-hooks when actually displaced.
|
||||
if CR.EnsureSendChatMessageHook then
|
||||
CR.EnsureSendChatMessageHook()
|
||||
end
|
||||
|
||||
-- Process deferred equipment index updates (for throttled UNIT_INVENTORY_CHANGED)
|
||||
-- PERFORMANCE: Skip check entirely if no pending update
|
||||
local pendingTime = CR.equipIndexPendingTime
|
||||
@@ -4425,9 +4176,11 @@ function GameTooltip.SetAction(self, slot)
|
||||
|
||||
local current_spell_data = CleveRoids.GetSpell(action_name)
|
||||
if current_spell_data and current_spell_data.id then
|
||||
-- ClassicAPI: render by spellID (rank included) instead of routing
|
||||
-- through the spellbook slot via SetSpell(spellSlot, bookType).
|
||||
GameTooltip:SetSpellByID(current_spell_data.id)
|
||||
if current_spell_data.spellSlot and current_spell_data.bookType then
|
||||
GameTooltip:SetSpell(current_spell_data.spellSlot, current_spell_data.bookType)
|
||||
else
|
||||
GameTooltip:SetSpellByID(current_spell_data.id)
|
||||
end
|
||||
GameTooltip:Show()
|
||||
return
|
||||
end
|
||||
@@ -4453,7 +4206,11 @@ function GameTooltip.SetAction(self, slot)
|
||||
|
||||
current_spell_data = CleveRoids.GetSpell(nested_action_name)
|
||||
if current_spell_data and current_spell_data.id then
|
||||
GameTooltip:SetSpellByID(current_spell_data.id)
|
||||
if current_spell_data.spellSlot and current_spell_data.bookType then
|
||||
GameTooltip:SetSpell(current_spell_data.spellSlot, current_spell_data.bookType)
|
||||
else
|
||||
GameTooltip:SetSpellByID(current_spell_data.id)
|
||||
end
|
||||
GameTooltip:Show()
|
||||
return
|
||||
end
|
||||
@@ -4650,6 +4407,10 @@ function IsCurrentAction(slot)
|
||||
else
|
||||
local name
|
||||
if actionToCheck.spell then
|
||||
if CleveRoids.IsAutoAttackSpell(actionToCheck.spell) then
|
||||
return CleveRoids.CurrentSpell.autoAttack and 1 or nil
|
||||
end
|
||||
|
||||
local rank = actionToCheck.spell.rank or actionToCheck.spell.highest.rank
|
||||
name = actionToCheck.spell.name..(rank and ("("..rank..")"))
|
||||
|
||||
@@ -4692,9 +4453,6 @@ function IsCurrentAction(slot)
|
||||
end
|
||||
end
|
||||
|
||||
-- Macro icon for an action slot, via ClassicAPI's GetActionInfo (macro slot
|
||||
-- directly, no GetActionText -> GetMacroIndexByName name round-trip).
|
||||
-- Returns the macro texture, or nil if the slot isn't a macro / has no icon.
|
||||
local function GetSlotMacroTexture(slot)
|
||||
local kind, macroId = CleveRoids.ClassicAPI.GetActionInfo(slot)
|
||||
if kind == "macro" and macroId then
|
||||
@@ -4704,6 +4462,18 @@ local function GetSlotMacroTexture(slot)
|
||||
return nil
|
||||
end
|
||||
|
||||
local function IsAutoAttackSpell(spell)
|
||||
if not spell then return false end
|
||||
if C_Spell and C_Spell.IsAutoAttackSpell and spell.id then
|
||||
return C_Spell.IsAutoAttackSpell(spell.id)
|
||||
end
|
||||
if C_SpellBook and C_SpellBook.IsAutoAttackSpellBookItem and spell.spellSlot then
|
||||
return C_SpellBook.IsAutoAttackSpellBookItem(spell.spellSlot, spell.bookType)
|
||||
end
|
||||
return false
|
||||
end
|
||||
CleveRoids.IsAutoAttackSpell = IsAutoAttackSpell
|
||||
|
||||
CleveRoids.Hooks.GetActionTexture = GetActionTexture
|
||||
function GetActionTexture(slot)
|
||||
if not slot then return nil end
|
||||
@@ -4792,6 +4562,13 @@ function GetActionTexture(slot)
|
||||
end
|
||||
end
|
||||
|
||||
if a and a.spell and CleveRoids.IsAutoAttackSpell(a.spell) then
|
||||
local mainHandTexture = GetInventoryItemTexture("player", 16)
|
||||
if mainHandTexture then
|
||||
texture = mainHandTexture
|
||||
end
|
||||
end
|
||||
|
||||
if texture then
|
||||
return texture
|
||||
end
|
||||
@@ -4893,21 +4670,17 @@ function GetActionCount(slot)
|
||||
count = CleveRoids.GetLiveItemCount(actionToCheck.item.name or actionToCheck.action)
|
||||
|
||||
elseif actionToCheck.spell then
|
||||
local reagent = actionToCheck.spell.reagent
|
||||
if not reagent or reagent == "" then
|
||||
reagent = nil
|
||||
local reagentId = actionToCheck.spell.reagentId
|
||||
if not reagentId then
|
||||
local ss, bt = actionToCheck.spell.spellSlot, actionToCheck.spell.bookType
|
||||
if ss and bt then
|
||||
local _, r = CleveRoids.GetSpellCost(ss, bt)
|
||||
reagent = r
|
||||
local _, _, rid = CleveRoids.GetSpellCost(ss, bt, actionToCheck.spell.id)
|
||||
actionToCheck.spell.reagentId = rid -- cache itemID so we don't re-derive every frame
|
||||
reagentId = rid
|
||||
end
|
||||
if (not reagent) and _ReagentBySpell and actionToCheck.spell.name then
|
||||
reagent = _ReagentBySpell[actionToCheck.spell.name] -- e.g., Vanish → Flash Powder
|
||||
end
|
||||
actionToCheck.spell.reagent = reagent -- cache it so we don't re-scan every frame
|
||||
end
|
||||
if reagent and reagent ~= "" then
|
||||
count = CleveRoids.GetReagentCount(reagent) -- id-first bag scan, falls back to name/tooltip
|
||||
if reagentId then
|
||||
count = CleveRoids.GetReagentCount(reagentId) -- id-based bag scan
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4945,17 +4718,8 @@ function IsConsumableAction(slot)
|
||||
end
|
||||
|
||||
|
||||
if actionToCheck.spell then
|
||||
local reagent = actionToCheck.spell.reagent
|
||||
if (not reagent or reagent == "") and actionToCheck.spell.name then
|
||||
reagent = _ReagentBySpell[actionToCheck.spell.name]
|
||||
if reagent then
|
||||
actionToCheck.spell.reagent = reagent
|
||||
end
|
||||
end
|
||||
if reagent and reagent ~= "" then
|
||||
return 1
|
||||
end
|
||||
if actionToCheck.spell and actionToCheck.spell.reagentId then
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5025,37 +4789,9 @@ if not CleveRoids.RunMacroHooked then
|
||||
CleveRoids.RunMacroHooked = true
|
||||
end
|
||||
|
||||
-- Robust named tooltip for scanning spells/items
|
||||
if not CleveRoidsTooltipScan then
|
||||
-- Try to create with the standard template first
|
||||
local ok, _ = pcall(CreateFrame, "GameTooltip", "CleveRoidsTooltipScan", UIParent, "GameTooltipTemplate")
|
||||
if not ok or not CleveRoidsTooltipScan then
|
||||
-- Fallback: manual tooltip with plenty of prebuilt lines
|
||||
CleveRoidsTooltipScan = CreateFrame("GameTooltip", "CleveRoidsTooltipScan", UIParent)
|
||||
local L1 = CleveRoidsTooltipScan:CreateFontString("$parentTextLeft1", nil, "GameTooltipText")
|
||||
local R1 = CleveRoidsTooltipScan:CreateFontString("$parentTextRight1", nil, "GameTooltipText")
|
||||
CleveRoidsTooltipScan:AddFontStrings(L1, R1)
|
||||
for i = 2, 32 do
|
||||
CleveRoidsTooltipScan:CreateFontString("$parentTextLeft"..i, nil, "GameTooltipText")
|
||||
CleveRoidsTooltipScan:CreateFontString("$parentTextRight"..i, nil, "GameTooltipText")
|
||||
end
|
||||
end
|
||||
CleveRoidsTooltipScan:SetOwner(WorldFrame, "ANCHOR_NONE")
|
||||
CleveRoidsTooltipScan:EnableMouse(false)
|
||||
end
|
||||
|
||||
-- This single dummy frame handles events AND serves as our tooltip scanner.
|
||||
CleveRoids.Frame = CreateFrame("GameTooltip")
|
||||
CleveRoids.Frame:EnableMouse(false) -- Prevent tooltip from capturing mouse input
|
||||
|
||||
-- Create the extra font strings needed for other functions like GetSpellCost.
|
||||
CleveRoids.Frame.costFontString = CleveRoids.Frame:CreateFontString()
|
||||
CleveRoids.Frame.rangeFontString = CleveRoids.Frame:CreateFontString()
|
||||
CleveRoids.Frame.reagentFontString = CleveRoids.Frame:CreateFontString()
|
||||
CleveRoids.Frame:AddFontStrings(CleveRoids.Frame:CreateFontString(), CleveRoids.Frame:CreateFontString())
|
||||
CleveRoids.Frame:AddFontStrings(CleveRoids.Frame.costFontString, CleveRoids.Frame.rangeFontString)
|
||||
CleveRoids.Frame:AddFontStrings(CleveRoids.Frame:CreateFontString(), CleveRoids.Frame:CreateFontString())
|
||||
CleveRoids.Frame:AddFontStrings(CleveRoids.Frame.reagentFontString, CleveRoids.Frame:CreateFontString())
|
||||
-- This single frame drives the addon's event loop and OnUpdate. (Spell cost /
|
||||
-- reagent lookups now read Spell.dbc directly via ClassicAPI -- no tooltip scan.)
|
||||
CleveRoids.Frame = CreateFrame("Frame")
|
||||
|
||||
CleveRoids.Frame:SetScript("OnUpdate", CleveRoids.OnUpdate)
|
||||
CleveRoids.Frame:SetScript("OnEvent", function(...)
|
||||
@@ -5088,6 +4824,7 @@ CleveRoids.Frame:RegisterEvent("PLAYER_REGEN_DISABLED") -- Entered actual combat
|
||||
CleveRoids.Frame:RegisterEvent("PLAYER_REGEN_ENABLED") -- Left actual combat (no threat)
|
||||
CleveRoids.Frame:RegisterEvent("UPDATE_SHAPESHIFT_FORM")
|
||||
CleveRoids.Frame:RegisterEvent("SPELL_UPDATE_COOLDOWN")
|
||||
CleveRoids.Frame:RegisterEvent("PLAYER_STARTED_MOVING")
|
||||
-- Use GUID events when available (v2.39+), fall back to standard per-token events
|
||||
if CleveRoids.NampowerAPI.features.hasUnitGuidEvents then
|
||||
CleveRoids.Frame:RegisterEvent("UNIT_AURA_GUID")
|
||||
@@ -5234,8 +4971,7 @@ function CleveRoids.DoWDBWarmup()
|
||||
for bag = 0, 4 do
|
||||
local slots = GetContainerNumSlots(bag) or 0
|
||||
for slot = 1, slots do
|
||||
local link = GetContainerItemLink(bag, slot)
|
||||
if link then
|
||||
if C_Container.GetContainerItemID(bag, slot) then
|
||||
-- Tooltip scan loads the item into WDB
|
||||
tip:ClearLines()
|
||||
tip:SetBagItem(bag, slot)
|
||||
@@ -5245,9 +4981,8 @@ function CleveRoids.DoWDBWarmup()
|
||||
end
|
||||
|
||||
-- Scan equipped items
|
||||
for slot = 0, 19 do
|
||||
local link = GetInventoryItemLink("player", slot)
|
||||
if link then
|
||||
for slot = 1, 19 do
|
||||
if GetInventoryItemID("player", slot) then
|
||||
tip:ClearLines()
|
||||
tip:SetInventoryItem("player", slot)
|
||||
scanned = scanned + 1
|
||||
@@ -5804,6 +5539,48 @@ function CleveRoids.Frame:PLAYER_REGEN_ENABLED()
|
||||
end
|
||||
end
|
||||
|
||||
-- Movement -> [moving]/[nomoving] icon refresh.
|
||||
-- PLAYER_STARTED_MOVING is reliable; PLAYER_STOPPED_MOVING is not (key-release
|
||||
-- based, misses geometry/click-to-move/root/knockback stops). So STARTED flips
|
||||
-- the icon to "moving" and starts a bounded poll; the poll detects the real
|
||||
-- stop from IsPlayerMoving() (the signal [moving] uses), refreshes once, and
|
||||
-- shuts itself off. Only runs while actually moving, so no idle cost.
|
||||
local MOVE_POLL_INTERVAL = 0.1
|
||||
local moveTicker = nil
|
||||
|
||||
local function StopMovePoll()
|
||||
if moveTicker then
|
||||
moveTicker:Cancel()
|
||||
moveTicker = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function StartMovePoll()
|
||||
if moveTicker then return end -- already polling this movement
|
||||
moveTicker = C_Timer.NewTicker(MOVE_POLL_INTERVAL, function()
|
||||
if CleveRoids.isShuttingDown then
|
||||
StopMovePoll()
|
||||
return
|
||||
end
|
||||
if not CleveRoids.IsPlayerMoving() then
|
||||
-- Movement actually ended -- repaint [moving]/[nomoving] icons, stop polling.
|
||||
StopMovePoll()
|
||||
if CleveRoidMacros.realtime == 0 then
|
||||
CleveRoids.QueueActionUpdate()
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function CleveRoids.Frame:PLAYER_STARTED_MOVING()
|
||||
-- Started moving: flip to the [moving] icon now...
|
||||
if CleveRoidMacros.realtime == 0 then
|
||||
CleveRoids.QueueActionUpdate()
|
||||
end
|
||||
-- ...and watch for the (unreliable-event) stop ourselves.
|
||||
StartMovePoll()
|
||||
end
|
||||
|
||||
function CleveRoids.Frame:PLAYER_TARGET_CHANGED()
|
||||
CleveRoids.CurrentSpell.autoAttack = false
|
||||
CleveRoids.CurrentSpell.autoAttackLock = false
|
||||
@@ -6220,18 +5997,36 @@ function CleveRoids.Frame:KEY_UP()
|
||||
CleveRoids.isActionUpdateQueued = true
|
||||
end
|
||||
|
||||
CleveRoids.Hooks.SendChatMessage = SendChatMessage
|
||||
function SendChatMessage(msg, ...)
|
||||
-- Filter out #showtooltip lines
|
||||
-- pfUI's macrotweak also does this, but our pattern is more specific
|
||||
-- Filter out the #showtooltip line that Blizzard's native macro executor sends
|
||||
-- to chat when a macro runs from an action button. Named (not anonymous) so we
|
||||
-- can detect displacement and reclaim the top of the hook chain.
|
||||
local function CleveRoids_SendChatMessage(msg, ...)
|
||||
if msg and string.find(msg, "^#showtooltip") then
|
||||
return
|
||||
end
|
||||
|
||||
-- Call the original (or pfUI's hook if it's in the chain)
|
||||
-- Call whatever we chained over (another addon's hook, or the real function)
|
||||
CleveRoids.Hooks.SendChatMessage(msg, unpack(arg))
|
||||
end
|
||||
|
||||
-- (Re-)assert our filter as the OUTERMOST SendChatMessage hook. This is a no-op
|
||||
-- once we're already on top. It matters because some addons snapshot
|
||||
-- SendChatMessage at their file-load and later install a hook that calls that
|
||||
-- snapshot DIRECTLY -- if they loaded before us, their snapshot predates our
|
||||
-- filter, so calling it directly orphans us and #showtooltip leaks to chat.
|
||||
-- (LeafVillageAchievements does exactly this at PLAYER_ENTERING_WORLD+3s; and
|
||||
-- because the fork sorts after it alphabetically, we load too late to be in its
|
||||
-- snapshot -- upstream "CleveRoidMacros" sorted before it and wasn't affected.)
|
||||
-- OnUpdate calls this so we reclaim the top within a frame of being displaced;
|
||||
-- being outermost also guarantees we see the pristine line for the anchor match.
|
||||
function CleveRoids.EnsureSendChatMessageHook()
|
||||
if SendChatMessage ~= CleveRoids_SendChatMessage then
|
||||
CleveRoids.Hooks.SendChatMessage = SendChatMessage
|
||||
SendChatMessage = CleveRoids_SendChatMessage
|
||||
end
|
||||
end
|
||||
|
||||
CleveRoids.EnsureSendChatMessageHook()
|
||||
|
||||
CleveRoids.RegisterActionEventHandler = function(fn)
|
||||
if type(fn) == "function" then
|
||||
table.insert(CleveRoids.actionEventHandlers, fn)
|
||||
@@ -7258,9 +7053,9 @@ local function FindItemInBags(itemName)
|
||||
local searchName = string.lower(itemName)
|
||||
for bag = 0, 4 do
|
||||
for slot = 1, GetContainerNumSlots(bag) do
|
||||
local link = GetContainerItemLink(bag, slot)
|
||||
if link then
|
||||
local _, _, foundName = string.find(link, "%[(.+)%]")
|
||||
local id = C_Container.GetContainerItemID(bag, slot)
|
||||
if id then
|
||||
local foundName = C_Item.GetItemNameByID(id)
|
||||
if foundName and string.find(string.lower(foundName), searchName, 1, true) then
|
||||
return bag, slot, foundName
|
||||
end
|
||||
|
||||
@@ -58,11 +58,7 @@ function CleveRoids.IndexSpells()
|
||||
bookType = CleveRoids.bookTypes[book]
|
||||
spells[bookType] = {}
|
||||
else
|
||||
local cost, reagent = CleveRoids.GetSpellCost(i, bookType)
|
||||
-- Fallback for known reagent spells if tooltip scan failed
|
||||
if (not reagent or reagent == "") and CleveRoids.ReagentBySpell then
|
||||
reagent = CleveRoids.ReagentBySpell[spellName]
|
||||
end
|
||||
local cost, reagent, reagentId = CleveRoids.GetSpellCost(i, bookType, spellId)
|
||||
if not spells[bookType][spellName] then
|
||||
spells[bookType][spellName] = {
|
||||
spellSlot = i,
|
||||
@@ -71,7 +67,7 @@ function CleveRoids.IndexSpells()
|
||||
bookType = bookType,
|
||||
texture = texture,
|
||||
cost = cost,
|
||||
reagent = reagent,
|
||||
reagentId = reagentId,
|
||||
}
|
||||
end
|
||||
if spellRank and not spells[bookType][spellName][spellRank] then
|
||||
@@ -83,7 +79,7 @@ function CleveRoids.IndexSpells()
|
||||
bookType = bookType,
|
||||
texture = texture,
|
||||
cost = cost,
|
||||
reagent = reagent
|
||||
reagentId = reagentId,
|
||||
}
|
||||
spells[bookType][spellName].highest = spells[bookType][spellName][spellRank]
|
||||
end
|
||||
@@ -94,6 +90,13 @@ function CleveRoids.IndexSpells()
|
||||
|
||||
if reagent then
|
||||
CleveRoids.countedItemTypes[reagent] = true
|
||||
elseif reagentId and Item then
|
||||
Item:CreateFromItemID(reagentId):ContinueOnItemLoad(function()
|
||||
local loadedName = C_Item.GetItemNameByID(reagentId)
|
||||
if loadedName and loadedName ~= "" then
|
||||
CleveRoids.countedItemTypes[loadedName] = true
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -156,10 +159,9 @@ end
|
||||
function CleveRoids.IndexEquippedItems()
|
||||
local items = CleveRoids.Items or {}
|
||||
|
||||
for inventoryID = 0, 19 do
|
||||
local link = GetInventoryItemLink("player", inventoryID)
|
||||
if link then
|
||||
local _, _, itemID = string.find(link, "item:(%d+)")
|
||||
for inventoryID = 1, 19 do
|
||||
local itemID = GetInventoryItemID("player", inventoryID)
|
||||
if itemID then
|
||||
local name, link, _, _, itemType, itemSubType, _, _, texture = GetItemInfo(itemID)
|
||||
if name then
|
||||
local count = GetInventoryItemCount("player", inventoryID)
|
||||
@@ -199,10 +201,9 @@ function CleveRoids.IndexEquipSlot(inventoryID)
|
||||
if not inventoryID then return end
|
||||
|
||||
local items = CleveRoids.Items or {}
|
||||
local link = GetInventoryItemLink("player", inventoryID)
|
||||
local itemID = GetInventoryItemID("player", inventoryID)
|
||||
|
||||
if link then
|
||||
local _, _, itemID = string.find(link, "item:(%d+)")
|
||||
if itemID then
|
||||
local name, itemLink, _, _, itemType, itemSubType, _, _, texture = GetItemInfo(itemID)
|
||||
if name then
|
||||
local count = GetInventoryItemCount("player", inventoryID)
|
||||
@@ -265,21 +266,17 @@ function CleveRoids.IndexItems()
|
||||
|
||||
-- PERFORMANCE: Local function references
|
||||
local GetContainerNumSlots = GetContainerNumSlots
|
||||
local GetContainerItemLink = GetContainerItemLink
|
||||
local GetContainerItemInfo = GetContainerItemInfo
|
||||
local GetInventoryItemLink = GetInventoryItemLink
|
||||
local GetInventoryItemCount = GetInventoryItemCount
|
||||
|
||||
-- Scan bags (reverse order to prefer first stack)
|
||||
for bagID = 0, NUM_BAG_SLOTS do
|
||||
local numSlots = GetContainerNumSlots(bagID)
|
||||
for slot = numSlots, 1, -1 do
|
||||
local link = GetContainerItemLink(bagID, slot)
|
||||
if link then
|
||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
||||
|
||||
-- PERFORMANCE: Try to extract name from link first to check for duplicates
|
||||
local _, _, linkName = string_find(link, "%[(.+)%]")
|
||||
local itemID = C_Container.GetContainerItemID(bagID, slot)
|
||||
if itemID then
|
||||
-- Decorated name for the duplicate fast-path (no link string built)
|
||||
local linkName = C_Item.GetItemName({ bagID = bagID, slotIndex = slot })
|
||||
local existing = linkName and items[linkName]
|
||||
|
||||
if existing then
|
||||
@@ -317,13 +314,11 @@ function CleveRoids.IndexItems()
|
||||
end
|
||||
|
||||
-- Scan equipped items
|
||||
for inventoryID = 0, 19 do
|
||||
local link = GetInventoryItemLink("player", inventoryID)
|
||||
if link then
|
||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
||||
|
||||
-- PERFORMANCE: Try to extract name from link first
|
||||
local _, _, linkName = string_find(link, "%[(.+)%]")
|
||||
for inventoryID = 1, 19 do
|
||||
local itemID = GetInventoryItemID("player", inventoryID)
|
||||
if itemID then
|
||||
-- Decorated name for the duplicate fast-path (no link string built)
|
||||
local linkName = C_Item.GetItemName({ equipmentSlotIndex = inventoryID })
|
||||
local existing = linkName and items[linkName]
|
||||
|
||||
if existing then
|
||||
@@ -477,10 +472,8 @@ local function makeInventoryItem(inventoryID, link, Items)
|
||||
if not link then link = GetInventoryItemLink("player", inventoryID) end
|
||||
if not link then return end
|
||||
|
||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
||||
itemID = itemID and tonumber(itemID) or nil
|
||||
|
||||
local name = itemID and GetItemInfo(itemID) or nil
|
||||
local itemID = GetInventoryItemID("player", inventoryID)
|
||||
local name = C_Item.GetItemName({ equipmentSlotIndex = inventoryID })
|
||||
local texture = GetInventoryItemTexture("player", inventoryID)
|
||||
local count = GetInventoryItemCount("player", inventoryID)
|
||||
|
||||
@@ -509,14 +502,12 @@ local function makeBagItem(bagID, slot, link, Items)
|
||||
end
|
||||
if not link then return end
|
||||
|
||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
||||
itemID = itemID and tonumber(itemID) or nil
|
||||
local itemID = C_Container.GetContainerItemID(bagID, slot)
|
||||
|
||||
local name, _, _, _, _, _, _, _, texture = GetItemInfo(itemID)
|
||||
local name = C_Item.GetItemName({bagID = bagID, slotIndex = slot})
|
||||
local count = 0
|
||||
local tex, itemCount = GetContainerItemInfo(bagID, slot)
|
||||
local texture, itemCount = GetContainerItemInfo(bagID, slot)
|
||||
if itemCount then count = itemCount end
|
||||
if not texture then texture = tex end
|
||||
|
||||
local it = {
|
||||
bagID = bagID,
|
||||
@@ -574,9 +565,7 @@ function CleveRoids.GetItem(text)
|
||||
for inv = 1, 19 do
|
||||
local link = GetInventoryItemLink("player", inv)
|
||||
if link then
|
||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
||||
itemID = itemID and tonumber(itemID) or nil
|
||||
|
||||
local itemID = GetInventoryItemID("player", inv)
|
||||
if qid and itemID and qid == itemID then
|
||||
return makeInventoryItem(inv, link, Items)
|
||||
elseif qname then
|
||||
@@ -595,9 +584,7 @@ function CleveRoids.GetItem(text)
|
||||
for slot = 1, slots do
|
||||
local link = GetContainerItemLink(bag, slot)
|
||||
if link then
|
||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
||||
itemID = itemID and tonumber(itemID) or nil
|
||||
|
||||
local itemID = C_Container.GetContainerItemID(bag, slot)
|
||||
if qid and itemID and qid == itemID then
|
||||
return makeBagItem(bag, slot, link, Items)
|
||||
elseif qname then
|
||||
@@ -720,18 +707,16 @@ function CleveRoids.FindItemQuick(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 qid then
|
||||
if GetInventoryItemID("player", cached.inventoryID) == qid then
|
||||
cached._validated = true
|
||||
return cached -- Cache is valid
|
||||
end
|
||||
else
|
||||
local nm = C_Item.GetItemName({ equipmentSlotIndex = cached.inventoryID })
|
||||
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
|
||||
@@ -740,18 +725,16 @@ function CleveRoids.FindItemQuick(text)
|
||||
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 qid then
|
||||
if C_Container.GetContainerItemID(cached.bagID, cached.slot) == qid then
|
||||
cached._validated = true
|
||||
return cached -- Cache is valid
|
||||
end
|
||||
else
|
||||
local nm = C_Item.GetItemName({ bagID = cached.bagID, slotIndex = cached.slot })
|
||||
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
|
||||
@@ -767,20 +750,17 @@ function CleveRoids.FindItemQuick(text)
|
||||
for inv = 1, 19 do
|
||||
local link = GetInventoryItemLink("player", inv)
|
||||
if link then
|
||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
||||
if itemID then
|
||||
itemID = tonumber(itemID)
|
||||
-- ID match: fast path
|
||||
if qid and qid == itemID then
|
||||
local itemID = GetInventoryItemID("player", inv)
|
||||
-- ID match: fast path
|
||||
if qid and qid == itemID then
|
||||
return makeInventoryItem(inv, link, Items)
|
||||
end
|
||||
-- Name match: extract name from link (faster than GetItemInfo)
|
||||
if qname then
|
||||
local nm = GetNameFromLink(link)
|
||||
if nm and string_lower(nm) == qname then
|
||||
return makeInventoryItem(inv, link, Items)
|
||||
end
|
||||
-- Name match: extract name from link (faster than GetItemInfo)
|
||||
if qname then
|
||||
local nm = GetNameFromLink(link)
|
||||
if nm and string_lower(nm) == qname then
|
||||
return makeInventoryItem(inv, link, Items)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -791,20 +771,17 @@ function CleveRoids.FindItemQuick(text)
|
||||
for slot = 1, slots do
|
||||
local link = GetContainerItemLink(bag, slot)
|
||||
if link then
|
||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
||||
if itemID then
|
||||
itemID = tonumber(itemID)
|
||||
-- ID match: fast path
|
||||
if qid and qid == itemID then
|
||||
local itemID = C_Container.GetContainerItemID(bag, slot)
|
||||
-- ID match: fast path
|
||||
if qid and qid == itemID then
|
||||
return makeBagItem(bag, slot, link, Items)
|
||||
end
|
||||
-- Name match: extract name from link (faster than GetItemInfo)
|
||||
if qname then
|
||||
local nm = GetNameFromLink(link)
|
||||
if nm and string_lower(nm) == qname then
|
||||
return makeBagItem(bag, slot, link, Items)
|
||||
end
|
||||
-- Name match: extract name from link (faster than GetItemInfo)
|
||||
if qname then
|
||||
local nm = GetNameFromLink(link)
|
||||
if nm and string_lower(nm) == qname then
|
||||
return makeBagItem(bag, slot, link, Items)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -818,25 +795,19 @@ end
|
||||
function CleveRoids.IsItemEquipped(text, inventoryId)
|
||||
if not text or not inventoryId then return false end
|
||||
|
||||
local link = GetInventoryItemLink("player", inventoryId)
|
||||
if not link then return false end
|
||||
|
||||
local _, _, currentID = string_find(link, "item:(%d+)")
|
||||
local currentID = GetInventoryItemID("player", inventoryId)
|
||||
if not currentID then return false end
|
||||
|
||||
-- Check by ID (fast path)
|
||||
local textId = tonumber(text)
|
||||
if textId and textId == tonumber(currentID) then
|
||||
if textId and textId == currentID then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Check by name - extract from link instead of GetItemInfo for performance
|
||||
local currentName = GetNameFromLink(link)
|
||||
if currentName then
|
||||
local textLower = string_lower(text)
|
||||
if string_lower(currentName) == textLower then
|
||||
return true
|
||||
end
|
||||
-- Check by name (decorated, so suffixed gear still matches)
|
||||
local currentName = C_Item.GetItemName({ equipmentSlotIndex = inventoryId })
|
||||
if currentName and string_lower(currentName) == string_lower(text) then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
|
||||
+1
-6
@@ -6918,12 +6918,7 @@ function CleveRoids.ApplyEquipmentModifier(spellID, baseDuration)
|
||||
local modifiedDuration = modifier.modifier(baseDuration, itemID)
|
||||
|
||||
if modifiedDuration ~= baseDuration and CleveRoids.debug then
|
||||
local itemName = "Unknown"
|
||||
local itemLink = GetInventoryItemLink("player", modifier.slot)
|
||||
if itemLink then
|
||||
local _, _, _n = string.find(itemLink, "%[(.-)%]")
|
||||
itemName = _n or "Unknown"
|
||||
end
|
||||
local itemName = C_Item.GetItemName({ equipmentSlotIndex = modifier.slot }) or "Unknown"
|
||||
|
||||
DEFAULT_CHAT_FRAME:AddMessage(
|
||||
string.format("|cffff00ff[Equipment Modifier]|r %s (ID:%d): %ds -> %ds (item: %s [%d])",
|
||||
|
||||
Reference in New Issue
Block a user