-- Onload & Click Functionality ------------------------------------------------------------------------- WindowWidth = 350 function ConsumesManager_OnLoad(self) self:RegisterForDrag("LeftButton") self:SetScript("OnDragStart", function() ConsumesManager_OnDragStart(self) end) self:SetScript("OnDragStop", function() ConsumesManager_OnDragStop(self) end) self:SetScript("OnClick", ConsumesManager_HandleClick) end function ConsumesManager_HandleClick(self, button) -- Only respond to left-clicks without the Shift key pressed if button == "LeftButton" and not IsShiftKeyDown() then -- Toggle the visibility of the main frame if ConsumesManager_MainFrame and ConsumesManager_MainFrame:IsShown() then ConsumesManager_MainFrame:Hide() else ConsumesManager_ShowMainWindow() end end end function ConsumesManager_OnDragStart(self) if IsShiftKeyDown() then self:StartMoving() self.isMoving = true end end function ConsumesManager_OnDragStop(self) if self and self.isMoving then self:StopMovingOrSizing() self.isMoving = false end end if not ConsumesManager_Options then ConsumesManager_Options = {} end if not ConsumesManager_SelectedItems then ConsumesManager_SelectedItems = {} end if not ConsumesManager_Data then ConsumesManager_Data = {} end if not ConsumesManager_Presets then ConsumesManager_Presets = {} end -- Event frame for updating data ------------------------------------------------------------------------ local ConsumesManager_EventFrame = CreateFrame("Frame") ConsumesManager_EventFrame:RegisterEvent("PLAYER_LOGIN") ConsumesManager_EventFrame:RegisterEvent("BAG_UPDATE") ConsumesManager_EventFrame:RegisterEvent("BANKFRAME_OPENED") ConsumesManager_EventFrame:RegisterEvent("PLAYERBANKSLOTS_CHANGED") ConsumesManager_EventFrame:RegisterEvent("ITEM_LOCK_CHANGED") ConsumesManager_EventFrame:RegisterEvent("MAIL_SHOW") ConsumesManager_EventFrame:RegisterEvent("MAIL_INBOX_UPDATE") ConsumesManager_EventFrame:RegisterEvent("MAIL_CLOSED") ConsumesManager_EventFrame:SetScript("OnEvent", function() local isDefaultBankVisible = BankFrame and BankFrame:IsVisible() local isOneBankVisible = OneBankFrame and OneBankFrame:IsVisible() if event == "BANKFRAME_OPENED" then isBankOpen = true elseif event == "MAIL_SHOW" then isMailOpen = true elseif event == "MAIL_CLOSED" then isMailOpen = false elseif not (isDefaultBankVisible or isOneBankVisible) then isBankOpen = false end if event == "PLAYER_LOGIN" then function ConsumesManager_CheckVersionUpdate() -- Initialize options if needed ConsumesManager_Options = ConsumesManager_Options or {} -- Check if we've already shown the popup for this version if not ConsumesManager_Options.LastVersionReset or ConsumesManager_Options.LastVersionReset ~= GetAddOnMetadata("ConsumesManager", "Version") then -- Show the popup with a slight delay to ensure UI is loaded local delayFrame = ConsumesManager_EventFrame.delayVersionCheck or CreateFrame("Frame") ConsumesManager_EventFrame.delayVersionCheck = delayFrame delayFrame:SetScript("OnUpdate", function() local elapsed = 0 elapsed = elapsed + arg1 if elapsed >= 1 then ConsumesManager_ShowVersionUpdatePopup() delayFrame:SetScript("OnUpdate", nil) end end) end end -- Find this in your existing code, typically in a function that runs when the addon loads local ConsumesManager_EventFrame = CreateFrame("Frame") ConsumesManager_EventFrame:RegisterEvent("PLAYER_LOGIN") ConsumesManager_EventFrame:SetScript("OnEvent", function() if event == "PLAYER_LOGIN" then -- Your existing code... -- Add this line to check for version updates -- ConsumesManager_CheckVersionUpdate() -- Continue with your existing code... end end) if ConsumesManager_Options and ConsumesManager_Options.Channel and ConsumesManager_Options.Password then local channelName = DecodeMessage(ConsumesManager_Options.Channel) local channelPassword = DecodeMessage(ConsumesManager_Options.Password) JoinChannelByName(channelName, channelPassword) SetChannelPassword(channelName, channelPassword) MultiAccountChannelAnnounce = "|cffffffff" .. GetAddOnMetadata("ConsumesManager", "Title") .. ":|r |cffffffffJoined|r |cffffc0c0[" .. channelName .. "]|r|cffffffff. Multi-account synchronization |cff00ff00enabled|r|cffffffff.|r" ReadData("start") else MultiAccountChannelAnnounce = "|cffffffff" .. GetAddOnMetadata("ConsumesManager", "Title") .. ":|r Multi-account synchronization |cffff0000disabled|r|cffffffff. Check the addon options for setup.|r" ReadData("stop") end local delayFrameLogin = ConsumesManager_EventFrame.delayFrameLogin or CreateFrame("Frame") ConsumesManager_EventFrame.delayFrameLogin = delayFrameLogin local elapsed = 0 local delay = 1 delayFrameLogin:SetScript("OnUpdate", function() elapsed = elapsed + arg1 -- arg1 provides the time since the last frame if elapsed >= delay then DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00" .. GetAddOnMetadata("ConsumesManager", "Title") .. "|r |cffaaaaaa(v" .. GetAddOnMetadata("ConsumesManager", "Version") .. ")|r |cffffffffLoaded!|r") DEFAULT_CHAT_FRAME:AddMessage(MultiAccountChannelAnnounce) delayFrameLogin:SetScript("OnUpdate", nil) -- Stop the OnUpdate script end end) ConsumesManager_ScanPlayerInventory() ConsumesManager_ScanPlayerBank() ConsumesManager_ScanPlayerMail() elseif event == "BAG_UPDATE" then ConsumesManager_ScanPlayerInventory() if isBankOpen == true then ConsumesManager_ScanPlayerBank() end if isMailOpen == true then ConsumesManager_ScanPlayerMail() end elseif event == "BANKFRAME_OPENED" then ConsumesManager_ScanPlayerBank() elseif event == "PLAYERBANKSLOTS_CHANGED" then ConsumesManager_ScanPlayerBank() ConsumesManager_ScanPlayerInventory() elseif event == "ITEM_LOCK_CHANGED" then if isBankOpen == true then ConsumesManager_ScanPlayerInventory() ConsumesManager_ScanPlayerBank() end if isMailOpen == true then ConsumesManager_ScanPlayerInventory() ConsumesManager_ScanPlayerMail() end elseif event == "MAIL_SHOW" or event == "MAIL_INBOX_UPDATE" then ConsumesManager_ScanPlayerMail() end end) -- Plugin Reset on New Version function ConsumesManager_ShowVersionUpdatePopup() -- Check if a popup already exists if ConsumesManager_VersionUpdateFrame then return end -- Create the popup frame local popup = CreateFrame("Frame", "ConsumesManager_VersionUpdateFrame", UIParent) popup:SetWidth(400) popup:SetHeight(200) popup:SetPoint("CENTER", UIParent, "CENTER", 0, 0) popup:SetFrameStrata("DIALOG") popup:SetBackdrop({ bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background", edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", tile = true, tileSize = 32, edgeSize = 32, insets = { left = 11, right = 12, top = 12, bottom = 11 } }) popup:SetBackdropColor(0, 0, 0, 1) popup:EnableMouse(true) popup:SetMovable(true) popup:RegisterForDrag("LeftButton") popup:SetScript("OnDragStart", function() this:StartMoving() end) popup:SetScript("OnDragStop", function() this:StopMovingOrSizing() end) -- Add a title local title = popup:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") title:SetPoint("TOP", popup, "TOP", 0, -20) title:SetText("ConsumesManager Update " .. GetAddOnMetadata("ConsumesManager", "Version")) title:SetTextColor(1, 0.8, 0) -- Add a message local message = popup:CreateFontString(nil, "OVERLAY", "GameFontNormal") message:SetPoint("TOP", title, "BOTTOM", 0, -20) message:SetWidth(360) message:SetText("This version adds cross-faction support, allowing you to track and trade consumables with characters from both factions.\n\nYou need to reset the addon to update the data structure. Don't worry, your settings will be preserved!") message:SetJustifyH("CENTER") -- Create Reset Button local resetButton = CreateFrame("Button", nil, popup, "UIPanelButtonTemplate") resetButton:SetWidth(100) resetButton:SetHeight(24) resetButton:SetPoint("BOTTOM", popup, "BOTTOM", 0, 40) resetButton:SetText("Reset Now") resetButton:SetScript("OnClick", function() -- Store that we've shown this version popup ConsumesManager_Options = ConsumesManager_Options or {} ConsumesManager_Options.LastVersionReset = GetAddOnMetadata("ConsumesManager", "Version") -- Reset the addon data structures, preserving important settings local channelInfo = nil local passwordInfo = nil local characterOptions = nil local enableCategories = nil local showUseButton = nil local selectedItems = nil -- Backup important settings if ConsumesManager_Options then channelInfo = ConsumesManager_Options.Channel passwordInfo = ConsumesManager_Options.Password characterOptions = ConsumesManager_Options.Characters enableCategories = ConsumesManager_Options.enableCategories showUseButton = ConsumesManager_Options.showUseButton end if ConsumesManager_SelectedItems then selectedItems = {} for id, selected in pairs(ConsumesManager_SelectedItems) do selectedItems[id] = selected end end -- Reset data ConsumesManager_Data = {} -- Restore important settings ConsumesManager_Options = { Channel = channelInfo, Password = passwordInfo, Characters = characterOptions, enableCategories = enableCategories, showUseButton = showUseButton, LastVersionReset = GetAddOnMetadata("ConsumesManager", "Version") } ConsumesManager_SelectedItems = selectedItems or {} -- Close the popup popup:Hide() -- Reload UI ReloadUI() end) -- Create Later Button local laterButton = CreateFrame("Button", nil, popup, "UIPanelButtonTemplate") laterButton:SetWidth(100) laterButton:SetHeight(24) laterButton:SetPoint("BOTTOMLEFT", popup, "BOTTOMLEFT", 30, 40) laterButton:SetText("Later") laterButton:SetScript("OnClick", function() popup:Hide() end) -- Create Skip Button (never show again) local skipButton = CreateFrame("Button", nil, popup, "UIPanelButtonTemplate") skipButton:SetWidth(100) skipButton:SetHeight(24) skipButton:SetPoint("BOTTOMRIGHT", popup, "BOTTOMRIGHT", -30, 40) skipButton:SetText("Skip") skipButton:SetScript("OnClick", function() -- Store that we've shown this version popup even if they didn't reset ConsumesManager_Options = ConsumesManager_Options or {} ConsumesManager_Options.LastVersionReset = GetAddOnMetadata("ConsumesManager", "Version") popup:Hide() end) -- Close button in corner local closeButton = CreateFrame("Button", nil, popup, "UIPanelCloseButton") closeButton:SetPoint("TOPRIGHT", popup, "TOPRIGHT", -5, -5) closeButton:SetScript("OnClick", function() popup:Hide() end) popup:Show() end -- Main Windows Setup ----------------------------------------------------------------------------------- function ConsumesManager_CreateMainWindow() -- Main Frame ConsumesManager_MainFrame = CreateFrame("Frame", "ConsumesManager_MainFrame", UIParent) ConsumesManager_MainFrame:SetWidth(WindowWidth) ConsumesManager_MainFrame:SetHeight(512) ConsumesManager_MainFrame:SetPoint("CENTER", UIParent, "CENTER") ConsumesManager_MainFrame:SetFrameStrata("DIALOG") ConsumesManager_MainFrame:SetMovable(true) ConsumesManager_MainFrame:EnableMouse(true) ConsumesManager_MainFrame:RegisterForDrag("LeftButton") ConsumesManager_MainFrame:SetScript("OnDragStart", function() this:StartMoving() end) ConsumesManager_MainFrame:SetScript("OnDragStop", function() this:StopMovingOrSizing() end) table.insert(UISpecialFrames, "ConsumesManager_MainFrame") -- Background Texture local background = ConsumesManager_MainFrame:CreateTexture(nil, "BACKGROUND") background:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Background") background:SetPoint("TOPLEFT", ConsumesManager_MainFrame, "TOPLEFT", 12, -12) background:SetPoint("BOTTOMRIGHT", ConsumesManager_MainFrame, "BOTTOMRIGHT", -12, 12) -- Border ConsumesManager_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 } }) ConsumesManager_MainFrame:SetBackdropColor(0.1, 0.1, 0.1, 1) -- Progress Bar for Syncing ProgressBarFrame = CreateFrame("Frame", "ConsumesManager_ProgressBar", ConsumesManager_MainFrame, BackdropTemplateMixin and "BackdropTemplate") ProgressBarFrame:SetWidth(20) ProgressBarFrame:SetHeight(496) ProgressBarFrame:SetPoint("TOPRIGHT", ConsumesManager_MainFrame, "TOPRIGHT", 10, -8) ProgressBarFrame:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8x8", edgeFile = "Interface\\Buttons\\WHITE8x8", edgeSize = 1, }) ProgressBarFrame:SetBackdropColor(0,0,0,1) ProgressBarFrame:SetBackdropBorderColor(0.5,0.5,0.5,1) ProgressBarFrame:SetFrameLevel(ConsumesManager_MainFrame:GetFrameLevel() - 1) ProgressBarFrame:Hide() ProgressBarFrame_fill = CreateFrame("Frame", "ConsumesManager_ProgressBarFill", ProgressBarFrame, BackdropTemplateMixin and "BackdropTemplate") ProgressBarFrame_fill:SetWidth(17) ProgressBarFrame_fill:SetHeight(0) ProgressBarFrame_fill:SetPoint("BOTTOMLEFT", ProgressBarFrame, "BOTTOMLEFT", 1, 2) ProgressBarFrame_fill:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8x8" }) ProgressBarFrame_fill:SetBackdropColor(0,0.6,0,1) ProgressBarFrame_fill:Hide() ProgressBarFrame_fill:SetFrameLevel(ProgressBarFrame:GetFrameLevel() + 1) ProgressBarFrame_Text = ProgressBarFrame_fill:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") ProgressBarFrame_Text:SetText("S\n\nY\n\nN\n\nC\n\nI\n\nN\n\nG") ProgressBarFrame_Text:SetPoint("CENTER", ProgressBarFrame, "CENTER", 1, 0) ProgressBarFrame_Text:SetTextColor(1,1,1) ProgressBarFrame_Text:Hide() -- Title Text local titleText = ConsumesManager_MainFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") titleText:SetText("Consumes Manager") titleText:SetPoint("TOP", ConsumesManager_MainFrame, "TOP", 0, -2) -- Calculate the width of the title text and adjust the title background accordingly local titleWidth = titleText:GetStringWidth() + 200 -- Title Background local titleBg = ConsumesManager_MainFrame:CreateTexture(nil, "ARTWORK") titleBg:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header") titleBg:SetWidth(titleWidth) titleBg:SetHeight(64) titleBg:SetPoint("TOP", ConsumesManager_MainFrame, "TOP", 0, 12) -- Close Button local closeButton = CreateFrame("Button", nil, ConsumesManager_MainFrame, "UIPanelCloseButton") closeButton:SetWidth(32) closeButton:SetHeight(32) closeButton:SetPoint("TOPRIGHT", ConsumesManager_MainFrame, "TOPRIGHT", -5, -5) closeButton:SetScript("OnClick", function() ConsumesManager_MainFrame:Hide() end) -- Create a custom tooltip frame ConsumesManagerTooltip = CreateFrame("Frame", "ConsumesManagerTooltip", UIParent) ConsumesManagerTooltip:SetWidth(100) ConsumesManagerTooltip:SetHeight(40) ConsumesManagerTooltip:SetFrameStrata("TOOLTIP") ConsumesManagerTooltip: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 } }) ConsumesManagerTooltip:SetBackdropColor(0, 0, 0, 1) ConsumesManagerTooltip:Hide() -- Tooltip text local tooltipText = ConsumesManagerTooltip:CreateFontString(nil, "OVERLAY", "GameFontNormal") tooltipText:SetPoint("CENTER", ConsumesManagerTooltip, "CENTER", 0, 0) ConsumesManagerTooltip.text = tooltipText -- Tabs local tabs = {} ConsumesManager_Tabs = {} -- Adjusted CreateTab function local function CreateTab(name, texture, xOffset, tooltipText, tabIndex) local tab = CreateFrame("Button", name, ConsumesManager_MainFrame) tab:SetWidth(36) tab:SetHeight(36) tab:SetPoint("TOPLEFT", ConsumesManager_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) tab.hoverTexture = hoverTexture -- Store hoverTexture in 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 -- Store tooltip text tab.tooltipText = tooltipText -- Custom Tooltip Handlers tab:SetScript("OnEnter", function() ShowTooltip(tab, tab.tooltipText) end) tab:SetScript("OnLeave", HideTooltip) -- Initialize as enabled tab.isEnabled = true -- Store tab in global table ConsumesManager_Tabs[tabIndex] = tab return tab end -- Manager Tab local tab1 = CreateTab("ConsumesManager_MainFrameTab1", "Interface\\AddOns\\ConsumesManager\\images\\minimap_icon", 30, "Tracker", 1) tab1.originalOnClick = function() ConsumesManager_ShowTab(1) end tab1:SetScript("OnClick", tab1.originalOnClick) -- Items Tab local tab2 = CreateTab("ConsumesManager_MainFrameTab2", "Interface\\Icons\\Inv_misc_book_03", 80, "Items", 2) tab2.originalOnClick = function() ConsumesManager_ShowTab(2) end tab2:SetScript("OnClick", tab2.originalOnClick) -- Presets Tab local tab3 = CreateTab("ConsumesManager_MainFrameTab3", "Interface\\Icons\\inv_misc_note_06", 130, "Presets", 3) tab3.originalOnClick = function() ConsumesManager_ShowTab(3) end tab3:SetScript("OnClick", tab3.originalOnClick) -- Settings Tab local tab4 = CreateTab("ConsumesManager_MainFrameTab4", "Interface\\Icons\\INV_Misc_Gear_01", 180, "Settings", 4) tab4.originalOnClick = function() ConsumesManager_ShowTab(4) end tab4:SetScript("OnClick", tab4.originalOnClick) -- Send Data Button sendDataButton = CreateTab("ConsumesManager_sendDataButton", "Interface\\Icons\\inv_misc_punchcards_prismatic", 280, "Push Data", 5) function updateSenDataButtonState() if ConsumesManager_Options.Channel == nil or ConsumesManager_Options.Channel == "" or ConsumesManager_Options.Password == nil or ConsumesManager_Options.Password == "" then sendDataButton:Hide() ReadData("stop") else sendDataButton:Show() ReadData("start") end end updateSenDataButtonState() sendDataButton.originalOnClick = function() PushData() end sendDataButton:SetScript("OnClick", sendDataButton.originalOnClick) -- Add Grey Line Under Tabs local tabsLine = ConsumesManager_MainFrame:CreateTexture(nil, "ARTWORK") tabsLine:SetHeight(1) tabsLine:SetPoint("TOPLEFT", ConsumesManager_MainFrame, "TOPLEFT", 12, -72) tabsLine:SetPoint("TOPRIGHT", ConsumesManager_MainFrame, "TOPRIGHT", -12, -72) tabsLine:SetTexture("Interface\\Buttons\\WHITE8x8") tabsLine:SetVertexColor(0.4, 0.4, 0.4, 1) -- Tab Content ConsumesManager_MainFrame.tabs = {} local tab1Content = CreateFrame("Frame", nil, ConsumesManager_MainFrame) tab1Content:SetWidth(WindowWidth - 50) tab1Content:SetHeight(380) tab1Content:SetPoint("TOPLEFT", ConsumesManager_MainFrame, "TOPLEFT", 30, -80) ConsumesManager_MainFrame.tabs[1] = tab1Content local tab2Content = CreateFrame("Frame", nil, ConsumesManager_MainFrame) tab2Content:SetWidth(WindowWidth - 50) tab2Content:SetHeight(380) tab2Content:SetPoint("TOPLEFT", ConsumesManager_MainFrame, "TOPLEFT", 30, -80) ConsumesManager_MainFrame.tabs[2] = tab2Content local tab3Content = CreateFrame("Frame", nil, ConsumesManager_MainFrame) tab3Content:SetWidth(WindowWidth - 50) tab3Content:SetHeight(380) tab3Content:SetPoint("TOPLEFT", ConsumesManager_MainFrame, "TOPLEFT", 30, -80) ConsumesManager_MainFrame.tabs[3] = tab3Content local tab4Content = CreateFrame("Frame", nil, ConsumesManager_MainFrame) tab4Content:SetWidth(WindowWidth - 50) tab4Content:SetHeight(380) tab4Content:SetPoint("TOPLEFT", ConsumesManager_MainFrame, "TOPLEFT", 30, -80) ConsumesManager_MainFrame.tabs[4] = tab4Content -- Footer Button to Push Database local footerText = ConsumesManager_MainFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") footerText:SetText("Made by Horyoshi (v" .. GetAddOnMetadata("ConsumesManager", "Version") .. ")") footerText:SetTextColor(0.6, 0.6, 0.6) footerText:SetPoint("BOTTOM", ConsumesManager_MainFrame, "BOTTOM", 0, 15) -- Add Grey Line Above Footer local footerLine = ConsumesManager_MainFrame:CreateTexture(nil, "ARTWORK") footerLine:SetHeight(1) footerLine:SetPoint("BOTTOMLEFT", ConsumesManager_MainFrame, "BOTTOMLEFT", 12, 30) footerLine:SetPoint("BOTTOMRIGHT", ConsumesManager_MainFrame, "BOTTOMRIGHT", -12, 30) footerLine:SetTexture("Interface\\Buttons\\WHITE8x8") footerLine:SetVertexColor(0.4, 0.4, 0.4, 1) -- Set Default Tab ConsumesManager_ShowTab(1) -- Add Custom Content for Tabs ConsumesManager_CreateManagerContent(tab1Content) ConsumesManager_CreateItemsContent(tab2Content) ConsumesManager_CreatePresetsContent(tab3Content) ConsumesManager_CreateSettingsContent(tab4Content) ConsumesManager_UpdateTabStates() end function ConsumesManager_ShowMainWindow() if not ConsumesManager_MainFrame then ConsumesManager_CreateMainWindow() end ConsumesManager_MainFrame:Show() -- Scan inventory when the window is opened ConsumesManager_ScanPlayerInventory() -- Only scan bank and mail if they are open if event == "BANKFRAME_OPENED" then ConsumesManager_ScanPlayerBank() end if event == "MAIL_SHOW" or event == "MAIL_INBOX_UPDATE" then ConsumesManager_ScanPlayerMail() end -- Update the tabs based on whether bank and mail have been scanned ConsumesManager_UpdateTabStates() -- Update the Manager content ConsumesManager_UpdateManagerContent() -- Update the Presets content ConsumesManager_UpdatePresetsContent() -- Update Settings content ConsumesManager_UpdateSettingsContent() end function ConsumesManager_ShowTab(tabIndex) if not ConsumesManager_MainFrame or not ConsumesManager_MainFrame.tabs then return end -- Check if the tab is enabled local tabButton = ConsumesManager_Tabs[tabIndex] if tabButton and not tabButton.isEnabled then return -- Do not switch to disabled tabs end for i, tabContent in pairs(ConsumesManager_MainFrame.tabs) do if i == tabIndex then tabContent:Show() -- Set the tab button to active local tabButton = ConsumesManager_Tabs[i] if tabButton then tabButton:SetNormalTexture("Interface\\ItemsFrame\\UI-ItemsFrame-ActiveTab") tabButton.activeHighlight:Show() end else tabContent:Hide() -- Set the tab button to inactive local tabButton = ConsumesManager_Tabs[i] if tabButton then tabButton:SetNormalTexture("Interface\\ItemsFrame\\UI-ItemsFrame-InActiveTab") tabButton.activeHighlight:Hide() end end end end -- Tracker Window ----------------------------------------------------------------------------------- function ConsumesManager_CreateManagerContent(parentFrame) -- Initialize sort order if not set ConsumesManager_Options.sortOrder = ConsumesManager_Options.sortOrder or "name" ConsumesManager_Options.sortDirection = ConsumesManager_Options.sortDirection or "asc" -- ========================== -- Tracker Search Box -- ========================== local trackerSearchBox = CreateFrame("EditBox", "ConsumesManager_TrackerSearchBox", parentFrame, "InputBoxTemplate") trackerSearchBox:SetWidth(WindowWidth - 50) trackerSearchBox:SetHeight(25) trackerSearchBox:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, -5) trackerSearchBox:SetAutoFocus(false) trackerSearchBox:SetText("Search...") trackerSearchBox:SetTextColor(0.5, 0.5, 0.5) trackerSearchBox:SetScript("OnEditFocusGained", function() if this:GetText() == "Search..." then this:SetText("") this:SetTextColor(1, 1, 1) end end) trackerSearchBox:SetScript("OnEditFocusLost", function() if this:GetText() == "" then this:SetText("Search...") this:SetTextColor(0.5, 0.5, 0.5) end end) trackerSearchBox:SetScript("OnTextChanged", function() ConsumesManager_UpdateManagerContent() end) parentFrame.searchBox = trackerSearchBox -- Create buttons for sorting local orderByNameButton = CreateFrame("Button", "ConsumesManager_OrderByNameButton", parentFrame, "UIPanelButtonTemplate") orderByNameButton:SetWidth(100) orderByNameButton:SetHeight(24) orderByNameButton:SetText("Order by Name") orderByNameButton:GetFontString():SetFont("Fonts\\FRIZQT__.TTF", 10, "NORMAL") orderByNameButton:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, -40) orderByNameButton:SetScript("OnClick", function() if ConsumesManager_Options.sortOrder == "name" then -- Toggle sort direction if ConsumesManager_Options.sortDirection == "asc" then ConsumesManager_Options.sortDirection = "desc" else ConsumesManager_Options.sortDirection = "asc" end else ConsumesManager_Options.sortOrder = "name" ConsumesManager_Options.sortDirection = "asc" end ConsumesManager_UpdateManagerContent() end) local orderByAmountButton = CreateFrame("Button", "ConsumesManager_OrderByAmountButton", parentFrame, "UIPanelButtonTemplate") orderByAmountButton:SetWidth(100) orderByAmountButton:SetHeight(24) orderByAmountButton:SetText("Order by Amount") orderByAmountButton:GetFontString():SetFont("Fonts\\FRIZQT__.TTF", 10, "NORMAL") orderByAmountButton:SetPoint("LEFT", orderByNameButton, "RIGHT", 10, 0) orderByAmountButton:SetScript("OnClick", function() if ConsumesManager_Options.sortOrder == "amount" then -- Toggle sort direction if ConsumesManager_Options.sortDirection == "desc" then ConsumesManager_Options.sortDirection = "asc" else ConsumesManager_Options.sortDirection = "desc" end else ConsumesManager_Options.sortOrder = "amount" ConsumesManager_Options.sortDirection = "desc" end ConsumesManager_UpdateManagerContent() end) parentFrame.orderByNameButton = orderByNameButton parentFrame.orderByAmountButton = orderByAmountButton -- Initially hide the order buttons orderByNameButton:Hide() orderByAmountButton:Hide() -- Scroll Frame local scrollFrame = CreateFrame("ScrollFrame", "ConsumesManager_ManagerScrollFrame", parentFrame) scrollFrame:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, -75) -- Adjusted to be below the buttons and search scrollFrame:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -20, 16) scrollFrame:EnableMouseWheel(true) scrollFrame:SetScript("OnMouseWheel", function() local delta = arg1 local current = this:GetVerticalScroll() local maxScroll = this.range 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) parentFrame.scrollChild = scrollChild parentFrame.scrollFrame = scrollFrame -- Initialize data structures parentFrame.categoryInfo = {} local index = 0 -- Position index local lineHeight = 18 -- Sort categories alphabetically local sortedCategories = {} for categoryName, _ in pairs(consumablesCategories) do table.insert(sortedCategories, categoryName) end table.sort(sortedCategories) -- Iterate over sorted categories 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:Show() local categoryInfo = { name = categoryName, label = categoryLabel, Items = {} } index = index + 1 -- Position for the category label local numItemsInCategory = 0 -- Counter for items in this category -- 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 local itemID = consumable.id local itemName = consumable.name -- Create a frame that encompasses the button and label local itemFrame = CreateFrame("Frame", "ConsumesManager_ManagerItemFrame" .. index, scrollChild) itemFrame:SetWidth(WindowWidth - 10) itemFrame:SetHeight(lineHeight) itemFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index) * lineHeight) itemFrame:Hide() itemFrame:EnableMouse(true) -- Enable mouse events for OnEnter and OnLeave -- Create the 'Use' button inside the itemFrame local useButton = CreateFrame("Button", "ConsumesManager_UseButton" .. index, itemFrame, "UIPanelButtonTemplate") useButton:SetWidth(40) useButton:SetHeight(16) useButton:SetPoint("LEFT", itemFrame, "LEFT", 0, 0) useButton:SetText("Use") -- Initially show or hide the use button based on settings if ConsumesManager_Options.showUseButton then useButton:Show() else useButton:Hide() end -- Create FontString for label local label = itemFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") if ConsumesManager_Options.showUseButton then label:SetPoint("LEFT", useButton, "RIGHT", 4, 0) else label:SetPoint("LEFT", itemFrame, "LEFT", 0, 0) end label:SetText(itemName) label:SetJustifyH("LEFT") -- Set up the button OnClick handler useButton:SetScript("OnClick", function() local bag, slot = ConsumesManager_FindItemInBags(itemID) if bag and slot then UseContainerItem(bag, slot) else DEFAULT_CHAT_FRAME:AddMessage("Item not found in bags.") end end) -- Initialize button state useButton:Disable() -- Mouseover Tooltip itemFrame:SetScript("OnEnter", function() ConsumesManager_ShowConsumableTooltip(itemID) end) itemFrame:SetScript("OnLeave", function() if ConsumesManager_CustomTooltip then ConsumesManager_CustomTooltip:Hide() end end) -- Store item info table.insert(categoryInfo.Items, { frame = itemFrame, label = label, name = itemName, itemID = itemID, button = useButton }) index = index + 1 -- Increment index after adding item numItemsInCategory = numItemsInCategory + 1 -- Increment item count end -- Position the category label above its items categoryLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index - numItemsInCategory - 1) * lineHeight) -- Store category info table.insert(parentFrame.categoryInfo, categoryInfo) -- Add extra spacing after the category index = index + 1 -- Add one extra line of spacing between categories end -- Adjust the scroll child height scrollChild.contentHeight = (index - 1) * lineHeight scrollChild:SetHeight(scrollChild.contentHeight) -- Warning label for incomplete scans (subtle top bar) local warningLabel = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") warningLabel:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 2, -33) warningLabel:SetWidth(WindowWidth - 50) warningLabel:SetJustifyH("LEFT") warningLabel:Hide() parentFrame.warningLabel = warningLabel -- Message Label (adjusted to be a child of parentFrame) local messageLabel = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") messageLabel:SetText("|cffff0000No consumables selected|r\n\n|cffffffffClick on |rItems|cffffffff to get started|r") messageLabel:SetPoint("CENTER", parentFrame, "CENTER", 0, 0) messageLabel:Hide() -- Initially hidden parentFrame.messageLabel = messageLabel -- Scroll Bar local scrollBar = CreateFrame("Slider", "ConsumesManager_ManagerScrollBar", parentFrame) scrollBar:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", -2, -75) -- Adjusted to be below the buttons and search 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 -- Initially hide the scrollbar scrollBar:Hide() end function ConsumesManager_UpdateManagerContent() if not ConsumesManager_MainFrame or not ConsumesManager_MainFrame.tabs or not ConsumesManager_MainFrame.tabs[1] then return end local ManagerFrame = ConsumesManager_MainFrame.tabs[1] local realmName = GetRealmName() local playerName = UnitName("player") -- Read Tracker filter text (if present) local filterText = "" if ManagerFrame and ManagerFrame.searchBox and ManagerFrame.searchBox:GetText() then filterText = string.lower(ManagerFrame.searchBox:GetText()) if filterText == "search..." then filterText = "" end end -- Ensure data structure exists if not ConsumesManager_Data[realmName] then ConsumesManager_Data[realmName] = {} end -- Hide the message label and order buttons by default ManagerFrame.messageLabel:Hide() ManagerFrame.orderByNameButton:Hide() ManagerFrame.orderByAmountButton:Hide() -- Hide all item frames and category labels for _, categoryInfo in ipairs(ManagerFrame.categoryInfo) do categoryInfo.label:Hide() for _, itemInfo in ipairs(categoryInfo.Items) do itemInfo.frame:Hide() end end -- Reset the scrollChild height ManagerFrame.scrollChild.contentHeight = 0 ManagerFrame.scrollChild:SetHeight(0) -- Check if bank and mail have been scanned for current character local currentCharData = ConsumesManager_Data[realmName][playerName] local bankScanned = currentCharData and currentCharData["bank"] ~= nil local mailScanned = currentCharData and currentCharData["mail"] ~= nil if not bankScanned and not mailScanned then ManagerFrame.warningLabel:SetText("|cffffff00Open your Bank and Mail for complete counts.|r") ManagerFrame.warningLabel:Show() elseif not bankScanned then ManagerFrame.warningLabel:SetText("|cffffff00Open your Bank to include bank counts.|r") ManagerFrame.warningLabel:Show() elseif not mailScanned then ManagerFrame.warningLabel:SetText("|cffffff00Open your Mail to include mail counts.|r") ManagerFrame.warningLabel:Show() else ManagerFrame.warningLabel:Hide() end -- Proceed with normal content update local index = 0 -- Positioning index for items local hasAnyVisibleItems = false -- Track if any items are visible local lineHeight = 18 -- Get the current sort order and direction local sortOrder = ConsumesManager_Options.sortOrder or "name" local sortDirection = ConsumesManager_Options.sortDirection or "asc" local enableCategories = ConsumesManager_Options.enableCategories or false local showUseButton = ConsumesManager_Options.showUseButton or false -- Check if categories are enabled if ConsumesManager_Options.enableCategories then -- Iterate over categories for _, categoryInfo in ipairs(ManagerFrame.categoryInfo) do local anyItemVisible = false -- First, collect the enabled items and their counts local enabledItems = {} for _, itemInfo in ipairs(categoryInfo.Items) do local itemID = itemInfo.itemID local nameMatches = (filterText == "" or string.find(string.lower(itemInfo.name), filterText, 1, true)) if ConsumesManager_SelectedItems[itemID] and nameMatches then -- Sum counts across all selected characters local totalCount = 0 for character, _ in pairs(ConsumesManager_Data[realmName]) do if type(ConsumesManager_Data[realmName][character]) == "table" and ConsumesManager_Options["Characters"][character] == true then -- Make sure it's not a special field like "faction" if character ~= "faction" then local inventory = ConsumesManager_Data[realmName][character]["inventory"] and ConsumesManager_Data[realmName][character]["inventory"][itemID] or 0 local bank = ConsumesManager_Data[realmName][character]["bank"] and ConsumesManager_Data[realmName][character]["bank"][itemID] or 0 local mail = ConsumesManager_Data[realmName][character]["mail"] and ConsumesManager_Data[realmName][character]["mail"][itemID] or 0 totalCount = totalCount + inventory + bank + mail end end end table.insert(enabledItems, {itemInfo = itemInfo, totalCount = totalCount}) anyItemVisible = true else itemInfo.frame:Hide() end end -- If any items are visible, handle category label if anyItemVisible then categoryInfo.label:SetPoint("TOPLEFT", ManagerFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) categoryInfo.label:Show() index = index + 1 -- Sort enabled items based on sort order and direction if sortOrder == "name" then if sortDirection == "asc" then table.sort(enabledItems, function(a, b) return a.itemInfo.name < b.itemInfo.name end) else table.sort(enabledItems, function(a, b) return a.itemInfo.name > b.itemInfo.name end) end elseif sortOrder == "amount" then if sortDirection == "desc" then table.sort(enabledItems, function(a, b) return a.totalCount > b.totalCount end) else table.sort(enabledItems, function(a, b) return a.totalCount < b.totalCount end) end end -- Now, position and show the enabled items for _, itemData in ipairs(enabledItems) do local itemInfo = itemData.itemInfo local itemID = itemInfo.itemID local itemName = itemInfo.name local label = itemInfo.label local button = itemInfo.button local frame = itemInfo.frame local totalCount = itemData.totalCount -- Update label text with counts label:SetText(itemName .. " (" .. totalCount .. ")") -- 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 <= 19 then label:SetTextColor(1, 0.85, 0) -- Yellow else label:SetTextColor(0, 1, 0) -- Green end -- Enable or disable the 'Use' button based on whether the item is in the player's inventory local playerInventory = ConsumesManager_Data[realmName][playerName]["inventory"] or {} local countInInventory = playerInventory[itemID] or 0 if ConsumesManager_Options.showUseButton then button:Show() if countInInventory > 0 then button:Enable() else button:Disable() end label:SetPoint("LEFT", button, "RIGHT", 4, 0) else button:Disable() button:Hide() label:SetPoint("LEFT", frame, "LEFT", 0, 0) end -- Show and position the item frame frame:SetPoint("TOPLEFT", ManagerFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) frame:Show() index = index + 1 hasAnyVisibleItems = true end -- Add extra spacing after the category index = index + 1 -- Add one extra line of spacing between categories else categoryInfo.label:Hide() -- Hide all items under this category for _, itemInfo in ipairs(categoryInfo.Items) do itemInfo.frame:Hide() end end end else -- Categories are disabled -- Collect all enabled items into a single list with their counts local allItems = {} for _, categoryInfo in ipairs(ManagerFrame.categoryInfo) do categoryInfo.label:Hide() for _, itemInfo in ipairs(categoryInfo.Items) do local itemID = itemInfo.itemID local nameMatches = (filterText == "" or string.find(string.lower(itemInfo.name), filterText, 1, true)) if ConsumesManager_SelectedItems[itemID] and nameMatches then -- Sum counts across all selected characters local totalCount = 0 for character, charData in pairs(ConsumesManager_Data[realmName]) do if type(charData) == "table" and ConsumesManager_Options["Characters"][character] == true then if character ~= "faction" then 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 totalCount = totalCount + inventory + bank + mail end end end table.insert(allItems, {itemInfo = itemInfo, totalCount = totalCount}) hasAnyVisibleItems = true else itemInfo.frame:Hide() end end end -- Sort allItems based on sort order and direction if sortOrder == "name" then if sortDirection == "asc" then table.sort(allItems, function(a, b) return a.itemInfo.name < b.itemInfo.name end) else table.sort(allItems, function(a, b) return a.itemInfo.name > b.itemInfo.name end) end elseif sortOrder == "amount" then if sortDirection == "desc" then table.sort(allItems, function(a, b) return a.totalCount > b.totalCount end) else table.sort(allItems, function(a, b) return a.totalCount < b.totalCount end) end end -- Display all items for _, itemData in ipairs(allItems) do local itemInfo = itemData.itemInfo local itemID = itemInfo.itemID local itemName = itemInfo.name local label = itemInfo.label local button = itemInfo.button local frame = itemInfo.frame local totalCount = itemData.totalCount -- Update label text with counts label:SetText(itemName .. " (" .. totalCount .. ")") -- 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 -- Enable or disable the 'Use' button based on whether the item is in the player's inventory local playerInventory = ConsumesManager_Data[realmName][playerName]["inventory"] or {} local countInInventory = playerInventory[itemID] or 0 if showUseButton then button:Show() if countInInventory > 0 then button:Enable() else button:Disable() end label:SetPoint("LEFT", button, "RIGHT", 4, 0) else button:Disable() button:Hide() label:SetPoint("LEFT", frame, "LEFT", 0, 0) end -- Show and position the item frame frame:SetPoint("TOPLEFT", ManagerFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) frame:Show() index = index + 1 end end -- Adjust the scroll child height ManagerFrame.scrollChild.contentHeight = index * lineHeight ManagerFrame.scrollChild:SetHeight(ManagerFrame.scrollChild.contentHeight) -- Update the scrollbar ConsumesManager_UpdateManagerScrollBar() if not hasAnyVisibleItems then -- Hide the order buttons ManagerFrame.orderByNameButton:Hide() ManagerFrame.orderByAmountButton:Hide() -- Show message when no items are selected ManagerFrame.messageLabel:SetText("|cffff0000No consumables selected|r\n\n|cffffffffClick on |rItems|cffffffff to get started|r") ManagerFrame.messageLabel:Show() -- Reset the scrollChild height ManagerFrame.scrollChild.contentHeight = 0 ManagerFrame.scrollChild:SetHeight(0) -- Update the scrollbar ConsumesManager_UpdateManagerScrollBar() else -- Show the order buttons ManagerFrame.orderByNameButton:Show() ManagerFrame.orderByAmountButton:Show() -- Hide the message label as we have content to display ManagerFrame.messageLabel:Hide() end end function ConsumesManager_UpdateManagerScrollBar() local ManagerFrame = ConsumesManager_MainFrame.tabs[1] local scrollBar = ManagerFrame.scrollBar local scrollFrame = ManagerFrame.scrollFrame local scrollChild = ManagerFrame.scrollChild local totalHeight = scrollChild:GetHeight() local shownHeight = ManagerFrame: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 -- Items Window ----------------------------------------------------------------------------------- function ConsumesManager_CreateItemsContent(parentFrame) -- Create Search Input local searchBox = CreateFrame("EditBox", "ConsumesManager_SearchBox", parentFrame, "InputBoxTemplate") searchBox:SetWidth(WindowWidth - 50) searchBox:SetHeight(25) searchBox:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, -5) searchBox:SetAutoFocus(false) searchBox:SetText("Search...") searchBox:SetTextColor(0.5, 0.5, 0.5) -- Placeholder text color searchBox:SetScript("OnEditFocusGained", function() if this:GetText() == "Search..." then this:SetText("") this:SetTextColor(1, 1, 1) -- User input text color end end) searchBox:SetScript("OnEditFocusLost", function() if this:GetText() == "" then this:SetText("Search...") this:SetTextColor(0.5, 0.5, 0.5) -- Placeholder text color end end) -- Function to update the filter local function UpdateFilter() local filterText = string.lower(searchBox:GetText()) if filterText == "search..." then filterText = "" end local index = 0 -- Position index local lineHeight = 18 -- Iterate over categories for _, categoryInfo in ipairs(parentFrame.categoryInfo) do local categoryLabel = categoryInfo.label local anyitemVisible = false -- First, check if any Items in the category match the filter for _, itemInfo in ipairs(categoryInfo.Items) do local itemNameLower = string.lower(itemInfo.name) if filterText == "" or string.find(itemNameLower, filterText, 1, true) then anyitemVisible = true break end end -- If any Items are visible, show the category label if anyitemVisible then categoryLabel:SetPoint("TOPLEFT", parentFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) categoryLabel:Show() index = index + 1 -- Now, position and show the matching Items for _, itemInfo in ipairs(categoryInfo.Items) do local itemFrame = itemInfo.frame local itemNameLower = string.lower(itemInfo.name) if filterText == "" or string.find(itemNameLower, filterText, 1, true) then -- Show the item itemFrame:SetPoint("TOPLEFT", parentFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) itemFrame:Show() index = index + 1 else itemFrame:Hide() end end -- Add extra spacing after the category index = index + 1 -- Add one extra line of spacing between categories else categoryLabel:Hide() -- Hide all Items under this category for _, itemInfo in ipairs(categoryInfo.Items) do itemInfo.frame:Hide() end end end -- Adjust the scroll child height parentFrame.scrollChild.contentHeight = index * lineHeight parentFrame.scrollChild:SetHeight(parentFrame.scrollChild.contentHeight) -- Update the scrollbar ConsumesManager_UpdateItemsScrollBar() end searchBox:SetScript("OnTextChanged", function() UpdateFilter() end) -- Adjust the size of the scroll frame to make room for the search box and add extra spacing local scrollFrame = CreateFrame("ScrollFrame", "ConsumesManager_ItemsScrollFrame", parentFrame) scrollFrame:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, -40) -- Start below the search box scrollFrame:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -20, 60) -- Leave space for buttons 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) parentFrame.scrollChild = scrollChild parentFrame.scrollFrame = scrollFrame -- Sort categories alphabetically local sortedCategories = {} for categoryName, _ in pairs(consumablesCategories) do table.insert(sortedCategories, categoryName) end table.sort(sortedCategories) -- Checkboxes parentFrame.checkboxes = {} parentFrame.categoryInfo = {} local index = 0 -- Position index local lineHeight = 18 -- Iterate over sorted categories 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:Show() local categoryInfo = { name = categoryName, label = categoryLabel, Items = {} } index = index + 1 -- Position for the category label local numItemsInCategory = 0 -- Counter for Items in this category -- 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 local currentItemID = consumable.id local itemName = consumable.name -- Create a frame that encompasses the checkbox and label local itemFrame = CreateFrame("Frame", "ConsumesManager_ItemsFrame" .. index, scrollChild) itemFrame:SetWidth(WindowWidth - 10) itemFrame:SetHeight(lineHeight) itemFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index) * lineHeight) itemFrame:Show() -- Create the checkbox inside the itemFrame local checkbox = CreateFrame("CheckButton", "ConsumesManager_ItemsCheckbox" .. index, itemFrame) checkbox:SetWidth(16) checkbox:SetHeight(16) checkbox:SetPoint("LEFT", itemFrame, "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 = itemFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") label:SetPoint("LEFT", checkbox, "RIGHT", 4, 0) label:SetText(itemName) -- Set up the checkbox OnClick handler checkbox:SetScript("OnClick", function() ConsumesManager_SelectedItems[currentItemID] = checkbox:GetChecked() ConsumesManager_UpdateManagerContent() end) -- Load saved setting if ConsumesManager_SelectedItems[currentItemID] then checkbox:SetChecked(true) end parentFrame.checkboxes[currentItemID] = checkbox -- Make the itemFrame clickable itemFrame:EnableMouse(true) itemFrame:SetScript("OnMouseDown", function() checkbox:Click() end) -- Mouseover Tooltip itemFrame:SetScript("OnEnter", function() ConsumesManager_ShowItemsTooltip(currentItemID) end) itemFrame:SetScript("OnLeave", function() if ConsumesManager_ItemsTooltip then ConsumesManager_ItemsTooltip:Hide() end end) -- Store item info table.insert(categoryInfo.Items, { frame = itemFrame, name = itemName }) index = index + 1 -- Increment index after adding item numItemsInCategory = numItemsInCategory + 1 -- Increment Items count end -- Position the category label above its Items categoryLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index - numItemsInCategory - 1) * lineHeight) -- Store category info table.insert(parentFrame.categoryInfo, categoryInfo) -- Add extra spacing after the category index = index + 1 -- Add one extra line of spacing between categories end -- Adjust the scroll child height scrollChild.contentHeight = (index - 1) * lineHeight scrollChild:SetHeight(scrollChild.contentHeight) -- Scroll Bar local scrollBar = CreateFrame("Slider", "ConsumesManager_ItemsScrollBar", parentFrame) -- Corrected Y-offsets to prevent overlapping scrollBar:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", -2, -40) -- Start below the search box scrollBar:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -2, 16) -- End above the buttons 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 -- Update the scrollbar ConsumesManager_UpdateItemsScrollBar() -- Create Select All Button local selectAllButton = CreateFrame("Button", "ConsumesManager_SelectAllButton", parentFrame, "UIPanelButtonTemplate") selectAllButton:SetWidth(100) selectAllButton:SetHeight(24) selectAllButton:SetText("Select All") selectAllButton:SetPoint("BOTTOMLEFT", parentFrame, "BOTTOMLEFT", 20, 10) selectAllButton:SetScript("OnClick", function() for itemID, checkbox in pairs(parentFrame.checkboxes) do checkbox:SetChecked(true) ConsumesManager_SelectedItems[itemID] = true end ConsumesManager_UpdateManagerContent() end) -- Create Deselect All Button local deselectAllButton = CreateFrame("Button", "ConsumesManager_DeselectAllButton", parentFrame, "UIPanelButtonTemplate") deselectAllButton:SetWidth(100) deselectAllButton:SetHeight(24) deselectAllButton:SetText("Deselect All") deselectAllButton:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -40, 10) deselectAllButton:SetScript("OnClick", function() for itemID, checkbox in pairs(parentFrame.checkboxes) do checkbox:SetChecked(false) ConsumesManager_SelectedItems[itemID] = false end ConsumesManager_UpdateManagerContent() end) end function ConsumesManager_UpdateItemsScrollBar() local ItemsFrame = ConsumesManager_MainFrame.tabs[2] if not ItemsFrame then print("Error: ItemsFrame (tabs[2]) is nil in UpdateItemsScrollBar") return end local scrollBar = ItemsFrame.scrollBar local scrollFrame = ItemsFrame.scrollFrame local scrollChild = ItemsFrame.scrollChild local totalHeight = scrollChild.contentHeight local parentHeight = ItemsFrame:GetHeight() local searchBoxHeight = 36 -- Adjusted height including padding local buttonsHeight = 40 -- Space reserved for Select/Deselect buttons local shownHeight = parentHeight - searchBoxHeight - buttonsHeight - 20 -- Additional padding 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 -- Presets Window ----------------------------------------------------------------------------------- local ConsumesManager_PresetFrameCounter = 0 function ConsumesManager_CreatePresetsContent(parentFrame) -- "Save Current Layout" title local saveTitle = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") saveTitle:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, 0) saveTitle:SetText("Save Current Layout") saveTitle:SetTextColor(1, 0.82, 0) -- Name input local nameInput = CreateFrame("EditBox", "ConsumesManager_PresetNameInput", parentFrame, "InputBoxTemplate") nameInput:SetWidth(170) nameInput:SetHeight(25) nameInput:SetPoint("TOPLEFT", saveTitle, "BOTTOMLEFT", 0, -5) nameInput:SetAutoFocus(false) nameInput:SetMaxLetters(30) nameInput:SetText("Enter preset name...") nameInput:SetTextColor(0.5, 0.5, 0.5) nameInput:SetScript("OnEditFocusGained", function() if this:GetText() == "Enter preset name..." then this:SetText("") this:SetTextColor(1, 1, 1) end end) nameInput:SetScript("OnEditFocusLost", function() if this:GetText() == "" then this:SetText("Enter preset name...") this:SetTextColor(0.5, 0.5, 0.5) end end) -- Save logic (shared by button and enter key) local function SaveCurrentPreset() local name = nameInput:GetText() if name == "" or name == "Enter preset name..." then ConsumesManager_ShowPresetStatus("|cffff4444Please enter a preset name.|r") return end local itemCount = 0 local items = {} for id, selected in pairs(ConsumesManager_SelectedItems) do if selected then items[id] = true itemCount = itemCount + 1 end end if itemCount == 0 then ConsumesManager_ShowPresetStatus("|cffff4444No items selected in tracker.|r") return end table.insert(ConsumesManager_Presets, { name = name, items = items }) nameInput:SetText("Enter preset name...") nameInput:SetTextColor(0.5, 0.5, 0.5) nameInput:ClearFocus() ConsumesManager_ShowPresetStatus("|cff44ff44Preset '" .. name .. "' saved! (" .. itemCount .. " items)|r") ConsumesManager_UpdatePresetsContent() end -- Save button local saveButton = CreateFrame("Button", "ConsumesManager_PresetSaveButton", parentFrame, "UIPanelButtonTemplate") saveButton:SetWidth(100) saveButton:SetHeight(25) saveButton:SetPoint("LEFT", nameInput, "RIGHT", 5, 0) saveButton:SetText("Save Preset") saveButton:GetFontString():SetFont("Fonts\\FRIZQT__.TTF", 10, "NORMAL") saveButton:SetScript("OnClick", function() SaveCurrentPreset() end) nameInput:SetScript("OnEnterPressed", function() SaveCurrentPreset() end) nameInput:SetScript("OnEscapePressed", function() this:ClearFocus() end) -- Separator line local separator = parentFrame:CreateTexture(nil, "ARTWORK") separator:SetHeight(1) separator:SetPoint("TOPLEFT", nameInput, "BOTTOMLEFT", 0, -8) separator:SetPoint("RIGHT", parentFrame, "RIGHT", -5, 0) separator:SetTexture("Interface\\Buttons\\WHITE8x8") separator:SetVertexColor(0.4, 0.4, 0.4, 1) -- "Your Presets" list title local listTitle = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") listTitle:SetPoint("TOPLEFT", separator, "BOTTOMLEFT", 0, -6) listTitle:SetText("Your Presets") listTitle:SetTextColor(1, 0.82, 0) -- Scroll frame for presets list local scrollFrame = CreateFrame("ScrollFrame", "ConsumesManager_PresetsScrollFrame", parentFrame) scrollFrame:SetPoint("TOPLEFT", listTitle, "BOTTOMLEFT", 0, -5) scrollFrame:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -20, 20) scrollFrame:EnableMouseWheel(true) local scrollChild = CreateFrame("Frame", nil, scrollFrame) scrollChild:SetWidth(parentFrame:GetWidth() - 40) scrollChild:SetHeight(1) scrollFrame:SetScrollChild(scrollChild) parentFrame.scrollChild = scrollChild parentFrame.scrollFrame = scrollFrame -- Scrollbar local scrollBar = CreateFrame("Slider", "ConsumesManager_PresetsScrollBar", parentFrame) scrollBar:SetPoint("TOPRIGHT", scrollFrame, "TOPRIGHT", 18, 0) scrollBar:SetPoint("BOTTOMRIGHT", scrollFrame, "BOTTOMRIGHT", 18, 0) 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 = 1, tileSize = 8, edgeSize = 8, insets = { left = 3, right = 3, top = 3, bottom = 3 } }) scrollBar:SetScript("OnValueChanged", function() parentFrame.scrollFrame:SetVerticalScroll(this:GetValue()) end) parentFrame.scrollBar = scrollBar scrollBar:Hide() scrollFrame:SetScript("OnMouseWheel", function() local d = arg1 local cur = this:GetVerticalScroll() local mx = this.range or 0 local new = 0 if d < 0 then new = math.min(cur + 20, mx) else new = math.max(cur - 20, 0) end this:SetVerticalScroll(new) parentFrame.scrollBar:SetValue(new) end) -- Empty state message local emptyMessage = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") emptyMessage:SetPoint("CENTER", scrollFrame, "CENTER", 0, 0) emptyMessage:SetText("|cff808080No presets saved yet.\nSelect items in the Items tab, then\nsave your layout here.|r") emptyMessage:SetJustifyH("CENTER") parentFrame.emptyMessage = emptyMessage -- Status message for feedback local statusMessage = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") statusMessage:SetPoint("BOTTOM", parentFrame, "BOTTOM", 0, 2) statusMessage:SetJustifyH("CENTER") statusMessage:Hide() parentFrame.statusMessage = statusMessage parentFrame.presetFrames = {} end -- Show a temporary status message in the presets tab function ConsumesManager_ShowPresetStatus(text) local parentFrame = ConsumesManager_MainFrame and ConsumesManager_MainFrame.tabs and ConsumesManager_MainFrame.tabs[3] if not parentFrame or not parentFrame.statusMessage then return end parentFrame.statusMessage:SetText(text) parentFrame.statusMessage:Show() if not parentFrame.statusTimer then parentFrame.statusTimer = CreateFrame("Frame") end parentFrame.statusTimer.elapsed = 0 parentFrame.statusTimer:SetScript("OnUpdate", function() this.elapsed = this.elapsed + arg1 if this.elapsed >= 3 then parentFrame.statusMessage:Hide() this:SetScript("OnUpdate", nil) end end) end -- Count the number of items in a preset function ConsumesManager_CountPresetItems(preset) local count = 0 if preset and preset.items then for _, v in pairs(preset.items) do if v then count = count + 1 end end end return count end -- Load a preset: replaces current item selection with preset items function ConsumesManager_LoadPreset(presetIndex) local preset = ConsumesManager_Presets[presetIndex] if not preset then return end -- Clear current selection safely (collect keys first) local keysToRemove = {} for id in pairs(ConsumesManager_SelectedItems) do table.insert(keysToRemove, id) end for i = 1, table.getn(keysToRemove) do ConsumesManager_SelectedItems[keysToRemove[i]] = nil end -- Load preset items for id, selected in pairs(preset.items) do if selected then ConsumesManager_SelectedItems[id] = true end end -- Update Items tab checkboxes if available local itemsTab = ConsumesManager_MainFrame and ConsumesManager_MainFrame.tabs and ConsumesManager_MainFrame.tabs[2] if itemsTab and itemsTab.checkboxes then for itemID, checkbox in pairs(itemsTab.checkboxes) do if ConsumesManager_SelectedItems[itemID] then checkbox:SetChecked(true) else checkbox:SetChecked(false) end end end -- Update tracker ConsumesManager_UpdateManagerContent() local count = ConsumesManager_CountPresetItems(preset) ConsumesManager_ShowPresetStatus("|cff44ff44Preset '|r|cffffffff" .. preset.name .. "|r|cff44ff44' loaded! (" .. count .. " items)|r") end -- Update a preset: overwrites its items with the current selection function ConsumesManager_UpdatePresetItems(presetIndex) local preset = ConsumesManager_Presets[presetIndex] if not preset then return end local items = {} local itemCount = 0 for id, selected in pairs(ConsumesManager_SelectedItems) do if selected then items[id] = true itemCount = itemCount + 1 end end if itemCount == 0 then ConsumesManager_ShowPresetStatus("|cffff4444No items selected to save.|r") return end preset.items = items ConsumesManager_ShowPresetStatus("|cff44ff44Preset '|r|cffffffff" .. preset.name .. "|r|cff44ff44' updated! (" .. itemCount .. " items)|r") ConsumesManager_UpdatePresetsContent() end -- Delete a preset function ConsumesManager_DeletePreset(presetIndex) local preset = ConsumesManager_Presets[presetIndex] if not preset then return end local name = preset.name table.remove(ConsumesManager_Presets, presetIndex) ConsumesManager_ShowPresetStatus("|cffffff44Preset '|r|cffffffff" .. name .. "|r|cffffff44' deleted.|r") ConsumesManager_UpdatePresetsContent() end -- Rename a preset function ConsumesManager_RenamePreset(presetIndex, newName) local preset = ConsumesManager_Presets[presetIndex] if not preset or not newName or newName == "" then return end preset.name = newName ConsumesManager_ShowPresetStatus("|cff44ff44Preset renamed to '|r|cffffffff" .. newName .. "|r|cff44ff44'.|r") ConsumesManager_UpdatePresetsContent() end -- Build sorted item names for a preset (used in tooltip) function ConsumesManager_GetPresetItemNames(preset) local names = {} if preset and preset.items then for id, selected in pairs(preset.items) do if selected and consumablesList and consumablesList[id] then table.insert(names, consumablesList[id]) end end end table.sort(names) return names end -- Update the presets list display function ConsumesManager_UpdatePresetsContent() local parentFrame = ConsumesManager_MainFrame and ConsumesManager_MainFrame.tabs and ConsumesManager_MainFrame.tabs[3] if not parentFrame then return end local scrollChild = parentFrame.scrollChild if not scrollChild then return end -- Hide existing preset frames if parentFrame.presetFrames then for i = 1, table.getn(parentFrame.presetFrames) do if parentFrame.presetFrames[i] then parentFrame.presetFrames[i]:Hide() end end end parentFrame.presetFrames = {} -- Ensure presets table exists if not ConsumesManager_Presets then ConsumesManager_Presets = {} end local presetCount = table.getn(ConsumesManager_Presets) if presetCount == 0 then if parentFrame.emptyMessage then parentFrame.emptyMessage:Show() end if parentFrame.scrollBar then parentFrame.scrollBar:Hide() end scrollChild:SetHeight(1) return end if parentFrame.emptyMessage then parentFrame.emptyMessage:Hide() end -- Build preset rows local yOffset = 0 local rowHeight = 52 for i = 1, presetCount do local preset = ConsumesManager_Presets[i] local presetIndex = i local itemCount = ConsumesManager_CountPresetItems(preset) ConsumesManager_PresetFrameCounter = ConsumesManager_PresetFrameCounter + 1 local frameID = ConsumesManager_PresetFrameCounter -- Container frame local presetFrame = CreateFrame("Frame", "ConsumesManager_PF" .. frameID, scrollChild) presetFrame:SetWidth(scrollChild:GetWidth()) presetFrame:SetHeight(rowHeight - 4) presetFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, -yOffset) presetFrame:EnableMouse(true) presetFrame:Show() -- Subtle background local bg = presetFrame:CreateTexture(nil, "BACKGROUND") bg:SetAllPoints(presetFrame) bg:SetTexture("Interface\\Buttons\\WHITE8x8") bg:SetVertexColor(1, 1, 1, 0.03) -- Preset name + item count local nameLabel = presetFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") nameLabel:SetPoint("TOPLEFT", presetFrame, "TOPLEFT", 4, -2) nameLabel:SetText("|cffffffff" .. preset.name .. "|r |cff808080(" .. itemCount .. " items)|r") nameLabel:SetJustifyH("LEFT") -- Tooltip showing preset contents on hover presetFrame:SetScript("OnEnter", function() local names = ConsumesManager_GetPresetItemNames(preset) if table.getn(names) > 0 then GameTooltip:SetOwner(presetFrame, "ANCHOR_RIGHT") GameTooltip:AddLine(preset.name, 1, 0.82, 0) GameTooltip:AddLine(" ") local maxShow = 15 for idx = 1, math.min(table.getn(names), maxShow) do GameTooltip:AddLine(names[idx], 1, 1, 1) end if table.getn(names) > maxShow then GameTooltip:AddLine("... and " .. (table.getn(names) - maxShow) .. " more", 0.5, 0.5, 0.5) end GameTooltip:Show() end bg:SetVertexColor(1, 1, 1, 0.08) end) presetFrame:SetScript("OnLeave", function() GameTooltip:Hide() bg:SetVertexColor(1, 1, 1, 0.03) end) -- Rename edit box (hidden by default) local renameBox = CreateFrame("EditBox", "ConsumesManager_RB" .. frameID, presetFrame, "InputBoxTemplate") renameBox:SetWidth(150) renameBox:SetHeight(20) renameBox:SetPoint("TOPLEFT", presetFrame, "TOPLEFT", 0, -1) renameBox:SetAutoFocus(false) renameBox:SetMaxLetters(30) renameBox:Hide() renameBox:SetScript("OnEnterPressed", function() local newName = this:GetText() if newName and newName ~= "" then ConsumesManager_RenamePreset(presetIndex, newName) end this:Hide() nameLabel:Show() end) renameBox:SetScript("OnEscapePressed", function() this:Hide() nameLabel:Show() end) renameBox:SetScript("OnEditFocusLost", function() if this:IsVisible() then this:Hide() nameLabel:Show() end end) -- Action buttons row local btnY = -18 local btnSpacing = 4 -- Load button local loadBtn = CreateFrame("Button", "ConsumesManager_PLB" .. frameID, presetFrame, "UIPanelButtonTemplate") loadBtn:SetWidth(55) loadBtn:SetHeight(20) loadBtn:SetPoint("TOPLEFT", presetFrame, "TOPLEFT", 2, btnY) loadBtn:SetText("Load") loadBtn:GetFontString():SetFont("Fonts\\FRIZQT__.TTF", 10, "NORMAL") loadBtn:SetScript("OnClick", function() ConsumesManager_LoadPreset(presetIndex) end) -- Update button (with inline confirmation) local updateBtn = CreateFrame("Button", "ConsumesManager_PUB" .. frameID, presetFrame, "UIPanelButtonTemplate") updateBtn:SetWidth(65) updateBtn:SetHeight(20) updateBtn:SetPoint("LEFT", loadBtn, "RIGHT", btnSpacing, 0) updateBtn:SetText("Update") updateBtn:GetFontString():SetFont("Fonts\\FRIZQT__.TTF", 10, "NORMAL") updateBtn.confirming = false updateBtn:SetScript("OnClick", function() if updateBtn.confirming then ConsumesManager_UpdatePresetItems(presetIndex) updateBtn.confirming = false else updateBtn:SetText("|cffff4444Confirm?|r") updateBtn.confirming = true if not updateBtn.resetTimer then updateBtn.resetTimer = CreateFrame("Frame") end updateBtn.resetTimer.elapsed = 0 updateBtn.resetTimer:SetScript("OnUpdate", function() this.elapsed = this.elapsed + arg1 if this.elapsed >= 3 then updateBtn:SetText("Update") updateBtn.confirming = false this:SetScript("OnUpdate", nil) end end) end end) -- Rename button local renameBtn = CreateFrame("Button", "ConsumesManager_PRnB" .. frameID, presetFrame, "UIPanelButtonTemplate") renameBtn:SetWidth(65) renameBtn:SetHeight(20) renameBtn:SetPoint("LEFT", updateBtn, "RIGHT", btnSpacing, 0) renameBtn:SetText("Rename") renameBtn:GetFontString():SetFont("Fonts\\FRIZQT__.TTF", 10, "NORMAL") renameBtn:SetScript("OnClick", function() nameLabel:Hide() renameBox:SetText(preset.name) renameBox:Show() renameBox:SetFocus() renameBox:HighlightText() end) -- Delete button (with inline confirmation) local deleteBtn = CreateFrame("Button", "ConsumesManager_PDB" .. frameID, presetFrame, "UIPanelButtonTemplate") deleteBtn:SetWidth(60) deleteBtn:SetHeight(20) deleteBtn:SetPoint("LEFT", renameBtn, "RIGHT", btnSpacing, 0) deleteBtn:SetText("Delete") deleteBtn:GetFontString():SetFont("Fonts\\FRIZQT__.TTF", 10, "NORMAL") deleteBtn.confirming = false deleteBtn:SetScript("OnClick", function() if deleteBtn.confirming then ConsumesManager_DeletePreset(presetIndex) deleteBtn.confirming = false else deleteBtn:SetText("|cffff4444Sure?|r") deleteBtn.confirming = true if not deleteBtn.resetTimer then deleteBtn.resetTimer = CreateFrame("Frame") end deleteBtn.resetTimer.elapsed = 0 deleteBtn.resetTimer:SetScript("OnUpdate", function() this.elapsed = this.elapsed + arg1 if this.elapsed >= 3 then deleteBtn:SetText("Delete") deleteBtn.confirming = false this:SetScript("OnUpdate", nil) end end) end end) -- Separator between presets if i < presetCount then local sep = presetFrame:CreateTexture(nil, "ARTWORK") sep:SetHeight(1) sep:SetPoint("BOTTOMLEFT", presetFrame, "BOTTOMLEFT", 0, 0) sep:SetPoint("BOTTOMRIGHT", presetFrame, "BOTTOMRIGHT", 0, 0) sep:SetTexture("Interface\\Buttons\\WHITE8x8") sep:SetVertexColor(0.3, 0.3, 0.3, 0.5) end table.insert(parentFrame.presetFrames, presetFrame) yOffset = yOffset + rowHeight end -- Update scroll child height scrollChild:SetHeight(yOffset + 10) -- Update scrollbar ConsumesManager_UpdatePresetsScrollBar() end function ConsumesManager_UpdatePresetsScrollBar() local parentFrame = ConsumesManager_MainFrame and ConsumesManager_MainFrame.tabs and ConsumesManager_MainFrame.tabs[3] if not parentFrame then return end local scrollFrame = parentFrame.scrollFrame local scrollChild = parentFrame.scrollChild local scrollBar = parentFrame.scrollBar if not scrollFrame or not scrollChild or not scrollBar then return end local totalHeight = scrollChild:GetHeight() local shownHeight = scrollFrame:GetHeight() if totalHeight > shownHeight then local maxScroll = totalHeight - shownHeight scrollFrame.range = maxScroll scrollBar:SetMinMaxValues(0, maxScroll) scrollBar:SetValue(math.min(scrollBar:GetValue(), maxScroll)) scrollBar:Show() else scrollFrame.range = 0 scrollBar:SetMinMaxValues(0, 0) scrollBar:SetValue(0) scrollBar:Hide() end end -- Settings Window ----------------------------------------------------------------------------------- function ConsumesManager_CreateSettingsContent(parentFrame) -- Scroll Frame Setup local scrollFrame = CreateFrame("ScrollFrame", "ConsumesManager_SettingsScrollFrame", 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) local scrollChild = CreateFrame("Frame", nil, scrollFrame) scrollChild:SetWidth(WindowWidth - 10) scrollChild:SetHeight(1) scrollFrame:SetScrollChild(scrollChild) parentFrame.scrollChild = scrollChild parentFrame.scrollFrame = scrollFrame parentFrame.checkboxes = {} local index = 0 local lineHeight = 20 ConsumesManager_Options["Characters"] = ConsumesManager_Options["Characters"] or {} ConsumesManager_Options.enableCategories = (ConsumesManager_Options.enableCategories == nil) and true or ConsumesManager_Options.enableCategories ConsumesManager_Options.showUseButton = (ConsumesManager_Options.showUseButton == nil) and true or ConsumesManager_Options.showUseButton local realmName = GetRealmName() local playerName = UnitName("player") local playerFaction = UnitFactionGroup("player") -- Build character list with faction info local characterList = {} if ConsumesManager_Data[realmName] then for characterName, charData in pairs(ConsumesManager_Data[realmName]) do if type(charData) == "table" then table.insert(characterList, { name = characterName, faction = charData.faction or "Unknown" }) end end end local playerInList = false for _, charInfo in ipairs(characterList) do if charInfo.name == playerName then playerInList = true break end end if not playerInList then table.insert(characterList, { name = playerName, faction = playerFaction }) end -- Create faction-specific character lists local allianceCharacters = {} local hordeCharacters = {} for _, charInfo in ipairs(characterList) do if charInfo.faction == "Alliance" then table.insert(allianceCharacters, charInfo) elseif charInfo.faction == "Horde" then table.insert(hordeCharacters, charInfo) else -- For characters with unknown faction, add to both lists table.insert(allianceCharacters, charInfo) table.insert(hordeCharacters, charInfo) end end -- Sort characters by name table.sort(allianceCharacters, function(a, b) return a.name < b.name end) table.sort(hordeCharacters, function(a, b) return a.name < b.name end) -- Title local title = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") title:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, 0) title:SetText("Select Characters To Track") title:SetTextColor(1, 1, 1) local startYOffset = -20 local currentYOffset = startYOffset -- Track whether we have characters from each faction local hasAlliance = table.getn(allianceCharacters) > 0 local hasHorde = table.getn(hordeCharacters) > 0 -- First add Alliance section if there are Alliance characters if hasAlliance then -- Alliance Header local allianceHeader = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormal") allianceHeader:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, currentYOffset) allianceHeader:SetText("|cff0078ffAlliance Characters:|r") allianceHeader:SetJustifyH("LEFT") currentYOffset = currentYOffset - lineHeight -- Alliance Characters for i, charInfo in ipairs(allianceCharacters) do local currentCharacterName = charInfo.name local itemFrame = CreateFrame("Frame", "ConsumesManager_AllianceCharFrame" .. i, scrollChild) itemFrame:SetWidth(WindowWidth - 10) itemFrame:SetHeight(18) itemFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 20, currentYOffset) local checkbox = CreateFrame("CheckButton", "ConsumesManager_AllianceCharCheckbox" .. i, itemFrame) checkbox:SetWidth(16) checkbox:SetHeight(16) checkbox:SetPoint("LEFT", itemFrame, "LEFT", 0, 0) 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") local label = itemFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") label:SetPoint("LEFT", checkbox, "RIGHT", 4, 0) label:SetText(currentCharacterName) label:SetTextColor(0, 0.48, 1) -- Blue for Alliance label:SetJustifyH("LEFT") checkbox:SetScript("OnClick", function() ConsumesManager_Options["Characters"][currentCharacterName] = (checkbox:GetChecked() == 1) ConsumesManager_UpdateAllContent() end) if ConsumesManager_Options["Characters"][currentCharacterName] == nil then checkbox:SetChecked(true) ConsumesManager_Options["Characters"][currentCharacterName] = true else checkbox:SetChecked(ConsumesManager_Options["Characters"][currentCharacterName] == true) end parentFrame.checkboxes[currentCharacterName] = checkbox itemFrame:EnableMouse(true) itemFrame:SetScript("OnMouseDown", function() checkbox:Click() end) currentYOffset = currentYOffset - lineHeight index = index + 1 end -- Add extra spacing after Alliance section currentYOffset = currentYOffset - lineHeight / 2 end -- Then add Horde section if there are Horde characters if hasHorde then -- Horde Header local hordeHeader = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormal") hordeHeader:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, currentYOffset) hordeHeader:SetText("|cffb30000Horde Characters:|r") hordeHeader:SetJustifyH("LEFT") currentYOffset = currentYOffset - lineHeight -- Horde Characters for i, charInfo in ipairs(hordeCharacters) do local currentCharacterName = charInfo.name local itemFrame = CreateFrame("Frame", "ConsumesManager_HordeCharFrame" .. i, scrollChild) itemFrame:SetWidth(WindowWidth - 10) itemFrame:SetHeight(18) itemFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 20, currentYOffset) local checkbox = CreateFrame("CheckButton", "ConsumesManager_HordeCharCheckbox" .. i, itemFrame) checkbox:SetWidth(16) checkbox:SetHeight(16) checkbox:SetPoint("LEFT", itemFrame, "LEFT", 0, 0) 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") local label = itemFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") label:SetPoint("LEFT", checkbox, "RIGHT", 4, 0) label:SetText(currentCharacterName) label:SetTextColor(0.7, 0, 0) -- Red for Horde label:SetJustifyH("LEFT") checkbox:SetScript("OnClick", function() ConsumesManager_Options["Characters"][currentCharacterName] = (checkbox:GetChecked() == 1) ConsumesManager_UpdateAllContent() end) if ConsumesManager_Options["Characters"][currentCharacterName] == nil then checkbox:SetChecked(true) ConsumesManager_Options["Characters"][currentCharacterName] = true else checkbox:SetChecked(ConsumesManager_Options["Characters"][currentCharacterName] == true) end parentFrame.checkboxes[currentCharacterName] = checkbox itemFrame:EnableMouse(true) itemFrame:SetScript("OnMouseDown", function() checkbox:Click() end) currentYOffset = currentYOffset - lineHeight index = index + 1 end -- Add extra spacing after Horde section currentYOffset = currentYOffset - lineHeight / 2 end -- Ensure we have a good spacing after character lists currentYOffset = currentYOffset - lineHeight / 2 -- General Settings Title local generalSettingsTitle = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") generalSettingsTitle:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, currentYOffset) generalSettingsTitle:SetText("General Settings") generalSettingsTitle:SetTextColor(1, 1, 1) currentYOffset = currentYOffset - lineHeight -- Enable Categories Checkbox local enableCategoriesFrame = CreateFrame("Frame", "ConsumesManager_EnableCategoriesFrame", scrollChild) enableCategoriesFrame:SetWidth(WindowWidth - 10) enableCategoriesFrame:SetHeight(18) enableCategoriesFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, currentYOffset) enableCategoriesFrame:EnableMouse(true) local enableCategoriesCheckbox = CreateFrame("CheckButton", "ConsumesManager_EnableCategoriesCheckbox", enableCategoriesFrame) enableCategoriesCheckbox:SetWidth(16) enableCategoriesCheckbox:SetHeight(16) enableCategoriesCheckbox:SetPoint("LEFT", enableCategoriesFrame, "LEFT", 0, 0) enableCategoriesCheckbox:SetNormalTexture("Interface\\Buttons\\UI-CheckBox-Up") enableCategoriesCheckbox:SetPushedTexture("Interface\\Buttons\\UI-CheckBox-Down") enableCategoriesCheckbox:SetHighlightTexture("Interface\\Buttons\\UI-CheckBox-Highlight") enableCategoriesCheckbox:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check") enableCategoriesCheckbox:SetChecked(ConsumesManager_Options.enableCategories) enableCategoriesCheckbox:SetScript("OnClick", function() if enableCategoriesCheckbox:GetChecked() then ConsumesManager_Options.enableCategories = true else ConsumesManager_Options.enableCategories = false end ConsumesManager_UpdateManagerContent() ConsumesManager_UpdatePresetsContent() end) local enableCategoriesLabel = enableCategoriesFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") enableCategoriesLabel:SetPoint("LEFT", enableCategoriesCheckbox, "RIGHT", 4, 0) enableCategoriesLabel:SetText("Enable Categories") enableCategoriesLabel:SetJustifyH("LEFT") enableCategoriesFrame:SetScript("OnMouseDown", function() enableCategoriesCheckbox:Click() end) currentYOffset = currentYOffset - lineHeight -- Show Use Button Checkbox local showUseButtonFrame = CreateFrame("Frame", "ConsumesManager_ShowUseButtonFrame", scrollChild) showUseButtonFrame:SetWidth(WindowWidth - 10) showUseButtonFrame:SetHeight(18) showUseButtonFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, currentYOffset) showUseButtonFrame:EnableMouse(true) local showUseButtonCheckbox = CreateFrame("CheckButton", "ConsumesManager_ShowUseButtonCheckbox", showUseButtonFrame) showUseButtonCheckbox:SetWidth(16) showUseButtonCheckbox:SetHeight(16) showUseButtonCheckbox:SetPoint("LEFT", showUseButtonFrame, "LEFT", 0, 0) showUseButtonCheckbox:SetNormalTexture("Interface\\Buttons\\UI-CheckBox-Up") showUseButtonCheckbox:SetPushedTexture("Interface\\Buttons\\UI-CheckBox-Down") showUseButtonCheckbox:SetHighlightTexture("Interface\\Buttons\\UI-CheckBox-Highlight") showUseButtonCheckbox:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check") showUseButtonCheckbox:SetChecked(ConsumesManager_Options.showUseButton) showUseButtonCheckbox:SetScript("OnClick", function() if showUseButtonCheckbox:GetChecked() then ConsumesManager_Options.showUseButton = true else ConsumesManager_Options.showUseButton = false end ConsumesManager_UpdateManagerContent() ConsumesManager_UpdatePresetsContent() end) local showUseButtonLabel = showUseButtonFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") showUseButtonLabel:SetPoint("LEFT", showUseButtonCheckbox, "RIGHT", 4, 0) showUseButtonLabel:SetText("Show Use Button") showUseButtonLabel:SetJustifyH("LEFT") showUseButtonFrame:SetScript("OnMouseDown", function() showUseButtonCheckbox:Click() end) currentYOffset = currentYOffset - lineHeight - 20 -- Multi-Account Setup local multiAccountTitle = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") multiAccountTitle:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, currentYOffset) multiAccountTitle:SetText("Multi-Account Setup |cffff0000(BETA!)|r") multiAccountTitle:SetTextColor(1, 1, 1) currentYOffset = currentYOffset - lineHeight local multiAccountInfo = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormal") multiAccountInfo:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, currentYOffset) multiAccountInfo:SetText("Set a unique channel name and password. \nRepeat this setup for each of your alt-accounts.") multiAccountInfo:SetJustifyH("LEFT") currentYOffset = currentYOffset - lineHeight * 2 -- More Info Button local popup = MultiAccountInfoPopup() local MoreInfoBtn = CreateFrame("Button", "ConsumesManager_MoreInfoBtn", scrollChild, "UIPanelButtonTemplate") MoreInfoBtn:SetWidth(70) MoreInfoBtn:SetHeight(20) MoreInfoBtn:SetText("More Info") MoreInfoBtn:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, currentYOffset) MoreInfoBtn:SetScript("OnClick", function() if popup:IsShown() then popup:Hide() else popup:Show() end end) currentYOffset = currentYOffset - lineHeight - 10 -- Channel Input local channelLabel = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormal") channelLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, currentYOffset) channelLabel:SetText("Channel:") channelLabel:SetWidth(60) channelLabel:SetJustifyH("LEFT") channelLabel:SetTextColor(1, 1, 1) -- Create a frame to hold the editbox so the sub-textures stay aligned local channelFrame = CreateFrame("Frame", nil, scrollChild) channelFrame:SetHeight(20) channelFrame:SetWidth(140) channelFrame:SetPoint("LEFT", channelLabel, "RIGHT", 10, 0) local channelEditBox = CreateFrame("EditBox", "ConsumesManager_ChannelEditBox", channelFrame, "InputBoxTemplate") channelEditBox:SetAutoFocus(false) channelEditBox:SetMaxLetters(50) channelEditBox:SetAllPoints(channelFrame) -- Fill the entire holding frame local leftTex = getglobal(channelEditBox:GetName().."Left") local midTex = getglobal(channelEditBox:GetName().."Middle") local rightTex= getglobal(channelEditBox:GetName().."Right") -- Anchor them so they move with the EditBox if leftTex then leftTex:ClearAllPoints() leftTex:SetPoint("LEFT", channelEditBox, "LEFT", -5, 0) end if midTex then midTex:ClearAllPoints() midTex:SetPoint("LEFT", leftTex, "RIGHT", 0, 0) midTex:SetPoint("RIGHT", rightTex, "LEFT", 0, 0) end if rightTex then rightTex:ClearAllPoints() rightTex:SetPoint("RIGHT", channelEditBox, "RIGHT", 5, 0) end -- Retrieve stored channel local stored_channel = "" if ConsumesManager_Options.Channel and ConsumesManager_Options.Channel ~= "" then stored_channel = DecodeMessage(ConsumesManager_Options.Channel) end channelEditBox:SetText(stored_channel) currentYOffset = currentYOffset - lineHeight -- Password Input local passwordLabel = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormal") passwordLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, currentYOffset) passwordLabel:SetText("Password:") passwordLabel:SetWidth(60) passwordLabel:SetJustifyH("LEFT") passwordLabel:SetTextColor(1, 1, 1) -- Same holding frame approach local passwordFrame = CreateFrame("Frame", nil, scrollChild) passwordFrame:SetHeight(20) passwordFrame:SetWidth(140) passwordFrame:SetPoint("LEFT", passwordLabel, "RIGHT", 10, 0) local passwordEditBox = CreateFrame("EditBox", "ConsumesManager_PasswordEditBox", passwordFrame, "InputBoxTemplate") passwordEditBox:SetAutoFocus(false) passwordEditBox:SetMaxLetters(50) passwordEditBox:SetAllPoints(passwordFrame) local pLeft = getglobal(passwordEditBox:GetName().."Left") local pMid = getglobal(passwordEditBox:GetName().."Middle") local pRight= getglobal(passwordEditBox:GetName().."Right") if pLeft then pLeft:ClearAllPoints() pLeft:SetPoint("LEFT", passwordEditBox, "LEFT", -5, 0) end if pMid then pMid:ClearAllPoints() pMid:SetPoint("LEFT", pLeft, "RIGHT", 0, 0) pMid:SetPoint("RIGHT", pRight, "LEFT", 0, 0) end if pRight then pRight:ClearAllPoints() pRight:SetPoint("RIGHT", passwordEditBox, "RIGHT", 5, 0) end local stored_password = "" if ConsumesManager_Options.Password and ConsumesManager_Options.Password ~= "" then stored_password = DecodeMessage(ConsumesManager_Options.Password) end passwordEditBox:SetText(stored_password) currentYOffset = currentYOffset - lineHeight - 10 -- Join and Leave Channel Buttons joinChannelButton = CreateFrame("Button", "ConsumesManager_JoinChannelButton", scrollChild, "UIPanelButtonTemplate") joinChannelButton:SetWidth(140) joinChannelButton:SetHeight(24) joinChannelButton:SetText("Save & Join Channel") joinChannelButton:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, currentYOffset) LeaveChannelButton = CreateFrame("Button", "ConsumesManager_LeaveChannelButton", scrollChild, "UIPanelButtonTemplate") LeaveChannelButton:SetWidth(140) LeaveChannelButton:SetHeight(24) LeaveChannelButton:SetText("Leave Channel") LeaveChannelButton:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 140, currentYOffset) -- Function to Update Leave Button State local function UpdateLeaveButtonState() if ConsumesManager_Options.Channel == "" or ConsumesManager_Options.Channel == nil then LeaveChannelButton:Disable() LeaveChannelButton:SetAlpha(0.5) channelEditBox:SetText("") passwordEditBox:SetText("") else LeaveChannelButton:Enable() LeaveChannelButton:SetAlpha(1) end end UpdateLeaveButtonState() -- Leave Channel Button Script LeaveChannelButton:SetScript("OnClick", function() if ConsumesManager_Options.Channel == "" or ConsumesManager_Options.Channel == nil then UpdateLeaveButtonState() updateSenDataButtonState() else local decoded_channel = DecodeMessage(ConsumesManager_Options.Channel) DEFAULT_CHAT_FRAME:AddMessage("|cffffffff" .. GetAddOnMetadata("ConsumesManager", "Title") .. ":|r |cffffffffYou left|r |cffffc0c0[" .. decoded_channel .. "]|r|cffffffff. Multi-account sync |cffff0000disabled|r|cffffffff.|r") LeaveChannelByName(decoded_channel) ConsumesManager_Options.Channel = nil ConsumesManager_Options.Password = nil UpdateLeaveButtonState() updateSenDataButtonState() end end) -- Channel Error Message local channelErrorMessage = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormal") channelErrorMessage:SetPoint("TOPLEFT", joinChannelButton, "BOTTOMLEFT", 0, -5) channelErrorMessage:SetTextColor(1, 0, 0) channelErrorMessage:SetText("Failed to join channel. Read chat for more info.") channelErrorMessage:Hide() currentYOffset = currentYOffset - lineHeight - 30 -- Function to Update Join Button State local function UpdateJoinButtonState() local ctext = channelEditBox:GetText() local ptext = passwordEditBox:GetText() if ctext ~= "" and ptext ~= "" then joinChannelButton:Enable() joinChannelButton:SetAlpha(1) else joinChannelButton:Disable() joinChannelButton:SetAlpha(0.5) end end channelEditBox:SetScript("OnTextChanged", UpdateJoinButtonState) passwordEditBox:SetScript("OnTextChanged", UpdateJoinButtonState) UpdateJoinButtonState() -- Function to Handle Channel Join Failures function ConsumesManager_ChannelJoinFailed(error_message) ConsumesManager_Options.Channel = nil ConsumesManager_Options.Password = nil channelEditBox:SetText("") passwordEditBox:SetText("") UpdateJoinButtonState() channelErrorMessage:Show() channelErrorMessage:SetText(error_message) end -- Danger Zone Title local DangerZonTitle = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") DangerZonTitle:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, currentYOffset) DangerZonTitle:SetText("Danger Zone") DangerZonTitle:SetTextColor(1, 1, 1) currentYOffset = currentYOffset - lineHeight * 2 -- Reset Addon Button resetButton = CreateFrame("Button", "ConsumesManager_ResetButton", scrollChild, "UIPanelButtonTemplate") resetButton:SetWidth(120) resetButton:SetHeight(24) resetButton:SetText("Reset Addon") resetButton:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, currentYOffset) resetButton:SetScript("OnClick", function() if ConsumesManager_Options.Channel then local decoded_channel = DecodeMessage(ConsumesManager_Options.Channel) LeaveChannelByName(decoded_channel) end ConsumesManager_Options = {} ConsumesManager_SelectedItems = {} ConsumesManager_Data = {} ReloadUI() end) -- Set the scroll child height to accommodate all content scrollChild.contentHeight = math.abs(startYOffset - currentYOffset) + 100 scrollChild:SetHeight(scrollChild.contentHeight) -- Scroll Bar Setup local scrollBar = CreateFrame("Slider", "ConsumesManager_SettingsScrollBar", 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() scrollFrame:SetVerticalScroll(value) end) parentFrame.scrollBar = scrollBar ConsumesManager_UpdateSettingsScrollBar() if not ConsumesManager_ChannelFrame then ConsumesManager_ChannelFrame = CreateFrame("Frame", "ConsumesManager_ChannelFrame") end local channelmsg = "" joinChannelButton:SetScript("OnClick", function() ConsumesManager_ChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE") channelErrorMessage:Hide() local ctext = channelEditBox:GetText() local ptext = passwordEditBox:GetText() local final_result = nil local try_again = false ConsumesManager_ChannelFrame:SetScript("OnEvent", function() local noticeType = string.upper(arg1 or "") local channelName = string.upper(arg9 or "") local inputChannelName = string.upper(ctext or "") if noticeType == "WRONG_PASSWORD" and channelName == inputChannelName then channelmsg = "WRONG_PASSWORD" --DEFAULT_CHAT_FRAME:AddMessage(noticeType) elseif noticeType == "NOT_MODERATOR" and channelName == inputChannelName then channelmsg = "NOT_MODERATOR" --DEFAULT_CHAT_FRAME:AddMessage(noticeType) elseif noticeType == "YOU_JOINED" and channelName == inputChannelName then channelmsg = "YOU_JOINED" --DEFAULT_CHAT_FRAME:AddMessage(noticeType) end end) -- Don't join same channel if ConsumesManager_Options.Channel then if DecodeMessage(ConsumesManager_Options.Channel) == ctext then channelErrorMessage:SetText("Already in this channel") channelErrorMessage:Show() return end end -- Block attempts to join big system channels local blocked = { "world", "general", "localdefense", "hardcore", "lft", "trade" } local lowerChannel = string.lower(ctext) for _, b in pairs(blocked) do if lowerChannel == b then ConsumesManager_ChannelJoinFailed("You cannot use a global channel") return end end JoinChannelByName(ctext, ptext) joinChannelButton:SetText("Connecting... (4)") joinChannelButton:Disable() joinChannelButton:SetAlpha(0.5) local delayFrame = ConsumesManager_ChannelFrame.delayFrameJoin or CreateFrame("Frame") ConsumesManager_ChannelFrame.delayFrameJoin = delayFrame delayFrame:Show() local elapsed = 0 local delay = 5 local one_attempt = 0 delayFrame:SetScript("OnUpdate", function() if elapsed > 1 and elapsed < 2 then joinChannelButton:SetText("Connecting... (3)") if one_attempt == 0 then DEFAULT_CHAT_FRAME:AddMessage("message: " .. channelmsg) if channelmsg == "WRONG_PASSWORD" then final_result = "WRONG_PASSWORD" elseif channelmsg == "YOU_JOINED" then DEFAULT_CHAT_FRAME:AddMessage("setting password") SetChannelPassword(ctext, ptext) end end one_attempt = 1 elseif elapsed > 2 and elapsed < 3 then joinChannelButton:SetText("Connecting... (2)") if one_attempt == 1 then DEFAULT_CHAT_FRAME:AddMessage("message: " .. channelmsg) if channelmsg == "YOU_JOINED" then final_result = "SUCCESS" elseif channelmsg == "NOT_MODERATOR" then LeaveChannelByName(ctext) try_again = true end end one_attempt = 2 elseif elapsed > 3 and elapsed < 4 then joinChannelButton:SetText("Connecting... (1)") if one_attempt == 2 then DEFAULT_CHAT_FRAME:AddMessage("message: " .. channelmsg) if try_again == true then JoinChannelByName(ctext, ptext) end end one_attempt = 3 elseif elapsed > 4 and elapsed < 5 then joinChannelButton:SetText("Connecting... (0)") if one_attempt == 3 then DEFAULT_CHAT_FRAME:AddMessage("message: " .. channelmsg) if try_again == true then if channelmsg == "YOU_JOINED" then final_result = "SUCCESS" elseif channelmsg == "NOT_MODERATOR" then final_result = "NOT_MODERATOR" end end end one_attempt = 4 end elapsed = elapsed + arg1 if elapsed >= delay then delayFrame:SetScript("OnUpdate", nil) delayFrame:Hide() -- ACTION AFTER 3 SECONDS if final_result == "SUCCESS" then ConsumesManager_Options.Channel = EncodeMessage(ctext) ConsumesManager_Options.Password = EncodeMessage(ptext) DEFAULT_CHAT_FRAME:AddMessage("|cffffffff" .. GetAddOnMetadata("ConsumesManager", "Title") .. ":|r |cffffffffYou joined|r |cffffc0c0[" .. ctext .. "]|r|cffffffff. Multi-account sync |cff00ff00enabled|r|cffffffff.|r") elseif final_result == "WRONG_PASSWORD" then ConsumesManager_ChannelJoinFailed("Wrong password. Try again.") DEFAULT_CHAT_FRAME:AddMessage("|cffffffff" .. GetAddOnMetadata("ConsumesManager", "Title") .. ":|r |cffffffffWrong password for|r |cffffc0c0[" .. ctext .. "]|r|cffffffff. Multi-account sync |cffff0000disabled|r|cffffffff.|r") elseif final_result == "NOT_MODERATOR" then ConsumesManager_ChannelJoinFailed("This is not your channel.") DEFAULT_CHAT_FRAME:AddMessage("|cffffffff" .. GetAddOnMetadata("ConsumesManager", "Title") .. ":|r |cffffffffYou don't own|r |cffffc0c0[" .. ctext .. "]|r|cffffffff. Multi-account sync |cffff0000disabled|r|cffffffff.|r") else LeaveChannelByName(ctext) ConsumesManager_ChannelJoinFailed("Unknown error") DEFAULT_CHAT_FRAME:AddMessage("|cffffffff" .. GetAddOnMetadata("ConsumesManager", "Title") .. ":|r |cffffffffFailed to join|r |cffffc0c0[" .. ctext .. "]|r|cffffffff. Multi-account sync |cffff0000disabled|r|cffffffff.|r") end joinChannelButton:SetText("Save & Join Channel") joinChannelButton:Enable() joinChannelButton:SetAlpha(1) updateSenDataButtonState() UpdateLeaveButtonState() ConsumesManager_ChannelFrame:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE") end delayFrame:Show() end) end) end function ConsumesManager_UpdateSettingsContent() local parentFrame = ConsumesManager_MainFrame and ConsumesManager_MainFrame.tabs and ConsumesManager_MainFrame.tabs[4] if not parentFrame or not parentFrame.scrollFrame then return end -- Remove existing child local oldChild = parentFrame.scrollFrame:GetScrollChild() if oldChild then oldChild:Hide() oldChild:SetParent(nil) parentFrame.scrollFrame:SetScrollChild(nil) end -- Remove old scrollbar if parentFrame.scrollBar then parentFrame.scrollBar:Hide() parentFrame.scrollBar:SetParent(nil) parentFrame.scrollBar = nil end -- New scroll child local newScrollChild = CreateFrame("Frame", nil, parentFrame.scrollFrame) newScrollChild:SetWidth(WindowWidth - 10) newScrollChild:SetHeight(1) newScrollChild.contentHeight = 0 parentFrame.scrollFrame:SetScrollChild(newScrollChild) parentFrame.scrollChild = newScrollChild -- Build content ConsumesManager_CreateSettingsContent(parentFrame) -- Reset scroll if parentFrame.scrollBar then parentFrame.scrollBar:SetValue(0) end end function ConsumesManager_UpdateSettingsScrollBar() local OptionsFrame = ConsumesManager_MainFrame and ConsumesManager_MainFrame.tabs and ConsumesManager_MainFrame.tabs[4] if not OptionsFrame then return end local scrollBar = OptionsFrame.scrollBar local scrollFrame = OptionsFrame.scrollFrame local scrollChild = OptionsFrame.scrollChild local totalHeight = scrollChild.contentHeight local shownHeight = 420 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 scrollFrame.maxScroll = 0 scrollBar:SetMinMaxValues(0, 0) scrollBar:SetValue(0) scrollBar:Hide() end end -- Global Functions ----------------------------------------------------------------------------- function ConsumesManager_UpdateUseButtons() if not ConsumesManager_MainFrame or not ConsumesManager_MainFrame.tabs or not ConsumesManager_MainFrame.tabs[1] then return end local ManagerFrame = ConsumesManager_MainFrame.tabs[1] local playerName = UnitName("player") local realmName = GetRealmName() local faction = UnitFactionGroup("player") -- Ensure data structure exists if not ConsumesManager_Data[realmName] or not ConsumesManager_Data[realmName][faction] then return end local data = ConsumesManager_Data[realmName][faction] local charData = data[playerName] if not charData then return end local inventory = charData["inventory"] or {} -- Iterate over the Items to update the buttons for _, categoryInfo in ipairs(ManagerFrame.categoryInfo) do for _, itemInfo in ipairs(categoryInfo.Items) do local itemID = itemInfo.itemID local button = itemInfo.button local label = itemInfo.label local frame = itemInfo.frame if ConsumesManager_SelectedItems[itemID] then local count = inventory[itemID] or 0 if count > 0 then if ConsumesManager_Options.showUseButton then button:Enable() button:Show() label:SetPoint("LEFT", button, "RIGHT", 4, 0) else button:Disable() button:Hide() label:SetPoint("LEFT", frame, "LEFT", 0, 0) end else button:Disable() button:Hide() label:SetPoint("LEFT", frame, "LEFT", 0, 0) end else button:Disable() button:Hide() label:SetPoint("LEFT", frame, "LEFT", 0, 0) end end end end function ConsumesManager_FindItemInBags(itemID) 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 _, _, linkItemID = string.find(link, "item:(%d+)") if linkItemID then linkItemID = tonumber(linkItemID) if linkItemID == itemID then return bag, slot end end end end end end return nil, nil end function ConsumesManager_GetConsumableCount(itemID) local totalCount = 0 local realmName = GetRealmName() -- Early exit if no data if not ConsumesManager_Data[realmName] then return 0 end -- Loop through all characters regardless of faction for character, charData in pairs(ConsumesManager_Data[realmName]) do if type(charData) == "table" and ConsumesManager_Options["Characters"] and ConsumesManager_Options["Characters"][character] == true then if character ~= "faction" then -- Skip non-character metadata if charData["inventory"] and charData["inventory"][itemID] then totalCount = totalCount + charData["inventory"][itemID] end if charData["bank"] and charData["bank"][itemID] then totalCount = totalCount + charData["bank"][itemID] end if charData["mail"] and charData["mail"][itemID] then totalCount = totalCount + charData["mail"][itemID] end end end end return totalCount end function ConsumesManager_UpdateAllContent() ConsumesManager_UpdateManagerContent() ConsumesManager_UpdatePresetsContent() ConsumesManager_UpdateSettingsContent() end function ConsumesManager_DisableTab(tab) tab.isEnabled = false tab:EnableMouse(true) -- Keep mouse enabled for tooltip tab.icon:SetDesaturated(true) -- Grey out the icon tab:SetScript("OnClick", nil) -- Remove OnClick handler -- Hide highlight effect if tab.hoverTexture then tab.hoverTexture:SetAlpha(0) end -- Adjust OnEnter handler to show tooltip only tab:SetScript("OnEnter", function() ShowTooltip(tab, tab.tooltipText) end) tab:SetScript("OnLeave", HideTooltip) end function ConsumesManager_EnableTab(tab) tab.isEnabled = true tab:EnableMouse(true) tab.icon:SetDesaturated(false) tab:SetScript("OnClick", tab.originalOnClick) -- Restore OnClick handler -- Show highlight effect if tab.hoverTexture then tab.hoverTexture:SetAlpha(1) end -- Restore original OnEnter and OnLeave handlers tab:SetScript("OnEnter", function() ShowTooltip(tab, tab.tooltipText) end) tab:SetScript("OnLeave", HideTooltip) end function ConsumesManager_UpdateTabStates() if not ConsumesManager_Tabs or not ConsumesManager_Tabs[2] or not ConsumesManager_Tabs[3] then -- Tabs have not been created yet; exit the function return end -- Items and Presets tabs are always enabled (they don't require scan data) ConsumesManager_EnableTab(ConsumesManager_Tabs[2]) -- Items Tab ConsumesManager_EnableTab(ConsumesManager_Tabs[3]) -- Presets Tab end function MultiAccountInfoPopup() -- Create the frame local popup = CreateFrame("Frame", "MyPopupFrame", UIParent) popup:SetHeight(180) popup:SetPoint("CENTER", UIParent, "CENTER", 0, 0) popup:SetBackdrop({ bgFile = "Interface\\ChatFrame\\ChatFrameBackground", -- Solid black background edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", tile = false, tileSize = 0, edgeSize = 32, insets = { left = 8, right = 8, top = 8, bottom = 8 } }) popup:SetBackdropColor(0, 0, 0, 1) -- Black background with full opacity popup:SetBackdropBorderColor(1, 1, 1, 1) -- White border popup:SetFrameStrata("DIALOG") popup:SetFrameLevel(1000) popup:SetMovable(true) popup:EnableMouse(true) popup:RegisterForDrag("LeftButton") popup:SetScript("OnDragStart", function() this:StartMoving() end) popup:SetScript("OnDragStop", function() this:StopMovingOrSizing() end) -- Add a close button local closeButton = CreateFrame("Button", nil, popup, "UIPanelCloseButton") closeButton:SetPoint("TOPRIGHT", popup, "TOPRIGHT", -5, -5) -- Add a title local infotitle = popup:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") infotitle:SetPoint("TOPLEFT", popup, "TOPLEFT", 20, -20) infotitle:SetText("How does it work?") infotitle:SetTextColor(1,1,1) -- Add the infotext popup.infotext = popup:CreateFontString(nil, "OVERLAY", "GameFontNormal") popup.infotext:SetPoint("TOPLEFT", popup, "TOPLEFT", 20, -35) popup.infotext:SetJustifyH("LEFT") popup.infotext:SetText( "|cffff0000(Beta Feature! Might be glitchy!)|r\n\n" .. "To sync accounts, follow these steps:\n\n" .. "1. Join a private channel with a unique name and password.\n" .. "2. The channel info is saved for all characters on your account.\n" .. "3. Log into your alt account on a second client.\n" .. "4. Join the same channel via this addon to link both accounts.\n" .. "5. Keep one character from your main account online for syncing.\n" .. "6. Click on 'Push Data' to send the database from one account to all the other that are online." ) -- Function to adjust width dynamically after text is set popup:SetScript("OnShow", function() local textWidth = popup.infotext:GetStringWidth() + 40 -- Add padding popup:SetWidth(textWidth) end) popup:Hide() return popup end -- Tooltip Functions -------------------------------------------------------------------------------------- function ConsumesManager_ShowConsumableTooltip(itemID) -- Ensure item is enabled in settings if not ConsumesManager_SelectedItems[itemID] then return end -- Create or reuse custom tooltip frame if not ConsumesManager_CustomTooltip then -- Create the frame local tooltipFrame = CreateFrame("Frame", "ConsumesManager_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:SetJustifyH("LEFT") tooltipFrame.title = title -- Item Total local total = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") total:SetPoint("TOPLEFT", icon, "TOPRIGHT", 10, -20) total:SetJustifyH("LEFT") tooltipFrame.total = total -- Content text local content = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") content:SetPoint("TOPLEFT", icon, "BOTTOMLEFT", 0, -10) content:SetJustifyH("LEFT") tooltipFrame.content = content -- Mats text local mats = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") mats:SetPoint("TOPLEFT", content, "BOTTOMLEFT", 0, -10) mats:SetJustifyH("LEFT") tooltipFrame.mats = mats ConsumesManager_CustomTooltip = tooltipFrame end local tooltipFrame = ConsumesManager_CustomTooltip -- Get item info local itemName = consumablesList[itemID] or "Unknown Item" local itemTexture = consumablesTexture[itemID] or "Interface\\Icons\\INV_Misc_QuestionMark" -- Set icon and title tooltipFrame.icon:SetTexture(itemTexture) tooltipFrame.title:SetText(itemName) local mats = consumablesMats[itemID] or {} local matsText = "" if type(mats) == "table" and next(mats) then local index = 0 local divider = "" for _, mat in ipairs(mats) do index = index + 1 if index > 1 then divider = " | " else divider = "" end matsText = matsText .. divider .. "|cff4ac9ff" .. mat .. "|r" end else matsText = "|cff696969No materials specified for this item.|r" end tooltipFrame.mats:SetText(matsText) -- Prepare content text local contentText = "" local realmName = GetRealmName() local playerName = UnitName("player") local playerFaction = UnitFactionGroup("player") -- Ensure data structure exists ConsumesManager_Data[realmName] = ConsumesManager_Data[realmName] or {} -- Initialize totals local totalInventory, totalBank, totalMail = 0, 0, 0 local hasItems = false local characterList = {} -- Ensure character settings exist ConsumesManager_Options["Characters"] = ConsumesManager_Options["Characters"] or {} -- Collect data for each character for character, charData in pairs(ConsumesManager_Data[realmName]) do -- Make sure it's a character data table and not metadata if type(charData) == "table" and ConsumesManager_Options["Characters"][character] == true then -- Get faction info for display local charFaction = charData.faction or "Unknown" 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, faction = charFaction, inventory = inventory, bank = bank, mail = mail, total = total, isPlayer = (character == playerName) }) end end end local totalItems = totalInventory + totalBank + totalMail -- Adjust label color based on count if totalItems == 0 then tooltipFrame.total:SetTextColor(1, 0, 0) -- Red elseif totalItems < 10 then tooltipFrame.total:SetTextColor(1, 0.4, 0) -- Orange elseif totalItems <= 20 then tooltipFrame.total:SetTextColor(1, 0.85, 0) -- Yellow else tooltipFrame.total:SetTextColor(0, 1, 0) -- Green end tooltipFrame.total:SetText("Total: " .. totalItems) if not hasItems then contentText = contentText .. "|cffff0000No items found for this consumable.|r" lineHeightAdjust = 10 else lineHeightAdjust = 0 -- Sort characters alphabetically 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 "|cffffffff" -- Green for player, white for others local factionColor = "" -- Color code by faction if charInfo.faction == "Alliance" then factionColor = "|cff0078ff" -- Blue for Alliance elseif charInfo.faction == "Horde" then factionColor = "|cffb30000" -- Red for Horde else factionColor = "|cff808080" -- Grey for unknown end contentText = contentText .. nameColor .. charInfo.name .. factionColor .. " [" .. charInfo.faction .. "]|r (" .. charInfo.total .. ")\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 lineHeightTooltip = 12 local totalHeight = 60 + (numLines * lineHeightTooltip) + lineHeightAdjust tooltipFrame:SetHeight(totalHeight) -- Set the width based on content local titleWidth = math.max(tooltipFrame.mats:GetStringWidth() + 20, tooltipFrame.title:GetStringWidth() + 60) local maxWidth = math.max(titleWidth, 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 ConsumesManager_ShowItemsTooltip(itemID) -- Set the maximum width for the description text local maxDescriptionWidth = 200 -- Create or reuse the tooltip frame if not ConsumesManager_ItemsTooltip then -- Create the frame local tooltipFrame = CreateFrame("Frame", "ConsumesManager_ItemsTooltip", UIParent) 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) -- 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:SetJustifyH("LEFT") tooltipFrame.title = title -- Item description local description = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") description:SetPoint("TOPLEFT", icon, "BOTTOMLEFT", 0, -10) description:SetWidth(maxDescriptionWidth) description:SetJustifyH("LEFT") description:SetTextColor(1, 1, 1) tooltipFrame.description = description -- Mats text local mats = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") mats:SetPoint("TOPLEFT", description, "BOTTOMLEFT", 0, -10) mats:SetJustifyH("LEFT") tooltipFrame.mats = mats ConsumesManager_ItemsTooltip = tooltipFrame end local tooltipFrame = ConsumesManager_ItemsTooltip -- Get item info local itemName = consumablesList[itemID] or "Unknown Item" local itemTexture = consumablesTexture[itemID] or "Interface\\Icons\\INV_Misc_QuestionMark" local itemDescription = consumablesDescription[itemID] or "" -- Set icon, title, and description tooltipFrame.icon:SetTexture(itemTexture) tooltipFrame.title:SetText(itemName) tooltipFrame.description:SetText(itemDescription) local mats = consumablesMats[itemID] or {} local matsText = "" if type(mats) == "table" and next(mats) then local index = 0 local divider = "" for _, mat in ipairs(mats) do index = index + 1 if index > 1 then divider = " | " else divider = "" end matsText = matsText .. divider .. "|cff4ac9ff" .. mat .. "|r" end else matsText = "|cff696969No materials specified for this item.|r" end tooltipFrame.mats:SetText(matsText) -- Adjust the height of the description based on its content tooltipFrame.description:SetWidth(maxDescriptionWidth) tooltipFrame.description:SetText(itemDescription) local descriptionHeight = tooltipFrame.description:GetHeight() -- Adjust tooltip height based on content local totalHeight = 80 + descriptionHeight tooltipFrame:SetHeight(totalHeight) -- Set the width of the tooltip local titleWidth = math.max(tooltipFrame.title:GetStringWidth() + 70, tooltipFrame.mats:GetStringWidth() + 20) local maxWidth = math.max(titleWidth, maxDescriptionWidth + 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 ShowTooltip(tab, text) ConsumesManagerTooltip.text:SetText(text) ConsumesManagerTooltip:SetPoint("BOTTOMLEFT", tab, "TOPLEFT", 0, 0) ConsumesManagerTooltip:Show() end function HideTooltip() ConsumesManagerTooltip:Hide() end -- Scan Functions --------------------------------------------------------------------------------------- function ConsumesManager_ScanPlayerInventory() local delayFrameInv = ConsumesManager_EventFrame.delayFrameInv or CreateFrame("Frame") ConsumesManager_EventFrame.delayFrameInv = delayFrameInv local delayStartTime = GetTime() local delay = 0.5 delayFrameInv:SetScript("OnUpdate", function() if GetTime() - delayStartTime >= delay then local playerName = UnitName("player") local realmName = GetRealmName() local faction = UnitFactionGroup("player") -- Initialize data structure without faction separation ConsumesManager_Data[realmName] = ConsumesManager_Data[realmName] or {} -- Store faction information with player data for display purposes if not ConsumesManager_Data[realmName][playerName] then ConsumesManager_Data[realmName][playerName] = { faction = faction -- Store faction information with the character } else ConsumesManager_Data[realmName][playerName].faction = faction end -- Initialize inventory data ConsumesManager_Data[realmName][playerName]["inventory"] = {} -- Scan bags 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 consumablesList[itemID] then local _, itemCount = GetContainerItemInfo(bag, slot) if itemCount and itemCount ~= 0 then if itemCount < 0 then itemCount = -itemCount end ConsumesManager_Data[realmName][playerName]["inventory"][itemID] = (ConsumesManager_Data[realmName][playerName]["inventory"][itemID] or 0) + itemCount end end end end end end end ConsumesManager_UpdateUseButtons() ConsumesManager_UpdateManagerContent() ConsumesManager_UpdateTabStates() delayFrameInv:SetScript("OnUpdate", nil) end end) end function ConsumesManager_ScanPlayerBank() if not isBankOpen then return end local delayFrameBank = ConsumesManager_EventFrame.delayFrameBank or CreateFrame("Frame") ConsumesManager_EventFrame.delayFrameBank = delayFrameBank local delayStartTime = GetTime() local delay = 0.5 delayFrameBank:SetScript("OnUpdate", function() if GetTime() - delayStartTime >= delay then local playerName = UnitName("player") local realmName = GetRealmName() local faction = UnitFactionGroup("player") -- Initialize data structure without faction separation ConsumesManager_Data[realmName] = ConsumesManager_Data[realmName] or {} -- Update faction information (in case it changed or was missing) if not ConsumesManager_Data[realmName][playerName] then ConsumesManager_Data[realmName][playerName] = { faction = faction } else ConsumesManager_Data[realmName][playerName].faction = faction end -- Initialize bank data if it doesn't exist yet ConsumesManager_Data[realmName][playerName]["bank"] = {} -- Create a temporary table to track what we find local tempBankData = {} 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 consumablesList[itemID] then local _, itemCount = GetContainerItemInfo(bag, slot) if itemCount and itemCount ~= 0 then if itemCount < 0 then itemCount = -itemCount end tempBankData[itemID] = (tempBankData[itemID] or 0) + itemCount end end end end end end end end -- Update only the items we found, preserve existing data for items we didn't scan for itemID, count in pairs(tempBankData) do ConsumesManager_Data[realmName][playerName]["bank"][itemID] = count end ConsumesManager_UpdateManagerContent() ConsumesManager_UpdateTabStates() delayFrameBank:SetScript("OnUpdate", nil) end end) end function ConsumesManager_ScanPlayerMail() if not isMailOpen then return end local delayFrameMail = ConsumesManager_EventFrame.delayFrameMail or CreateFrame("Frame") ConsumesManager_EventFrame.delayFrameMail = delayFrameMail local delayStartTime = GetTime() local delay = 0.5 delayFrameMail:SetScript("OnUpdate", function() if GetTime() - delayStartTime >= delay then local playerName = UnitName("player") local realmName = GetRealmName() local faction = UnitFactionGroup("player") -- Initialize data structure without faction separation ConsumesManager_Data[realmName] = ConsumesManager_Data[realmName] or {} -- Update faction information if not ConsumesManager_Data[realmName][playerName] then ConsumesManager_Data[realmName][playerName] = { faction = faction } else ConsumesManager_Data[realmName][playerName].faction = faction end -- Initialize mail data ConsumesManager_Data[realmName][playerName]["mail"] = {} local numInboxItems = GetInboxNumItems() if numInboxItems and numInboxItems > 0 then for mailIndex = 1, numInboxItems do local itemName, _, itemCount = GetInboxItem(mailIndex) if itemName and itemCount and itemCount > 0 then local itemID = consumablesNameToID[itemName] if itemID and consumablesList[itemID] then ConsumesManager_Data[realmName][playerName]["mail"][itemID] = (ConsumesManager_Data[realmName][playerName]["mail"][itemID] or 0) + itemCount end end end end ConsumesManager_UpdateManagerContent() ConsumesManager_UpdateTabStates() delayFrameMail:SetScript("OnUpdate", nil) end end) end -- Multi-Account Data Handling -------------------------------------------------------------------------- local Converter = LibStub("LibCompress", true) -- SEND DATA local countdownFrame = CreateFrame("Frame") countdownFrame:Hide() local countdownTimer = 0 local Syncing = false local ProgressBar = 0 local total_time_stored = 0 local BarHeight = 0 local function SyncInProgress(syncing, totalTime) if syncing then sendDataButton:Disable() sendDataButton.icon:SetDesaturated(true) LeaveChannelButton:Disable() LeaveChannelButton:SetAlpha(0.5) joinChannelButton:Disable() joinChannelButton:SetAlpha(0.5) resetButton:Disable() resetButton:SetAlpha(0.5) ProgressBarFrame:Show() ProgressBarFrame_Text:Show() ProgressBar = totalTime if total_time_stored == 0 then total_time_stored = totalTime end countdownTimer = 0 BarHeight = 0 ProgressBarFrame_fill:SetHeight(0) countdownFrame:Show() else sendDataButton:Enable() sendDataButton.icon:SetDesaturated(false) LeaveChannelButton:Enable() LeaveChannelButton:SetAlpha(1) joinChannelButton:Enable() joinChannelButton:SetAlpha(1) resetButton:Enable() resetButton:SetAlpha(1) ProgressBarFrame:Hide() ProgressBarFrame_Text:Hide() ProgressBarFrame_fill:SetHeight(0) ProgressBar = 0 total_time_stored = 0 BarHeight = 0 ProgressBarFrame_fill:Hide() countdownFrame:Hide() end Syncing = syncing end countdownFrame:SetScript("OnUpdate", function() if Syncing and ProgressBar > 0 then countdownTimer = countdownTimer + arg1 if countdownTimer >= 1 then ProgressBar = ProgressBar - 1 if BarHeight < 492 then BarHeight = 492 - (492 / (total_time_stored - 1)) * (ProgressBar - 1) if BarHeight > 492 then BarHeight = 492 end end ProgressBarFrame_fill:Show() ProgressBarFrame_fill:SetHeight(BarHeight) countdownTimer = 0 end end end) function PushData() if not ConsumesManager_Options.Channel or ConsumesManager_Options.Channel == "" or not ConsumesManager_Options.Password or ConsumesManager_Options.Password == "" then return end local realmName = GetRealmName() local dataTable = ConsumesManager_Data[realmName] -- Send all data, not just current faction local serialized = Converter:TableToString(dataTable) local compressed = lzw:compress(serialized) local data = EncodeMessage(compressed) local channelName = DecodeMessage(ConsumesManager_Options.Channel) local channelNumber = GetChannelName(channelName) local length = string.len(data) local chunkSize = 100 local pos = 1 local queue = {} while pos <= length do local chunk = string.sub(data, pos, pos + chunkSize - 1) pos = pos + chunkSize table.insert(queue, chunk) end local totalMessages = table.getn(queue) DEFAULT_CHAT_FRAME:AddMessage("|cffffffff" .. GetAddOnMetadata("ConsumesManager", "Title") .. ":|r |cffffffffData pushing started in|r |cffffc0c0[" .. DecodeMessage(ConsumesManager_Options.Channel) .. "]|r|cffffffff. Please wait...|r") SendChatMessage("CM_SYNC_STARTED", "CHANNEL", nil, channelNumber) SyncInProgress(true, totalMessages) local receivedChunks = {} local sendFrame = CreateFrame("Frame") local eventFrame = CreateFrame("Frame") eventFrame:RegisterEvent("CHAT_MSG_CHANNEL") local state = "IDLE" local currentIndex = 0 local sendTimeout = 2 local lockoutDuration = 10 local lockedOutUntil = 0 local waitingForVerification = false local verificationElapsed = 0 local verificationWaitTime = 1 local currentStartTime = 0 eventFrame:SetScript("OnEvent", function() if event == "CHAT_MSG_CHANNEL" then local msg = arg1 local iStart, iEnd, msgIndex, chunk = string.find(msg, "^CM_(%d+):(.+)$") if msgIndex and chunk then msgIndex = tonumber(msgIndex) receivedChunks[msgIndex] = chunk if state == "WAITING" and msgIndex == currentIndex + 1 then table.remove(queue, 1) currentIndex = currentIndex + 1 state = "IDLE" end end end end) local function verifyData() for i=1, totalMessages do if not receivedChunks[i] then DEFAULT_CHAT_FRAME:AddMessage("|cffffffff" .. GetAddOnMetadata("ConsumesManager", "Title") .. ":|r Data validation |cffff0000failed|r|cffffffff!|r") return end end local reconstructed = table.concat(receivedChunks, "") if reconstructed == data then DEFAULT_CHAT_FRAME:AddMessage("|cffffffff" .. GetAddOnMetadata("ConsumesManager", "Title") .. ":|r Data validation |cff00ff00succeeded|r|cffffffff!|r") else DEFAULT_CHAT_FRAME:AddMessage("|cffffffff" .. GetAddOnMetadata("ConsumesManager", "Title") .. ":|r Data validation |cffff0000failed|r|cffffffff!|r") end end sendFrame:SetScript("OnUpdate", function() local now = GetTime() if waitingForVerification then verificationElapsed = verificationElapsed + arg1 if verificationElapsed >= verificationWaitTime then DEFAULT_CHAT_FRAME:AddMessage("|cffffffff" .. GetAddOnMetadata("ConsumesManager", "Title") .. ":|r Data pushing |cff00ff00finished|r|cffffffff!|r") verifyData() SendChatMessage("CM_SYNC_STOPPED", "CHANNEL", nil, channelNumber) SyncInProgress(false, 0) eventFrame:UnregisterEvent("CHAT_MSG_CHANNEL") sendFrame:SetScript("OnUpdate", nil) sendFrame:Hide() end return end if table.getn(queue) == 0 then waitingForVerification = true verificationElapsed = 0 return end if state == "LOCKED" then if now >= lockedOutUntil then state = "IDLE" else return end end if state == "IDLE" then local nextMsg = queue[1] if nextMsg then SendChatMessage("CM_" .. (currentIndex + 1) .. ":" .. nextMsg, "CHANNEL", nil, channelNumber) currentStartTime = now state = "WAITING" end elseif state == "WAITING" then if now - currentStartTime > sendTimeout then state = "LOCKED" lockedOutUntil = now + lockoutDuration end end end) sendFrame:Show() end -- READ DATA local collecting = false local collectedChunks = {} local collectingFrom = "" local collectingCount = 0 local dataComplete = false local eventFrame = nil local channelName = "" local function ResetCollection() collecting = false collectingFrom = "" collectedChunks = {} collectingCount = 0 dataComplete = false end -- Adjusted to accept 'sender' as a parameter local function StopCollecting(sender) collecting = false if sendDataButton then sendDataButton:Enable() sendDataButton.icon:SetDesaturated(false) end local total = table.getn(collectedChunks) if total > 0 then local i = 1 local finalString = "" while i <= total do if collectedChunks[i] then finalString = finalString .. collectedChunks[i] end i = i + 1 end DEFAULT_CHAT_FRAME:AddMessage( "|cffffffff" .. GetAddOnMetadata("ConsumesManager", "Title") .. ":|r |cffffffffData |cff00ff00successfully|r retrieved from|r |cff00ccff" .. sender .. "|r|cffffffff (" .. total .. " chunks & " .. string.len(finalString) .. " data length)|r" ) combineVariableTables(finalString) else DEFAULT_CHAT_FRAME:AddMessage( "|cffffffff" .. GetAddOnMetadata("ConsumesManager", "Title") .. ":|r |cffffffffData transmission |cffff0000failed|r from|r |cff00ccff" .. sender .. "|r" ) end end local function StartCollecting(sender) collecting = true collectingFrom = sender collectedChunks = {} collectingCount = 0 dataComplete = false if sendDataButton then sendDataButton:Disable() sendDataButton.icon:SetDesaturated(true) end DEFAULT_CHAT_FRAME:AddMessage( "|cffffffff" .. GetAddOnMetadata("ConsumesManager", "Title") .. ":|r |cffffffffReceiving data from|r |cff00ccff" .. sender .. "|r|cffffffff...|r" ) end local function OnChatMessage() if event == "CHAT_MSG_CHANNEL" then local msg = arg1 local sender = arg2 local chName = arg9 if chName == channelName and channelName ~= "" then if msg == "CM_SYNC_STARTED" then if sender ~= UnitName("player") and not collecting then StartCollecting(sender) end elseif msg == "CM_SYNC_STOPPED" then if collecting and sender == collectingFrom then StopCollecting(sender) -- Pass 'sender' here ResetCollection() end else if collecting and sender == collectingFrom then local iStart, iEnd, msgIndex, chunk = string.find(msg, "^CM_(%d+):(.+)$") if msgIndex and chunk then msgIndex = tonumber(msgIndex) collectedChunks[msgIndex] = chunk collectingCount = collectingCount + 1 end end end end end end function ReadData(mode) if not ConsumesManager_Options.Channel or ConsumesManager_Options.Channel == "" then return end channelName = DecodeMessage(ConsumesManager_Options.Channel) local channelNumber = GetChannelName(channelName) if not channelNumber or channelNumber == 0 then return end if mode == "start" then if not eventFrame then eventFrame = CreateFrame("Frame") eventFrame:SetScript("OnEvent", OnChatMessage) end eventFrame:RegisterEvent("CHAT_MSG_CHANNEL") elseif mode == "stop" then if eventFrame then eventFrame:UnregisterEvent("CHAT_MSG_CHANNEL") if collecting then StopCollecting(collectingFrom) -- Pass 'collectingFrom' ResetCollection() end end end end -- Register for PLAYER_LOGIN (or VARIABLES_LOADED in 1.12) to start listening automatically local startupFrame = CreateFrame("Frame") startupFrame:RegisterEvent("PLAYER_LOGIN") startupFrame:SetScript("OnEvent", function() if event == "PLAYER_LOGIN" then ReadData("start") end end) function combineVariableTables(compressed_message) local receivedTable = {} local realmName = GetRealmName() local decoded = DecodeMessage(compressed_message) local uncompressed = lzw:decompress(decoded) receivedTable = Converter:StringToTable(uncompressed) -- Initialize if needed ConsumesManager_Data[realmName] = ConsumesManager_Data[realmName] or {} -- Auto-select new characters ConsumesManager_Options["Characters"] = ConsumesManager_Options["Characters"] or {} for characterName, charData in pairs(receivedTable) do if type(charData) == "table" and characterName ~= "faction" then if ConsumesManager_Options["Characters"][characterName] == nil then ConsumesManager_Options["Characters"][characterName] = true end end end -- Replace character data completely instead of merging for characterName, charData in pairs(receivedTable) do if type(charData) == "table" then -- Create a new table for this character ConsumesManager_Data[realmName][characterName] = {} -- Copy faction data if charData.faction then ConsumesManager_Data[realmName][characterName].faction = charData.faction end -- Copy inventory data if type(charData.inventory) == "table" then ConsumesManager_Data[realmName][characterName].inventory = {} for itemID, count in pairs(charData.inventory) do ConsumesManager_Data[realmName][characterName].inventory[itemID] = count end end -- Copy bank data if type(charData.bank) == "table" then ConsumesManager_Data[realmName][characterName].bank = {} for itemID, count in pairs(charData.bank) do ConsumesManager_Data[realmName][characterName].bank[itemID] = count end end -- Copy mail data if type(charData.mail) == "table" then ConsumesManager_Data[realmName][characterName].mail = {} for itemID, count in pairs(charData.mail) do ConsumesManager_Data[realmName][characterName].mail[itemID] = count end end end end ConsumesManager_UpdateAllContent() end -- BASE64 ENCODE AND DECODE -------------------------------------------------------------------------------------------------- local b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" local d = {} do local i = 1 while i <= string.len(b) do local c = string.sub(b, i, i) d[c] = i - 1 i = i + 1 end end function EncodeMessage(data) local encoded = {} local length = string.len(data) local i = 1 while i <= length do local c1 = string.byte(data, i, i) i = i + 1 local c2 = (i <= length) and string.byte(data, i, i) or nil i = i + 1 local c3 = (i <= length) and string.byte(data, i, i) or nil i = i + 1 local o1 = bit.rshift(c1, 2) local o2 = bit.bor(bit.lshift(bit.band(c1, 3),4), (c2 and bit.rshift(c2, 4) or 0)) local o3 = (c2 and bit.bor(bit.lshift(bit.band(c2,15),2), (c3 and bit.rshift(c3,6) or 0)) or 64) local o4 = (c3 and bit.band(c3,63) or 64) table.insert(encoded, string.sub(b, o1+1, o1+1)) table.insert(encoded, string.sub(b, o2+1, o2+1)) if o3 ~= 64 then table.insert(encoded, string.sub(b, o3+1, o3+1)) else table.insert(encoded, "=") end if o4 ~= 64 then table.insert(encoded, string.sub(b, o4+1, o4+1)) else table.insert(encoded, "=") end end return table.concat(encoded, "") end function DecodeMessage(str) local decoded = {} local length = string.len(str) local i = 1 while i <= length do local c1 = string.sub(str, i, i) i = i + 1 local c2 = string.sub(str, i, i) i = i + 1 if (not c2) or (c1 == '=') or (c2 == '=') then break end local c3 = string.sub(str, i, i) i = i + 1 local c4 = string.sub(str, i, i) i = i + 1 local dc1 = d[c1] local dc2 = d[c2] local dc3 = (c3 and c3 ~= '=' and d[c3]) or nil local dc4 = (c4 and c4 ~= '=' and d[c4]) or nil local o1 = bit.bor(bit.lshift(dc1, 2), bit.rshift(dc2, 4)) table.insert(decoded, string.char(o1)) if dc3 then local o2 = bit.bor(bit.lshift(bit.band(dc2, 15),4), bit.rshift(dc3, 2)) table.insert(decoded, string.char(o2)) if dc4 then local o3 = bit.bor(bit.lshift(bit.band(dc3, 3),6), dc4) table.insert(decoded, string.char(o3)) end end end return table.concat(decoded, "") end