BenchTracker = {} local mainFrame = nil local playerFrames = {} local logEntries = {} local currentCheckData = {} local isListening = false local listenTimer = nil local listenElapsed = 0 local checkCounter = 0 local activeTab = "afk" -- Class colors (same as raidstrike) local CLASS_COLORS = { ["WARRIOR"] = {r = 0.78, g = 0.61, b = 0.43}, ["PALADIN"] = {r = 0.96, g = 0.55, b = 0.73}, ["HUNTER"] = {r = 0.67, g = 0.83, b = 0.45}, ["ROGUE"] = {r = 1.0, g = 0.96, b = 0.41}, ["PRIEST"] = {r = 1.0, g = 1.0, b = 1.0}, ["SHAMAN"] = {r = 0.0, g = 0.44, b = 0.87}, ["MAGE"] = {r = 0.41, g = 0.8, b = 0.94}, ["WARLOCK"] = {r = 0.58, g = 0.51, b = 0.79}, ["DRUID"] = {r = 1.0, g = 0.49, b = 0.04} } local LOCALIZED_CLASS_LOOKUP = { ["Warrior"] = "WARRIOR", ["Paladin"] = "PALADIN", ["Hunter"] = "HUNTER", ["Rogue"] = "ROGUE", ["Priest"] = "PRIEST", ["Shaman"] = "SHAMAN", ["Mage"] = "MAGE", ["Warlock"] = "WARLOCK", ["Druid"] = "DRUID", ["Krieger"] = "WARRIOR", ["Jager"] = "HUNTER", ["Schurke"] = "ROGUE", ["Priester"] = "PRIEST", ["Schamane"] = "SHAMAN", ["Magier"] = "MAGE", ["Hexenmeister"] = "WARLOCK", ["Druide"] = "DRUID", ["Guerrier"] = "WARRIOR", ["Chasseur"] = "HUNTER", ["Voleur"] = "ROGUE", ["Chaman"] = "SHAMAN", } function BenchTracker:GetEnglishClass(localizedClass) if not localizedClass then return nil end local upperClass = string.upper(localizedClass) if CLASS_COLORS[upperClass] then return upperClass end local englishClass = LOCALIZED_CLASS_LOOKUP[localizedClass] if englishClass then return englishClass end return upperClass end function BenchTracker:IsOfficer() local numMembers = GetNumGuildMembers() local playerName = UnitName("player") for i = 1, numMembers do local name, rank, rankIndex = GetGuildRosterInfo(i) if name == playerName then if rank == "Officer" or rank == "Supreme Leader" then return true end return false end end return false end function BenchTracker:GetTimestamp() local dateInfo = date("*t") local day = dateInfo.day local month = dateInfo.month local year = dateInfo.year local hour = dateInfo.hour local min = dateInfo.min local dayStr = day local monthStr = month if day < 10 then dayStr = "0" .. day end if month < 10 then monthStr = "0" .. month end local hourStr = hour local minStr = min if hour < 10 then hourStr = "0" .. hour end if min < 10 then minStr = "0" .. min end return "[" .. dayStr .. "/" .. monthStr .. "/" .. year .. " - " .. hourStr .. ":" .. minStr .. "]" end -- Get list of raid members (bench group) function BenchTracker:GetRaidMembers() local members = {} local raidSize = GetNumRaidMembers() if raidSize == 0 then return members end for i = 1, raidSize do local name, rank, subgroup, level, class = GetRaidRosterInfo(i) if name then table.insert(members, {name = name, class = class, subgroup = subgroup}) end end -- Sort alphabetically table.sort(members, function(a, b) return a.name < b.name end) return members end ---------------------------------------------------------------- -- MAIN FRAME ---------------------------------------------------------------- function BenchTracker:CreateMainFrame() if mainFrame then return end mainFrame = CreateFrame("Frame", "BenchTrackerFrame", UIParent) mainFrame:SetWidth(280) mainFrame:SetHeight(380) mainFrame:SetPoint("CENTER", UIParent, "CENTER", 0, 0) mainFrame:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 16, edgeSize = 8, insets = {left = 2, right = 2, top = 2, bottom = 2} }) mainFrame:SetBackdropColor(0.05, 0.05, 0.05, 0.95) mainFrame:SetBackdropBorderColor(0.3, 0.3, 0.3, 1) mainFrame:SetMovable(true) mainFrame:EnableMouse(true) mainFrame:RegisterForDrag("LeftButton") mainFrame:SetScript("OnDragStart", function() this:StartMoving() end) mainFrame:SetScript("OnDragStop", function() this:StopMovingOrSizing() end) mainFrame:SetFrameStrata("DIALOG") mainFrame:Hide() -- Title local title = mainFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") title:SetPoint("TOP", mainFrame, "TOP", 0, -8) title:SetText("Bench Tracker") title:SetTextColor(0.9, 0.9, 0.9, 1) -- Close button local closeButton = CreateFrame("Button", nil, mainFrame) closeButton:SetWidth(12) closeButton:SetHeight(12) closeButton:SetPoint("TOPRIGHT", mainFrame, "TOPRIGHT", -6, -6) closeButton:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 8, edgeSize = 4, insets = {left = 1, right = 1, top = 1, bottom = 1} }) closeButton:SetBackdropColor(0.8, 0.2, 0.2, 0.8) closeButton:SetBackdropBorderColor(0.6, 0.6, 0.6, 1) local closeText = closeButton:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") closeText:SetPoint("CENTER", closeButton, "CENTER", 0, 0) closeText:SetText("x") closeText:SetTextColor(1, 1, 1, 1) closeButton:SetScript("OnEnter", function() this:SetBackdropColor(1, 0.3, 0.3, 1) end) closeButton:SetScript("OnLeave", function() this:SetBackdropColor(0.8, 0.2, 0.2, 0.8) end) closeButton:SetScript("OnClick", function() mainFrame:Hide() end) -- Tab buttons BenchTracker:CreateTabButtons() -- AFK Check content area local afkContent = CreateFrame("Frame", "BenchAfkContent", mainFrame) afkContent:SetPoint("TOPLEFT", mainFrame, "TOPLEFT", 6, -42) afkContent:SetPoint("BOTTOMRIGHT", mainFrame, "BOTTOMRIGHT", -6, 6) afkContent:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = nil, tile = true, tileSize = 16, insets = {left = 0, right = 0, top = 0, bottom = 0} }) afkContent:SetBackdropColor(0.08, 0.08, 0.08, 0.5) mainFrame.afkContent = afkContent -- Column labels above the list (centered over the radio buttons) local readyLabel = afkContent:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") readyLabel:SetPoint("TOP", afkContent, "TOPRIGHT", -50, -2) readyLabel:SetText("|cFF00CC00R|r") readyLabel:SetTextColor(0.0, 0.8, 0.0, 1) mainFrame.colReadyLabel = readyLabel local afkLabel = afkContent:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") afkLabel:SetPoint("TOP", afkContent, "TOPRIGHT", -32, -2) afkLabel:SetText("|cFFCC0000A|r") afkLabel:SetTextColor(0.8, 0.0, 0.0, 1) mainFrame.colAfkLabel = afkLabel -- Scroll frame for AFK list local scrollFrame = CreateFrame("ScrollFrame", "BenchAfkScrollFrame", afkContent, "UIPanelScrollFrameTemplate") scrollFrame:SetPoint("TOPLEFT", afkContent, "TOPLEFT", 2, -14) scrollFrame:SetPoint("BOTTOMRIGHT", afkContent, "BOTTOMRIGHT", -2, 26) local scrollChild = CreateFrame("Frame", "BenchAfkScrollChild", scrollFrame) scrollChild:SetWidth(245) scrollChild:SetHeight(1) scrollFrame:SetScrollChild(scrollChild) mainFrame.afkScrollFrame = scrollFrame mainFrame.afkScrollChild = scrollChild -- Style scrollbar BenchTracker:StyleScrollbar(scrollFrame) -- AFK Check button (bottom-left of afk content) local afkBtn = CreateFrame("Button", nil, afkContent) afkBtn:SetWidth(85) afkBtn:SetHeight(18) afkBtn:SetPoint("BOTTOMLEFT", afkContent, "BOTTOMLEFT", 4, 4) afkBtn:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 8, edgeSize = 4, insets = {left = 1, right = 1, top = 1, bottom = 1} }) afkBtn:SetBackdropColor(0.6, 0.3, 0.0, 0.9) afkBtn:SetBackdropBorderColor(0.5, 0.5, 0.5, 1) local afkBtnText = afkBtn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") afkBtnText:SetPoint("CENTER", afkBtn, "CENTER", 0, 0) afkBtnText:SetText("AFK Check") afkBtnText:SetTextColor(1, 1, 1, 1) afkBtn:SetScript("OnEnter", function() this:SetBackdropColor(0.8, 0.4, 0.0, 1) end) afkBtn:SetScript("OnLeave", function() this:SetBackdropColor(0.6, 0.3, 0.0, 0.9) end) afkBtn:SetScript("OnClick", function() BenchTracker:StartAFKCheck() end) mainFrame.afkButton = afkBtn -- Add to Log button (bottom-right of afk content) local logBtn = CreateFrame("Button", nil, afkContent) logBtn:SetWidth(85) logBtn:SetHeight(18) logBtn:SetPoint("BOTTOMRIGHT", afkContent, "BOTTOMRIGHT", -4, 4) logBtn:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 8, edgeSize = 4, insets = {left = 1, right = 1, top = 1, bottom = 1} }) logBtn:SetBackdropColor(0.2, 0.4, 0.6, 0.9) logBtn:SetBackdropBorderColor(0.5, 0.5, 0.5, 1) local logBtnText = logBtn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") logBtnText:SetPoint("CENTER", logBtn, "CENTER", 0, 0) logBtnText:SetText("Add to Log") logBtnText:SetTextColor(1, 1, 1, 1) logBtn:SetScript("OnEnter", function() this:SetBackdropColor(0.3, 0.5, 0.7, 1) end) logBtn:SetScript("OnLeave", function() this:SetBackdropColor(0.2, 0.4, 0.6, 0.9) end) logBtn:SetScript("OnClick", function() BenchTracker:ManualLogAFKCheck() end) mainFrame.logButton = logBtn -- Log content area local logContent = CreateFrame("Frame", "BenchLogContent", mainFrame) logContent:SetPoint("TOPLEFT", mainFrame, "TOPLEFT", 6, -42) logContent:SetPoint("BOTTOMRIGHT", mainFrame, "BOTTOMRIGHT", -6, 6) logContent:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = nil, tile = true, tileSize = 16, insets = {left = 0, right = 0, top = 0, bottom = 0} }) logContent:SetBackdropColor(0.08, 0.08, 0.08, 0.5) logContent:Hide() mainFrame.logContent = logContent -- Clear Log button (bottom of log content) local clearLogBtn = CreateFrame("Button", nil, logContent) clearLogBtn:SetWidth(70) clearLogBtn:SetHeight(18) clearLogBtn:SetPoint("BOTTOM", logContent, "BOTTOM", 0, 4) clearLogBtn:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 8, edgeSize = 4, insets = {left = 1, right = 1, top = 1, bottom = 1} }) clearLogBtn:SetBackdropColor(0.5, 0.15, 0.15, 0.9) clearLogBtn:SetBackdropBorderColor(0.5, 0.5, 0.5, 1) local clearLogText = clearLogBtn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") clearLogText:SetPoint("CENTER", clearLogBtn, "CENTER", 0, 0) clearLogText:SetText("Clear Log") clearLogText:SetTextColor(1, 1, 1, 1) clearLogBtn:SetScript("OnEnter", function() this:SetBackdropColor(0.7, 0.2, 0.2, 1) end) clearLogBtn:SetScript("OnLeave", function() this:SetBackdropColor(0.5, 0.15, 0.15, 0.9) end) clearLogBtn:SetScript("OnClick", function() BenchTracker:ClearLog() end) mainFrame.clearLogButton = clearLogBtn -- Scroll frame for Log local logScrollFrame = CreateFrame("ScrollFrame", "BenchLogScrollFrame", logContent, "UIPanelScrollFrameTemplate") logScrollFrame:SetPoint("TOPLEFT", logContent, "TOPLEFT", 2, -2) logScrollFrame:SetPoint("BOTTOMRIGHT", logContent, "BOTTOMRIGHT", -2, 26) local logScrollChild = CreateFrame("Frame", "BenchLogScrollChild", logScrollFrame) logScrollChild:SetWidth(245) logScrollChild:SetHeight(1) logScrollFrame:SetScrollChild(logScrollChild) mainFrame.logScrollFrame = logScrollFrame mainFrame.logScrollChild = logScrollChild BenchTracker:StyleScrollbar(logScrollFrame) end function BenchTracker:StyleScrollbar(scrollFrame) local scrollBar = getglobal(scrollFrame:GetName() .. "ScrollBar") if scrollBar then scrollBar:ClearAllPoints() scrollBar:SetPoint("TOPRIGHT", scrollFrame, "TOPRIGHT", 0, -16) scrollBar:SetPoint("BOTTOMRIGHT", scrollFrame, "BOTTOMRIGHT", 0, 16) scrollBar:SetWidth(5) scrollBar:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 8, edgeSize = 2, insets = {left = 1, right = 1, top = 1, bottom = 1} }) scrollBar:SetBackdropColor(0.15, 0.15, 0.15, 0.8) scrollBar:SetBackdropBorderColor(0.3, 0.3, 0.3, 0.6) local thumb = getglobal(scrollBar:GetName() .. "ThumbTexture") if thumb then thumb:SetTexture("Interface\\Tooltips\\UI-Tooltip-Background") thumb:SetVertexColor(0.9, 0.9, 0.9, 0.9) thumb:SetWidth(8) end local upButton = getglobal(scrollBar:GetName() .. "ScrollUpButton") local downButton = getglobal(scrollBar:GetName() .. "ScrollDownButton") if upButton then upButton:SetWidth(0) end if downButton then downButton:SetWidth(0) end end end ---------------------------------------------------------------- -- TAB BUTTONS ---------------------------------------------------------------- function BenchTracker:CreateTabButtons() -- AFK Check tab local tabAfk = CreateFrame("Button", "BenchTabAfk", mainFrame) tabAfk:SetWidth(80) tabAfk:SetHeight(16) tabAfk:SetPoint("TOPLEFT", mainFrame, "TOPLEFT", 8, -22) tabAfk:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 8, edgeSize = 4, insets = {left = 1, right = 1, top = 1, bottom = 1} }) local tabAfkText = tabAfk:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") tabAfkText:SetPoint("CENTER", tabAfk, "CENTER", 0, 0) tabAfkText:SetText("AFK Check") tabAfk:SetScript("OnClick", function() BenchTracker:SwitchTab("afk") end) mainFrame.tabAfk = tabAfk mainFrame.tabAfkText = tabAfkText -- Log tab local tabLog = CreateFrame("Button", "BenchTabLog", mainFrame) tabLog:SetWidth(80) tabLog:SetHeight(16) tabLog:SetPoint("LEFT", tabAfk, "RIGHT", 4, 0) tabLog:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 8, edgeSize = 4, insets = {left = 1, right = 1, top = 1, bottom = 1} }) local tabLogText = tabLog:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") tabLogText:SetPoint("CENTER", tabLog, "CENTER", 0, 0) tabLogText:SetText("Log") tabLog:SetScript("OnClick", function() BenchTracker:SwitchTab("log") end) mainFrame.tabLog = tabLog mainFrame.tabLogText = tabLogText end function BenchTracker:SwitchTab(tab) activeTab = tab if tab == "afk" then mainFrame.afkContent:Show() mainFrame.logContent:Hide() mainFrame.tabAfk:SetBackdropColor(0.3, 0.3, 0.5, 1) mainFrame.tabAfk:SetBackdropBorderColor(0.6, 0.6, 0.8, 1) mainFrame.tabAfkText:SetTextColor(1, 1, 1, 1) mainFrame.tabLog:SetBackdropColor(0.15, 0.15, 0.15, 0.8) mainFrame.tabLog:SetBackdropBorderColor(0.4, 0.4, 0.4, 1) mainFrame.tabLogText:SetTextColor(0.6, 0.6, 0.6, 1) BenchTracker:UpdateAFKDisplay() else mainFrame.afkContent:Hide() mainFrame.logContent:Show() mainFrame.tabLog:SetBackdropColor(0.3, 0.3, 0.5, 1) mainFrame.tabLog:SetBackdropBorderColor(0.6, 0.6, 0.8, 1) mainFrame.tabLogText:SetTextColor(1, 1, 1, 1) mainFrame.tabAfk:SetBackdropColor(0.15, 0.15, 0.15, 0.8) mainFrame.tabAfk:SetBackdropBorderColor(0.4, 0.4, 0.4, 1) mainFrame.tabAfkText:SetTextColor(0.6, 0.6, 0.6, 1) BenchTracker:UpdateLogDisplay() end end ---------------------------------------------------------------- -- PLAYER ROW (AFK TAB) ---------------------------------------------------------------- function BenchTracker:CreatePlayerRow(parent, index) local frame = CreateFrame("Frame", "BenchPlayerRow" .. index, parent) frame:SetWidth(245) frame:SetHeight(16) frame:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = nil, tile = true, tileSize = 16, insets = {left = 0, right = 0, top = 0, bottom = 0} }) frame:SetBackdropColor(0.15, 0.15, 0.15, 0.6) frame:EnableMouse(true) frame:SetScript("OnEnter", function() this:SetBackdropColor(0.25, 0.35, 0.45, 0.8) end) frame:SetScript("OnLeave", function() this:SetBackdropColor(0.15, 0.15, 0.15, 0.6) end) -- Player name local nameText = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") nameText:SetPoint("LEFT", frame, "LEFT", 4, 0) nameText:SetWidth(140) nameText:SetJustifyH("LEFT") frame.nameText = nameText -- Ready button (green) local readyBtn = CreateFrame("Button", nil, frame) readyBtn:SetWidth(14) readyBtn:SetHeight(14) readyBtn:SetPoint("RIGHT", frame, "RIGHT", -22, 0) readyBtn:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 8, edgeSize = 4, insets = {left = 1, right = 1, top = 1, bottom = 1} }) readyBtn:SetScript("OnEnter", function() GameTooltip:SetOwner(this, "ANCHOR_RIGHT") GameTooltip:SetText("Ready", 0.2, 0.8, 0.2) GameTooltip:Show() end) readyBtn:SetScript("OnLeave", function() GameTooltip:Hide() end) frame.readyBtn = readyBtn -- AFK button (red) local afkBtn = CreateFrame("Button", nil, frame) afkBtn:SetWidth(14) afkBtn:SetHeight(14) afkBtn:SetPoint("RIGHT", frame, "RIGHT", -4, 0) afkBtn:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 8, edgeSize = 4, insets = {left = 1, right = 1, top = 1, bottom = 1} }) afkBtn:SetScript("OnEnter", function() GameTooltip:SetOwner(this, "ANCHOR_RIGHT") GameTooltip:SetText("AFK", 0.8, 0.2, 0.2) GameTooltip:Show() end) afkBtn:SetScript("OnLeave", function() GameTooltip:Hide() end) frame.afkBtn = afkBtn frame:Hide() return frame end function BenchTracker:SetRadioState(frame, state) -- state: "ready", "afk", or "none" if state == "ready" then frame.readyBtn:SetBackdropColor(0.1, 0.7, 0.1, 0.9) frame.readyBtn:SetBackdropBorderColor(0.1, 0.5, 0.1, 1) frame.afkBtn:SetBackdropColor(0.25, 0.25, 0.25, 0.6) frame.afkBtn:SetBackdropBorderColor(0.4, 0.4, 0.4, 0.8) elseif state == "afk" then frame.readyBtn:SetBackdropColor(0.25, 0.25, 0.25, 0.6) frame.readyBtn:SetBackdropBorderColor(0.4, 0.4, 0.4, 0.8) frame.afkBtn:SetBackdropColor(0.8, 0.1, 0.1, 0.9) frame.afkBtn:SetBackdropBorderColor(0.6, 0.1, 0.1, 1) else frame.readyBtn:SetBackdropColor(0.25, 0.25, 0.25, 0.6) frame.readyBtn:SetBackdropBorderColor(0.4, 0.4, 0.4, 0.8) frame.afkBtn:SetBackdropColor(0.25, 0.25, 0.25, 0.6) frame.afkBtn:SetBackdropBorderColor(0.4, 0.4, 0.4, 0.8) end end ---------------------------------------------------------------- -- AFK DISPLAY ---------------------------------------------------------------- function BenchTracker:UpdateAFKDisplay() if not mainFrame or not mainFrame:IsVisible() then return end if activeTab ~= "afk" then return end local isOfficer = BenchTracker:IsOfficer() local members = BenchTracker:GetRaidMembers() local scrollChild = mainFrame.afkScrollChild -- Show/hide buttons based on officer status if mainFrame.afkButton then if isOfficer then mainFrame.afkButton:Show() else mainFrame.afkButton:Hide() end end if mainFrame.logButton then if isOfficer then mainFrame.logButton:Show() else mainFrame.logButton:Hide() end end -- Hide all existing rows for i = 1, table.getn(playerFrames) do playerFrames[i]:Hide() end local yOffset = 0 for i = 1, table.getn(members) do local member = members[i] if not playerFrames[i] then playerFrames[i] = BenchTracker:CreatePlayerRow(scrollChild, i) end local row = playerFrames[i] row:ClearAllPoints() row:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, yOffset) row:Show() row.nameText:SetText(member.name) local englishClass = BenchTracker:GetEnglishClass(member.class) local color = CLASS_COLORS[englishClass] or {r = 0.5, g = 0.5, b = 0.5} row.nameText:SetTextColor(color.r, color.g, color.b, 1) row.playerName = member.name -- Determine current status local status = "none" if currentCheckData[member.name] then status = currentCheckData[member.name] end BenchTracker:SetRadioState(row, status) -- Button click handlers if isOfficer then row.readyBtn:SetScript("OnClick", function() local pName = this:GetParent().playerName currentCheckData[pName] = "ready" BenchTracker:UpdateAFKDisplay() end) row.afkBtn:SetScript("OnClick", function() local pName = this:GetParent().playerName currentCheckData[pName] = "afk" BenchTracker:UpdateAFKDisplay() end) row.readyBtn:EnableMouse(true) row.afkBtn:EnableMouse(true) else row.readyBtn:EnableMouse(false) row.afkBtn:EnableMouse(false) end yOffset = yOffset - 17 end local contentHeight = table.getn(members) * 17 + 10 scrollChild:SetHeight(contentHeight) if mainFrame.afkScrollFrame then mainFrame.afkScrollFrame:UpdateScrollChildRect() end end ---------------------------------------------------------------- -- AFK CHECK LOGIC ---------------------------------------------------------------- function BenchTracker:StartAFKCheck() if not BenchTracker:IsOfficer() then return end if isListening then DEFAULT_CHAT_FRAME:AddMessage("|cFFFF0000BenchTracker: An AFK check is already in progress!|r") return end -- Reset status for current raid members currentCheckData = {} local members = BenchTracker:GetRaidMembers() for i = 1, table.getn(members) do currentCheckData[members[i].name] = "afk" end checkCounter = checkCounter + 1 isListening = true listenElapsed = 0 -- Send announcements SendChatMessage("BENCH GROUP: Type in [Guild Chat] or in [#General on Discord] within 30sec if you're here.", "GUILD") SendChatMessage("BENCH GROUP: Type in [Guild Chat] or in [#General on Discord] within 30sec if you're here.", "RAID_WARNING") -- Trigger /pull 30 (BigWigs Pulltimer) if SlashCmdList["BWPT_SHORTHAND"] then SlashCmdList["BWPT_SHORTHAND"]("30") end -- Start 30 second timer BenchTracker:StartListenTimer() BenchTracker:UpdateAFKDisplay() DEFAULT_CHAT_FRAME:AddMessage("|cFF00FF00BenchTracker: AFK check started! Listening for 30 seconds...|r") end function BenchTracker:StartListenTimer() if not listenTimer then listenTimer = CreateFrame("Frame", "BenchListenTimer", UIParent) end listenElapsed = 0 listenTimer:SetScript("OnUpdate", function() listenElapsed = listenElapsed + arg1 if listenElapsed >= 30 then BenchTracker:StopListening() end end) listenTimer:Show() end function BenchTracker:StopListening() isListening = false if listenTimer then listenTimer:SetScript("OnUpdate", nil) listenTimer:Hide() end DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00BenchTracker: AFK check listening ended. Review results and click 'Add to Log' when ready.|r") BenchTracker:UpdateAFKDisplay() end function BenchTracker:OnChatMessage(playerName) if not isListening then return end -- Check if this player is in our bench raid if currentCheckData[playerName] then currentCheckData[playerName] = "ready" BenchTracker:UpdateAFKDisplay() end end ---------------------------------------------------------------- -- LOGGING ---------------------------------------------------------------- function BenchTracker:LogAFKCheck() local timestamp = BenchTracker:GetTimestamp() local readyList = {} local afkList = {} for name, status in pairs(currentCheckData) do if status == "ready" then table.insert(readyList, name) else table.insert(afkList, name) end end table.sort(readyList) table.sort(afkList) local entry = { entryType = "afk", timestamp = timestamp, checkNum = checkCounter, ready = readyList, afk = afkList } table.insert(logEntries, entry) BenchTrackerDB.log = logEntries BenchTrackerDB.checkCounter = checkCounter if activeTab == "log" then BenchTracker:UpdateLogDisplay() end end function BenchTracker:ManualLogAFKCheck() if not BenchTracker:IsOfficer() then return end -- Check if there is any data to log local hasData = false for name, status in pairs(currentCheckData) do hasData = true break end if not hasData then DEFAULT_CHAT_FRAME:AddMessage("|cFFFF0000BenchTracker: Nothing to log. Run an AFK check first.|r") return end BenchTracker:LogAFKCheck() -- Reset radio buttons currentCheckData = {} DEFAULT_CHAT_FRAME:AddMessage("|cFF00FF00BenchTracker: AFK check results added to log.|r") BenchTracker:UpdateAFKDisplay() if activeTab == "log" then BenchTracker:UpdateLogDisplay() end end function BenchTracker:LogEPAward(epAmount) local timestamp = BenchTracker:GetTimestamp() local members = BenchTracker:GetRaidMembers() local nameList = {} for i = 1, table.getn(members) do table.insert(nameList, members[i].name) end table.sort(nameList) local entry = { entryType = "ep", timestamp = timestamp, ep = epAmount, players = nameList } table.insert(logEntries, entry) BenchTrackerDB.log = logEntries if activeTab == "log" then BenchTracker:UpdateLogDisplay() end end ---------------------------------------------------------------- -- LOG DISPLAY ---------------------------------------------------------------- local logFontStrings = {} function BenchTracker:ClearLog() if not BenchTracker:IsOfficer() then return end logEntries = {} checkCounter = 0 BenchTrackerDB.log = logEntries BenchTrackerDB.checkCounter = 0 DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00BenchTracker: Log cleared.|r") BenchTracker:UpdateLogDisplay() end function BenchTracker:UpdateLogDisplay() if not mainFrame or not mainFrame:IsVisible() then return end if activeTab ~= "log" then return end -- Show/hide Clear Log button based on officer status if mainFrame.clearLogButton then if BenchTracker:IsOfficer() then mainFrame.clearLogButton:Show() else mainFrame.clearLogButton:Hide() end end local scrollChild = mainFrame.logScrollChild -- Hide all existing log text for i = 1, table.getn(logFontStrings) do logFontStrings[i]:Hide() end local yOffset = 0 local fsIndex = 0 local function getFS() fsIndex = fsIndex + 1 if not logFontStrings[fsIndex] then logFontStrings[fsIndex] = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") end local fs = logFontStrings[fsIndex] fs:ClearAllPoints() fs:Show() return fs end -- Render entries in reverse order (newest first) local numEntries = table.getn(logEntries) for idx = numEntries, 1, -1 do local entry = logEntries[idx] if entry.entryType == "afk" then -- Header local header = getFS() header:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 2, yOffset) header:SetWidth(240) header:SetJustifyH("LEFT") header:SetText("|cFFFFCC00" .. entry.timestamp .. " AFK Check #" .. entry.checkNum .. "|r") header:SetTextColor(1, 0.8, 0, 1) yOffset = yOffset - 12 -- Ready list if table.getn(entry.ready) > 0 then local readyLabel = getFS() readyLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 6, yOffset) readyLabel:SetWidth(236) readyLabel:SetJustifyH("LEFT") local readyStr = "|cFF00CC00Ready:|r " for i = 1, table.getn(entry.ready) do if i > 1 then readyStr = readyStr .. ", " end readyStr = readyStr .. entry.ready[i] end readyLabel:SetText(readyStr) readyLabel:SetTextColor(0.8, 0.8, 0.8, 1) yOffset = yOffset - 12 end -- AFK list if table.getn(entry.afk) > 0 then local afkLabel = getFS() afkLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 6, yOffset) afkLabel:SetWidth(236) afkLabel:SetJustifyH("LEFT") local afkStr = "|cFFCC0000AFK:|r " for i = 1, table.getn(entry.afk) do if i > 1 then afkStr = afkStr .. ", " end afkStr = afkStr .. entry.afk[i] end afkLabel:SetText(afkStr) afkLabel:SetTextColor(0.8, 0.8, 0.8, 1) yOffset = yOffset - 12 end yOffset = yOffset - 4 elseif entry.entryType == "ep" then -- EP award header local header = getFS() header:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 2, yOffset) header:SetWidth(240) header:SetJustifyH("LEFT") header:SetText("|cFF33BBFF" .. entry.timestamp .. " EP Award: +" .. entry.ep .. " EP|r") header:SetTextColor(0.2, 0.7, 1, 1) yOffset = yOffset - 12 -- Player list local playerLabel = getFS() playerLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 6, yOffset) playerLabel:SetWidth(236) playerLabel:SetJustifyH("LEFT") local pStr = "" for i = 1, table.getn(entry.players) do if i > 1 then pStr = pStr .. ", " end pStr = pStr .. entry.players[i] end playerLabel:SetText(pStr) playerLabel:SetTextColor(0.7, 0.7, 0.7, 1) yOffset = yOffset - 12 yOffset = yOffset - 4 end end if numEntries == 0 then local emptyMsg = getFS() emptyMsg:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 2, yOffset) emptyMsg:SetWidth(240) emptyMsg:SetJustifyH("LEFT") emptyMsg:SetText("|cFF666666No log entries yet.|r") emptyMsg:SetTextColor(0.4, 0.4, 0.4, 1) yOffset = yOffset - 14 end local contentHeight = math.abs(yOffset) + 10 scrollChild:SetHeight(contentHeight) if mainFrame.logScrollFrame then mainFrame.logScrollFrame:UpdateScrollChildRect() end end ---------------------------------------------------------------- -- SLASH COMMAND ---------------------------------------------------------------- SLASH_BENCH1 = "/bench" SlashCmdList["BENCH"] = function(msg) if not mainFrame then BenchTracker:CreateMainFrame() end if mainFrame:IsVisible() then mainFrame:Hide() else mainFrame:Show() BenchTracker:SwitchTab(activeTab) end end ---------------------------------------------------------------- -- EVENT HANDLING ---------------------------------------------------------------- local benchEventFrame = CreateFrame("Frame", "BenchEventFrame", UIParent) benchEventFrame:RegisterEvent("RAID_ROSTER_UPDATE") benchEventFrame:RegisterEvent("PARTY_MEMBERS_CHANGED") benchEventFrame:RegisterEvent("CHAT_MSG_GUILD") benchEventFrame:RegisterEvent("CHAT_MSG_RAID") benchEventFrame:RegisterEvent("CHAT_MSG_RAID_LEADER") benchEventFrame:RegisterEvent("ADDON_LOADED") benchEventFrame:SetScript("OnEvent", function() if event == "ADDON_LOADED" and arg1 == "shootyepgp" then -- Load saved log data if not BenchTrackerDB then BenchTrackerDB = {} end if BenchTrackerDB.log then logEntries = BenchTrackerDB.log else BenchTrackerDB.log = {} end if BenchTrackerDB.checkCounter then checkCounter = BenchTrackerDB.checkCounter else BenchTrackerDB.checkCounter = 0 end DEFAULT_CHAT_FRAME:AddMessage("|cFF00FF00BenchTracker loaded! Type /bench to open.|r") elseif event == "RAID_ROSTER_UPDATE" or event == "PARTY_MEMBERS_CHANGED" then if mainFrame and mainFrame:IsVisible() and activeTab == "afk" then BenchTracker:UpdateAFKDisplay() end elseif event == "CHAT_MSG_GUILD" then if isListening and arg1 and arg2 then BenchTracker:OnChatMessage(arg2) end elseif event == "CHAT_MSG_RAID" or event == "CHAT_MSG_RAID_LEADER" then if isListening and arg1 and arg2 then BenchTracker:OnChatMessage(arg2) end end end) ---------------------------------------------------------------- -- HOOK: EP AWARDS ---------------------------------------------------------------- local original_award_raid_ep = nil local original_award_reserve_ep = nil local epHookFrame = CreateFrame("Frame", "BenchEPHookFrame", UIParent) epHookFrame:RegisterEvent("ADDON_LOADED") epHookFrame:SetScript("OnEvent", function() if event == "ADDON_LOADED" and arg1 == "shootyepgp" then -- Hook after sepgp is available if sepgp and sepgp.award_reserve_ep then original_award_reserve_ep = sepgp.award_reserve_ep sepgp.award_reserve_ep = function(self, ep) original_award_reserve_ep(self, ep) BenchTracker:LogEPAward(ep) end end if sepgp and sepgp.award_raid_ep then original_award_raid_ep = sepgp.award_raid_ep sepgp.award_raid_ep = function(self, ep) original_award_raid_ep(self, ep) BenchTracker:LogEPAward(ep) end end end end)