Zerotorescue@176: local addon = select(2, ...); Zerotorescue@176: local mod = addon:NewModule("Alerts", "AceEvent-3.0", "AceTimer-3.0"); Zerotorescue@176: Zerotorescue@176: local queue, cache = {}, {}; Zerotorescue@176: Zerotorescue@176: function mod:OnEnable() Zerotorescue@176: addon:Debug("Alerts:OnEnable"); Zerotorescue@176: Zerotorescue@176: -- Register our alert slash command Zerotorescue@176: -- /im alert Zerotorescue@176: addon:RegisterSlash(function(this) Zerotorescue@176: mod:Scan(true); Zerotorescue@176: 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."); Zerotorescue@176: Zerotorescue@184: mod:Scan(false); Zerotorescue@176: Zerotorescue@176: --[[if addon.db.profile.defaults.scanInterval["00Login"] then Zerotorescue@176: self:RegisterEvent("PLAYER_LOGIN", "Scan"); Zerotorescue@176: end Zerotorescue@176: Zerotorescue@176: if addon.db.profile.defaults.scanInterval["01Repeat5"] then Zerotorescue@176: self:ScheduleTimer("Scan", 5 * 60); Zerotorescue@176: elseif addon.db.profile.defaults.scanInterval["01Repeat10"] then Zerotorescue@176: self:ScheduleTimer("Scan", 10 * 60); Zerotorescue@176: elseif addon.db.profile.defaults.scanInterval["01Repeat15"] then Zerotorescue@176: self:ScheduleTimer("Scan", 15 * 60); Zerotorescue@176: elseif addon.db.profile.defaults.scanInterval["01Repeat30"] then Zerotorescue@176: self:ScheduleTimer("Scan", 30 * 60); Zerotorescue@176: elseif addon.db.profile.defaults.scanInterval["01Repeat60"] then Zerotorescue@176: self:ScheduleTimer("Scan", 60 * 60); Zerotorescue@176: elseif addon.db.profile.defaults.scanInterval["01Repeat120"] then Zerotorescue@176: self:ScheduleTimer("Scan", 120 * 60); Zerotorescue@176: end]] Zerotorescue@176: end Zerotorescue@176: Zerotorescue@176: local scanTypes = { Zerotorescue@176: Local = "local", Zerotorescue@176: Global = "global", Zerotorescue@176: }; Zerotorescue@176: Zerotorescue@176: function mod:Scan(verbal) Zerotorescue@176: addon:Debug("Alerts:Scan"); Zerotorescue@176: Zerotorescue@176: if verbal then Zerotorescue@176: addon:Print("Scanning items, the results will be presented to you in a moment. Please be patient."); Zerotorescue@176: end Zerotorescue@176: Zerotorescue@176: local playerName = UnitName("player"); Zerotorescue@176: Zerotorescue@176: table.wipe(queue); Zerotorescue@176: table.wipe(cache); Zerotorescue@176: Zerotorescue@176: -- Go through all groups Zerotorescue@176: for groupName, values in pairs(addon.db.profile.groups) do Zerotorescue@176: -- Settings Zerotorescue@176: local trackAt = addon:GetOptionByKey(groupName, "trackAtCharacters"); Zerotorescue@176: local dontAlertAt = addon:GetOptionByKey(groupName, "dontAlertAtCharacters"); Zerotorescue@176: Zerotorescue@176: local alertBelowLocalMinimum = addon:GetOptionByKey(groupName, "alertBelowLocalMinimum"); Zerotorescue@176: local minLocalStock = alertBelowLocalMinimum and addon:GetOptionByKey(groupName, "minLocalStock"); Zerotorescue@176: Zerotorescue@176: local alertBelowGlobalMinimum = addon:GetOptionByKey(groupName, "alertBelowGlobalMinimum"); Zerotorescue@176: local minGlobalStock = alertBelowGlobalMinimum and addon:GetOptionByKey(groupName, "minGlobalStock"); Zerotorescue@176: Zerotorescue@176: 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? Zerotorescue@176: Zerotorescue@176: if values.items and isTracked and (alertBelowLocalMinimum or alertBelowGlobalMinimum) then Zerotorescue@176: addon:Debug("Scanning |cff00ff00%s|r", groupName); Zerotorescue@176: Zerotorescue@176: local groupSettings = { Zerotorescue@176: ["name"] = groupName, Zerotorescue@176: ["minLocalStock"] = minLocalStock, Zerotorescue@176: ["minGlobalStock"] = minGlobalStock, Zerotorescue@176: }; Zerotorescue@176: Zerotorescue@176: for itemId, _ in pairs(values.items) do Zerotorescue@176: if alertBelowLocalMinimum then Zerotorescue@176: self:QeueueScan(groupSettings, itemId, scanTypes.Local); Zerotorescue@176: end Zerotorescue@176: Zerotorescue@176: if alertBelowGlobalMinimum then Zerotorescue@176: self:QeueueScan(groupSettings, itemId, scanTypes.Global); Zerotorescue@176: end Zerotorescue@176: end Zerotorescue@176: end Zerotorescue@176: end Zerotorescue@176: Zerotorescue@176: if self:HasQueue() then Zerotorescue@176: addon:Debug("Alerts:HasQueue, processing"); Zerotorescue@176: --addon:Debug(queue); Zerotorescue@176: Zerotorescue@176: self:ProcessScan(verbal); Zerotorescue@176: end Zerotorescue@176: end Zerotorescue@176: Zerotorescue@176: function mod:QeueueScan(groupSettings, itemId, scanType) Zerotorescue@176: table.insert(queue, { Zerotorescue@176: ["group"] = groupSettings, Zerotorescue@176: ["itemId"] = itemId, Zerotorescue@176: ["type"] = scanType, Zerotorescue@176: }); Zerotorescue@176: end Zerotorescue@176: Zerotorescue@176: function mod:HasQueue() Zerotorescue@176: return (#queue > 0); Zerotorescue@176: end Zerotorescue@176: Zerotorescue@176: function mod:ProcessScan(verbal) Zerotorescue@184: local nextScanDelay = (tonumber(addon.db.profile.defaults.scanInterval) or .1); Zerotorescue@184: local runs = (0.1 / nextScanDelay); -- 0.01 = 10, 0.05 = 2, 0.1 and smaller = 1 Zerotorescue@184: runs = (runs < 1 and 1) or runs; Zerotorescue@184: runs = (nextScanDelay == 0 and 100) or runs; Zerotorescue@176: Zerotorescue@184: for no = 1, runs do Zerotorescue@184: -- Get the last item added to the queue Zerotorescue@184: local thisItem = table.remove(queue, 1); Zerotorescue@176: Zerotorescue@184: if not thisItem then Zerotorescue@184: -- If no item exists then we processed everything, show summary Zerotorescue@184: self:ScanFinished(); Zerotorescue@184: return; Zerotorescue@176: end Zerotorescue@176: Zerotorescue@184: if thisItem.type == scanTypes.Global then Zerotorescue@184: -- Global scan Zerotorescue@184: local globalCount = addon:GetItemCount(thisItem.itemId, thisItem.group.name); Zerotorescue@184: Zerotorescue@184: if not cache[thisItem.itemId] then Zerotorescue@184: cache[thisItem.itemId] = { Zerotorescue@184: ["itemId"] = thisItem.itemId, -- needed later for displaying Zerotorescue@184: ["group"] = thisItem.group, Zerotorescue@184: ["globalCount"] = globalCount, Zerotorescue@184: }; Zerotorescue@184: else Zerotorescue@184: cache[thisItem.itemId].globalCount = globalCount; Zerotorescue@184: end Zerotorescue@184: elseif thisItem.type == scanTypes.Local then Zerotorescue@184: -- Local scan Zerotorescue@184: local localCount = addon:GetLocalItemCount(thisItem.itemId, thisItem.group.name); Zerotorescue@184: Zerotorescue@184: if not cache[thisItem.itemId] then Zerotorescue@184: cache[thisItem.itemId] = { Zerotorescue@184: ["itemId"] = thisItem.itemId, -- needed later for displaying Zerotorescue@184: ["group"] = thisItem.group, Zerotorescue@184: ["localCount"] = localCount, Zerotorescue@184: }; Zerotorescue@184: else Zerotorescue@184: cache[thisItem.itemId].globalCount = localCount; Zerotorescue@184: end Zerotorescue@176: end Zerotorescue@176: end Zerotorescue@176: Zerotorescue@184: self:ScheduleTimer(function() Zerotorescue@176: mod:ProcessScan(verbal); Zerotorescue@184: end, nextScanDelay); -- scan next item in nextScanDelay seconds Zerotorescue@176: end Zerotorescue@176: Zerotorescue@176: local function OnProceed() Zerotorescue@176: InventoriumCommandHandler("summary"); Zerotorescue@176: Zerotorescue@176: if InventoriumItemMover then Zerotorescue@176: InventoriumItemMover:Hide(); Zerotorescue@176: end Zerotorescue@176: end Zerotorescue@176: Zerotorescue@176: local function OnCancel() Zerotorescue@176: if InventoriumItemMover then Zerotorescue@176: InventoriumItemMover:Hide(); Zerotorescue@176: end Zerotorescue@176: end Zerotorescue@176: Zerotorescue@176: local function UseScanST() Zerotorescue@176: if not InventoriumItemMover then Zerotorescue@176: addon:CreateMoverFrame(); Zerotorescue@176: end Zerotorescue@176: Zerotorescue@176: local frame = InventoriumItemMover; -- both for speed as code-consistency Zerotorescue@176: Zerotorescue@176: -- Scrolling table with a list of items to be moved Zerotorescue@176: local scrollTableWidth = ( frame.frmMeasureDummy:GetWidth() - 30 ); -- adjust width by the scrollbar size Zerotorescue@176: local headers = { Zerotorescue@176: { Zerotorescue@176: ["name"] = "Item", Zerotorescue@176: ["width"] = (scrollTableWidth * .6), Zerotorescue@176: ["defaultsort"] = "asc", Zerotorescue@176: ["comparesort"] = function(this, aRow, bRow, column) Zerotorescue@176: local aName, _, aRarity = GetItemInfo(this:GetRow(aRow).rowData.itemId); Zerotorescue@176: local bName, _, bRarity = GetItemInfo(this:GetRow(bRow).rowData.itemId); Zerotorescue@176: local template = "%d%s"; Zerotorescue@176: aName = template:format((10 - (aRarity or 10)), (aName or ""):lower()); Zerotorescue@176: bName = template:format((10 - (bRarity or 10)), (bName or ""):lower()); Zerotorescue@176: Zerotorescue@176: if this.cols[column].sort == "dsc" then Zerotorescue@176: return aName > bName; Zerotorescue@176: else Zerotorescue@176: return aName < bName; Zerotorescue@176: end Zerotorescue@176: end, Zerotorescue@176: ["sort"] = "asc", -- when the data is set, use this column so sort the default data Zerotorescue@176: ["tooltipTitle"] = "Item", Zerotorescue@176: ["tooltip"] = "Click to sort the list by item quality then item name.", Zerotorescue@176: }, Zerotorescue@176: { Zerotorescue@176: ["name"] = "Local", Zerotorescue@176: ["width"] = (scrollTableWidth * .2), Zerotorescue@176: ["align"] = "RIGHT", Zerotorescue@176: ["defaultsort"] = "dsc", Zerotorescue@176: ["sortnext"] = 1, Zerotorescue@176: ["comparesort"] = function(this, aRow, bRow, column) Zerotorescue@176: local aAvailablePercent = (this:GetRow(aRow).rowData.localCount / this:GetRow(aRow).rowData.group.minLocalStock); Zerotorescue@176: local bAvailablePercent = (this:GetRow(bRow).rowData.localCount / this:GetRow(bRow).rowData.group.minLocalStock); Zerotorescue@176: Zerotorescue@176: if this.cols[column].sort == "dsc" then Zerotorescue@176: return aAvailablePercent > bAvailablePercent; Zerotorescue@176: else Zerotorescue@176: return aAvailablePercent < bAvailablePercent; Zerotorescue@176: end Zerotorescue@176: end, Zerotorescue@176: ["tooltipTitle"] = "Local Stock", Zerotorescue@176: ["tooltip"] = "Click to sort the list by the local stock percentage.", Zerotorescue@176: }, Zerotorescue@176: { Zerotorescue@176: ["name"] = "Global", Zerotorescue@176: ["width"] = (scrollTableWidth * .2), Zerotorescue@176: ["align"] = "RIGHT", Zerotorescue@176: ["defaultsort"] = "dsc", Zerotorescue@176: ["sortnext"] = 1, Zerotorescue@176: ["comparesort"] = function(this, aRow, bRow, column) Zerotorescue@176: local aAvailablePercent = (this:GetRow(aRow).rowData.globalCount / this:GetRow(aRow).rowData.group.minGlobalStock); Zerotorescue@176: local bAvailablePercent = (this:GetRow(bRow).rowData.globalCount / this:GetRow(bRow).rowData.group.minGlobalStock); Zerotorescue@176: Zerotorescue@176: if this.cols[column].sort == "dsc" then Zerotorescue@176: return aAvailablePercent > bAvailablePercent; Zerotorescue@176: else Zerotorescue@176: return aAvailablePercent < bAvailablePercent; Zerotorescue@176: end Zerotorescue@176: end, Zerotorescue@176: ["tooltipTitle"] = "Global Stock", Zerotorescue@176: ["tooltip"] = "Click to sort the list by the global stock percentage.", Zerotorescue@176: }, Zerotorescue@176: }; Zerotorescue@176: Zerotorescue@176: local proceedButton = { Zerotorescue@176: text = "Show in summary", Zerotorescue@176: tooltipTitle = "Show in Summary", Zerotorescue@176: tooltip = "Show the summary giving a more detailed list of missing items.", Zerotorescue@176: onClick = OnProceed, Zerotorescue@176: }; Zerotorescue@176: local cancelButton = { Zerotorescue@176: text = "Cancel", Zerotorescue@176: tooltipTitle = "Cancel", Zerotorescue@176: tooltip = "Close the window.", Zerotorescue@176: onClick = OnCancel, Zerotorescue@176: }; Zerotorescue@176: Zerotorescue@176: 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); Zerotorescue@176: end Zerotorescue@176: Zerotorescue@176: function mod:ScanFinished() Zerotorescue@176: table.wipe(queue); Zerotorescue@176: Zerotorescue@176: --addon:Debug(cache); Zerotorescue@176: Zerotorescue@176: -- This table is never copied, just referenced. It is the same for every row. Zerotorescue@176: local missingRow = { Zerotorescue@176: { Zerotorescue@176: ["value"] = function(data, cols, realrow, column, table) Zerotorescue@176: return IdToItemLink(data[realrow].rowData.itemId); Zerotorescue@176: end, Zerotorescue@176: }, -- item Zerotorescue@176: { Zerotorescue@176: ["value"] = function(data, cols, realrow, column, table) Zerotorescue@188: return addon:DisplayItemCount(data[realrow].rowData.localCount or -2, data[realrow].rowData.group.minLocalStock); Zerotorescue@176: end, Zerotorescue@176: }, -- local Zerotorescue@176: { Zerotorescue@176: ["value"] = function(data, cols, realrow, column, table) Zerotorescue@188: return addon:DisplayItemCount(data[realrow].rowData.globalCount or -2, data[realrow].rowData.group.minGlobalStock); Zerotorescue@176: end, Zerotorescue@176: }, -- global Zerotorescue@176: }; Zerotorescue@176: Zerotorescue@176: -- Store the list with rows in this Zerotorescue@176: local missingList = {}; Zerotorescue@176: Zerotorescue@176: for itemId, info in pairs(cache) do Zerotorescue@176: if (info.globalCount and info.globalCount < info.group.minGlobalStock) or (info.localCount and info.localCount < info.group.minLocalStock) then Zerotorescue@176: -- Not enough global or not enough local Zerotorescue@176: Zerotorescue@176: tinsert(missingList, { Zerotorescue@176: ["rowData"] = info, -- this is not a key usually found in a row item and ignored by the library Zerotorescue@176: ["cols"] = missingRow, Zerotorescue@176: }); Zerotorescue@176: end Zerotorescue@176: end Zerotorescue@176: Zerotorescue@176: table.wipe(cache); -- no longer needed, all missing items are now stored in the missingList Zerotorescue@176: Zerotorescue@176: if #missingList > 0 then Zerotorescue@176: UseScanST(); Zerotorescue@176: Zerotorescue@176: addon:SetMoverFrameData(missingList); Zerotorescue@176: Zerotorescue@176: if verbal then Zerotorescue@176: addon:Print("Presenting the data..."); Zerotorescue@176: end Zerotorescue@176: elseif verbal then Zerotorescue@176: addon:Print("No items that you elected to be alerted about are below the selected stock thresholds."); Zerotorescue@176: end Zerotorescue@176: end