--[[ Author: Dennis Werner Garske (DWG) / brian / Mewtiny License: MIT License ]] local _G = _G or getfenv(0) local CleveRoids = _G.CleveRoids or {} -- PERFORMANCE: Upvalues for frequently called functions local GetTime = GetTime local GetContainerItemLink = GetContainerItemLink local GetContainerItemInfo = GetContainerItemInfo local GetContainerNumSlots = GetContainerNumSlots local GetInventoryItemLink = GetInventoryItemLink local GetInventoryItemTexture = GetInventoryItemTexture local GetInventoryItemCount = GetInventoryItemCount local GetItemInfo = GetItemInfo local GetSpellName = GetSpellName local GetSpellTexture = GetSpellTexture local tonumber = tonumber local type = type local pairs = pairs local string_find = string.find local string_lower = string.lower local string_gsub = string.gsub local table_insert = table.insert local table_getn = table.getn -- Indexes all spells the current player and pet knows function CleveRoids.IndexSpells() local spells = {} local i = 0 local book = 1 local bookType = CleveRoids.bookTypes[book] local maxBooks = table.getn(CleveRoids.bookTypes) spells[bookType] = {} while true do i = i + 1 local spellName, spellRank = GetSpellName(i, bookType) if not spellName then i = 0 book = book + 1 if book > maxBooks then break end bookType = CleveRoids.bookTypes[book] spells[bookType] = {} else local cost, reagent = CleveRoids.GetSpellCost(i, bookType) local texture = GetSpellTexture(i, bookType) if not spells[bookType][spellName] then spells[bookType][spellName] = { spellSlot = i, name = spellName, bookType = bookType, texture = texture, cost = cost, reagent = reagent, } end if spellRank and not spells[bookType][spellName][spellRank] then spells[bookType][spellName][spellRank] = { spellSlot = i, name = spellName, rank = spellRank, bookType = bookType, texture = texture, cost = cost, reagent = reagent } spells[bookType][spellName].highest = spells[bookType][spellName][spellRank] end if reagent then CleveRoids.countedItemTypes[reagent] = true end end end CleveRoids.Spells = spells end -- Indexes all pet spells and their action bar slots function CleveRoids.IndexPetSpells() local petSpells = {} if not UnitExists("pet") then CleveRoids.PetSpells = petSpells return end for i = 1, NUM_PET_ACTION_SLOTS do local name, subtext, texture, isToken = GetPetActionInfo(i) if name and not isToken then -- Store the spell name and its slot petSpells[name] = { slot = i, name = name, subtext = subtext, texture = texture } end end CleveRoids.PetSpells = petSpells end -- Gets a pet spell by name function CleveRoids.GetPetSpell(spellName) if not spellName or not CleveRoids.PetSpells then return nil end spellName = CleveRoids.Trim(spellName) return CleveRoids.PetSpells[spellName] end function CleveRoids.IndexTalents() local talents = {[1] = true} for tab = 1, GetNumTalentTabs() do for i = 1, GetNumTalents(tab) do local name, _, _, _, rank = GetTalentInfo(tab, i) talents[name] = tonumber(rank) end end CleveRoids.Talents = talents end -- Lightweight equipment-only indexing for combat situations -- Updates existing cache rather than rebuilding it 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+)") local name, link, _, _, itemType, itemSubType, _, _, texture = GetItemInfo(itemID) if name then local count = GetInventoryItemCount("player", inventoryID) if not items[name] then items[name] = { inventoryID = inventoryID, id = itemID, name = name, count = count, texture = texture, link = link, } items[itemID] = name local lowerName = string.lower(name) if lowerName ~= name then items[lowerName] = name end else -- Update existing entry with current equipment state items[name].inventoryID = inventoryID items[name].count = count end end else -- Slot is now empty - clear inventoryID from any item that was there -- This is handled lazily by GetItem() fallback, so we skip expensive iteration end end CleveRoids.lastGetItem = nil CleveRoids.Items = items end function CleveRoids.IndexItems() local items = {} local NUM_BAG_SLOTS = NUM_BAG_SLOTS -- Upvalue for bag constant -- 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+)") local name, itemLink, _, _, itemType, itemSubType, _, _, texture = GetItemInfo(itemID) if name then local _, count = GetContainerItemInfo(bagID, slot) local existing = items[name] if not existing then items[name] = { bagID = bagID, slot = slot, id = itemID, name = name, type = itemType, count = count, texture = texture, link = itemLink, bagSlots = {{bagID, slot}}, slotsIndex = 1, } items[itemID] = name local lowerName = string_lower(name) if lowerName ~= name then items[lowerName] = name end else existing.count = (existing.count or 0) + (count or 0) table_insert(existing.bagSlots, {bagID, slot}) end end end end end -- Scan equipped items for inventoryID = 0, 19 do local link = GetInventoryItemLink("player", inventoryID) if link then local _, _, itemID = string_find(link, "item:(%d+)") local name, itemLink, _, _, itemType, itemSubType, _, _, texture = GetItemInfo(itemID) if name then local count = GetInventoryItemCount("player", inventoryID) local existing = items[name] if not existing then items[name] = { inventoryID = inventoryID, id = itemID, name = name, count = count, texture = texture, link = itemLink, } items[itemID] = name local lowerName = string_lower(name) if lowerName ~= name then items[lowerName] = name end else existing.inventoryID = inventoryID existing.count = (existing.count or 0) + (count or 0) end end end end CleveRoids.lastGetItem = nil CleveRoids.Items = items -- relink an item action to the item if it wasn't in inventory/bags before for slot, actions in CleveRoids.Actions do for i, action in actions.list do if action.item then action.item = CleveRoids.GetItem(action.action) end end end end function CleveRoids.ClearSlot(slots, slot) if slots[slot] then slots[slots[slot]] = nil end slots[slot] = nil end -- Local helper for 1.12.1: safely get action button info via tooltip function CleveRoids.GetActionButtonInfo(slot) if not CleveRoidsActionTooltip then CreateFrame("GameTooltip", "CleveRoidsActionTooltip", UIParent, "GameTooltipTemplate") end local tooltip = CleveRoidsActionTooltip tooltip:SetOwner(UIParent, "ANCHOR_NONE") tooltip:ClearLines() tooltip:SetAction(slot) local name, rank local text = _G["CleveRoidsActionTooltipTextLeft1"] if text then name = text:GetText() end local text2 = _G["CleveRoidsActionTooltipTextLeft2"] if text2 then local maybeRank = text2:GetText() -- Rank lines usually start with "Rank" if maybeRank and string.find(maybeRank, "Rank") then rank = maybeRank end end -- Determine if it's an item or spell based on texture (heuristic) local tex = GetActionTexture(slot) local actionType = tex and "SPELL" or "ITEM" return actionType, tex, name, rank end function CleveRoids.IndexActionSlot(slot) if not HasAction(slot) then CleveRoids.Actions[slot] = nil -- When clearing a reactive slot, check if we need to find it elsewhere local clearedReactiveName = CleveRoids.reactiveSlots[slot] CleveRoids.ClearSlot(CleveRoids.reactiveSlots, slot) CleveRoids.ClearSlot(CleveRoids.actionSlots, slot) -- If we cleared a reactive spell, rescan to find it in another slot if clearedReactiveName then for i = 1, 120 do if i ~= slot and HasAction(i) then local _, _, scanName = CleveRoids.GetActionButtonInfo(i) if scanName == clearedReactiveName then CleveRoids.reactiveSlots[clearedReactiveName] = i CleveRoids.reactiveSlots[i] = clearedReactiveName break end end end end else local actionType, _, name, rank = CleveRoids.GetActionButtonInfo(slot) if name then local reactiveName = CleveRoids.reactiveSpells[name] and name local actionSlotName = name..(rank and ("("..rank..")") or "") if reactiveName then -- Always update the mapping to ensure we track the correct slot -- Clear old mapping for this spell name first local oldSlot = CleveRoids.reactiveSlots[reactiveName] if oldSlot and oldSlot ~= slot then CleveRoids.reactiveSlots[oldSlot] = nil end CleveRoids.reactiveSlots[reactiveName] = slot CleveRoids.reactiveSlots[slot] = reactiveName elseif not reactiveName then CleveRoids.ClearSlot(CleveRoids.reactiveSlots, slot) end if actionType == "SPELL" or actionType == "ITEM" then if not CleveRoids.actionSlots[actionSlotName] then CleveRoids.actionSlots[actionSlotName] = slot CleveRoids.actionSlots[slot] = actionSlotName end end end end CleveRoids.TestForActiveAction(CleveRoids.GetAction(slot)) CleveRoids.SendEventForAction(slot, "ACTIONBAR_SLOT_CHANGED", slot) end function CleveRoids.IndexActionBars() for i = 1, 120 do CleveRoids.IndexActionSlot(i) end end function CleveRoids.GetSpell(text) text = CleveRoids.Trim(text) local rs, _, rank = string.find(text, "[^%s]%((Rank %d+)%)$") local name = rank and string.sub(text, 1, rs) or text for book, spells in CleveRoids.Spells do if spells and spells[name] then return spells[name][rank or "highest"] end end end function CleveRoids.GetTalent(text) text = CleveRoids.Trim(text) return CleveRoids.Talents[text] end -- PERFORMANCE: Helper functions defined once (not inline per-call) 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 texture = GetInventoryItemTexture("player", inventoryID) local count = GetInventoryItemCount("player", inventoryID) local it = { inventoryID = inventoryID, id = itemID, name = name, texture = texture, link = link, count = count } if name then Items[name] = it local lowerName = string_lower(name) if lowerName ~= name then Items[lowerName] = name end end if itemID then Items[itemID] = name end return it end local function makeBagItem(bagID, slot, link, Items) if not link then link = GetContainerItemLink(bagID, slot) end if not link then return end local _, _, itemID = string_find(link, "item:(%d+)") itemID = itemID and tonumber(itemID) or nil local name, _, _, _, _, _, _, _, texture = GetItemInfo(itemID) local count = 0 local tex, itemCount = GetContainerItemInfo(bagID, slot) if itemCount then count = itemCount end if not texture then texture = tex end local it = { bagID = bagID, slot = slot, id = itemID, name = name, texture = texture, link = link, count = count, bagSlots = { { bagID, slot } }, slotsIndex = 1 } if name then Items[name] = it local lowerName = string_lower(name) if lowerName ~= name then Items[lowerName] = name end end if itemID then Items[itemID] = name end return it end function CleveRoids.GetItem(text) if not text or text == "" then return end local Items = CleveRoids.Items local item = Items[text] or Items[tostring(text)] if not item then local lowerText = string_lower(text) local canonicalName = Items[lowerText] if canonicalName and type(canonicalName) == "string" then item = Items[canonicalName] end end if item then if type(item) == "table" then return item else return Items[tostring(item)] end end local qid = tonumber(text) local qname = (type(text) == "string") and string_lower(text) or nil -- Direct inventory slot lookup by ID if qid and qid >= 1 and qid <= 19 then local it = makeInventoryItem(qid, nil, Items) if it then return it end end -- Scan equipped items 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 if qid and itemID and qid == itemID then return makeInventoryItem(inv, link, Items) elseif qname and itemID then local nm = GetItemInfo(itemID) if nm and string_lower(nm) == qname then return makeInventoryItem(inv, link, Items) end end end end -- Scan bags for bag = 0, 4 do local slots = GetContainerNumSlots(bag) or 0 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 if qid and itemID and qid == itemID then return makeBagItem(bag, slot, link, Items) elseif qname and itemID then local nm = GetItemInfo(itemID) if nm and string_lower(nm) == qname then return makeBagItem(bag, slot, link, Items) end end end end end local name, link, _, _, _, _, _, _, texture = GetItemInfo(text) if not name then return end local fallback = { id = text, name = name, link = link, texture = texture } CleveRoids.Items[name] = fallback local lowerName = string.lower(name) if lowerName ~= name then CleveRoids.Items[lowerName] = name end return fallback end function CleveRoids.GetNextBagSlotForUse(item, text) if not item then return end if CleveRoids.lastGetItem == CleveRoids.GetItem(text) then if table.getn(item.bagSlots) > item.slotsIndex then item.slotsIndex = item.slotsIndex + 1 item.bagID, item.slot = unpack(item.bagSlots[item.slotsIndex]) end end CleveRoids.lastGetItem = item return item end local Extension = CleveRoids.RegisterExtension("Generic_show") Extension.RegisterEvent("SPELLS_CHANGED", "SPELLS_CHANGED") function Extension.OnLoad() end function Extension.SPELLS_CHANGED() end