-- LibItemUtils found in epgp - effort points/gear points loot system for World of Warcraft which is under a BSD license on google code. -- http://code.google.com/p/epgp/source/browse/LibItemUtils-1.0.lua -- Due to revision issues with minor version and also having LibDebug, I forked the lib, removing lib debug. -- Jafula. -- -- This library provides an interface to query if an item can be -- use by a certain class. The API is as follows: -- -- CanClassUse(class, itemType): class is one of **** and itemType a localized itemType (http://www.wowwiki.com/ItemType). -- local MAJOR_VERSION = "LibItemUtilsJamba-1.0" local MINOR_VERSION = 1 local lib, oldMinor = LibStub:NewLibrary(MAJOR_VERSION, MINOR_VERSION) if not lib then return end -- Inventory types are localized on each client. For this we need -- LibBabble-Inventory to unlocalize the strings. local LBIR = LibStub("LibBabble-Inventory-3.0"):GetReverseLookupTable() -- Class restrictions are localized on each client. For this we need -- LibBabble-Class to unlocalize the strings. local LBCR = LibStub("LibBabble-Class-3.0"):GetReverseLookupTable() local deformat = LibStub("LibDeformat-3.0") -- Make a frame for our repeating calls to GetItemInfo. lib.frame = lib.frame or CreateFrame("Frame", MAJOR_VERSION .. "_Frame") local frame = lib.frame frame:Hide() frame:SetScript('OnUpdate', nil) frame:UnregisterAllEvents() -- Use the GameTooltip or create a new one and initialize it -- Used to extract Class limitations for an item and binding type. lib.tooltip = lib.tooltip or CreateFrame("GameTooltip", MAJOR_VERSION .. "_Tooltip", frame, "GameTooltipTemplate") local tooltip = lib.tooltip local bindingFrame = getglobal(tooltip:GetName().."TextLeft2") local restrictedClassFrameNameFormat = tooltip:GetName().."TextLeft%d" tooltip:Hide(); ------------- -- OTHER ------------- --- Convert an itemlink to itemID -- @param itemlink of which you want the itemID from -- @returns number or nils function lib:ItemlinkToID(itemlink) if not itemlink then return nil end local itemID = strmatch(itemlink, 'item:(%d+)') if not itemID then return end return tonumber(itemID) end ------------- -- ITEM USAGE ------------- --[[ All item types we care about: Cloth = true, Leather = true, Mail = true, Plate = true, Shields = true, Bows = true, Crossbows = true, Daggers = true, ["Fist Weapons"] = true, Guns = true, ["One-Handed Axes"] = true, ["One-Handed Maces"] = true, ["One-Handed Swords"] = true, Polearms = true, Staves = true, ["Two-Handed Axes"] = true, ["Two-Handed Maces"] = true, ["Two-Handed Swords"] = true, Idols = true, Librams = true, Sigils = true, Thrown = true, Totems = true, Wands = true, --]] local disallowed = { DEATHKNIGHT = { Shields = true, Bows = true, Crossbows = true, Daggers = true, ["Fist Weapons"] = true, Guns = true, Polearms = true, Staves = true, Idols = true, Librams = true, Thrown = true, Totems = true, Wands = true, }, DRUID = { Mail = true, Plate = true, Shields = true, Bows = true, Crossbows = true, Guns = true, ["One-Handed Axes"] = true, ["One-Handed Swords"] = true, ["Two-Handed Axes"] = true, ["Two-Handed Swords"] = true, Librams = true, Sigils = true, Thrown = true, Totems = true, Wands = true, }, HUNTER = { Plate = true, Shields = true, ["One-Handed Maces"] = true, ["Two-Handed Maces"] = true, Idols = true, Librams = true, Sigils = true, Totems = true, Wands = true, }, MAGE = { Leather = true, Mail = true, Plate = true, Shields = true, Bows = true, Crossbows = true, ["Fist Weapons"] = true, Guns = true, ["One-Handed Axes"] = true, ["One-Handed Maces"] = true, Polearms = true, ["Two-Handed Axes"] = true, ["Two-Handed Maces"] = true, ["Two-Handed Swords"] = true, Idols = true, Librams = true, Sigils = true, Thrown = true, Totems = true, }, PALADIN = { Bows = true, Crossbows = true, ["Fist Weapons"] = true, Guns = true, Staves = true, Idols = true, Sigils = true, Thrown = true, Totems = true, Wands = true, }, PRIEST = { Leather = true, Mail = true, Plate = true, Shields = true, Bows = true, Crossbows = true, ["Fist Weapons"] = true, Guns = true, ["One-Handed Axes"] = true, ["One-Handed Swords"] = true, Polearms = true, ["Two-Handed Axes"] = true, ["Two-Handed Maces"] = true, ["Two-Handed Swords"] = true, Idols = true, Librams = true, Sigils = true, Thrown = true, Totems = true, }, ROGUE = { Mail = true, Plate = true, Shields = true, Polearms = true, Staves = true, ["Two-Handed Axes"] = true, ["Two-Handed Maces"] = true, ["Two-Handed Swords"] = true, Idols = true, Librams = true, Sigils = true, Totems = true, Wands = true, }, SHAMAN = { Plate = true, Bows = true, Crossbows = true, Guns = true, ["One-Handed Swords"] = true, Polearms = true, ["Two-Handed Swords"] = true, Idols = true, Librams = true, Sigils = true, Thrown = true, Wands = true, }, WARLOCK = { Leather = true, Mail = true, Plate = true, Shields = true, Bows = true, Crossbows = true, ["Fist Weapons"] = true, Guns = true, ["One-Handed Axes"] = true, ["One-Handed Maces"] = true, Polearms = true, ["Two-Handed Axes"] = true, ["Two-Handed Maces"] = true, ["Two-Handed Swords"] = true, Idols = true, Librams = true, Sigils = true, Thrown = true, Totems = true, }, WARRIOR = { Idols = true, Librams = true, Sigils = true, Totems = true, Wands = true, }, } function lib:ClassCanUse(class, item) local subType = select(7, GetItemInfo(item)) if not subType then return true end -- Check if this is a restricted class token. -- TODO(alkis): Possibly cache this check if performance is an issue. local link = select(2, GetItemInfo(item)) tooltip:SetOwner(UIParent, "ANCHOR_NONE") tooltip:SetHyperlink(link) -- lets see if we can find a 'Classes: Mage, Druid' string on the itemtooltip -- Only scanning line 2 is not enough, we need to scan all the lines for lineID = 1, tooltip:NumLines(), 1 do local line = _G[restrictedClassFrameNameFormat:format(lineID)] if line then local text = line:GetText() if text then local classList = deformat(text, ITEM_CLASSES_ALLOWED) if classList then tooltip:Hide() for _, restrictedClass in pairs({strsplit(',', classList)}) do restrictedClass = strupper(LBCR[strtrim(restrictedClass)]) if class == restrictedClass then return true end end return false end end end end tooltip:Hide() -- Check if players can equip this item. subType = LBIR[subType] if disallowed[class][subType] then return false end return true end function lib:ClassCannotUse(class, item) return not self:ClassCanUse(class, item) end local function NewTableOrClear(t) if not t then return {} end wipe(t) return t end function lib:ClassesThatCanUse(item, t) t = NewTableOrClear(t) for class, _ in pairs(RAID_CLASS_COLORS) do if self:ClassCanUse(class, item) then table.insert(t, class) end end return t end function lib:ClassesThatCannotUse(item, t) t = NewTableOrClear(t) for class, _ in pairs(RAID_CLASS_COLORS) do if self:ClassCannotUse(class, item) then table.insert(t, class) end end return t end ----------------- -- ITEMS FOR SLOT ----------------- local slot_table = { INVTYPE_HEAD = {"HeadSlot", nil}, INVTYPE_NECK = {"NeckSlot", nil}, INVTYPE_SHOULDER = {"ShoulderSlot", nil}, INVTYPE_CLOAK = {"BackSlot", nil}, INVTYPE_CHEST = {"ChestSlot", nil}, INVTYPE_WRIST = {"WristSlot", nil}, INVTYPE_HAND = {"HandsSlot", nil}, INVTYPE_WAIST = {"WaistSlot", nil}, INVTYPE_LEGS = {"LegsSlot", nil}, INVTYPE_FEET = {"FeetSlot", nil}, INVTYPE_SHIELD = {"SecondaryHandSlot", nil}, INVTYPE_ROBE = {"ChestSlot", nil}, INVTYPE_2HWEAPON = {"MainHandSlot", "SecondaryHandSlot"}, INVTYPE_WEAPONMAINHAND = {"MainHandSlot", nil}, INVTYPE_WEAPONOFFHAND = {"SecondaryHandSlot", "MainHandSlot"}, INVTYPE_WEAPON = {"MainHandSlot","SecondaryHandSlot"}, INVTYPE_THROWN = {"RangedSlot", nil}, INVTYPE_RANGED = {"RangedSlot", nil}, INVTYPE_RANGEDRIGHT = {"RangedSlot", nil}, INVTYPE_FINGER = {"Finger0Slot", "Finger1Slot"}, INVTYPE_HOLDABLE = {"SecondaryHandSlot", "MainHandSlot"}, INVTYPE_TRINKET = {"Trinket0Slot", "Trinket1Slot"}, -- Hack for Tier 9 25M heroic tokens. -- TODO(alkis): Fix this to return more than 2 slots because these tokens -- can go in any of 5 slots. INVTYPE_CUSTOM_MULTISLOT_TIER = {"HeadSlot", "ChestSlot"} } function lib:ItemsForSlot(invtype, unit) local t = slot_table[invtype] if not t then return end local first, second = unpack(t) -- Translate to slot ids first = first and GetInventorySlotInfo(first) second = second and GetInventorySlotInfo(second) -- Translate to item links first = first and GetInventoryItemLink(unit or "player", first) second = second and GetInventoryItemLink(unit or "player", second) return first, second end ---------------- -- ITEM BINDINGS ---------------- -- binding is one of: ITEM_BIND_ON_PICKUP, ITEM_BIND_ON_EQUIP, -- ITEM_BIND_ON_USE, ITEM_BIND_TO_ACCOUNT function lib:IsBinding(binding, item) local link = select(2, GetItemInfo(item)) tooltip:SetOwner(UIParent, "ANCHOR_NONE") tooltip:SetHyperlink(link) if tooltip:NumLines() > 1 then local text = bindingFrame:GetText() if text then return text == binding end end tooltip:Hide() end function lib:IsBoP(item) return lib:IsBinding(ITEM_BIND_ON_PICKUP, item) end function lib:IsBoE(item) return lib:IsBinding(ITEM_BIND_ON_EQUIP, item) end -------------- -- ITEMCACHING -------------- -- Reuse or create a table to store the lookup queue in lib.itemQueue = lib.itemQueue or {} local itemQueue = lib.itemQueue --- Try to lookup the items on the itemQueue -- -- This will lookup all the items on the itemQueue, and if found it -- will call the callbacks and remove them. If they are not found it -- will retry once per second for a max of 30 seconds after the last -- callback was called, and after that it will give up. local timeout = 0 local ticker = 0 local function LookupItems(frame, elapsed) timeout = timeout + elapsed ticker = ticker + elapsed if timeout > 30 then wipe(itemQueue) ticker = 0 frame:Hide() return end if ticker > 1 then ticker = 0 -- Go through all the items and check if they have data in the -- client cache. If the do call the saved functions and args. for itemLink, itemData in pairs(itemQueue) do if GetItemInfo(itemLink) then -- If we found an item, reset the timeout. timeout = 0 itemQueue[itemLink] = nil for callback, args in pairs(itemData) do pcall(callback, unpack(args)) end else -- Otherwise set the hyperlink on a tooltip to make the cache -- fetch it. tooltip:SetHyperlink(itemLink) tooltip:Show() tooltip:Hide() end end end -- If we have no more items in the itemQueue to lookup, reset the -- timeout and hide the frame. if not next(itemQueue) then timeout = 0 ticker = 0 frame:Hide() end end frame:SetScript("OnUpdate", LookupItems) --- Try to cache an item and call the callback function when the item --- is available -- -- @param itemLink any itemLink in Hitem:1234 form -- @param callback function pointer to the callback function that -- should be called when the item is available -- @param ... a list of variables you would like to pass to the -- callback function. -- @return boolean true if the item has been registered successfully function lib:CacheItem(itemLink, callback, ...) -- Reset the timeout for the itemQueue timeout = 0 if type(itemLink) == 'number' then itemLink = format('item:%d', itemLink) end if type(callback) ~= "function" then error("Usage: CacheItem(itemLink, callback, [...]): 'callback' - function.", 2) end if not itemLink or not strmatch(itemLink, 'item:(%d+)') then error("Usage: CacheItem(itemLink, callback, [...]): 'itemLink' - not a valid itemLink (item:12345).", 2) end itemQueue[itemLink] = itemQueue[itemLink] or {} itemQueue[itemLink][callback] = {...} -- show the frame to start looking up items frame:Show() return true end