1316 lines
48 KiB
Lua
1316 lines
48 KiB
Lua
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"
|
|
local previousRaidRoster = {}
|
|
local rosterInitialized = false
|
|
|
|
-- 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.baseR, readyBtn.baseG, readyBtn.baseB, readyBtn.baseA = 0.25, 0.25, 0.25, 0.6
|
|
readyBtn:SetScript("OnEnter", function()
|
|
local r = this.baseR + 0.15
|
|
local g = this.baseG + 0.15
|
|
local b = this.baseB + 0.15
|
|
if r > 1 then r = 1 end
|
|
if g > 1 then g = 1 end
|
|
if b > 1 then b = 1 end
|
|
this:SetBackdropColor(r, g, b, 1)
|
|
this:GetParent():SetBackdropColor(0.25, 0.35, 0.45, 0.8)
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
GameTooltip:SetText("Ready", 0.2, 0.8, 0.2)
|
|
GameTooltip:Show()
|
|
end)
|
|
readyBtn:SetScript("OnLeave", function()
|
|
this:SetBackdropColor(this.baseR, this.baseG, this.baseB, this.baseA)
|
|
this:GetParent():SetBackdropColor(0.15, 0.15, 0.15, 0.6)
|
|
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.baseR, afkBtn.baseG, afkBtn.baseB, afkBtn.baseA = 0.25, 0.25, 0.25, 0.6
|
|
afkBtn:SetScript("OnEnter", function()
|
|
local r = this.baseR + 0.15
|
|
local g = this.baseG + 0.15
|
|
local b = this.baseB + 0.15
|
|
if r > 1 then r = 1 end
|
|
if g > 1 then g = 1 end
|
|
if b > 1 then b = 1 end
|
|
this:SetBackdropColor(r, g, b, 1)
|
|
this:GetParent():SetBackdropColor(0.25, 0.35, 0.45, 0.8)
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
GameTooltip:SetText("AFK", 0.8, 0.2, 0.2)
|
|
GameTooltip:Show()
|
|
end)
|
|
afkBtn:SetScript("OnLeave", function()
|
|
this:SetBackdropColor(this.baseR, this.baseG, this.baseB, this.baseA)
|
|
this:GetParent():SetBackdropColor(0.15, 0.15, 0.15, 0.6)
|
|
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.baseR, frame.readyBtn.baseG, frame.readyBtn.baseB, frame.readyBtn.baseA = 0.1, 0.7, 0.1, 0.9
|
|
frame.readyBtn:SetBackdropColor(0.1, 0.7, 0.1, 0.9)
|
|
frame.readyBtn:SetBackdropBorderColor(0.1, 0.5, 0.1, 1)
|
|
frame.afkBtn.baseR, frame.afkBtn.baseG, frame.afkBtn.baseB, frame.afkBtn.baseA = 0.25, 0.25, 0.25, 0.6
|
|
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.baseR, frame.readyBtn.baseG, frame.readyBtn.baseB, frame.readyBtn.baseA = 0.25, 0.25, 0.25, 0.6
|
|
frame.readyBtn:SetBackdropColor(0.25, 0.25, 0.25, 0.6)
|
|
frame.readyBtn:SetBackdropBorderColor(0.4, 0.4, 0.4, 0.8)
|
|
frame.afkBtn.baseR, frame.afkBtn.baseG, frame.afkBtn.baseB, frame.afkBtn.baseA = 0.8, 0.1, 0.1, 0.9
|
|
frame.afkBtn:SetBackdropColor(0.8, 0.1, 0.1, 0.9)
|
|
frame.afkBtn:SetBackdropBorderColor(0.6, 0.1, 0.1, 1)
|
|
else
|
|
frame.readyBtn.baseR, frame.readyBtn.baseG, frame.readyBtn.baseB, frame.readyBtn.baseA = 0.25, 0.25, 0.25, 0.6
|
|
frame.readyBtn:SetBackdropColor(0.25, 0.25, 0.25, 0.6)
|
|
frame.readyBtn:SetBackdropBorderColor(0.4, 0.4, 0.4, 0.8)
|
|
frame.afkBtn.baseR, frame.afkBtn.baseG, frame.afkBtn.baseB, frame.afkBtn.baseA = 0.25, 0.25, 0.25, 0.6
|
|
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 nameList = {}
|
|
local raidSize = GetNumRaidMembers()
|
|
for i = 1, raidSize do
|
|
local name, rank, subgroup, level, class, fileName, zone, online = GetRaidRosterInfo(i)
|
|
if name and online then
|
|
table.insert(nameList, name)
|
|
end
|
|
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
|
|
|
|
function BenchTracker:IsInBenchRaid(playerName)
|
|
local members = BenchTracker:GetRaidMembers()
|
|
for i = 1, table.getn(members) do
|
|
if members[i].name == playerName then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
function BenchTracker:LogIndividualEP(playerName, epAmount)
|
|
if not BenchTracker:IsInBenchRaid(playerName) then return end
|
|
|
|
local timestamp = BenchTracker:GetTimestamp()
|
|
local entry = {
|
|
entryType = "ep_individual",
|
|
timestamp = timestamp,
|
|
ep = epAmount,
|
|
player = playerName
|
|
}
|
|
table.insert(logEntries, entry)
|
|
BenchTrackerDB.log = logEntries
|
|
|
|
if activeTab == "log" then
|
|
BenchTracker:UpdateLogDisplay()
|
|
end
|
|
end
|
|
|
|
----------------------------------------------------------------
|
|
-- ROSTER CHANGE TRACKING
|
|
----------------------------------------------------------------
|
|
function BenchTracker:GetCurrentRosterMap()
|
|
local roster = {}
|
|
local raidSize = GetNumRaidMembers()
|
|
for i = 1, raidSize do
|
|
local name, rank, subgroup, level, class = GetRaidRosterInfo(i)
|
|
if name then
|
|
roster[name] = class or "Unknown"
|
|
end
|
|
end
|
|
return roster
|
|
end
|
|
|
|
function BenchTracker:CheckRosterChanges()
|
|
local currentRoster = BenchTracker:GetCurrentRosterMap()
|
|
|
|
if not rosterInitialized then
|
|
previousRaidRoster = currentRoster
|
|
rosterInitialized = true
|
|
return
|
|
end
|
|
|
|
local timestamp = BenchTracker:GetTimestamp()
|
|
local changed = false
|
|
|
|
-- Check for new players (joined)
|
|
for name, class in pairs(currentRoster) do
|
|
if not previousRaidRoster[name] then
|
|
local englishClass = BenchTracker:GetEnglishClass(class)
|
|
local entry = {
|
|
entryType = "roster_join",
|
|
timestamp = timestamp,
|
|
player = name,
|
|
class = englishClass
|
|
}
|
|
table.insert(logEntries, entry)
|
|
changed = true
|
|
end
|
|
end
|
|
|
|
-- Check for removed players (left)
|
|
for name, class in pairs(previousRaidRoster) do
|
|
if not currentRoster[name] then
|
|
local englishClass = BenchTracker:GetEnglishClass(class)
|
|
local entry = {
|
|
entryType = "roster_leave",
|
|
timestamp = timestamp,
|
|
player = name,
|
|
class = englishClass
|
|
}
|
|
table.insert(logEntries, entry)
|
|
changed = true
|
|
end
|
|
end
|
|
|
|
if changed then
|
|
BenchTrackerDB.log = logEntries
|
|
if activeTab == "log" then
|
|
BenchTracker:UpdateLogDisplay()
|
|
end
|
|
end
|
|
|
|
previousRaidRoster = currentRoster
|
|
end
|
|
|
|
----------------------------------------------------------------
|
|
-- LOG DISPLAY
|
|
----------------------------------------------------------------
|
|
local LOG_LINE_HEIGHT = 12
|
|
local logEntryFrames = {}
|
|
local emptyLogFS = nil
|
|
local pendingDeleteIndex = nil
|
|
|
|
local function GetWrappedHeight(fs, maxWidth)
|
|
local textWidth = fs:GetStringWidth()
|
|
if textWidth <= 0 then return LOG_LINE_HEIGHT end
|
|
local numLines = math.ceil(textWidth / maxWidth)
|
|
if numLines < 1 then numLines = 1 end
|
|
return numLines * LOG_LINE_HEIGHT
|
|
end
|
|
|
|
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
|
|
|
|
StaticPopupDialogs["BENCH_CONFIRM_DELETE"] = {
|
|
text = "Are you sure you want to delete this log entry?",
|
|
button1 = "Yes",
|
|
button2 = "No",
|
|
OnAccept = function()
|
|
if pendingDeleteIndex then
|
|
BenchTracker:DeleteLogEntry(pendingDeleteIndex)
|
|
pendingDeleteIndex = nil
|
|
end
|
|
end,
|
|
OnCancel = function()
|
|
pendingDeleteIndex = nil
|
|
end,
|
|
timeout = 0,
|
|
whileDead = true,
|
|
hideOnEscape = true,
|
|
}
|
|
|
|
function BenchTracker:DeleteLogEntry(index)
|
|
if index >= 1 and index <= table.getn(logEntries) then
|
|
table.remove(logEntries, index)
|
|
BenchTrackerDB.log = logEntries
|
|
DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00BenchTracker: Log entry deleted.|r")
|
|
BenchTracker:UpdateLogDisplay()
|
|
end
|
|
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
|
|
local isOfficer = BenchTracker:IsOfficer()
|
|
|
|
-- Hide all existing entry frames
|
|
for i = 1, table.getn(logEntryFrames) do
|
|
logEntryFrames[i]:Hide()
|
|
end
|
|
if emptyLogFS then emptyLogFS:Hide() end
|
|
|
|
local yOffset = 0
|
|
local entryIdx = 0
|
|
|
|
local function getEntryFrame()
|
|
entryIdx = entryIdx + 1
|
|
if not logEntryFrames[entryIdx] then
|
|
local ef = CreateFrame("Frame", "BenchLogEntryFrame" .. entryIdx, scrollChild)
|
|
ef:SetWidth(245)
|
|
ef:SetBackdrop({
|
|
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
|
|
edgeFile = nil, tile = true, tileSize = 16,
|
|
insets = {left = 0, right = 0, top = 0, bottom = 0}
|
|
})
|
|
ef:SetBackdropColor(0, 0, 0, 0)
|
|
ef:EnableMouse(true)
|
|
ef:SetScript("OnEnter", function()
|
|
this:SetBackdropColor(0.25, 0.28, 0.4, 0.35)
|
|
end)
|
|
ef:SetScript("OnLeave", function()
|
|
this:SetBackdropColor(0, 0, 0, 0)
|
|
end)
|
|
ef.fontStrings = {}
|
|
ef.fsCount = 0
|
|
ef.deleteBtn = nil
|
|
logEntryFrames[entryIdx] = ef
|
|
end
|
|
local ef = logEntryFrames[entryIdx]
|
|
for i = 1, table.getn(ef.fontStrings) do
|
|
ef.fontStrings[i]:Hide()
|
|
end
|
|
ef.fsCount = 0
|
|
if ef.deleteBtn then ef.deleteBtn:Hide() end
|
|
ef:ClearAllPoints()
|
|
ef:SetBackdropColor(0, 0, 0, 0)
|
|
ef:Show()
|
|
return ef
|
|
end
|
|
|
|
local function getEntryFS(ef)
|
|
ef.fsCount = ef.fsCount + 1
|
|
if not ef.fontStrings[ef.fsCount] then
|
|
ef.fontStrings[ef.fsCount] = ef:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
|
end
|
|
local fs = ef.fontStrings[ef.fsCount]
|
|
fs:ClearAllPoints()
|
|
fs:Show()
|
|
return fs
|
|
end
|
|
|
|
local function getEntryDeleteBtn(ef)
|
|
if not ef.deleteBtn then
|
|
local btn = CreateFrame("Button", nil, ef)
|
|
btn:SetWidth(12)
|
|
btn:SetHeight(12)
|
|
btn: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}
|
|
})
|
|
btn:SetBackdropColor(0.6, 0.15, 0.15, 0.8)
|
|
btn:SetBackdropBorderColor(0.5, 0.5, 0.5, 1)
|
|
local btnText = btn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
|
btnText:SetPoint("CENTER", btn, "CENTER", 0, 0)
|
|
btnText:SetText("x")
|
|
btnText:SetTextColor(1, 1, 1, 1)
|
|
btn:SetScript("OnEnter", function()
|
|
this:SetBackdropColor(0.9, 0.2, 0.2, 1)
|
|
this:GetParent():SetBackdropColor(0.25, 0.28, 0.4, 0.35)
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
GameTooltip:SetText("Delete this entry", 1, 0.3, 0.3)
|
|
GameTooltip:Show()
|
|
end)
|
|
btn:SetScript("OnLeave", function()
|
|
this:SetBackdropColor(0.6, 0.15, 0.15, 0.8)
|
|
this:GetParent():SetBackdropColor(0, 0, 0, 0)
|
|
GameTooltip:Hide()
|
|
end)
|
|
ef.deleteBtn = btn
|
|
end
|
|
ef.deleteBtn:ClearAllPoints()
|
|
ef.deleteBtn:Show()
|
|
return ef.deleteBtn
|
|
end
|
|
|
|
-- Render entries in reverse order (newest first)
|
|
local numEntries = table.getn(logEntries)
|
|
for idx = numEntries, 1, -1 do
|
|
local entry = logEntries[idx]
|
|
local ef = getEntryFrame()
|
|
local localY = 0
|
|
|
|
if entry.entryType == "afk" then
|
|
-- Header
|
|
local header = getEntryFS(ef)
|
|
header:SetPoint("TOPLEFT", ef, "TOPLEFT", 2, localY)
|
|
header:SetJustifyH("LEFT")
|
|
local headerWidth = 240
|
|
if isOfficer then
|
|
headerWidth = 220
|
|
local delBtn = getEntryDeleteBtn(ef)
|
|
delBtn:SetPoint("TOPRIGHT", ef, "TOPRIGHT", -2, localY)
|
|
delBtn.entryIndex = idx
|
|
delBtn:SetScript("OnClick", function()
|
|
pendingDeleteIndex = this.entryIndex
|
|
StaticPopup_Show("BENCH_CONFIRM_DELETE")
|
|
end)
|
|
end
|
|
header:SetWidth(headerWidth)
|
|
header:SetText("|cFFFFCC00" .. entry.timestamp .. " AFK Check #" .. entry.checkNum .. "|r")
|
|
header:SetTextColor(1, 0.8, 0, 1)
|
|
localY = localY - (GetWrappedHeight(header, headerWidth) + 2)
|
|
|
|
-- Ready list
|
|
if table.getn(entry.ready) > 0 then
|
|
local readyLabel = getEntryFS(ef)
|
|
readyLabel:SetPoint("TOPLEFT", ef, "TOPLEFT", 6, localY)
|
|
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)
|
|
localY = localY - (GetWrappedHeight(readyLabel, 236) + 2)
|
|
end
|
|
|
|
-- AFK list
|
|
if table.getn(entry.afk) > 0 then
|
|
local afkLabel = getEntryFS(ef)
|
|
afkLabel:SetPoint("TOPLEFT", ef, "TOPLEFT", 6, localY)
|
|
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)
|
|
localY = localY - (GetWrappedHeight(afkLabel, 236) + 2)
|
|
end
|
|
|
|
elseif entry.entryType == "ep" then
|
|
-- EP award header
|
|
local header = getEntryFS(ef)
|
|
header:SetPoint("TOPLEFT", ef, "TOPLEFT", 2, localY)
|
|
header:SetJustifyH("LEFT")
|
|
local headerWidth = 240
|
|
if isOfficer then
|
|
headerWidth = 220
|
|
local delBtn = getEntryDeleteBtn(ef)
|
|
delBtn:SetPoint("TOPRIGHT", ef, "TOPRIGHT", -2, localY)
|
|
delBtn.entryIndex = idx
|
|
delBtn:SetScript("OnClick", function()
|
|
pendingDeleteIndex = this.entryIndex
|
|
StaticPopup_Show("BENCH_CONFIRM_DELETE")
|
|
end)
|
|
end
|
|
header:SetWidth(headerWidth)
|
|
header:SetText("|cFF33BBFF" .. entry.timestamp .. " EP Award: +" .. entry.ep .. " EP|r")
|
|
header:SetTextColor(0.2, 0.7, 1, 1)
|
|
localY = localY - (GetWrappedHeight(header, headerWidth) + 2)
|
|
|
|
-- Player list
|
|
local playerLabel = getEntryFS(ef)
|
|
playerLabel:SetPoint("TOPLEFT", ef, "TOPLEFT", 6, localY)
|
|
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)
|
|
localY = localY - (GetWrappedHeight(playerLabel, 236) + 2)
|
|
|
|
elseif entry.entryType == "ep_individual" then
|
|
-- Individual EP adjust header
|
|
local header = getEntryFS(ef)
|
|
header:SetPoint("TOPLEFT", ef, "TOPLEFT", 2, localY)
|
|
header:SetJustifyH("LEFT")
|
|
local headerWidth = 240
|
|
if isOfficer then
|
|
headerWidth = 220
|
|
local delBtn = getEntryDeleteBtn(ef)
|
|
delBtn:SetPoint("TOPRIGHT", ef, "TOPRIGHT", -2, localY)
|
|
delBtn.entryIndex = idx
|
|
delBtn:SetScript("OnClick", function()
|
|
pendingDeleteIndex = this.entryIndex
|
|
StaticPopup_Show("BENCH_CONFIRM_DELETE")
|
|
end)
|
|
end
|
|
header:SetWidth(headerWidth)
|
|
local epSign = ""
|
|
if entry.ep >= 0 then epSign = "+" end
|
|
header:SetText("|cFFDDA0DD" .. entry.timestamp .. " EP Adjust: " .. entry.player .. " " .. epSign .. entry.ep .. " EP|r")
|
|
header:SetTextColor(0.87, 0.63, 0.87, 1)
|
|
localY = localY - (GetWrappedHeight(header, headerWidth) + 2)
|
|
|
|
elseif entry.entryType == "roster_join" or entry.entryType == "roster_leave" then
|
|
local header = getEntryFS(ef)
|
|
header:SetPoint("TOPLEFT", ef, "TOPLEFT", 2, localY)
|
|
header:SetJustifyH("LEFT")
|
|
local headerWidth = 240
|
|
if isOfficer then
|
|
headerWidth = 220
|
|
local delBtn = getEntryDeleteBtn(ef)
|
|
delBtn:SetPoint("TOPRIGHT", ef, "TOPRIGHT", -2, localY)
|
|
delBtn.entryIndex = idx
|
|
delBtn:SetScript("OnClick", function()
|
|
pendingDeleteIndex = this.entryIndex
|
|
StaticPopup_Show("BENCH_CONFIRM_DELETE")
|
|
end)
|
|
end
|
|
header:SetWidth(headerWidth)
|
|
local color = CLASS_COLORS[entry.class] or {r = 0.6, g = 0.6, b = 0.6}
|
|
local nameColored = string.format("|cFF%02x%02x%02x%s|r", color.r * 255, color.g * 255, color.b * 255, entry.player)
|
|
if entry.entryType == "roster_join" then
|
|
header:SetText("|cFF888888" .. entry.timestamp .. "|r " .. nameColored .. " |cFF00CC00joined|r")
|
|
header:SetTextColor(0.5, 0.8, 0.5, 1)
|
|
else
|
|
header:SetText("|cFF888888" .. entry.timestamp .. "|r " .. nameColored .. " |cFFCC0000left|r")
|
|
header:SetTextColor(0.8, 0.5, 0.5, 1)
|
|
end
|
|
localY = localY - (GetWrappedHeight(header, headerWidth) + 2)
|
|
end
|
|
|
|
-- Size and position entry frame
|
|
local entryHeight = math.abs(localY) + 4
|
|
ef:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, yOffset)
|
|
ef:SetHeight(entryHeight)
|
|
yOffset = yOffset - entryHeight
|
|
end
|
|
|
|
if numEntries == 0 then
|
|
if not emptyLogFS then
|
|
emptyLogFS = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
|
end
|
|
emptyLogFS:ClearAllPoints()
|
|
emptyLogFS:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 2, yOffset)
|
|
emptyLogFS:SetWidth(240)
|
|
emptyLogFS:SetJustifyH("LEFT")
|
|
emptyLogFS:SetText("|cFF666666No log entries yet.|r")
|
|
emptyLogFS:SetTextColor(0.4, 0.4, 0.4, 1)
|
|
emptyLogFS:Show()
|
|
yOffset = yOffset - (GetWrappedHeight(emptyLogFS, 240) + 2)
|
|
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
|
|
BenchTracker:CheckRosterChanges()
|
|
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 original_givename_ep = nil
|
|
local isBulkAward = false
|
|
|
|
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)
|
|
isBulkAward = true
|
|
original_award_reserve_ep(self, ep)
|
|
isBulkAward = false
|
|
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)
|
|
isBulkAward = true
|
|
original_award_raid_ep(self, ep)
|
|
isBulkAward = false
|
|
BenchTracker:LogEPAward(ep)
|
|
end
|
|
end
|
|
if sepgp and sepgp.givename_ep then
|
|
original_givename_ep = sepgp.givename_ep
|
|
sepgp.givename_ep = function(self, getname, ep)
|
|
original_givename_ep(self, getname, ep)
|
|
if not isBulkAward then
|
|
BenchTracker:LogIndividualEP(getname, ep)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|