Files
2026-03-30 18:35:22 +02:00

518 lines
19 KiB
Lua

BenchTracker = {}
local mainFrame = nil
local playerFrames = {}
local benchSessionActive = false
local lastSeenCheckNum = nil
-- Class colors
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: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
table.sort(members, function(a, b)
return a.name < b.name
end)
return members
end
function BenchTracker:GetRaidURL()
local url = "https://errorguild.com/raids"
if WEBLINK_RAID_EVENT_ID and WEBLINK_RAID_EVENT_ID ~= "" then
url = "https://errorguild.com/raids/" .. WEBLINK_RAID_EVENT_ID
end
return url
end
----------------------------------------------------------------
-- MAIN FRAME
----------------------------------------------------------------
function BenchTracker:CreateMainFrame()
if mainFrame then return end
mainFrame = CreateFrame("Frame", "BenchTrackerFrame", UIParent)
mainFrame:SetWidth(220)
mainFrame:SetHeight(340)
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)
-- Member count label
local countLabel = mainFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
countLabel:SetPoint("TOPLEFT", mainFrame, "TOPLEFT", 8, -24)
countLabel:SetTextColor(0.6, 0.6, 0.6, 1)
mainFrame.countLabel = countLabel
-- Content area
local content = CreateFrame("Frame", "BenchContent", mainFrame)
content:SetPoint("TOPLEFT", mainFrame, "TOPLEFT", 6, -38)
content:SetPoint("BOTTOMRIGHT", mainFrame, "BOTTOMRIGHT", -6, 30)
content:SetBackdrop({
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = nil, tile = true, tileSize = 16,
insets = {left = 0, right = 0, top = 0, bottom = 0}
})
content:SetBackdropColor(0.08, 0.08, 0.08, 0.5)
mainFrame.content = content
-- Scroll frame for member list
local scrollFrame = CreateFrame("ScrollFrame", "BenchScrollFrame", content, "UIPanelScrollFrameTemplate")
scrollFrame:SetPoint("TOPLEFT", content, "TOPLEFT", 2, -2)
scrollFrame:SetPoint("BOTTOMRIGHT", content, "BOTTOMRIGHT", -2, 2)
local scrollChild = CreateFrame("Frame", "BenchScrollChild", scrollFrame)
scrollChild:SetWidth(185)
scrollChild:SetHeight(1)
scrollFrame:SetScrollChild(scrollChild)
mainFrame.scrollFrame = scrollFrame
mainFrame.scrollChild = scrollChild
BenchTracker:StyleScrollbar(scrollFrame)
-- Bench Session button (bottom)
local benchBtn = CreateFrame("Button", nil, mainFrame)
benchBtn:SetWidth(200)
benchBtn:SetHeight(18)
benchBtn:SetPoint("BOTTOM", mainFrame, "BOTTOM", 0, 6)
benchBtn: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}
})
benchBtn:SetBackdropColor(0.4, 0.2, 0.6, 0.9)
benchBtn:SetBackdropBorderColor(0.5, 0.5, 0.5, 1)
local benchBtnText = benchBtn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
benchBtnText:SetPoint("CENTER", benchBtn, "CENTER", 0, 0)
benchBtnText:SetText("Bench this Raid")
benchBtnText:SetTextColor(1, 0.85, 0.5, 1)
benchBtn:SetScript("OnEnter", function()
this:SetBackdropColor(0.5, 0.3, 0.7, 1)
end)
benchBtn:SetScript("OnLeave", function()
if benchSessionActive then
this:SetBackdropColor(0.2, 0.5, 0.2, 0.9)
else
this:SetBackdropColor(0.4, 0.2, 0.6, 0.9)
end
end)
benchBtn:SetScript("OnClick", function()
if not BenchTracker:IsOfficer() then return end
if benchSessionActive then
BenchTracker:EndBenchSession()
benchBtnText:SetText("Bench this Raid")
benchBtnText:SetTextColor(1, 0.85, 0.5, 1)
benchBtn:SetBackdropColor(0.4, 0.2, 0.6, 0.9)
else
BenchTracker:StartBenchSession()
benchBtnText:SetText("End Bench Session")
benchBtnText:SetTextColor(0.5, 1, 0.5, 1)
benchBtn:SetBackdropColor(0.2, 0.5, 0.2, 0.9)
end
end)
-- Start disabled until live check confirms a raid
benchBtn:Disable()
benchBtnText:SetText("Checking...")
benchBtnText:SetTextColor(0.5, 0.5, 0.5, 1)
benchBtn:SetBackdropColor(0.2, 0.2, 0.2, 0.9)
mainFrame.benchButton = benchBtn
mainFrame.benchButtonText = benchBtnText
-- Update bench button state based on DLL globals (every 2s)
local benchBtnUpdateFrame = CreateFrame("Frame")
benchBtnUpdateFrame.elapsed = 0
benchBtnUpdateFrame:SetScript("OnUpdate", function()
this.elapsed = this.elapsed + arg1
if this.elapsed < 2 then return end
this.elapsed = 0
if not mainFrame or not mainFrame.benchButton then return end
-- Sync session state from website via DLL (survives /reload)
if WEBLINK_BENCH_SESSION and not benchSessionActive then
benchSessionActive = true
mainFrame.benchButtonText:SetText("End Bench Session")
mainFrame.benchButtonText:SetTextColor(0.5, 1, 0.5, 1)
mainFrame.benchButton:SetBackdropColor(0.2, 0.5, 0.2, 0.9)
mainFrame.benchButton:Enable()
elseif not WEBLINK_BENCH_SESSION and benchSessionActive then
benchSessionActive = false
end
if not BenchTracker:IsOfficer() then
mainFrame.benchButton:Hide()
return
end
mainFrame.benchButton:Show()
if benchSessionActive then return end
if WEBLINK_RAID_LIVE then
mainFrame.benchButton:Enable()
mainFrame.benchButtonText:SetTextColor(1, 0.85, 0.5, 1)
mainFrame.benchButton:SetBackdropColor(0.4, 0.2, 0.6, 0.9)
local title = WEBLINK_RAID_TITLE or ""
if title ~= "" then
mainFrame.benchButtonText:SetText("Bench: " .. title)
else
mainFrame.benchButtonText:SetText("Bench this Raid")
end
else
mainFrame.benchButton:Disable()
mainFrame.benchButtonText:SetText("No Live Raid")
mainFrame.benchButtonText:SetTextColor(0.5, 0.5, 0.5, 1)
mainFrame.benchButton:SetBackdropColor(0.2, 0.2, 0.2, 0.9)
end
end)
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
----------------------------------------------------------------
-- PLAYER ROW
----------------------------------------------------------------
function BenchTracker:CreatePlayerRow(parent, index)
local frame = CreateFrame("Frame", "BenchPlayerRow" .. index, parent)
frame:SetWidth(185)
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)
local nameText = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
nameText:SetPoint("LEFT", frame, "LEFT", 4, 0)
nameText:SetWidth(175)
nameText:SetJustifyH("LEFT")
frame.nameText = nameText
frame:Hide()
return frame
end
----------------------------------------------------------------
-- MEMBER LIST DISPLAY
----------------------------------------------------------------
function BenchTracker:UpdateMemberDisplay()
if not mainFrame or not mainFrame:IsVisible() then return end
local members = BenchTracker:GetRaidMembers()
local scrollChild = mainFrame.scrollChild
-- Update count label
if mainFrame.countLabel then
mainFrame.countLabel:SetText("Bench: " .. table.getn(members) .. " players")
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)
yOffset = yOffset - 17
end
local contentHeight = table.getn(members) * 17 + 10
scrollChild:SetHeight(contentHeight)
if mainFrame.scrollFrame then
mainFrame.scrollFrame:UpdateScrollChildRect()
end
end
----------------------------------------------------------------
-- BENCH SESSION (website sync via DLL)
----------------------------------------------------------------
function BenchTracker:SendBenchRoster()
if not WebLink_PostBench then
DEFAULT_CHAT_FRAME:AddMessage("|cFFFF0000BenchTracker: WebLink DLL not loaded! Cannot sync bench.|r")
return
end
local members = BenchTracker:GetRaidMembers()
if table.getn(members) == 0 then return end
local classMap = {
["Warrior"] = "Warrior", ["Hunter"] = "Hunter", ["Mage"] = "Mage",
["Priest"] = "Priest", ["Rogue"] = "Rogue", ["Shaman"] = "Shaman",
["Warlock"] = "Warlock", ["Druid"] = "Druid", ["Paladin"] = "Paladin",
}
local parts = {}
for i = 1, table.getn(members) do
local m = members[i]
local cls = classMap[m.class] or m.class or "Unknown"
table.insert(parts, '{"name":"' .. m.name .. '","class":"' .. cls .. '"}')
end
local officer = UnitName("player") or "Unknown"
local json = '{"action":"sync_roster","officer":"' .. officer .. '","players":[' .. table.concat(parts, ",") .. ']}'
WebLink_PostBench(json)
end
function BenchTracker:StartBenchSession()
if not BenchTracker:IsOfficer() then
DEFAULT_CHAT_FRAME:AddMessage("|cFFFF0000BenchTracker: Only officers can start bench sessions.|r")
return
end
if GetNumRaidMembers() == 0 then
DEFAULT_CHAT_FRAME:AddMessage("|cFFFF0000BenchTracker: You must be in a raid to start a bench session.|r")
return
end
benchSessionActive = true
lastSeenCheckNum = nil
BenchTracker:SendBenchRoster()
local url = BenchTracker:GetRaidURL()
local msg = "BENCH SESSION STARTED. SIGN HERE: " .. url
SendChatMessage(msg, "RAID_WARNING")
DEFAULT_CHAT_FRAME:AddMessage("|cFF00FF00BenchTracker: Bench session started! Roster synced to website.|r")
end
function BenchTracker:EndBenchSession()
if not benchSessionActive then return end
benchSessionActive = false
lastSeenCheckNum = nil
if WebLink_PostBench then
WebLink_PostBench('{"action":"end_session"}')
end
DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00BenchTracker: Bench session ended.|r")
end
----------------------------------------------------------------
-- BENCH CHECK DETECTION (polls WEBLINK_BENCH_CHECK_NUM every 5s)
----------------------------------------------------------------
function BenchTracker:OnBenchCheckDetected(checkNum)
local url = BenchTracker:GetRaidURL()
local msg = "AFK Check for the bench group! Please respond at " .. url
SendChatMessage(msg, "RAID_WARNING")
SendChatMessage(msg, "GUILD")
DEFAULT_CHAT_FRAME:AddMessage("|cFFFFCC00BenchTracker: AFK Check #" .. checkNum .. " triggered from website. Messages sent.|r")
end
local pollFrame = CreateFrame("Frame", "BenchCheckPollFrame", UIParent)
pollFrame.elapsed = 0
pollFrame:SetScript("OnUpdate", function()
this.elapsed = this.elapsed + arg1
if this.elapsed < 5 then return end
this.elapsed = 0
local checkNum = WEBLINK_BENCH_CHECK_NUM
if not benchSessionActive then return end
if GetNumRaidMembers() == 0 then return end
if checkNum and checkNum ~= lastSeenCheckNum then
lastSeenCheckNum = checkNum
BenchTracker:OnBenchCheckDetected(checkNum)
elseif not checkNum then
lastSeenCheckNum = nil
end
-- Refresh member list if frame is open
if mainFrame and mainFrame:IsVisible() then
BenchTracker:UpdateMemberDisplay()
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()
if WebLink_RefreshBenchLive then
WebLink_RefreshBenchLive()
end
BenchTracker:UpdateMemberDisplay()
end
end
----------------------------------------------------------------
-- EVENT HANDLING
----------------------------------------------------------------
local benchEventFrame = CreateFrame("Frame", "BenchEventFrame", UIParent)
benchEventFrame:RegisterEvent("RAID_ROSTER_UPDATE")
benchEventFrame:RegisterEvent("ADDON_LOADED")
benchEventFrame:SetScript("OnEvent", function()
if event == "ADDON_LOADED" and arg1 == "shootyepgp" then
DEFAULT_CHAT_FRAME:AddMessage("|cFF00FF00BenchTracker loaded! Type /bench to open.|r")
elseif event == "RAID_ROSTER_UPDATE" then
if mainFrame and mainFrame:IsVisible() then
BenchTracker:UpdateMemberDisplay()
end
-- Auto-sync bench roster if session is active
if benchSessionActive then
if GetNumRaidMembers() == 0 then
BenchTracker:EndBenchSession()
if mainFrame and mainFrame.benchButtonText then
mainFrame.benchButtonText:SetText("Bench this Raid")
mainFrame.benchButtonText:SetTextColor(1, 0.85, 0.5, 1)
mainFrame.benchButton:SetBackdropColor(0.4, 0.2, 0.6, 0.9)
end
else
BenchTracker:SendBenchRoster()
end
end
end
end)