Mercurial > wow > inventory
changeset 176:26c750a10b14
Renamed Inventorium debug channel to IMDebug (so it?s easier to recognize only IM changes, not from other addons), write /im d to register this new channel.
Implemented stock alerts.
Added ?don?t alert at characters? option which allows you to track groups at characters without being bothered about low stock.
You can change the speed of the stock alert at the extra config tab.
author | Zerotorescue |
---|---|
date | Sun, 30 Jan 2011 15:39:18 +0100 |
parents | 7aa0850952ef |
children | 210cf5679fc1 |
files | .hgignore Core.lua Modules/Alerts.lua Modules/Config.lua |
diffstat | 4 files changed, 449 insertions(+), 23 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Thu Jan 27 22:51:55 2011 +0100 +++ b/.hgignore Sun Jan 30 15:39:18 2011 +0100 @@ -1,3 +1,2 @@ glob:Libs glob:TODO.txt -glob:Modules/Alerts.lua
--- a/Core.lua Thu Jan 27 22:51:55 2011 +0100 +++ b/Core.lua Sun Jan 30 15:39:18 2011 +0100 @@ -41,6 +41,7 @@ itemCountAddon = "Altoholic", craftingAddon = "AdvancedTradeSkillWindow", trackAtCharacters = { }, + dontAlertAtCharacters = { }, localItemData = { ["Bag"] = true, ["Auction House"] = true, @@ -66,6 +67,7 @@ -- Global (can't be overridden) hideHelp = false, + scanInterval = "0.1", -- string because the associated select requires it to be summary = { speed = 5, width = 700, @@ -112,7 +114,7 @@ for i = 1, NUM_CHAT_WINDOWS do local name = GetChatWindowInfo(i); - if string.upper(name) == "DEBUG" then + if string.upper(name) == "IMDEBUG" then addon:Print("A debug channel already exists, removing the old one... (" .. i .. ")"); FCF_Close(_G["ChatFrame" .. i]); end @@ -120,13 +122,13 @@ if not this.debugChannel then -- Create a new debug channel - local chatFrame = FCF_OpenNewWindow('Debug'); + local chatFrame = FCF_OpenNewWindow('IMDebug'); ChatFrame_RemoveAllMessageGroups(chatFrame); this.debugChannel = chatFrame; addon:Print("New debug channel created."); end - end, { "d", "debug" }); + end, { "d", "debug", "imdebug" }); -- Remember this character is on this account local playerName = UnitName("player"); @@ -373,6 +375,8 @@ return "|cffffff00Unknown|r"; elseif value == -3 then return "|cffffff00Unknown|r"; + elseif value == -4 then + return "|cffffff00-|r"; else return sformat("%s / %d", self:ColorCode(value, minimumStock), minimumStock); end @@ -507,19 +511,38 @@ -- Debug -local function ReadableTable(t) - local temp = ""; - +local function ReadableTable(t, includeKeys, jumps) + local tabs = ""; + for i = 1, (jumps or 0) do + tabs = tabs .. " "; + end + + local temp = "{\n"; + for i, v in pairs(t) do - temp = temp .. " {"; if type(v) == "table" then - temp = temp .. ReadableTable(v) .. ","; + if includeKeys then + local key = (type(i) == "number" and tostring(i)) or string.format("\"%s\"", tostring(i)); + + temp = string.format("%s%s [%s] => %s,\n", temp, tabs, key, ReadableTable(v, includeKeys, (jumps or 0) + 1)); + else + temp = string.format("%s%s %s,\n", temp, tabs, ReadableTable(v, includeKeys, (jumps or 0) + 1)); + end else - temp = temp .. tostring(v) .. ","; + if includeKeys then + local key = (type(i) == "number" and tostring(i)) or string.format("\"%s\"", tostring(i)); + local value = (type(v) == "number" and tostring(v)) or string.format("\"%s\"", tostring(v)); + + temp = string.format("%s%s [%s] => %s,\n", temp, tabs, key, value); + else + local value = (type(v) == "number" and tostring(v)) or string.format("\"%s\"", tostring(v)); + + temp = string.format("%s%s %s,\n", temp, tabs, value); + end end - temp = temp .. "}"; end - + temp = temp .. tabs .. "}"; + return temp; end @@ -531,7 +554,7 @@ for i = 1, NUM_CHAT_WINDOWS do local name = GetChatWindowInfo(i); - if name:upper() == "DEBUG" then + if name:upper() == "IMDEBUG" then self.debugChannel = _G["ChatFrame" .. i]; end end @@ -539,7 +562,7 @@ if self.debugChannel then if type(t) == "table" then - t = ReadableTable(t); + t = ReadableTable(t, true); end self.debugChannel:AddMessage("|cffffff00Inventorium|r:" .. sformat(t, ...));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/Alerts.lua Sun Jan 30 15:39:18 2011 +0100 @@ -0,0 +1,307 @@ +local addon = select(2, ...); +local mod = addon:NewModule("Alerts", "AceEvent-3.0", "AceTimer-3.0"); + +local queue, cache = {}, {}; + +function mod:OnEnable() + addon:Debug("Alerts:OnEnable"); + + -- Register our alert slash command + -- /im alert + addon:RegisterSlash(function(this) + mod:Scan(true); + end, { "a", "alert" }, "|Hfunction:InventoriumCommandHandler:alert|h|cff00fff7/im alert|r|h (or /im a) - Rescan the items within all tracked groups and show item alerts for those items missing."); + + self:RegisterEvent("PLAYER_LOGIN", function() + mod:Scan(false); + end); + + --[[if addon.db.profile.defaults.scanInterval["00Login"] then + self:RegisterEvent("PLAYER_LOGIN", "Scan"); + end + + if addon.db.profile.defaults.scanInterval["01Repeat5"] then + self:ScheduleTimer("Scan", 5 * 60); + elseif addon.db.profile.defaults.scanInterval["01Repeat10"] then + self:ScheduleTimer("Scan", 10 * 60); + elseif addon.db.profile.defaults.scanInterval["01Repeat15"] then + self:ScheduleTimer("Scan", 15 * 60); + elseif addon.db.profile.defaults.scanInterval["01Repeat30"] then + self:ScheduleTimer("Scan", 30 * 60); + elseif addon.db.profile.defaults.scanInterval["01Repeat60"] then + self:ScheduleTimer("Scan", 60 * 60); + elseif addon.db.profile.defaults.scanInterval["01Repeat120"] then + self:ScheduleTimer("Scan", 120 * 60); + end]] +end + +local scanTypes = { + Local = "local", + Global = "global", +}; + +function mod:Scan(verbal) + addon:Debug("Alerts:Scan"); + + if verbal then + addon:Print("Scanning items, the results will be presented to you in a moment. Please be patient."); + end + + local playerName = UnitName("player"); + + table.wipe(queue); + table.wipe(cache); + + -- Go through all groups + for groupName, values in pairs(addon.db.profile.groups) do + -- Settings + local trackAt = addon:GetOptionByKey(groupName, "trackAtCharacters"); + local dontAlertAt = addon:GetOptionByKey(groupName, "dontAlertAtCharacters"); + + local alertBelowLocalMinimum = addon:GetOptionByKey(groupName, "alertBelowLocalMinimum"); + local minLocalStock = alertBelowLocalMinimum and addon:GetOptionByKey(groupName, "minLocalStock"); + + local alertBelowGlobalMinimum = addon:GetOptionByKey(groupName, "alertBelowGlobalMinimum"); + local minGlobalStock = alertBelowGlobalMinimum and addon:GetOptionByKey(groupName, "minGlobalStock"); + + local isTracked = (trackAt and trackAt[playerName] and (not dontAlertAt or not dontAlertAt[playerName])); -- Is this character interested in this data and does it want alerts? + + if values.items and isTracked and (alertBelowLocalMinimum or alertBelowGlobalMinimum) then + addon:Debug("Scanning |cff00ff00%s|r", groupName); + + local groupSettings = { + ["name"] = groupName, + ["minLocalStock"] = minLocalStock, + ["minGlobalStock"] = minGlobalStock, + }; + + for itemId, _ in pairs(values.items) do + if alertBelowLocalMinimum then + self:QeueueScan(groupSettings, itemId, scanTypes.Local); + end + + if alertBelowGlobalMinimum then + self:QeueueScan(groupSettings, itemId, scanTypes.Global); + end + end + end + end + + if self:HasQueue() then + addon:Debug("Alerts:HasQueue, processing"); + --addon:Debug(queue); + + self:ProcessScan(verbal); + end +end + +function mod:QeueueScan(groupSettings, itemId, scanType) + table.insert(queue, { + ["group"] = groupSettings, + ["itemId"] = itemId, + ["type"] = scanType, + }); +end + +function mod:HasQueue() + return (#queue > 0); +end + +function mod:ProcessScan(verbal) + local thisItem = table.remove(queue, 1); + + if not thisItem then + self:ScanFinished(); + return; + end + + if thisItem.type == scanTypes.Global then + local globalCount = addon:GetItemCount(thisItem.itemId, thisItem.group.name); + + if not cache[thisItem.itemId] then + cache[thisItem.itemId] = { + ["itemId"] = thisItem.itemId, -- needed later for displaying + ["group"] = thisItem.group, + ["globalCount"] = globalCount, + }; + else + cache[thisItem.itemId].globalCount = globalCount; + end + elseif thisItem.type == scanTypes.Local then + local localCount = addon:GetLocalItemCount(thisItem.itemId, thisItem.group.name); + + if not cache[thisItem.itemId] then + cache[thisItem.itemId] = { + ["itemId"] = thisItem.itemId, -- needed later for displaying + ["group"] = thisItem.group, + ["localCount"] = localCount, + }; + else + cache[thisItem.itemId].globalCount = localCount; + end + end + + local nextScanDelay = (tonumber(addon.db.profile.defaults.scanInterval) or .1); + + if nextScanDelay == 0 then + mod:ProcessScan(verbal); + else + self:ScheduleTimer(function() + mod:ProcessScan(verbal); + end, nextScanDelay); -- scan next item in nextScanDelay seconds + end +end + +local function OnProceed() + InventoriumCommandHandler("summary"); + + if InventoriumItemMover then + InventoriumItemMover:Hide(); + end +end + +local function OnCancel() + if InventoriumItemMover then + InventoriumItemMover:Hide(); + end +end + +local function UseScanST() + if not InventoriumItemMover then + addon:CreateMoverFrame(); + end + + local frame = InventoriumItemMover; -- both for speed as code-consistency + + -- Scrolling table with a list of items to be moved + local scrollTableWidth = ( frame.frmMeasureDummy:GetWidth() - 30 ); -- adjust width by the scrollbar size + local headers = { + { + ["name"] = "Item", + ["width"] = (scrollTableWidth * .6), + ["defaultsort"] = "asc", + ["comparesort"] = function(this, aRow, bRow, column) + local aName, _, aRarity = GetItemInfo(this:GetRow(aRow).rowData.itemId); + local bName, _, bRarity = GetItemInfo(this:GetRow(bRow).rowData.itemId); + local template = "%d%s"; + aName = template:format((10 - (aRarity or 10)), (aName or ""):lower()); + bName = template:format((10 - (bRarity or 10)), (bName or ""):lower()); + + if this.cols[column].sort == "dsc" then + return aName > bName; + else + return aName < bName; + end + end, + ["sort"] = "asc", -- when the data is set, use this column so sort the default data + ["tooltipTitle"] = "Item", + ["tooltip"] = "Click to sort the list by item quality then item name.", + }, + { + ["name"] = "Local", + ["width"] = (scrollTableWidth * .2), + ["align"] = "RIGHT", + ["defaultsort"] = "dsc", + ["sortnext"] = 1, + ["comparesort"] = function(this, aRow, bRow, column) + local aAvailablePercent = (this:GetRow(aRow).rowData.localCount / this:GetRow(aRow).rowData.group.minLocalStock); + local bAvailablePercent = (this:GetRow(bRow).rowData.localCount / this:GetRow(bRow).rowData.group.minLocalStock); + + if this.cols[column].sort == "dsc" then + return aAvailablePercent > bAvailablePercent; + else + return aAvailablePercent < bAvailablePercent; + end + end, + ["tooltipTitle"] = "Local Stock", + ["tooltip"] = "Click to sort the list by the local stock percentage.", + }, + { + ["name"] = "Global", + ["width"] = (scrollTableWidth * .2), + ["align"] = "RIGHT", + ["defaultsort"] = "dsc", + ["sortnext"] = 1, + ["comparesort"] = function(this, aRow, bRow, column) + local aAvailablePercent = (this:GetRow(aRow).rowData.globalCount / this:GetRow(aRow).rowData.group.minGlobalStock); + local bAvailablePercent = (this:GetRow(bRow).rowData.globalCount / this:GetRow(bRow).rowData.group.minGlobalStock); + + if this.cols[column].sort == "dsc" then + return aAvailablePercent > bAvailablePercent; + else + return aAvailablePercent < bAvailablePercent; + end + end, + ["tooltipTitle"] = "Global Stock", + ["tooltip"] = "Click to sort the list by the global stock percentage.", + }, + }; + + local proceedButton = { + text = "Show in summary", + tooltipTitle = "Show in Summary", + tooltip = "Show the summary giving a more detailed list of missing items.", + onClick = OnProceed, + }; + local cancelButton = { + text = "Cancel", + tooltipTitle = "Cancel", + tooltip = "Close the window.", + onClick = OnCancel, + }; + + addon:SetMoverFrameSettings("Inventorium Stock Alert", "You have elected to receive an alert when the following items are under your minimum stock requirement:", proceedButton, cancelButton, headers); +end + +function mod:ScanFinished() + table.wipe(queue); + + --addon:Debug(cache); + + -- This table is never copied, just referenced. It is the same for every row. + local missingRow = { + { + ["value"] = function(data, cols, realrow, column, table) + return IdToItemLink(data[realrow].rowData.itemId); + end, + }, -- item + { + ["value"] = function(data, cols, realrow, column, table) + return addon:DisplayItemCount(data[realrow].rowData.localCount or -4, data[realrow].rowData.group.minLocalStock); + end, + }, -- local + { + ["value"] = function(data, cols, realrow, column, table) + return addon:DisplayItemCount(data[realrow].rowData.globalCount or -4, data[realrow].rowData.group.minGlobalStock); + end, + }, -- global + }; + + -- Store the list with rows in this + local missingList = {}; + + for itemId, info in pairs(cache) do + if (info.globalCount and info.globalCount < info.group.minGlobalStock) or (info.localCount and info.localCount < info.group.minLocalStock) then + -- Not enough global or not enough local + + tinsert(missingList, { + ["rowData"] = info, -- this is not a key usually found in a row item and ignored by the library + ["cols"] = missingRow, + }); + end + end + + table.wipe(cache); -- no longer needed, all missing items are now stored in the missingList + + if #missingList > 0 then + UseScanST(); + + addon:SetMoverFrameData(missingList); + + if verbal then + addon:Print("Presenting the data..."); + end + elseif verbal then + addon:Print("No items that you elected to be alerted about are below the selected stock thresholds."); + end +end
--- a/Modules/Config.lua Thu Jan 27 22:51:55 2011 +0100 +++ b/Modules/Config.lua Sun Jan 30 15:39:18 2011 +0100 @@ -329,14 +329,14 @@ order = 19, type = "toggle", name = "Override track at", - desc = "Allows you to override at which characters items in this group should appear in the summary and generate alerts.", + desc = "Allows you to override at which characters groups should be tracked, appear in the summary or generate alerts.", arg = "trackAtCharacters", }, trackAtCharacters = { order = 20, type = "multiselect", name = "Track at", - desc = "Select at which characters this should appear in the summary and generate alerts.", + desc = "Select at which characters groups should be tracked, appear in the summary or generate alerts.", values = function() local temp = {}; for charName in pairs(addon.db.factionrealm.characters) do @@ -349,6 +349,30 @@ dialogControl = "Dropdown", -- this is not standard, normal multiselect control gives us a list of all chars with toggle-boxes. UGLY! We want a multiselect-box instead. arg = "overrideTrackAtCharacters", }, + overrideDontAlertAtCharacters = { + order = 29, + type = "toggle", + name = "Override not alerting at", + desc = "Allows you to override at which characters items in groups should |cffff0000not|r generate alerts when they are below the required stock.", + arg = "dontAlertAtCharacters", + }, + dontAlertAtCharacters = { + order = 30, + type = "multiselect", + name = "Do |cffff0000not|r alert at", + desc = "Select at which characters items in groups should |cffff0000not|r generate alerts when they are below the required stock.", + values = function() + local temp = {}; + for charName in pairs(addon.db.factionrealm.characters) do + temp[charName] = charName; + end + + return temp; + end, + get = GetMultiOption, + dialogControl = "Dropdown", -- this is not standard, normal multiselect control gives us a list of all chars with toggle-boxes. UGLY! We want a multiselect-box instead. + arg = "overrideDontAlertAtCharacters", + }, overrideLocalItemData = { order = 39, type = "toggle", @@ -1307,6 +1331,8 @@ temp.name = groupName; temp.trackAtCharacters = nil; temp.overrideTrackAtCharacters = nil; + temp.dontAlertAtCharacters = nil; + temp.overrideDontAlertAtCharacters = nil; if not AceSerializer then AceSerializer = LibStub("AceSerializer-3.0"); @@ -1415,9 +1441,8 @@ trackAtCharacters = { order = 10, type = "multiselect", - width = "double", - name = "Track at these characters:", - desc = "Select at which characters this should appear in the summary and generate alerts.", + name = "Track at", + desc = "Select at which characters groups should be tracked, appear in the summary or generate alerts.", values = function() local temp = {}; for charName in pairs(addon.db.factionrealm.characters) do @@ -1434,10 +1459,48 @@ end, dialogControl = "Dropdown", -- this is not standard, normal multiselect control gives us a list of all chars with toggle-boxes. UGLY! We want a multiselect-box instead. }, + dontAlertAtCharacters = { + order = 15, + type = "multiselect", + name = "Do |cffff0000not|r alert at:", + desc = "Select at which characters items in groups should |cffff0000not|r generate alerts when they are below the required stock.", + values = function() + local temp = {}; + for charName in pairs(addon.db.factionrealm.characters) do + temp[charName] = charName; + end + + return temp; + end, + get = function(i, v) + return addon.db.profile.defaults.dontAlertAtCharacters[v]; + end, + set = function(i, v, e) + addon.db.profile.defaults.dontAlertAtCharacters[v] = e or nil; + end, + dialogControl = "Dropdown", -- this is not standard, normal multiselect control gives us a list of all chars with toggle-boxes. UGLY! We want a multiselect-box instead. + }, + --[[stockLevelAlertScanRepeatInterval = { + order = 17, + type = "multiselect", + name = "Stock scan repeat interval", + desc = "Select when or how often your stock levels should be checked and show alerts.", + values = { + ["00Login"] = "At login", + ["01Repeat5"] = "Every 5 minutes", + ["02Repeat10"] = "Every 10 minutes", + ["03Repeat15"] = "Every 15 minutes", + ["04Repeat30"] = "Every 30 minutes", + ["05Repeat60"] = "Every 1 hour", + ["06Repeat120"] = "Every 2 hours", + }, + get = function(i, v) return addon.db.profile.defaults.scanRepeatInterval and addon.db.profile.defaults.scanRepeatInterval[v]; end, + set = function(i, v, e) addon.db.profile.defaults.scanRepeatInterval[v] = e; end, -- can't be nil or the defaults will be used + dialogControl = "Dropdown", -- this is not standard, normal multiselect control gives us a list of all chars with toggle-boxes. UGLY! We want a multiselect-box instead. + },]] localItemData = { order = 20, type = "multiselect", - width = "double", name = "Include in local item data", desc = "Select which data should be included in the local item data.", values = { @@ -1470,6 +1533,7 @@ name = "", hidden = function() return addon.db.profile.defaults.hideHelp; end, }, + minLocalStock = { order = 10, type = "range", @@ -1498,6 +1562,7 @@ get = function() return addon.db.profile.defaults.autoRefill; end, set = function(i, v) addon.db.profile.defaults.autoRefill = v; end, }, + minGlobalStock = { order = 20, type = "range", @@ -1518,6 +1583,7 @@ get = function() return addon.db.profile.defaults.alertBelowGlobalMinimum; end, set = function(i, v) addon.db.profile.defaults.alertBelowGlobalMinimum = v; end, }, + summaryThresholdShow = { order = 30, type = "range", @@ -1738,7 +1804,7 @@ local currentAddon, selectedAddonName = addon:GetItemCountAddon(groupName); - if currentAddon.GetGuildNames then + if currentAddon and currentAddon.GetGuildNames then local guilds = currentAddon.GetGuildNames(); if guilds and type(guilds) == "table" then @@ -1759,7 +1825,7 @@ get = function(i, v) local currentAddon, selectedAddonName = addon:GetItemCountAddon(groupName); - return currentAddon.GetGuildNames and currentAddon.GetGuildNames()[v]; + return currentAddon and currentAddon.GetGuildNames and currentAddon.GetGuildNames()[v]; end, set = function(i, v, e) local currentAddon, selectedAddonName = addon:GetItemCountAddon(groupName); @@ -1771,7 +1837,7 @@ addon.db.profile.defaults.itemCountGuildsExcluded[v] = true; -- this is excluded, excluded is indicated by true end - if currentAddon.SetGuildState then + if currentAddon and currentAddon.SetGuildState then currentAddon.SetGuildState(v, e); end end, -- can't be nil or the defaults will be used @@ -1815,6 +1881,30 @@ get = function() return addon.db.profile.defaults.autoRefillSkipConfirm; end, set = function(i, v) addon.db.profile.defaults.autoRefillSkipConfirm = v; end, }, + stockAlertScanInterval = { + order = 25, + type = "select", + width = "double", + name = "Stock scan speed", + desc = "Select the speed at which items should be scanned for stock alerts. Faster requires more resources and may drastically reduce your frame rate during a scan.", + values = { + ["0"] = "Instant", -- chains everything, no delay used + ["0.01"] = "Very fast", -- < 100 fps: once every frame, >= 100 fps, 100 scans per second + ["0.05"] = "Fast", + ["0.1"] = "Default", + ["0.2"] = "Medium", + ["0.3"] = "Slow", + ["0.5"] = "Very slow", + }, + get = function() return addon.db.profile.defaults.scanInterval; end, + set = function(i, v) addon.db.profile.defaults.scanInterval = v; end, + }, + spacer = { + order = 26, + type = "description", + name = "", + width = "double", + }, removeCharacter = { order = 30, type = "select", @@ -1839,10 +1929,15 @@ if value and value ~= "" then addon.db.factionrealm.characters[value] = nil; addon.db.profile.defaults.trackAtCharacters[value] = nil; + addon.db.profile.defaults.dontAlertAtCharacters[value] = nil; for name, values in pairs(addon.db.profile.groups) do if values.trackAtCharacters then values.trackAtCharacters[name] = nil; end + + if values.dontAlertAtCharacters then + values.dontAlertAtCharacters[name] = nil; + end end end end, @@ -2242,6 +2337,8 @@ -- Ensure this data isn't received (this would be silly/buggy as exports from other accounts - with different characters - won't know what to do with this) temp.trackAtCharacters = nil; temp.overrideTrackAtCharacters = nil; + temp.dontAlertAtCharacters = nil; + temp.overrideDontAlertAtCharacters = nil; addon.db.profile.groups[name] = temp; end