Mercurial > wow > inventory
view Modules/Summary.lua @ 146:ebe6f90c4bb9
Fixed IM crafting so the bonus queue isn?t reset after an item is created.
author | Zerotorescue |
---|---|
date | Thu, 20 Jan 2011 00:15:09 +0100 |
parents | 396c2960d54d |
children | 15f22a466596 |
line wrap: on
line source
local addon = select(2, ...); -- Get a reference to the main addon object local mod = addon:NewModule("Summary", "AceEvent-3.0", "AceTimer-3.0"); -- register a new module, Summary: resposible for building the summary window local _G = _G; -- prevent looking up of the global table local sformat, sgsub, supper, mceil, mfloor, tinsert, twipe, tsort = _G.string.format, _G.string.gsub, _G.string.upper, _G.math.ceil, _G.math.floor, _G.table.insert, _G.table.wipe, _G.table.sort; local pairs, type, select = _G.pairs, _G.type, _G.select; local unknownItemName = "Unknown (#%d)"; local CACHE_ITEMS_TOTAL, CACHE_ITEMS_CURRENT, itemsCache = 0, 0, {}; local AceGUI, cacheStart; function mod:OnEnable() -- Register the summary specific widget addon:GetModule("Widgets"):InlineGroupWithButton(); AceGUI = LibStub("AceGUI-3.0"); -- Register our own slash commands -- /im summary addon:RegisterSlash(function() mod:BuildMain(); mod:Build(); end, { "s", "sum", "summary" }, "|Hfunction:InventoriumCommandHandler:summary|h|cff00fff7/im summary|r|h (or /im s) - Show the summary window containing an overview of all items within the groups tracked at this current character."); -- /im reset addon:RegisterSlash(function() if mod.frame then mod.frame:SetWidth(700); mod.frame:SetHeight(600); addon:Print("Resetting width and height of the summary frame."); end end, { "r", "reset" }, "|Hfunction:InventoriumCommandHandler:reset|h|cff00fff7/im reset|r|h (or /im r) - Reset the size of the summary frame."); end local function ShowTooltip(self) -- If this function is called from a widget, self is the widget and self.frame the actual frame local this = self.frame or self; GameTooltip:SetOwner(this, "ANCHOR_NONE"); GameTooltip:SetPoint("BOTTOM", this, "TOP"); GameTooltip:SetText(this.tooltipTitle, 1, .82, 0, 1); if type(this.tooltip) == "string" then GameTooltip:AddLine(this.tooltip, 1, 1, 1, 1); end GameTooltip:Show(); end local function HideTooltip() GameTooltip:Hide(); end function mod:BuildMain() LibStub("AceConfigDialog-3.0"):Close("InventoriumOptions"); self:CloseFrame(); -- Main Window mod.frame = AceGUI:Create("Frame"); _G["InventoriumSummary"] = mod.frame; -- name the global frame so it can be put in the UISpecialFrames mod.frame:SetTitle("Inventory Summary"); mod.frame:SetLayout("Fill"); mod.frame:SetCallback("OnClose", function(widget) mod:CancelTimer(self.tmrUpdater, true); mod:CloseFrame(); end); mod.frame:SetWidth(addon.db.profile.defaults.summary.width); mod.frame:SetHeight(addon.db.profile.defaults.summary.height); mod.frame.OnWidthSet = function(_, width) addon.db.profile.defaults.summary.width = width; end; mod.frame.OnHeightSet = function(_, height) addon.db.profile.defaults.summary.height = height; end; -- Close on escape tinsert(UISpecialFrames, "InventoriumSummary"); -- ScrollFrame child mod.scrollFrame = AceGUI:Create("ScrollFrame"); mod.scrollFrame:SetLayout("Flow"); mod.frame:AddChild(mod.scrollFrame); -- Reset items cache twipe(itemsCache); end function mod:CloseFrame() if mod.frame then mod.frame:Release(); mod.frame = nil; -- Stop caching -- Stop timer self:CancelTimer(self.tmrUpdater, true); -- Reset trackers CACHE_ITEMS_TOTAL = 0; CACHE_ITEMS_CURRENT = 0; end end local sortMethod = "item"; local sortDirectory = "ASC"; local function ReSort(subject) if sortMethod == subject then sortDirectory = (sortDirectory == "ASC" and "DESC") or "ASC"; else sortDirectory = "ASC"; end sortMethod = subject; mod:Build(); end -- From http://www.wowwiki.com/API_sort local function pairsByKeys (t, f) local a = {} for n in pairs(t) do tinsert(a, n) end tsort(a, f) local i = 0 -- iterator variable local iter = function () -- iterator function i = i + 1 if a[i] == nil then return nil else return a[i], t[a[i]] end end return iter end function mod:Build() local buildStartTime, times = GetTime(), {}; -- We are going to add hunderds of widgets to this container, but don't want it to also cause hunderds of reflows, thus pause reflowing and just do it once when everything is prepared -- This appears to be required for each container we wish to pause, so also do this for the contents mod.scrollFrame:PauseLayout(); mod.scrollFrame:ReleaseChildren(); -- Refresh button local btnRefresh = AceGUI:Create("Button"); btnRefresh:SetText("Refresh"); btnRefresh:SetRelativeWidth(.2); btnRefresh:SetCallback("OnClick", function() -- Reset items cache twipe(itemsCache); -- Rebuild itemlist and start caching mod:Build(); end); btnRefresh:SetCallback("OnEnter", ShowTooltip); btnRefresh:SetCallback("OnLeave", HideTooltip); btnRefresh.frame.tooltipTitle = "Refresh Cache"; btnRefresh.frame.tooltip = "Refresh the list and recache the item counts and auction values."; mod.scrollFrame:AddChild(btnRefresh); local lblSpacer = AceGUI:Create("Label"); lblSpacer:SetRelativeWidth(.03); mod.scrollFrame:AddChild(lblSpacer); -- Speed slider local sdrSpeed = AceGUI:Create("Slider"); sdrSpeed:SetLabel("Processing speed"); sdrSpeed:SetSliderValues(0.01, 5, 0.01); -- min, max, interval sdrSpeed:SetIsPercent(true); sdrSpeed:SetRelativeWidth(.3); sdrSpeed:SetCallback("OnMouseUp", function(self, event, value) addon.db.profile.defaults.summary.speed = mceil( ( ( value * 100 ) / 5) - .5 ); end); sdrSpeed:SetValue( addon.db.profile.defaults.summary.speed * 5 / 100 ); sdrSpeed:SetCallback("OnEnter", ShowTooltip); sdrSpeed:SetCallback("OnLeave", HideTooltip); sdrSpeed.frame.tooltipTitle = "Caching Processing Speed"; sdrSpeed.frame.tooltip = "Change the speed at which item counts and auction values are being cached. Higher is faster but may drastically reduce your FPS while caching.\n\nAnything above 100% will probably become uncomfortable."; mod.scrollFrame:AddChild(sdrSpeed); local lblSpacer = AceGUI:Create("Label"); lblSpacer:SetRelativeWidth(.23); mod.scrollFrame:AddChild(lblSpacer); -- Config button --[[local btnConfig = AceGUI:Create("Button"); btnConfig:SetText("Config"); btnConfig:SetRelativeWidth(.2); btnConfig:SetCallback("OnClick", function() --TODO: Tidy up SlashCmdList["INVENTORIUM"]("config"); end); btnConfig:SetCallback("OnEnter", ShowTooltip); btnConfig:SetCallback("OnLeave", HideTooltip); btnConfig.frame.tooltipTitle = "Config"; btnConfig.frame.tooltip = "Click to open the config window. This will close the current window."; mod.scrollFrame:AddChild(btnConfig);]] local lblSpacer = AceGUI:Create("Label"); lblSpacer:SetRelativeWidth(.03); mod.scrollFrame:AddChild(lblSpacer); -- Queue all button local btnQueueAll = AceGUI:Create("Button"); btnQueueAll:SetText("Queue All"); btnQueueAll:SetRelativeWidth(.2); btnQueueAll:SetCallback("OnClick", function() self:SendMessage("IM_QUEUE_ALL"); end); btnQueueAll:SetCallback("OnEnter", ShowTooltip); btnQueueAll:SetCallback("OnLeave", HideTooltip); btnQueueAll.frame.tooltipTitle = "Queue all"; btnQueueAll.frame.tooltip = "Queue everything that requires restocking within every single visible group."; mod.scrollFrame:AddChild(btnQueueAll); times.init = mceil( ( GetTime() - buildStartTime ) * 1000 ); addon:Debug("Time spent legend: (init / sorting / preparing / building / all)."); local playerName = UnitName("player"); -- Go through all our stored groups for groupName, values in pairsByKeys(addon.db.profile.groups, function(a, b) return a:lower() < b:lower(); end) do local groupStartTime, groupTimes = GetTime(), {}; local trackAt = addon:GetOptionByKey(groupName, "trackAtCharacters"); -- Does this group have any items and do we want to track it at this char? if values.items and trackAt[playerName] then -- Get group settings local minLocalStock = addon:GetOptionByKey(groupName, "minLocalStock"); local minGlobalStock = addon:GetOptionByKey(groupName, "minGlobalStock"); local showWhenBelow = addon:GetOptionByKey(groupName, "summaryThresholdShow"); local priceThreshold = addon:GetOptionByKey(groupName, "priceThreshold"); local hideWhenBelowPriceThreshold = addon:GetOptionByKey(groupName, "summaryHidePriceThreshold"); local alwaysGetAuctionValue = addon:GetOptionByKey(groupName, "alwaysGetAuctionValue"); -- Make group container local iGroup = AceGUI:Create("InlineGroupWithButton"); iGroup:PauseLayout(); iGroup:SetTitle(groupName); iGroup:SetFullWidth(true); iGroup:SetLayout("Flow"); iGroup:MakeButton({ name = "Queue", desc = "Queue all items in this group.", exec = function() addon:Print(("Queuing all items within %s craftable by the currently open profession."):format(groupName)); self:SendMessage("IM_QUEUE_GROUP", groupName); end, }); -- Headers -- Itemlink local lblItem = AceGUI:Create("InteractiveLabel"); lblItem:SetText("|cfffed000Item|r"); lblItem:SetFontObject(GameFontHighlight); lblItem:SetRelativeWidth(.7); lblItem:SetCallback("OnClick", function() ReSort("item"); end); lblItem:SetCallback("OnEnter", ShowTooltip); lblItem:SetCallback("OnLeave", HideTooltip); lblItem.frame.tooltipTitle = "Item"; lblItem.frame.tooltip = "Sort on the item quality, then name."; iGroup:AddChild(lblItem); -- Local quantity local lblLocal = AceGUI:Create("InteractiveLabel"); lblLocal:SetText("|cfffed000Loc.|r"); lblLocal:SetFontObject(GameFontHighlight); lblLocal:SetRelativeWidth(.099); lblLocal:SetCallback("OnClick", function() ReSort("local"); end); lblLocal:SetCallback("OnEnter", ShowTooltip); lblLocal:SetCallback("OnLeave", HideTooltip); lblLocal.frame.tooltipTitle = "Local stock"; lblLocal.frame.tooltip = "Sort on the amount of items currently in local stock."; iGroup:AddChild(lblLocal); -- Current quantity local lblQuantity = AceGUI:Create("InteractiveLabel"); lblQuantity:SetText("|cfffed000Cur.|r"); lblQuantity:SetFontObject(GameFontHighlight); lblQuantity:SetRelativeWidth(.099); lblQuantity:SetCallback("OnClick", function() ReSort("current"); end); lblQuantity:SetCallback("OnEnter", ShowTooltip); lblQuantity:SetCallback("OnLeave", HideTooltip); lblQuantity.frame.tooltipTitle = "Current stock"; lblQuantity.frame.tooltip = "Sort on the amount of items currently in stock."; iGroup:AddChild(lblQuantity); -- Lowest value local lblValue = AceGUI:Create("InteractiveLabel"); lblValue:SetText("|cfffed000Value|r"); lblValue:SetFontObject(GameFontHighlight); lblValue:SetRelativeWidth(.099); lblValue:SetCallback("OnClick", function() ReSort("value"); end); lblValue:SetCallback("OnEnter", ShowTooltip); lblValue:SetCallback("OnLeave", HideTooltip); lblValue.frame.tooltipTitle = "Value"; lblValue.frame.tooltip = "Sort on the item auction value."; iGroup:AddChild(lblValue); -- Retrieve items list if not itemsCache[groupName] then itemsCache[groupName] = {}; end if #(itemsCache[groupName]) == 0 then -- If the item cache is empty for itemId, _ in pairs(values.items) do local newItemData = addon.ItemData:New(itemId); -- if no price threshold is set for this item and you don't want to always get the auction value, then don't look it up either if priceThreshold == 0 and not alwaysGetAuctionValue then newItemData.value = -4; end tinsert(itemsCache[groupName], newItemData); CACHE_ITEMS_TOTAL = CACHE_ITEMS_TOTAL + 1; end end groupTimes.init = mceil( ( GetTime() - groupStartTime ) * 1000 ); -- Sort items tsort(itemsCache[groupName], function(a, b) local aRarity = a.rarity or 1; local bRarity = b.rarity or 1; if sortMethod == "item" and aRarity == bRarity then -- Do a name-compare for items of the same rarity -- Otherwise epics first, then junk local aName = a.name or sformat(unknownItemName, a.id); local bName = b.name or sformat(unknownItemName, b.id); if sortDirectory == "ASC" then return supper(aName) < supper(bName); else return supper(aName) > supper(bName); end elseif sortMethod == "item" then if sortDirectory == "ASC" then return aRarity > bRarity; -- the comparers were reversed because we want epics first else return aRarity < bRarity; -- the comparers were reversed because we want epics first end elseif sortMethod == "current" then if sortDirectory == "ASC" then return a.globalCount < b.globalCount; else return a.globalCount > b.globalCount; end elseif sortMethod == "local" then if sortDirectory == "ASC" then return a.localCount < b.localCount; else return a.localCount > b.localCount; end elseif sortMethod == "value" then if sortDirectory == "ASC" then return a.value < b.value; else return a.value > b.value; end end end); groupTimes.sorting = mceil( ( GetTime() - groupStartTime ) * 1000 ); -- Show itemslist for i, item in pairs(itemsCache[groupName]) do -- Go through all items for this group if (( item.globalCount / minGlobalStock ) < showWhenBelow or ( item.localCount / minLocalStock ) < showWhenBelow) and (not hideWhenBelowPriceThreshold or priceThreshold == 0 or item.value < 0 or item.value >= priceThreshold) then -- if the option "hide when below threshold" is disabled or no price threshold is set or the value is above the price threshold or the value could not be determined, proceed local btnItemLink = AceGUI:Create("ItemLinkButton"); btnItemLink:SetUserData("exec", function(_, itemId, _, buttonName) local itemName, itemLink = GetItemInfo(itemId); if buttonName == "LeftButton" and IsShiftKeyDown() and itemLink then ChatEdit_InsertLink(itemLink); elseif buttonName == "LeftButton" and IsAltKeyDown() and itemName and AuctionFrame and AuctionFrame:IsShown() and AuctionFrameBrowse then -- Start search at page 0 AuctionFrameBrowse.page = 0; -- Set the search field (even though we could use "ChatEdit_InsertLink", this ensures the right field is updated) BrowseName:SetText(itemName) QueryAuctionItems(itemName, nil, nil, 0, 0, 0, 0, 0, 0); end end); btnItemLink:SetRelativeWidth(.7); btnItemLink:SetItem(item); iGroup:AddChild(btnItemLink); -- Local quantity local lblLocal = AceGUI:Create("Label"); lblLocal:SetText(addon:DisplayItemCount(item.localCount, minLocalStock)); lblLocal:SetRelativeWidth(.099); iGroup:AddChild(lblLocal); -- Current quantity local lblQuantity = AceGUI:Create("Label"); lblQuantity:SetText(addon:DisplayItemCount(item.globalCount, minGlobalStock)); lblQuantity:SetRelativeWidth(.099); iGroup:AddChild(lblQuantity); -- Value local lblValue = AceGUI:Create("Label"); lblValue:SetText(self:DisplayMoney(item.value, priceThreshold)); lblValue:SetRelativeWidth(.099); iGroup:AddChild(lblValue); -- Remember references to the value and current fields so we can fill them later item.set.value = lblValue; item.set.globalCount = lblQuantity; item.set.localCount = lblLocal; end end groupTimes.preparing = mceil( ( GetTime() - groupStartTime ) * 1000 ); iGroup:ResumeLayout(); mod.scrollFrame:AddChild(iGroup); -- this can take up to .5 seconds, might need to look into again at a later time groupTimes.building = mceil( ( GetTime() - groupStartTime ) * 1000 ); end if groupStartTime and groupTimes then addon:Debug("Building of %s took %d ms (%d / %d / %d / %d / %d).", groupName, mceil( ( GetTime() - groupStartTime ) * 1000 ), groupTimes.init or 0, groupTimes.sorting or 0, groupTimes.preparing or 0, groupTimes.building or 0, mceil( ( GetTime() - buildStartTime ) * 1000 )); end end mod.scrollFrame:ResumeLayout(); mod.scrollFrame:DoLayout(); addon:Debug("Done building summary after %d ms.", mceil( ( GetTime() - buildStartTime ) * 1000 )); if CACHE_ITEMS_TOTAL > 0 then cacheStart = GetTime(); self:CancelTimer(self.tmrUpdater, true); self.tmrUpdater = self:ScheduleRepeatingTimer("UpdateNextItem", .01); -- Once every 100 frames (or once every x frames if you have less than 100 FPS, basically, once every frame) end end function mod:UpdateNextItem() local i = 0; for groupName, items in pairs(itemsCache) do local minGlobalStock = addon:GetOptionByKey(groupName, "minGlobalStock"); local minLocalStock = addon:GetOptionByKey(groupName, "minLocalStock"); local priceThreshold = addon:GetOptionByKey(groupName, "priceThreshold"); for _, item in pairs(items) do if item.globalCount == -3 or item.localCount == -3 or item.value == -3 then if item.globalCount == -3 then -- Only if item count was queued, update it item.globalCount = addon:GetItemCount(item.id, groupName); if item.set.globalCount and item.set.globalCount.SetText then item.set.globalCount:SetText(addon:DisplayItemCount(item.globalCount, minGlobalStock)); end end if item.localCount == -3 then -- Only if item count was queued, update it item.localCount = addon:GetLocalItemCount(item.id, groupName); if item.set.localCount and item.set.localCount.SetText then item.set.localCount:SetText(addon:DisplayItemCount(item.localCount, minLocalStock)); end end if item.value == -3 then -- Only if item value was queued, update it -- The itemlink might not have been loaded yet in which case we retry item.link = item.link or select(2, GetItemInfo(item.id)); if item.link then item.value = addon:GetAuctionValue(item.link, groupName); if item.set.value and item.set.value.SetText then item.set.value:SetText(self:DisplayMoney(item.value, priceThreshold)); end end end i = i + 1; CACHE_ITEMS_CURRENT = CACHE_ITEMS_CURRENT + 1; if mod.frame then mod.frame:SetStatusText(sformat("Caching auction values and item-counts... %d%% has already been processed.", mfloor(CACHE_ITEMS_CURRENT / CACHE_ITEMS_TOTAL * 100))); end if i >= addon.db.profile.defaults.summary.speed then return; end end end end -- Reset trackers CACHE_ITEMS_TOTAL = 0; CACHE_ITEMS_CURRENT = 0; -- Stop timer self:CancelTimer(self.tmrUpdater, true); -- Rebuild list so hidden items due to too low prices get added self:Build(); -- Announce mod.frame:SetStatusText("All required prices and itemcounts have been cached. This process took " .. mceil(GetTime() - cacheStart) .. " seconds."); -- Forget time cacheStart = nil; end function mod:DisplayMoney(value, priceThreshold) if value == -1 then return "|cff0000ffNone up|r"; elseif value == -2 then return "|cff0000ffNo AH mod|r"; elseif value == -3 then return "|cffffff00Unknown|r"; elseif value == -4 then return "|cff00ff00-|r"; elseif value == -5 then return "|cffff9933Error|r"; elseif priceThreshold and value < priceThreshold then return sformat("|cffaaaaaa%s|r", sgsub(addon:ReadableMoney(value or 0, true), "|([a-fA-F0-9]+)([gsc]+)|r", "%2")); else return addon:ReadableMoney(value or 0, true); end end function mod:NumberFormat(num) local formatted = sgsub(num, "(%d)(%d%d%d)$", "%1,%2", 1); local matches; while true do formatted, matches = sgsub(formatted, "(%d)(%d%d%d),", "%1,%2,", 1); if matches == 0 then break; end end return formatted; end