From 522f5e1a37b0d7e04075e010e1d0500c003c2765 Mon Sep 17 00:00:00 2001 From: Cinecom <42314202+Cinecom@users.noreply.github.com> Date: Sat, 16 Nov 2024 17:52:17 +0100 Subject: [PATCH] Add files via upload --- Consumables.lua | 159 ++++++ ConsumesTracker.lua | 1103 +++++++++++++++++++++++++++++++++++++++ ConsumesTracker.toc | 10 + images/minimap_icon.tga | Bin 0 -> 16428 bytes images/resize_icon.tga | Bin 0 -> 4140 bytes minimap_icon.xml | 57 ++ 6 files changed, 1329 insertions(+) create mode 100644 Consumables.lua create mode 100644 ConsumesTracker.lua create mode 100644 ConsumesTracker.toc create mode 100644 images/minimap_icon.tga create mode 100644 images/resize_icon.tga create mode 100644 minimap_icon.xml diff --git a/Consumables.lua b/Consumables.lua new file mode 100644 index 0000000..d961ea7 --- /dev/null +++ b/Consumables.lua @@ -0,0 +1,159 @@ +-- consumables.lua + +consumablesCategories = { + ["Elixirs"] = { + { id = 13452, name = "Elixir of the Mongoose" }, + { id = 9187, name = "Elixir of Greater Agility" }, + { id = 13453, name = "Elixir of Brute Force" }, + { id = 13447, name = "Elixir of the Sages" }, + { id = 3825, name = "Elixir of Fortitude" }, + { id = 9179, name = "Elixir of Greater Intellect" }, + { id = 13454, name = "Greater Arcane Elixir" }, + { id = 17708, name = "Elixir of Frost Power" }, + { id = 9264, name = "Elixir of Shadow Power" }, + { id = 6373, name = "Elixir of Firepower" }, + { id = 21546, name = "Elixir of Greater Firepower" }, + { id = 18294, name = "Elixir of Greater Water Breathing" }, + { id = 9088, name = "Gift of Arthas" }, + { id = 9155, name = "Arcane Elixir" }, + }, + ["Protection Potions"] = { + { id = 13457, name = "Greater Fire Protection Potion" }, + { id = 13456, name = "Greater Frost Protection Potion" }, + { id = 13458, name = "Greater Nature Protection Potion" }, + { id = 13459, name = "Greater Shadow Protection Potion" }, + { id = 13461, name = "Greater Arcane Protection Potion" }, + { id = 9036, name = "Magic Resistance Potion" }, + }, + ["Utility Potions"] = { + { id = 20008, name = "Living Action Potion" }, + { id = 5634, name = "Free Action Potion" }, + { id = 3387, name = "Limited Invulnerability Potion" }, + { id = 9030, name = "Restorative Potion" }, + { id = 13462, name = "Purification Potion" }, + { id = 9172, name = "Invisibility Potion" }, + { id = 2459, name = "Swiftness Potion" }, + }, + ["Combat Potions"] = { + { id = 13455, name = "Greater Stoneshield Potion" }, + { id = 13442, name = "Mighty Rage Potion" }, + { id = 5631, name = "Rage Potion" }, + }, + ["Mana & Health Potions"] = { + { id = 13444, name = "Major Mana Potion" }, + { id = 13446, name = "Major Healing Potion" }, + { id = 12190, name = "Dreamless Sleep Potion" }, + { id = 2456, name = "Rejuvenation Potion" }, + }, + ["Juju & Other Buffs"] = { + { id = 12460, name = "Juju Power" }, + { id = 12451, name = "Juju Might" }, + { id = 12457, name = "Juju Chill" }, + { id = 12455, name = "Juju Ember" }, + { id = 12450, name = "Juju Flurry" }, + { id = 8412, name = "Ground Scorpok Assay" }, + { id = 8410, name = "R.O.I.D.S." }, + { id = 8411, name = "Lung Juice Cocktail" }, + { id = 18284, name = "Kreeg's Stout Beatdown" }, + { id = 21151, name = "Rumsey Rum Black Label" }, + { id = 12820, name = "Winterfall Firewater" }, + }, + ["Food Buffs"] = { + { id = 51711, name = "Sweet Mountain Berry (Agility)" }, + { id = 51714, name = "Sweet Mountain Berry (Stamina)" }, + { id = 12662, name = "Blasted Boar Lung" }, + { id = 21023, name = "Dirge's Kickin' Chimaerok Chops" }, + { id = 12210, name = "Tender Wolf Steak" }, + { id = 12217, name = "Dragonbreath Chili" }, + { id = 18254, name = "Runn Tum Tuber Surprise" }, + { id = 13931, name = "Nightfin Soup" }, + { id = 21217, name = "Sagefish Delight" }, + { id = 13928, name = "Grilled Squid" }, + { id = 20452, name = "Smoked Desert Dumplings" }, + { id = 12202, name = "Roast Raptor" }, + } +} + +consumablesList = {} +consumablesNameToID = {} +for categoryName, consumables in pairs(consumablesCategories) do + for _, consumable in ipairs(consumables) do + consumablesList[consumable.id] = consumable.name + consumablesNameToID[consumable.name] = consumable.id + end +end + + + +consumablesTextures = { + -- Elixirs + [13452] = "Interface\\Icons\\INV_Potion_32", -- Elixir of the Mongoose + [9187] = "Interface\\Icons\\INV_Potion_94", -- Elixir of Greater Agility + [13453] = "Interface\\Icons\\INV_Potion_80", -- Elixir of Brute Force + [13447] = "Interface\\Icons\\INV_Potion_29", -- Elixir of the Sages + [3825] = "Interface\\Icons\\INV_Potion_43", -- Elixir of Fortitude + [9179] = "Interface\\Icons\\INV_Potion_10", -- Elixir of Greater Intellect + [13454] = "Interface\\Icons\\INV_Potion_22", -- Greater Arcane Elixir + [17708] = "Interface\\Icons\\INV_Potion_20", -- Elixir of Frost Power + [9264] = "Interface\\Icons\\INV_Potion_48", -- Elixir of Shadow Power + [6373] = "Interface\\Icons\\INV_Potion_38", -- Elixir of Firepower + [21546] = "Interface\\Icons\\INV_Potion_24", -- Elixir of Greater Firepower + [18294] = "Interface\\Icons\\INV_Potion_13", -- Elixir of Greater Water Breathing + [9088] = "Interface\\Icons\\INV_Potion_16", -- Gift of Arthas + [9155] = "Interface\\Icons\\INV_Potion_83", -- Arcane Elixir + + -- Protection Potions + [13457] = "Interface\\Icons\\INV_Potion_24", -- Greater Fire Protection Potion + [13456] = "Interface\\Icons\\INV_Potion_20", -- Greater Frost Protection Potion + [13458] = "Interface\\Icons\\INV_Potion_22", -- Greater Nature Protection Potion + [13459] = "Interface\\Icons\\INV_Potion_23", -- Greater Shadow Protection Potion + [13461] = "Interface\\Icons\\INV_Potion_25", -- Greater Arcane Protection Potion + [9036] = "Interface\\Icons\\INV_Potion_81", -- Magic Resistance Potion + + -- Utility Potions + [20008] = "Interface\\Icons\\INV_Potion_16", -- Living Action Potion + [5634] = "Interface\\Icons\\INV_Potion_04", -- Free Action Potion + [3387] = "Interface\\Icons\\INV_Potion_62", -- Limited Invulnerability Potion + [9030] = "Interface\\Icons\\INV_Potion_68", -- Restorative Potion + [13462] = "Interface\\Icons\\INV_Potion_27", -- Purification Potion + [9172] = "Interface\\Icons\\INV_Potion_31", -- Invisibility Potion + [2459] = "Interface\\Icons\\INV_Potion_95", -- Swiftness Potion + + -- Combat Potions + [13455] = "Interface\\Icons\\INV_Potion_67", -- Greater Stoneshield Potion + [13442] = "Interface\\Icons\\INV_Potion_41", -- Mighty Rage Potion + [5631] = "Interface\\Icons\\INV_Potion_24", -- Rage Potion + + -- Mana & Health Potions + [13444] = "Interface\\Icons\\INV_Potion_76", -- Major Mana Potion + [13446] = "Interface\\Icons\\INV_Potion_54", -- Major Healing Potion + [12190] = "Interface\\Icons\\INV_Potion_83", -- Dreamless Sleep Potion + [2456] = "Interface\\Icons\\INV_Potion_01", -- Minor Rejuvenation Potion + + -- Juju & Other Buffs + [12460] = "Interface\\Icons\\INV_Misc_MonsterScales_07", -- Juju Power + [12451] = "Interface\\Icons\\INV_Misc_MonsterScales_11", -- Juju Might + [12457] = "Interface\\Icons\\INV_Misc_MonsterScales_09", -- Juju Chill + [12455] = "Interface\\Icons\\INV_Misc_MonsterScales_15", -- Juju Ember + [12450] = "Interface\\Icons\\INV_Misc_MonsterScales_17", -- Juju Flurry + [8412] = "Interface\\Icons\\INV_Misc_Food_54", -- Ground Scorpok Assay + [8410] = "Interface\\Icons\\INV_Misc_Food_54", -- R.O.I.D.S. + [8411] = "Interface\\Icons\\INV_Drink_17", -- Lung Juice Cocktail + [18284] = "Interface\\Icons\\INV_Drink_05", -- Kreeg's Stout Beatdown + [21151] = "Interface\\Icons\\INV_Drink_04", -- Rumsey Rum Black Label + [12820] = "Interface\\Icons\\INV_Misc_Powder_Mana", -- Winterfall Firewater + + -- Food Buffs + [12662] = "Interface\\Icons\\INV_Misc_Organ_03", -- Demonic Rune (Assuming for Blasted Boar Lung) + [21023] = "Interface\\Icons\\INV_Misc_Food_47", -- Dirge's Kickin' Chimaerok Chops + [12210] = "Interface\\Icons\\INV_Misc_Food_47", -- Roast Raptor + [12217] = "Interface\\Icons\\INV_Misc_Food_41", -- Dragonbreath Chili + [18254] = "Interface\\Icons\\INV_Misc_Food_64", -- Runn Tum Tuber Surprise + [13931] = "Interface\\Icons\\INV_Misc_Food_47", -- Nightfin Soup + [21217] = "Interface\\Icons\\INV_Misc_Food_64", -- Sagefish Delight + [13928] = "Interface\\Icons\\INV_Misc_Food_51", -- Grilled Squid + [20452] = "Interface\\Icons\\INV_Misc_Food_62", -- Smoked Desert Dumplings + [12202] = "Interface\\Icons\\INV_Misc_Food_14", -- Tiger Meat (Assuming for Roast Raptor) +} + + diff --git a/ConsumesTracker.lua b/ConsumesTracker.lua new file mode 100644 index 0000000..98d561f --- /dev/null +++ b/ConsumesTracker.lua @@ -0,0 +1,1103 @@ +-- ConsumesTracker.lua + +-- Metadata ------------------------------------------------------------------------------------------- +AddonName = "Consumes Tracker" +Version = "1.2" +VState = "multi character (beta)" +WindowWidth = 310 + +-- Onload & Click/Drag Functionality ------------------------------------------------------------------ +function ConsumesTracker_OnLoad(self) + self:RegisterForDrag("LeftButton") + self:SetScript("OnDragStart", function() ConsumesTracker_OnDragStart(self) end) + self:SetScript("OnDragStop", function() ConsumesTracker_OnDragStop(self) end) + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00" .. AddonName .. " Loaded! (version " .. Version .. "-" .. VState .. ")|r |cff808080[Made by Horyoshi]|r") +end + +-- Click Handling ------------------------------------------------------------------------------------- +function ConsumesTracker_HandleClick(self, button) + if not self then return end + if button == "LeftButton" and not IsShiftKeyDown() then + if ConsumeTracker_MainFrame and ConsumeTracker_MainFrame:IsShown() then + ConsumeTracker_MainFrame:Hide() + else + ConsumeTracker_ShowMainWindow() + end + end +end + +-- Drag Functions ------------------------------------------------------------------------------------- +function ConsumesTracker_OnDragStart(self) + if IsShiftKeyDown() then + self:StartMoving() + self.isMoving = true + end +end + +function ConsumesTracker_OnDragStop(self) + if self and self.isMoving then + self:StopMovingOrSizing() + self.isMoving = false + end +end + +-- Initialize SavedVariables ---------------------------------------------------------------------------- +if not ConsumeTracker_Settings then + ConsumeTracker_Settings = {} +end +if not ConsumeTracker_Data then + ConsumeTracker_Data = {} +end + +-- Event frame for updating data ---------------------------------------------------------------------- +local ConsumeTracker_EventFrame = CreateFrame("Frame") +ConsumeTracker_EventFrame:RegisterEvent("PLAYER_LOGIN") +ConsumeTracker_EventFrame:RegisterEvent("BAG_UPDATE") +ConsumeTracker_EventFrame:RegisterEvent("BANKFRAME_OPENED") +ConsumeTracker_EventFrame:RegisterEvent("PLAYERBANKSLOTS_CHANGED") +ConsumeTracker_EventFrame:RegisterEvent("ITEM_LOCK_CHANGED") +ConsumeTracker_EventFrame:RegisterEvent("MAIL_SHOW") +ConsumeTracker_EventFrame:RegisterEvent("MAIL_INBOX_UPDATE") +ConsumeTracker_EventFrame:RegisterEvent("MAIL_CLOSED") + +ConsumeTracker_EventFrame:SetScript("OnEvent", function() + if event == "PLAYER_LOGIN" then + ConsumeTracker_ScanPlayerInventory() + ConsumeTracker_ScanPlayerBank() + ConsumeTracker_ScanPlayerMail() + elseif event == "BAG_UPDATE" then + ConsumeTracker_ScanPlayerInventory() + if BankFrame and BankFrame:IsShown() then + ConsumeTracker_ScanPlayerBank() + end + if MailFrame and MailFrame:IsShown() then + ConsumeTracker_ScanPlayerMail() + end + elseif event == "BANKFRAME_OPENED" then + ConsumeTracker_ScanPlayerBank() + elseif event == "PLAYERBANKSLOTS_CHANGED" then + ConsumeTracker_ScanPlayerBank() + ConsumeTracker_ScanPlayerInventory() + elseif event == "ITEM_LOCK_CHANGED" then + if BankFrame and BankFrame:IsShown() then + ConsumeTracker_ScanPlayerInventory() + ConsumeTracker_ScanPlayerBank() + end + if MailFrame and MailFrame:IsShown() then + ConsumeTracker_ScanPlayerInventory() + ConsumeTracker_ScanPlayerMail() + end + elseif event == "MAIL_SHOW" or event == "MAIL_INBOX_UPDATE" then + ConsumeTracker_ScanPlayerMail() + end +end) + + + +function ConsumeTracker_CreateMainWindow() + -- Main Frame + ConsumeTracker_MainFrame = CreateFrame("Frame", "ConsumeTracker_MainFrame", UIParent) + ConsumeTracker_MainFrame:SetWidth(WindowWidth) + ConsumeTracker_MainFrame:SetHeight(512) + ConsumeTracker_MainFrame:SetPoint("CENTER", UIParent, "CENTER") + ConsumeTracker_MainFrame:SetFrameStrata("DIALOG") + ConsumeTracker_MainFrame:SetMovable(true) + ConsumeTracker_MainFrame:EnableMouse(true) + ConsumeTracker_MainFrame:RegisterForDrag("LeftButton") + ConsumeTracker_MainFrame:SetScript("OnDragStart", function() + this:StartMoving() + end) + ConsumeTracker_MainFrame:SetScript("OnDragStop", function() + this:StopMovingOrSizing() + end) + + -- Background Texture + local background = ConsumeTracker_MainFrame:CreateTexture(nil, "BACKGROUND") + background:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Background") + background:SetPoint("TOPLEFT", ConsumeTracker_MainFrame, "TOPLEFT", 12, -12) + background:SetPoint("BOTTOMRIGHT", ConsumeTracker_MainFrame, "BOTTOMRIGHT", -12, 12) + + -- Border + ConsumeTracker_MainFrame:SetBackdrop({ + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", + tile = true, tileSize = 32, edgeSize = 32, + insets = { left = 11, right = 12, top = 12, bottom = 11 } + }) + ConsumeTracker_MainFrame:SetBackdropColor(0.1, 0.1, 0.1, 1) + + -- Title Frame + local titleBg = ConsumeTracker_MainFrame:CreateTexture(nil, "ARTWORK") + titleBg:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header") + titleBg:SetWidth(266) + titleBg:SetHeight(64) + titleBg:SetPoint("TOP", ConsumeTracker_MainFrame, "TOP", 0, 12) + + -- Title Text + local titleText = ConsumeTracker_MainFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + titleText:SetText("Consumes Tracker") + titleText:SetPoint("TOP", titleBg, "TOP", 0, -14) + + -- Close Button + local closeButton = CreateFrame("Button", nil, ConsumeTracker_MainFrame, "UIPanelCloseButton") + closeButton:SetWidth(32) + closeButton:SetHeight(32) + closeButton:SetPoint("TOPRIGHT", ConsumeTracker_MainFrame, "TOPRIGHT", -5, -5) + closeButton:SetScript("OnClick", function() + ConsumeTracker_MainFrame:Hide() + end) + + -- Create a custom tooltip frame + local tooltipFrame = CreateFrame("Frame", "ConsumeTrackerTooltip", UIParent) + tooltipFrame:SetWidth(150) + tooltipFrame:SetHeight(40) + tooltipFrame:SetFrameStrata("TOOLTIP") + tooltipFrame:SetBackdrop({ + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, tileSize = 16, edgeSize = 16, + insets = { left = 4, right = 4, top = 4, bottom = 4 } + }) + tooltipFrame:SetBackdropColor(0, 0, 0, 1) + tooltipFrame:Hide() + + -- Tooltip text + local tooltipText = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + tooltipText:SetPoint("CENTER", tooltipFrame, "CENTER", 0, 0) + tooltipFrame.text = tooltipText + + local function ShowTooltip(tab, text) + tooltipFrame.text:SetText(text) + tooltipFrame:SetPoint("BOTTOMLEFT", tab, "TOPLEFT", 0, 0) + tooltipFrame:Show() + end + + local function HideTooltip() + tooltipFrame:Hide() + end + + -- Tabs + local tabs = {} + local function CreateTab(name, texture, xOffset, tooltipText, tabIndex) + local tab = CreateFrame("Button", name, ConsumeTracker_MainFrame) + tab:SetWidth(36) + tab:SetHeight(36) + tab:SetPoint("TOPLEFT", ConsumeTracker_MainFrame, "TOPLEFT", xOffset, -30) + + -- Tab Icon + local icon = tab:CreateTexture(nil, "ARTWORK") + icon:SetTexture(texture) + icon:SetWidth(36) + icon:SetHeight(36) + icon:SetPoint("CENTER", tab, "CENTER", 0, 0) + + tab.icon = icon + + -- Hover Glow + local hoverTexture = tab:CreateTexture(nil, "HIGHLIGHT") + hoverTexture:SetTexture("Interface\\Buttons\\CheckButtonHilight") + hoverTexture:SetBlendMode("ADD") + hoverTexture:SetAllPoints(tab) + + -- Active Highlight + local activeHighlight = tab:CreateTexture(nil, "OVERLAY") + activeHighlight:SetTexture("Interface\\Buttons\\CheckButtonHilight") + activeHighlight:SetBlendMode("ADD") + activeHighlight:SetAllPoints(tab) + activeHighlight:SetWidth(36) + activeHighlight:SetHeight(36) + activeHighlight:Hide() + tab.activeHighlight = activeHighlight + + -- Custom Tooltip Handlers + tab:SetScript("OnEnter", function() + ShowTooltip(tab, tooltipText) + end) + tab:SetScript("OnLeave", HideTooltip) + + -- Store tab for later + tabs[tabIndex] = tab + + return tab + end + + -- Tracker Tab + local tab1 = CreateTab("ConsumeTracker_MainFrameTab1", "Interface\\ICONS\\INV_Potion_93", 30, "Consumables Tracker", 1) + tab1:SetScript("OnClick", function() + ConsumeTracker_ShowTab(1) + end) + + -- Options Tab + local tab2 = CreateTab("ConsumeTracker_MainFrameTab2", "Interface\\ICONS\\INV_Misc_Gear_01", 80, "Options", 2) + tab2:SetScript("OnClick", function() + ConsumeTracker_ShowTab(2) + end) + + -- Add Grey Line Under Tabs + local tabsLine = ConsumeTracker_MainFrame:CreateTexture(nil, "ARTWORK") + tabsLine:SetHeight(1) + tabsLine:SetPoint("TOPLEFT", ConsumeTracker_MainFrame, "TOPLEFT", 12, -72) + tabsLine:SetPoint("TOPRIGHT", ConsumeTracker_MainFrame, "TOPRIGHT", -12, -72) + tabsLine:SetTexture("Interface\\Buttons\\WHITE8x8") + tabsLine:SetVertexColor(0.4, 0.4, 0.4, 1) + + -- Tab Content + ConsumeTracker_MainFrame.tabs = {} + local tab1Content = CreateFrame("Frame", nil, ConsumeTracker_MainFrame) + tab1Content:SetWidth(WindowWidth - 50) + tab1Content:SetHeight(380) + tab1Content:SetPoint("TOPLEFT", ConsumeTracker_MainFrame, "TOPLEFT", 30, -80) + ConsumeTracker_MainFrame.tabs[1] = tab1Content + + local tab2Content = CreateFrame("Frame", nil, ConsumeTracker_MainFrame) + tab2Content:SetWidth(WindowWidth - 50) + tab2Content:SetHeight(380) + tab2Content:SetPoint("TOPLEFT", ConsumeTracker_MainFrame, "TOPLEFT", 30, -80) + ConsumeTracker_MainFrame.tabs[2] = tab2Content + + -- Footer Text + local footerText = ConsumeTracker_MainFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + footerText:SetText("Made by Horyoshi (v" .. Version .. "-" .. VState .. ")") + footerText:SetTextColor(0.6, 0.6, 0.6) + footerText:SetPoint("BOTTOM", ConsumeTracker_MainFrame, "BOTTOM", 0, 15) + + -- Add Grey Line Above Footer + local footerLine = ConsumeTracker_MainFrame:CreateTexture(nil, "ARTWORK") + footerLine:SetHeight(1) + footerLine:SetPoint("BOTTOMLEFT", ConsumeTracker_MainFrame, "BOTTOMLEFT", 12, 30) + footerLine:SetPoint("BOTTOMRIGHT", ConsumeTracker_MainFrame, "BOTTOMRIGHT", -12, 30) + footerLine:SetTexture("Interface\\Buttons\\WHITE8x8") + footerLine:SetVertexColor(0.4, 0.4, 0.4, 1) + + -- Tab Switching Function + function ConsumeTracker_ShowTab(tabIndex) + for index, content in pairs(ConsumeTracker_MainFrame.tabs) do + if index == tabIndex then + content:Show() + tabs[index].activeHighlight:Show() + else + content:Hide() + tabs[index].activeHighlight:Hide() + end + end + end + + -- Set Default Tab + ConsumeTracker_ShowTab(1) + + -- Add Custom Content for Tabs + ConsumeTracker_CreateTrackerContent(tab1Content) + ConsumeTracker_CreateOptionsContent(tab2Content) +end + + + +-- Function to show the main window +function ConsumeTracker_ShowMainWindow() + if not ConsumeTracker_MainFrame then + ConsumeTracker_CreateMainWindow() + end + ConsumeTracker_MainFrame:Show() + -- Scan inventory when the window is opened + ConsumeTracker_ScanPlayerInventory() + -- Only scan bank and mail if they are open + if BankFrame and BankFrame:IsShown() then + ConsumeTracker_ScanPlayerBank() + end + if MailFrame and MailFrame:IsShown() then + ConsumeTracker_ScanPlayerMail() + end + -- Update the tracker content + ConsumeTracker_UpdateTrackerContent() +end + +-- Function to switch tabs +function ConsumeTracker_ShowTab(tabIndex) + if not ConsumeTracker_MainFrame or not ConsumeTracker_MainFrame.tabs then return end + for i, tabContent in pairs(ConsumeTracker_MainFrame.tabs) do + if i == tabIndex then + tabContent:Show() + -- Set the tab button to active + local tabButton = getglobal("ConsumeTracker_MainFrameTab" .. i) + tabButton:SetNormalTexture("Interface\\OptionsFrame\\UI-OptionsFrame-ActiveTab") + else + tabContent:Hide() + -- Set the tab button to inactive + local tabButton = getglobal("ConsumeTracker_MainFrameTab" .. i) + tabButton:SetNormalTexture("Interface\\OptionsFrame\\UI-OptionsFrame-InActiveTab") + end + end +end + +-- Function to create the content of the Tracker tab +function ConsumeTracker_CreateTrackerContent(parentFrame) + -- Scroll Frame + local scrollFrame = CreateFrame("ScrollFrame", "ConsumeTracker_ConsumablesScrollFrame", parentFrame) + scrollFrame:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, 0) + scrollFrame:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -20, 0) + scrollFrame:EnableMouseWheel(true) + + -- Define the OnMouseWheelHandler method + function scrollFrame:OnMouseWheelHandler(delta) + local current = self:GetVerticalScroll() + local maxScroll = self.range or 0 + local newScroll = math.max(0, math.min(current - (delta * 10), maxScroll)) + self:SetVerticalScroll(newScroll) + parentFrame.scrollBar:SetValue(newScroll) + end + + -- Set the OnMouseWheel script + scrollFrame:SetScript("OnMouseWheel", function() + local delta = arg1 + this:OnMouseWheelHandler(delta) + end) + + -- Scroll Child Frame + local scrollChild = CreateFrame("Frame", nil, scrollFrame) + scrollChild:SetWidth(WindowWidth - 10) + scrollChild:SetHeight(1) -- Will adjust later + scrollFrame:SetScrollChild(scrollChild) + + -- Initialize tables to store category and consumable labels + parentFrame.categoryLabels = {} + parentFrame.labels = {} + + -- Sort categories by name for consistent order + local sortedCategories = {} + for categoryName, _ in pairs(consumablesCategories) do + table.insert(sortedCategories, categoryName) + end + table.sort(sortedCategories, function(a, b) return a < b end) + + local index = 0 -- Initialize index for positioning + + for _, categoryName in ipairs(sortedCategories) do + local consumables = consumablesCategories[categoryName] + + -- Create category label + local categoryLabel = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + categoryLabel:SetText(categoryName) + categoryLabel:SetTextColor(1, 1, 1) + categoryLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, -index * 16) + categoryLabel:Hide() -- Initially hidden + parentFrame.categoryLabels[categoryName] = categoryLabel + index = index + 1 -- Reserve space for category label + + -- Sort consumables by name + table.sort(consumables, function(a, b) return a.name < b.name end) + + for _, consumable in ipairs(consumables) do + local itemID = consumable.id + local itemName = consumable.name + + -- Create consumable label + local label = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + label:SetText(itemName) + label:SetTextColor(1, 1, 1) -- White color + label:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 20, -index * 16) -- Indent consumables + label:Hide() -- Initially hidden + parentFrame.labels[itemID] = label + + -- Create a frame for tooltip + local tooltipFrame = CreateFrame("Frame", nil, scrollChild) + tooltipFrame:SetWidth(220) + tooltipFrame:SetHeight(16) + tooltipFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 20, -index * 16) + tooltipFrame:EnableMouse(true) + tooltipFrame:SetScript("OnEnter", function() + ConsumeTracker_ShowConsumableTooltip(itemID) + end) + tooltipFrame:SetScript("OnLeave", function() + if ConsumeTracker_CustomTooltip then + ConsumeTracker_CustomTooltip:Hide() + end + end) + + -- Enable mouse wheel and pass events to the scroll frame + tooltipFrame:EnableMouseWheel(true) + tooltipFrame:SetScript("OnMouseWheel", function() + local delta = arg1 + local scrollFrame = parentFrame.scrollFrame + if scrollFrame and scrollFrame:IsVisible() then + scrollFrame:OnMouseWheelHandler(delta) + end + end) + + -- Associate tooltip frame with the label + label.tooltipFrame = tooltipFrame + + index = index + 1 -- Increment index for next label + end + + -- Add spacing after each category + index = index + 1 + end + + -- Adjust the height of the scroll child + scrollChild.contentHeight = index * 16 + scrollChild:SetHeight(scrollChild.contentHeight) + + -- Message Label + local messageLabel = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormal") + messageLabel:SetText("|cffff0000No consumables selected|r\n\n|cffffffffClick on |roptions|cffffffff to get started|r") + messageLabel:SetPoint("CENTER", ConsumeTracker_MainFrame, "CENTER", 0, 0) + messageLabel:Hide() -- Initially hidden + parentFrame.messageLabel = messageLabel + + -- Scroll Bar + local scrollBar = CreateFrame("Slider", "ConsumeTracker_TrackerScrollBar", parentFrame) + scrollBar:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", -2, -16) + scrollBar:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -2, 16) + scrollBar:SetWidth(16) + scrollBar:SetOrientation('VERTICAL') + scrollBar:SetThumbTexture("Interface\\Buttons\\UI-ScrollBar-Knob") + scrollBar:SetBackdrop({ + bgFile = "Interface\\Buttons\\UI-SliderBar-Background", + edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", + tile = true, tileSize = 8, edgeSize = 8, + insets = { left = 3, right = 3, top = 3, bottom = 3 } + }) + scrollBar:SetScript("OnValueChanged", function() + local value = this:GetValue() + parentFrame.scrollFrame:SetVerticalScroll(value) + end) + parentFrame.scrollBar = scrollBar + parentFrame.scrollFrame = scrollFrame + parentFrame.scrollChild = scrollChild + + -- Update the scrollbar + ConsumeTracker_UpdateTrackerScrollBar() +end + + +-- Function to create the content of the Options tab +function ConsumeTracker_CreateOptionsContent(parentFrame) + -- Scroll Frame + local scrollFrame = CreateFrame("ScrollFrame", "ConsumeTracker_OptionsScrollFrame", parentFrame) + scrollFrame:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, 0) + scrollFrame:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -20, 0) + scrollFrame:EnableMouseWheel(true) + scrollFrame:SetScript("OnMouseWheel", function() + local delta = arg1 + local current = this:GetVerticalScroll() + local maxScroll = this.maxScroll or 0 + local newScroll = math.max(0, math.min(current - (delta * 20), maxScroll)) + this:SetVerticalScroll(newScroll) + parentFrame.scrollBar:SetValue(newScroll) + end) + + -- Scroll Child Frame + local scrollChild = CreateFrame("Frame", nil, scrollFrame) + scrollChild:SetWidth(WindowWidth-10) + scrollChild:SetHeight(1) -- Will adjust later + scrollFrame:SetScrollChild(scrollChild) + + -- Sort categories alphabetically for consistent order + local sortedCategories = {} + for categoryName, _ in pairs(consumablesCategories) do + table.insert(sortedCategories, categoryName) + end + table.sort(sortedCategories, function(a, b) return a < b end) + + -- Checkboxes + parentFrame.checkboxes = {} + local index = 0 -- Position index + + -- Iterate over sorted categories in tracker order + for _, categoryName in ipairs(sortedCategories) do + local consumables = consumablesCategories[categoryName] + + -- Create category label + index = index + 1 + local categoryLabel = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + categoryLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index - 1) * 18) + categoryLabel:SetText(categoryName) + categoryLabel:SetTextColor(1, 1, 1) + + -- Sort the consumables by name + table.sort(consumables, function(a, b) return a.name < b.name end) + + -- For each consumable in the category + for _, consumable in ipairs(consumables) do + index = index + 1 + + local currentItemID = consumable.id + local itemName = consumable.name + + -- Create a frame that encompasses the checkbox and label + local optionFrame = CreateFrame("Frame", "ConsumeTracker_OptionsFrame" .. index, scrollChild) + optionFrame:SetWidth(WindowWidth-10) + optionFrame:SetHeight(18) + optionFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index - 1) * 18) + + -- Create the checkbox inside the optionFrame + local checkbox = CreateFrame("CheckButton", "ConsumeTracker_OptionsCheckbox" .. index, optionFrame) + checkbox:SetWidth(16) + checkbox:SetHeight(16) + checkbox:SetPoint("LEFT", optionFrame, "LEFT", 0, 0) + + -- Create Textures for the checkbox + checkbox:SetNormalTexture("Interface\\Buttons\\UI-CheckBox-Up") + checkbox:SetPushedTexture("Interface\\Buttons\\UI-CheckBox-Down") + checkbox:SetHighlightTexture("Interface\\Buttons\\UI-CheckBox-Highlight") + checkbox:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check") + + -- Create FontString for label + local label = optionFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + label:SetPoint("LEFT", checkbox, "RIGHT", 4, 0) + label:SetText(itemName) + + -- Set up the checkbox OnClick handler + checkbox:SetScript("OnClick", function() + ConsumeTracker_Settings[currentItemID] = checkbox:GetChecked() + ConsumeTracker_UpdateTrackerContent() + end) + -- Load saved setting + if ConsumeTracker_Settings[currentItemID] then + checkbox:SetChecked(true) + end + parentFrame.checkboxes[currentItemID] = checkbox + + -- Make the optionFrame clickable (so clicking on the label checks/unchecks the box) + optionFrame:EnableMouse(true) + optionFrame:SetScript("OnMouseDown", function() + checkbox:Click() + end) + end + + -- Add some spacing after each category + index = index + 1 + end + + -- Adjust the scroll child height + scrollChild.contentHeight = index * 18 + scrollChild:SetHeight(scrollChild.contentHeight) + + -- Scroll Bar + local scrollBar = CreateFrame("Slider", "ConsumeTracker_OptionsScrollBar", parentFrame) + scrollBar:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", -2, -16) + scrollBar:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -2, 16) + scrollBar:SetWidth(16) + scrollBar:SetOrientation('VERTICAL') + scrollBar:SetThumbTexture("Interface\\Buttons\\UI-ScrollBar-Knob") + scrollBar:SetBackdrop({ + bgFile = "Interface\\Buttons\\UI-SliderBar-Background", + edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", + tile = true, tileSize = 8, edgeSize = 8, + insets = { left = 3, right = 3, top = 3, bottom = 3 } + }) + scrollBar:SetScript("OnValueChanged", function() + local value = this:GetValue() + parentFrame.scrollFrame:SetVerticalScroll(value) + end) + parentFrame.scrollBar = scrollBar + parentFrame.scrollFrame = scrollFrame + parentFrame.scrollChild = scrollChild + + -- Update the scrollbar + ConsumeTracker_UpdateOptionsScrollBar() +end + +function ConsumeTracker_UpdateTrackerContent() + if not ConsumeTracker_MainFrame or not ConsumeTracker_MainFrame.tabs or not ConsumeTracker_MainFrame.tabs[1] then + return + end + + local trackerFrame = ConsumeTracker_MainFrame.tabs[1] + local realmName = GetRealmName() + local faction = UnitFactionGroup("player") + local playerName = UnitName("player") + + -- Ensure data structure exists + if not ConsumeTracker_Data[realmName] or not ConsumeTracker_Data[realmName][faction] then + return + end + local data = ConsumeTracker_Data[realmName][faction] + + -- Check if bank and mail have been scanned for current character + local currentCharData = data[playerName] + local bankScanned = currentCharData and currentCharData["bank"] ~= nil + local mailScanned = currentCharData and currentCharData["mail"] ~= nil + + -- If bank and mail have not been scanned, show message and hide item labels + if not bankScanned or not mailScanned then + -- Hide all labels + for _, label in pairs(trackerFrame.labels) do + label:Hide() + if label.tooltipFrame then + label.tooltipFrame:Hide() + end + end + -- Hide category labels + for _, categoryLabel in pairs(trackerFrame.categoryLabels) do + categoryLabel:Hide() + end + + -- Show message + trackerFrame.messageLabel:SetText("|cffff0000This character is not scanned yet|r\n\n|cffffffffOpen your |rBank|cffffffff and |rMail |cffffffffto get started|r") + trackerFrame.messageLabel:Show() + + -- Adjust scroll child height + trackerFrame.scrollChild:SetHeight(50) + + -- Update the tracker scrollbar + ConsumeTracker_UpdateTrackerScrollBar() + + return + end + + -- Proceed with normal content update + local index = 0 -- Positioning index for items + local totalCounts = {} -- Track total counts for each item + local hasAnyVisibleItems = false -- Track if any items are visible + + -- Sort categories alphabetically for consistent order + local sortedCategories = {} + for categoryName, _ in pairs(consumablesCategories) do + table.insert(sortedCategories, categoryName) + end + table.sort(sortedCategories) + + -- Hide all labels initially to ensure proper cleanup + for _, label in pairs(trackerFrame.labels) do + label:Hide() + if label.tooltipFrame then + label.tooltipFrame:Hide() + end + end + + -- Hide all category labels initially + for _, categoryLabel in pairs(trackerFrame.categoryLabels) do + categoryLabel:Hide() + end + + -- Iterate over categories + for _, categoryName in ipairs(sortedCategories) do + local consumables = consumablesCategories[categoryName] + local hasVisibleItems = false + local categoryLabel = trackerFrame.categoryLabels[categoryName] + + -- Ensure the category label exists + if not categoryLabel then + -- Create category label dynamically if missing + categoryLabel = trackerFrame.scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + categoryLabel:SetText(categoryName) + categoryLabel:SetTextColor(1, 1, 1) + trackerFrame.categoryLabels[categoryName] = categoryLabel + end + + -- Position category label (always above items) + categoryLabel:SetPoint("TOPLEFT", trackerFrame.scrollChild, "TOPLEFT", 0, -index * 16) + categoryLabel:Hide() -- Initially hidden + + index = index + 1 -- Reserve space for the category label + + -- Calculate totals and show items if enabled + for _, consumable in ipairs(consumables) do + local itemID = consumable.id + if ConsumeTracker_Settings[itemID] then + hasVisibleItems = true + hasAnyVisibleItems = true -- Set to true when an item is displayed + totalCounts[itemID] = 0 -- Initialize count + + -- Sum counts across all characters + for character, charData in pairs(data) do + -- Ensure charData["inventory"], ["bank"], ["mail"] are initialized + local inventory = charData["inventory"] and charData["inventory"][itemID] or 0 + local bank = charData["bank"] and charData["bank"][itemID] or 0 + local mail = charData["mail"] and charData["mail"][itemID] or 0 + totalCounts[itemID] = totalCounts[itemID] + inventory + bank + mail + end + + -- Show and position item label + local label = trackerFrame.labels[itemID] + if not label then + -- Create the label dynamically if missing + label = trackerFrame.scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + trackerFrame.labels[itemID] = label + -- Tooltip frame + local tooltipFrame = CreateFrame("Frame", nil, trackerFrame.scrollChild) + tooltipFrame:SetWidth(WindowWidth - 30) + tooltipFrame:SetHeight(16) + tooltipFrame:EnableMouse(true) + tooltipFrame:SetScript("OnEnter", function() ConsumeTracker_ShowConsumableTooltip(itemID) end) + tooltipFrame:SetScript("OnLeave", function() + if ConsumeTracker_CustomTooltip then + ConsumeTracker_CustomTooltip:Hide() + end + end) + label.tooltipFrame = tooltipFrame + end + + local totalCount = totalCounts[itemID] + label:SetText(consumable.name .. " (" .. totalCount .. ")") + label:SetPoint("TOPLEFT", trackerFrame.scrollChild, "TOPLEFT", 20, -index * 16) + label:Show() + + -- Adjust label color based on count + if totalCount == 0 then + label:SetTextColor(1, 0, 0) -- Red + elseif totalCount < 10 then + label:SetTextColor(1, 0.4, 0) -- Orange + elseif totalCount <= 20 then + label:SetTextColor(1, 0.85, 0) -- Yellow + else + label:SetTextColor(0, 1, 0) -- Green + end + + -- Show tooltip frame + if label.tooltipFrame then + label.tooltipFrame:SetPoint("TOPLEFT", trackerFrame.scrollChild, "TOPLEFT", 20, -index * 16) + label.tooltipFrame:SetWidth(label:GetStringWidth()) + label.tooltipFrame:SetHeight(16) + label.tooltipFrame:Show() + end + + index = index + 1 + end + end + + -- Show the category label if it has visible items + if hasVisibleItems then + categoryLabel:Show() + else + index = index - 1 -- Remove extra space if no items + end + end + + -- Adjust scroll child height based on the total number of items + local contentHeight = index * 16 + if contentHeight < 50 then + contentHeight = 50 -- Minimum height to display the message label + end + trackerFrame.scrollChild:SetHeight(contentHeight) + + if not hasAnyVisibleItems then + -- Show message when no items are selected + trackerFrame.messageLabel:SetText("|cffff0000No consumables selected|r\n\n|cffffffffClick on |roptions|cffffffff to get started|r") + trackerFrame.messageLabel:Show() + else + -- Hide the message label as we have content to display + trackerFrame.messageLabel:Hide() + end + + -- Update the tracker scrollbar + ConsumeTracker_UpdateTrackerScrollBar() +end + + + + +function ConsumeTracker_UpdateTrackerScrollBar() + local trackerFrame = ConsumeTracker_MainFrame.tabs[1] + local scrollBar = trackerFrame.scrollBar + local scrollFrame = trackerFrame.scrollFrame + local scrollChild = trackerFrame.scrollChild + + local totalHeight = scrollChild:GetHeight() + local shownHeight = trackerFrame:GetHeight() - 20 -- Account for padding/margins + + if totalHeight > shownHeight then + local maxScroll = totalHeight - shownHeight + scrollFrame.range = maxScroll -- Set the scroll range + scrollBar:SetMinMaxValues(0, maxScroll) + scrollBar:SetValue(math.min(scrollBar:GetValue(), maxScroll)) + scrollBar:Show() + else + scrollFrame.range = 0 -- Also handle this case + scrollBar:SetMinMaxValues(0, 0) + scrollBar:SetValue(0) + scrollBar:Hide() + end +end + + + +-- Function to update the options scrollbar +function ConsumeTracker_UpdateOptionsScrollBar() + local optionsFrame = ConsumeTracker_MainFrame.tabs[2] + local scrollBar = optionsFrame.scrollBar + local scrollFrame = optionsFrame.scrollFrame + local scrollChild = optionsFrame.scrollChild + + local totalHeight = scrollChild.contentHeight + local shownHeight = 320 -- Adjust based on your UI + + local maxScroll = math.max(0, totalHeight - shownHeight) + scrollFrame.maxScroll = maxScroll + + if totalHeight > shownHeight then + scrollBar:SetMinMaxValues(0, maxScroll) + scrollBar:SetValue(math.min(scrollBar:GetValue(), maxScroll)) + scrollBar:Show() + else + scrollBar:SetMinMaxValues(0, 0) + scrollBar:SetValue(0) + scrollBar:Hide() + end +end + +function ConsumeTracker_ShowConsumableTooltip(itemID) + -- Ensure item is enabled in settings + if not ConsumeTracker_Settings[itemID] then + return + end + + -- Create or reuse custom tooltip frame + if not ConsumeTracker_CustomTooltip then + -- Create the frame + local tooltipFrame = CreateFrame("Frame", "ConsumeTracker_CustomTooltip", UIParent) + tooltipFrame:SetFrameStrata("TOOLTIP") + tooltipFrame:SetWidth(200) + tooltipFrame:SetBackdrop({ + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, tileSize = 16, edgeSize = 16, + insets = { left = 4, right = 4, top = 4, bottom = 4 } + }) + tooltipFrame:SetBackdropColor(0, 0, 0, 1) + + -- Item icon + local icon = tooltipFrame:CreateTexture(nil, "ARTWORK") + icon:SetWidth(32) + icon:SetHeight(32) + icon:SetPoint("TOPLEFT", tooltipFrame, "TOPLEFT", 10, -10) + tooltipFrame.icon = icon + + -- Item name + local title = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + title:SetPoint("TOPLEFT", icon, "TOPRIGHT", 10, -2) + title:SetPoint("RIGHT", tooltipFrame, "RIGHT", -10, 0) + title:SetJustifyH("LEFT") + tooltipFrame.title = title + + -- Content text + local content = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + content:SetPoint("TOPLEFT", icon, "BOTTOMLEFT", 0, -10) + content:SetPoint("RIGHT", tooltipFrame, "RIGHT", -10, 0) + content:SetJustifyH("LEFT") + tooltipFrame.content = content + + ConsumeTracker_CustomTooltip = tooltipFrame + end + + local tooltipFrame = ConsumeTracker_CustomTooltip + + -- Get item info + local itemName = consumablesList[itemID] or "Unknown Item" + local itemTexture = consumablesTextures[itemID] or "Interface\\Icons\\INV_Misc_QuestionMark" + + -- Set icon and title + tooltipFrame.icon:SetTexture(itemTexture) + tooltipFrame.title:SetText(itemName) + + -- Prepare content text + local contentText = "" + + local realmName = GetRealmName() + local faction = UnitFactionGroup("player") + local playerName = UnitName("player") + + -- Ensure data structure exists + ConsumeTracker_Data[realmName] = ConsumeTracker_Data[realmName] or {} + ConsumeTracker_Data[realmName][faction] = ConsumeTracker_Data[realmName][faction] or {} + + local data = ConsumeTracker_Data[realmName][faction] + + -- Initialize totals + local totalInventory, totalBank, totalMail = 0, 0, 0 + local hasItems = false + local characterList = {} + + -- Collect data for each character + for character, charData in pairs(data) do + local inventory = charData["inventory"] and charData["inventory"][itemID] or 0 + local bank = charData["bank"] and charData["bank"][itemID] or 0 + local mail = charData["mail"] and charData["mail"][itemID] or 0 + local total = inventory + bank + mail + + if total > 0 then + hasItems = true + totalInventory = totalInventory + inventory + totalBank = totalBank + bank + totalMail = totalMail + mail + + table.insert(characterList, { + name = character, + inventory = inventory, + bank = bank, + mail = mail, + total = total, + isPlayer = (character == playerName) + }) + end + end + + local totalItems = totalInventory + totalBank + totalMail + + if not hasItems then + contentText = contentText .. "|cffff0000No items found for this consumable.|r" + else + -- Display total counts at the top + contentText = contentText .. "|cffffff00Total:|r " .. totalItems .. "\n\n" + + -- Sort characters alphabetically (optional) + table.sort(characterList, function(a, b) return a.name < b.name end) + + -- Display data for each character + for _, charInfo in ipairs(characterList) do + local nameColor = charInfo.isPlayer and "|cff00ff00" or "|cffcccccc" -- Green for player, grey for others + contentText = contentText .. nameColor .. charInfo.name .. ": " .. charInfo.total .. "|r\n" + + local detailText = "" + if charInfo.inventory > 0 then + detailText = detailText .. "|cffffffffInventory:|r " .. charInfo.inventory .. " " + end + if charInfo.bank > 0 then + detailText = detailText .. "|cffffffffBank:|r " .. charInfo.bank .. " " + end + if charInfo.mail > 0 then + detailText = detailText .. "|cffffffffMail:|r " .. charInfo.mail .. " " + end + contentText = contentText .. " " .. detailText .. "\n\n" + end + end + + tooltipFrame.content:SetText(contentText) + + -- Calculate the number of lines in contentText using string.gsub + local _, numLines = string.gsub(contentText, "\n", "") + numLines = numLines + 2 -- Add lines for title and padding + + -- Adjust tooltip height based on the number of lines + local lineHeight = 10 + local totalHeight = 50 + (numLines * lineHeight) + tooltipFrame:SetHeight(totalHeight) + + -- Set the width based on content + local maxWidth = math.max(tooltipFrame.title:GetStringWidth() + 70, tooltipFrame.content:GetStringWidth() +20) + tooltipFrame:SetWidth(maxWidth) + + -- Position the tooltip near the cursor + local x, y = GetCursorPosition() + local scale = UIParent:GetEffectiveScale() + tooltipFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", x / scale + 10, y / scale - 10) + + tooltipFrame:Show() +end + + + + + + + + +-- Function to scan player's inventory +function ConsumeTracker_ScanPlayerInventory() + local playerName = UnitName("player") + local realmName = GetRealmName() + local faction = UnitFactionGroup("player") + + ConsumeTracker_Data[realmName] = ConsumeTracker_Data[realmName] or {} + ConsumeTracker_Data[realmName][faction] = ConsumeTracker_Data[realmName][faction] or {} + ConsumeTracker_Data[realmName][faction][playerName] = ConsumeTracker_Data[realmName][faction][playerName] or {} + local data = ConsumeTracker_Data[realmName][faction][playerName] + data["inventory"] = {} + + for bag = 0, 4 do + local numSlots = GetContainerNumSlots(bag) + if numSlots then + for slot = 1, numSlots do + local link = GetContainerItemLink(bag, slot) + if link then + local _, _, itemID = string.find(link, "item:(%d+)") + if itemID then + itemID = tonumber(itemID) + if ConsumeTracker_Settings[itemID] then + local texture, itemCount = GetContainerItemInfo(bag, slot) + data["inventory"][itemID] = (data["inventory"][itemID] or 0) + itemCount + end + end + end + end + end + end + + ConsumeTracker_UpdateTrackerContent() +end + +-- Function to scan player's bank +function ConsumeTracker_ScanPlayerBank() + + if not BankFrame or not BankFrame:IsShown() then + return + end + + local playerName = UnitName("player") + local realmName = GetRealmName() + local faction = UnitFactionGroup("player") + + local data = ConsumeTracker_Data[realmName][faction][playerName] + data["bank"] = {} + + -- Bank bags are -1 and 5 to 10 in WoW 1.12 + for bag = -1, 10 do + if bag == -1 or (bag >= 5 and bag <= 10) then + local numSlots = GetContainerNumSlots(bag) + if numSlots then + for slot = 1, numSlots do + local link = GetContainerItemLink(bag, slot) + if link then + local _, _, itemID = string.find(link, "item:(%d+)") + if itemID then + itemID = tonumber(itemID) + if ConsumeTracker_Settings[itemID] then + local texture, itemCount = GetContainerItemInfo(bag, slot) + data["bank"][itemID] = (data["bank"][itemID] or 0) + itemCount + end + end + end + end + end + end + end + + ConsumeTracker_UpdateTrackerContent() +end + +-- Function to scan player's mail +function ConsumeTracker_ScanPlayerMail() + if not MailFrame or not MailFrame:IsShown() then + + return + end + + local playerName = UnitName("player") + local realmName = GetRealmName() + local faction = UnitFactionGroup("player") + + -- Ensure data tables are initialized + ConsumeTracker_Data[realmName] = ConsumeTracker_Data[realmName] or {} + ConsumeTracker_Data[realmName][faction] = ConsumeTracker_Data[realmName][faction] or {} + ConsumeTracker_Data[realmName][faction][playerName] = ConsumeTracker_Data[realmName][faction][playerName] or {} + local data = ConsumeTracker_Data[realmName][faction][playerName] + data["mail"] = {} + + local numInboxItems = GetInboxNumItems() + if numInboxItems and numInboxItems > 0 then + for mailIndex = 1, numInboxItems do + local packageIcon, stationeryIcon, sender, subject, money, CODAmount, daysLeft = GetInboxHeaderInfo(mailIndex) + local itemName, itemTexture, itemCount, itemQuality = GetInboxItem(mailIndex) + if itemName and itemCount and itemCount > 0 then + -- Since GetInboxItemLink is not available, use itemName to get itemID + local itemID = consumablesNameToID[itemName] + if itemID and ConsumeTracker_Settings[itemID] then + data["mail"][itemID] = (data["mail"][itemID] or 0) + itemCount + end + end + end + end + + ConsumeTracker_UpdateTrackerContent() +end diff --git a/ConsumesTracker.toc b/ConsumesTracker.toc new file mode 100644 index 0000000..5ceac82 --- /dev/null +++ b/ConsumesTracker.toc @@ -0,0 +1,10 @@ +## Interface: 11200 +## Title: |cff00ff00Consumes Tracker|r |cff808080[Horyoshi]|r +## Notes: Tracks your inventory, bank and mail for consumables. Works through multiple characters on the same account. +## Author: Horyoshi +## Version: 1.2 +## SavedVariables: ConsumeTracker_Settings, ConsumeTracker_Data + +ConsumesTracker.lua +Consumables.lua +minimap_icon.xml \ No newline at end of file diff --git a/images/minimap_icon.tga b/images/minimap_icon.tga new file mode 100644 index 0000000000000000000000000000000000000000..ddb80948ae4887ac559b666a27088ef30bfde697 GIT binary patch literal 16428 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 F`ackgw}b!y literal 0 HcmV?d00001 diff --git a/images/resize_icon.tga b/images/resize_icon.tga new file mode 100644 index 0000000000000000000000000000000000000000..28c89cc1c7d3b6a7f5dfcf67cd31afce231d90c9 GIT binary patch literal 4140 zcmZQzU}As)1qLAG`2YVu!zdm!VE~K@6o4ykP!s_iM5hshz+)K&*G8=JY< z+(2!0gzQIm11|fCRR^;V8%?R%#JB;Qy~tv;b_23qxY*Qo11@{;iI2JglK)4;VKf|& z^9iNt0og2a*_60}TszVAk?RI@b12~x>jp~fAf$KH4WyM1gu;^&`O&x-{Bc1^{F15{ tTY4bX3`*6byMa>kM)gC|IK|-r8x05v3Uv+h4EFT*(~WTR^l{Z=007BsB<%nI literal 0 HcmV?d00001 diff --git a/minimap_icon.xml b/minimap_icon.xml new file mode 100644 index 0000000..7451ea7 --- /dev/null +++ b/minimap_icon.xml @@ -0,0 +1,57 @@ + + +