local addonName, addonTable = ...; --[[ Usage note: The methods of this object manipulate the Blizzard APIs to populate the fields of the Character object. To retrieve the final data, access the tables directly. --]] local Character = {}; Character.__index = Character; addonTable.Character = Character; local ItemScanner, left = CreateFrame("GameTooltip"), {} for i = 1, 5 do local L, R = ItemScanner:CreateFontString(), ItemScanner:CreateFontString() L:SetFontObject(GameFontNormal) R:SetFontObject(GameFontNormal) ItemScanner:AddFontStrings(L,R) left[i] = L end ItemScanner.left = left local function IsSoulboundItem(bag, slot) -- returns boolean ItemScanner:SetOwner(UIParent,"ANCHOR_NONE") ItemScanner:ClearLines() ItemScanner:SetBagItem(bag, slot) local t for i = 1, 5 do if ItemScanner.left[i]:GetText() == ITEM_SOULBOUND then return true end end ItemScanner:Hide() return false end ItemScanner:RegisterEvent("GET_ITEM_INFO_RECEIVED") ItemScanner.itemWaitingPool = {} function ItemScanner:AddToItemPool(link, callback) -- This check will itself send the remote request. Whee! if not GetItemInfo(link) then self.itemWaitingPool[link] = callback else callback(link) end end ItemScanner:SetScript("OnEvent", function(self, event, ...) if event == "GET_ITEM_INFO_RECEIVED" then for link, callback in pairs(self.itemWaitingPool) do if GetItemInfo(link) then self.itemWaitingPool[link] = nil callback(link) end end end end) function Character:new(db) local self = {}; setmetatable(self, Character); self.db = db; self.db.currencies = {}; -- <name, count> self.db.heirlooms = {}; -- <name, location> self.db.conveyable = {}; -- <name, count> self.miscData = {}; -- <key, data> -- Subtle bug averted here. If these functions are defined using colon syntax, it creates conflicting "self" references as written local function UpdateCurrencies() self:GetCurrencyData(); self.db.char.currency = self.currencies; addon:RedrawCurrencyFrame() end local function UpdateItems() self:GetHeirloomData(1); self:GetConveyableData(1); if self.isBankOpen then -- print("Scanning bank too!") self:GetHeirloomData(0); self:GetConveyableData(0); end self.db.char.heirloom = self.heirlooms self.db.char.conveyable = self.conveyable end return self; end function Character:GetName() return GetUnitName("player") end --[[ Collects the names and quantities of the character's Currencies. NOTE: Includes gold as well, though this is not considered a Currency by Blizzard @return currencies - an associative array mapping currency names to their quantities --]] function Character:GetCurrencyData() local currencies = {}; self:ExpandCurrencyHeaders(); for i=1, GetCurrencyListSize() do -- extraCurrencyType and itemID are used inconsistently, fuck em local name, isHeader, isExpanded, isUnused, isWatched, count, extraCurrencyType, icon, itemID = GetCurrencyListInfo(i); if (not isHeader) then currencies[name] = count; end end -- Using a global string, en-US "Money", as key. currencies[MONEY] = GetMoney(); self.currencies = currencies; return currencies; end --[[ Expands all header rows in the Currency list --]] function Character:ExpandCurrencyHeaders() local recheck = false; for i=1, GetCurrencyListSize() do local _, isHeader, isExpanded = GetCurrencyListInfo(i); if isHeader and (not isExpanded) then recheck = true; ExpandCurrencyList(i, 1); end end if recheck then self:ExpandCurrencyHeaders(); end end --[[ Collects a list of the heirloom items the character possesses, and their general locations (bank, bags, equipped). NOTE: Will fail to detect any heirlooms currently in a mailbox. @params location - 1 for bags, 2 for bank @return heirlooms - an associative array mapping heirloom itemIDs to their general location --]] function Character:GetHeirloomData(location) local heirlooms = {}; local link if location == 1 then for bag = 0, NUM_BAG_SLOTS do for slot = 1, GetContainerNumSlots(bag) do link = GetContainerItemLink(bag, slot) if link then ItemScanner:AddToItemPool(link, function(link) local name, id, rarity = GetItemInfo(link) if rarity == 7 then heirlooms[name] = {bag, slot} end end) end end end else for bag = NUM_BAG_SLOTS, NUM_BAG_SLOTS+NUM_BANKBAGSLOTS do if bag == NUM_BAG_SLOTS then bag = 0 end -- a little hackish; the main bank inventory is slot 0 and bank bags start at NUM_BAG_SLOTS+1 for slot = 1, GetContainerNumSlots(bag) do link = GetContainerItemLink(bag, slot) if link then ItemScanner:AddToItemPool(link, function(link) local name, id, rarity = GetItemInfo(link) if rarity == 7 then heirlooms[name] = {bag, slot} end end) end end end end self.heirlooms = heirlooms; return heirlooms; end --[[ Collects the names and quantities of conveyable items the character possesses, and their general locations (bank, bags, equipped). Conveyable is defined as any item that is not soulbound or an heirloom. @params location - 1 for bags, 2 for bank @return conveyables - an associative array mapping conveyable itemIDs to their general locations --]] function Character:UpdateBags(location) local conveyables = {}; local link, name, link, rarity, count if location == 1 then for bag = 0, NUM_BAG_SLOTS do for slot = 1, GetContainerNumSlots(bag) do link = GetContainerItemLink(bag, slot) if link then ItemScanner:AddToItemPool(link, function(link) -- local name, id, rarity = GetItemInfo(link) -- if rarity > 1 and rarity < 7 and not IsSoulboundItem(bag, slot) then -- skip heirlooms and vendor trash -- count = select(2, GetContainerItemInfo(bag, slot)) -- if not conveyables[name] then -- conveyables[name] = count -- else -- conveyables[name] = conveyables[name] +count -- end -- end end) end end end else for bag = NUM_BAG_SLOTS, NUM_BAG_SLOTS+NUM_BANKBAGSLOTS do if bag == NUM_BAG_SLOTS then bag = 0 end -- a little hackish; the main bank inventory is slot 0 and bank bags start at NUM_BAG_SLOTS+1 for slot = 1, GetContainerNumSlots(bag) do link = GetContainerItemLink(bag, slot) if link then ItemScanner:AddToItemPool(link, function(link) local name, id, rarity = GetItemInfo(link) if rarity > 1 and rarity < 7 and not IsSoulboundItem(bag, slot) then -- skip heirlooms and vendor trash count = select(2, GetContainerItemInfo(bag, slot)) if not conveyables[name] then conveyables[name] = count else conveyables[name] = conveyables[name] +count end end end) end end end end self.conveyables = conveyables; return conveyables; end --[[ Collects broad identifying information about the character. Currently collected data consists of: @return charData - an associative array mapping noteworthy quality names to their values --]] function Character:GetMiscData() local miscData; return miscData; end