You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

810 lines
22 KiB

--------------------------------------------------------------------------------
-- Module Declaration
--
local oldPlugin = BigWigs:NewPlugin("Alt Power") -- XXX temp 9.0.2
oldPlugin.defaultDB = {}
local plugin = BigWigs:NewPlugin("AltPower")
if not plugin then return end
do
local name = plugin:GetDefaultFont()
local _, size = plugin:GetDefaultFont(12)
plugin.defaultDB = {
position = {"CENTER", "CENTER", 450, -160},
fontName = name,
fontSize = size,
outline = "NONE",
additionalWidth = 0,
additionalHeight = 0,
barTextColor = {1, 0.82, 0},
barColor = {0.2, 0, 1, 0.5},
backgroundColor = {0, 0, 0, 0.3},
monochrome = false,
expanded = false,
disabled = false,
lock = false,
}
end
--------------------------------------------------------------------------------
-- Locals
--
local L = BigWigsAPI:GetLocale("BigWigs: Plugins")
local media = LibStub("LibSharedMedia-3.0")
local FONT = media.MediaType and media.MediaType.FONT or "font"
plugin.displayName = L.altPowerTitle
local powerList, powerMaxList, sortedUnitList, roleColoredList = nil, nil, nil, nil
local unitList = nil
local maxPlayers = 0
local display, updater = nil, nil
local opener = nil
local currentTitle = nil
local inTestMode = nil
local sortDir = nil
local repeatSync = nil
local syncPowerList = nil
local syncPowerMaxList = nil
local UpdateDisplay
local tsort, min = table.sort, math.min
local UnitPower, IsInGroup = UnitPower, IsInGroup
local db = nil
local roleIcons = {
["TANK"] = "|TInterface\\AddOns\\BigWigs\\Media\\Icons\\Menus\\Role_Tank:0|t",
["HEALER"] = "|TInterface\\AddOns\\BigWigs\\Media\\Icons\\Menus\\Role_Healer:0|t",
["DAMAGER"] = "|TInterface\\AddOns\\BigWigs\\Media\\Icons\\Menus\\Role_Damage:0|t",
["NONE"] = "",
}
local function colorize(power, powerMax)
if power == -1 then return 0, 255 end
if not powerMax or powerMax == 0 then
powerMax = 100
if power > powerMax then
powerMax = power
end
end
local ratio = power/powerMax*510
local r, g = min(ratio, 255), min(510-ratio, 255)
if sortDir == "AZ" then -- red to green
return r, g
else -- green to red
return g, r
end
end
function plugin:RestyleWindow()
display:ClearAllPoints()
local point, relPoint = db.position[1], db.position[2]
local x, y = db.position[3], db.position[4]
display:SetPoint(point, UIParent, relPoint, x, y)
if db.lock then
display:SetMovable(false)
else
display:SetMovable(true)
end
local font = media:Fetch(FONT, db.fontName)
local flags = nil
if db.monochrome and db.outline ~= "NONE" then
flags = "MONOCHROME," .. db.outline
elseif db.monochrome then
flags = "MONOCHROME"
elseif db.outline ~= "NONE" then
flags = db.outline
end
display.bar:SetColorTexture(db.barColor[1], db.barColor[2], db.barColor[3], db.barColor[4])
display.bg:SetColorTexture(db.backgroundColor[1], db.backgroundColor[2], db.backgroundColor[3], db.backgroundColor[4])
display.title:SetFont(font, db.fontSize, flags)
display.title:SetTextColor(db.barTextColor[1], db.barTextColor[2], db.barTextColor[3], 1)
display.title:SetHeight(16+db.additionalHeight)
for i = 1, 26 do
display.text[i]:SetFont(font, db.fontSize, flags)
display.text[i]:SetSize(115+db.additionalWidth, 16+db.additionalHeight)
end
-- 240 = 115*2 + (5*2 padding - left and right)
-- 210 = 16*13 + (1*2 padding - top and bottom)
-- 82 = 16*5 + (1*2 padding - top and bottom)
display:SetSize(240+(db.additionalWidth*2), db.expanded and 210+(db.additionalHeight*13) or 82+(db.additionalHeight*5))
end
-------------------------------------------------------------------------------
-- Options
--
do
local disabled = function() return plugin.db.profile.disabled end
plugin.pluginOptions = {
name = "|TInterface\\AddOns\\BigWigs\\Media\\Icons\\Menus\\AltPower:20|t ".. L.altPowerTitle,
type = "group",
childGroups = "tab",
order = 14,
get = function(info)
return db[info[#info]]
end,
set = function(info, value)
local entry = info[#info]
db[entry] = value
plugin:RestyleWindow()
end,
args = {
general = {
type = "group",
name = L.general,
order = 1,
args = {
heading = {
type = "description",
name = L.altPowerDesc .."\n\n",
order = 1,
width = "full",
fontSize = "medium",
},
test = {
type = "execute",
name = L.test,
desc = L.altPowerTestDesc,
func = function()
plugin:Test()
end,
width = 1.5,
order = 2,
disabled = disabled,
},
lock = {
type = "toggle",
name = L.lock,
desc = L.lockDesc,
order = 3,
disabled = disabled,
},
barHeader = {
type = "header",
name = L.yourPowerBar,
order = 4,
},
barColor = {
type = "color",
name = L.barColor,
get = function(info)
return db.barColor[1], db.barColor[2], db.barColor[3], db.barColor[4]
end,
set = function(info, r, g, b, a)
db.barColor = {r, g, b, a}
plugin:RestyleWindow()
end,
hasAlpha = true,
width = 1.5,
order = 5,
disabled = disabled,
},
barTextColor = {
type = "color",
name = L.barTextColor,
get = function(info)
return db.barTextColor[1], db.barTextColor[2], db.barTextColor[3]
end,
set = function(info, r, g, b)
db.barTextColor = {r, g, b}
plugin:RestyleWindow()
end,
width = 1.5,
order = 6,
disabled = disabled,
},
generalHeader = {
type = "header",
name = L.general,
order = 7,
},
backgroundColor = {
type = "color",
name = L.background,
get = function(info)
return db.backgroundColor[1], db.backgroundColor[2], db.backgroundColor[3], db.backgroundColor[4]
end,
set = function(info, r, g, b, a)
db.backgroundColor = {r, g, b, a}
plugin:RestyleWindow()
end,
hasAlpha = true,
order = 8,
disabled = disabled,
},
fontName = {
type = "select",
name = L.font,
order = 9,
values = media:List(FONT),
itemControl = "DDI-Font",
get = function()
for i, v in next, media:List(FONT) do
if v == db.fontName then return i end
end
end,
set = function(_, value)
db.fontName = media:List(FONT)[value]
plugin:RestyleWindow()
end,
width = 2,
disabled = disabled,
},
monochrome = {
type = "toggle",
name = L.monochrome,
desc = L.monochromeDesc,
order = 10,
disabled = disabled,
},
fontSize = {
type = "range",
name = L.fontSize,
desc = L.fontSizeDesc,
order = 11,
max = 200, softMax = 25,
min = 1,
step = 1,
disabled = disabled,
},
outline = {
type = "select",
name = L.outline,
order = 12,
values = {
NONE = L.none,
OUTLINE = L.thin,
THICKOUTLINE = L.thick,
},
disabled = disabled,
},
additionalWidth = {
type = "range",
name = L.additionalWidth,
desc = L.additionalSizeDesc,
order = 13,
max = 100, softMax = 50,
min = 0,
step = 1,
width = 1.5,
disabled = disabled,
},
additionalHeight = {
type = "range",
name = L.additionalHeight,
desc = L.additionalSizeDesc,
order = 14,
max = 100, softMax = 20,
min = 0,
step = 1,
width = 1.5,
disabled = disabled,
},
resetHeader = {
type = "header",
name = "",
order = 15,
},
reset = {
type = "execute",
name = L.resetAll,
desc = L.resetAltPowerDesc,
func = function()
plugin:Contract()
plugin.db:ResetProfile()
end,
order = 16,
},
spacer = {
type = "description",
name = "\n\n",
order = 17,
width = "full",
fontSize = "medium",
},
disabled = {
type = "toggle",
name = L.disabled,
desc = L.disableAltPowerDesc,
order = 18,
set = function(_, value)
db.disabled = value
if value then
plugin:Close()
end
end,
confirm = function(_, value)
if value then
return L.disableDesc:format(L.altPowerTitle)
end
end,
},
},
},
exactPositioning = {
type = "group",
name = L.positionExact,
order = 2,
disabled = disabled,
args = {
posx = {
type = "range",
name = L.positionX,
desc = L.positionDesc,
min = -2048,
max = 2048,
step = 1,
order = 1,
width = "full",
get = function()
return db.position[3]
end,
set = function(_, value)
db.position[3] = value
plugin:RestyleWindow()
end,
},
posy = {
type = "range",
name = L.positionY,
desc = L.positionDesc,
min = -2048,
max = 2048,
step = 1,
order = 2,
width = "full",
get = function()
return db.position[4]
end,
set = function(_, value)
db.position[4] = value
plugin:RestyleWindow()
end,
},
},
},
},
}
end
-------------------------------------------------------------------------------
-- Initialization
--
do
local function updateProfile()
db = plugin.db.profile
for k, v in next, db do
local defaultType = type(plugin.defaultDB[k])
if defaultType == "nil" then
db[k] = nil
elseif type(v) ~= defaultType then
db[k] = plugin.defaultDB[k]
end
end
if db.fontSize < 1 or db.fontSize > 200 then
db.fontSize = plugin.defaultDB.fontSize
end
if db.outline ~= "NONE" and db.outline ~= "OUTLINE" and db.outline ~= "THICKOUTLINE" then
db.outline = plugin.defaultDB.outline
end
if db.additionalWidth < 0 or db.additionalWidth > 100 then
db.additionalWidth = plugin.defaultDB.additionalWidth
end
if db.additionalHeight < 0 or db.additionalHeight > 100 then
db.additionalHeight = plugin.defaultDB.additionalHeight
end
for i = 1, 3 do
local n = db.barTextColor[i]
if type(n) ~= "number" or n < 0 or n > 1 then
db.barTextColor = plugin.defaultDB.barTextColor
break
end
end
for i = 1, 4 do
local n = db.barColor[i]
if type(n) ~= "number" or n < 0 or n > 1 then
db.barColor = plugin.defaultDB.barColor
break
end
end
for i = 1, 4 do
local n = db.backgroundColor[i]
if type(n) ~= "number" or n < 0 or n > 1 then
db.backgroundColor = plugin.defaultDB.backgroundColor
break
end
end
oldPlugin.db:ResetProfile(nil, true) -- XXX temp 9.0.2 // no callbacks
plugin:RestyleWindow()
end
function plugin:OnPluginEnable()
self:RegisterMessage("BigWigs_StartSyncingPower")
self:RegisterMessage("BigWigs_ShowAltPower")
self:RegisterMessage("BigWigs_HideAltPower", "Close")
self:RegisterMessage("BigWigs_OnBossDisable")
self:RegisterMessage("BigWigs_OnBossWipe", "BigWigs_OnBossDisable")
self:RegisterMessage("BigWigs_ProfileUpdate", updateProfile)
updateProfile()
end
end
function plugin:OnPluginDisable()
self:Close()
end
-------------------------------------------------------------------------------
-- Event Handlers
--
do
-- Realistically this should never fire during an encounter, we're just compensating for someone leaving the group
-- whilst the display is shown (more likely to happen in LFR). The display should not be shown outside of an encounter
-- where the event seems to fire frequently, which would make this very inefficient.
local function GROUP_ROSTER_UPDATE()
if not IsInGroup() then plugin:Close() return end
local players = GetNumGroupMembers()
if players ~= maxPlayers then
if updater then plugin:CancelTimer(updater) end
if repeatSync then
syncPowerList = {}
syncPowerMaxList = {}
end
maxPlayers = players
unitList = IsInRaid() and plugin:GetRaidList() or plugin:GetPartyList()
powerList, powerMaxList, sortedUnitList, roleColoredList = {}, {}, {}, {}
local UnitClass, UnitGroupRolesAssigned = UnitClass, UnitGroupRolesAssigned
local colorTbl = CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS
for i = 1, players do
local unit = unitList[i]
sortedUnitList[i] = unit
local name = plugin:UnitName(unit, true) or "???"
local _, class = UnitClass(unit)
local tbl = class and colorTbl[class] or GRAY_FONT_COLOR
roleColoredList[unit] = ("%s|cFF%02x%02x%02x%s|r"):format(roleIcons[UnitGroupRolesAssigned(unit)], tbl.r*255, tbl.g*255, tbl.b*255, name)
end
updater = plugin:ScheduleRepeatingTimer(UpdateDisplay, 1)
end
if repeatSync then
plugin:RosterUpdateForHiddenDisplay() -- Maybe a player logged back on after a DC, force sync refresh to send them our power.
end
end
do
-- USE THIS CALLBACK TO SKIN THIS WINDOW! NO NEED FOR UGLY HAX! E.g.
-- local addonName, addonTable = ...
-- if BigWigsLoader then
-- BigWigsLoader.RegisterMessage(addonTable, "BigWigs_FrameCreated", function(event, frame, name) print(name.." frame created.") end)
-- end
display = CreateFrame("Frame", nil, UIParent)
display:SetSize(230, 80)
display:SetClampedToScreen(true)
display:EnableMouse(true)
display:SetFrameStrata("MEDIUM")
display:SetFixedFrameStrata(true)
display:SetFrameLevel(125)
display:SetFixedFrameLevel(true)
display:RegisterForDrag("LeftButton")
display:SetScript("OnDragStart", function(self)
if self:IsMovable() then
self:StartMoving()
end
end)
display:SetScript("OnDragStop", function(self)
self:StopMovingOrSizing()
local point, _, relPoint, x, y = self:GetPoint()
db.position = {point, relPoint, x, y}
plugin:UpdateGUI() -- Update X/Y if GUI is open.
end)
display:Hide()
local bg = display:CreateTexture()
bg:SetPoint("BOTTOMLEFT", display, "BOTTOMLEFT")
bg:SetPoint("BOTTOMRIGHT", display, "BOTTOMRIGHT")
display.bg = bg
local close = CreateFrame("Button", nil, display)
close:SetPoint("BOTTOMRIGHT", display, "TOPRIGHT", -2, 2)
close:SetSize(16, 16)
close:SetNormalTexture("Interface\\AddOns\\BigWigs\\Media\\Icons\\close")
close:SetScript("OnClick", function()
if inTestMode then
plugin:Close()
else
display:Hide() -- Don't call :Close as that would disable syncing (if enabled)
BigWigs:Print(L.toggleDisplayPrint)
end
end)
local expand = CreateFrame("Button", nil, display)
expand:SetPoint("BOTTOMLEFT", display, "TOPLEFT", 2, 2)
expand:SetSize(16, 16)
expand:SetNormalTexture("Interface\\AddOns\\BigWigs\\Media\\Icons\\arrows_down")
expand:SetScript("OnClick", function()
if db.expanded then
plugin:Contract()
else
plugin:Expand()
end
end)
display.expand = expand
local header = display:CreateFontString()
header:SetShadowOffset(1, -1)
header:SetTextColor(1,0.82,0,1)
header:SetPoint("BOTTOM", display, "TOP", 0, 4)
header:SetHeight(16)
bg:SetPoint("TOP", header, "TOP", 0, 2)
display.title = header
local bar = display:CreateTexture(nil, nil, nil, 1) -- above background
bar:SetPoint("LEFT", expand, "RIGHT", 4, 0)
bar:SetPoint("BOTTOM", header, "BOTTOM")
bar:SetPoint("TOP", header, "TOP")
display.bar = bar
display.text = {}
for i = 1, 26 do
local text = display:CreateFontString()
text:SetShadowOffset(1, -1)
text:SetTextColor(1,0.82,0,1)
text:SetSize(115, 16)
text:SetJustifyH("LEFT")
if i == 1 then
text:SetPoint("TOPLEFT", display, "TOPLEFT", 5, -1)
elseif i % 2 == 0 then
text:SetPoint("LEFT", display.text[i-1], "RIGHT")
else
text:SetPoint("TOP", display.text[i-2], "BOTTOM")
end
display.text[i] = text
end
display:SetScript("OnEvent", GROUP_ROSTER_UPDATE)
end
-- This module is rarely used, and opened once during an encounter where it is.
-- We will prefer on-demand variables over permanent ones.
function plugin:BigWigs_ShowAltPower(event, module, title, sorting, sync)
if db.disabled or not IsInGroup() then return end -- Solo runs of old content
self:RestyleWindow()
self:Close()
if sync then
self:RegisterMessage("BigWigs_PluginComm")
end
display:RegisterEvent("GROUP_ROSTER_UPDATE")
opener = module
sortDir = sorting
currentTitle = title
maxPlayers = 0 -- Force an update via GROUP_ROSTER_UPDATE
display:Show()
GROUP_ROSTER_UPDATE()
UpdateDisplay()
end
end
do
local classList = {"HUNTER","WARRIOR","ROGUE","MAGE","PRIEST","SHAMAN","WARLOCK","DEMONHUNTER","DEATHKNIGHT","DRUID","MONK","PALADIN"}
local roleList = {"DAMAGER","TANK","DAMAGER","DAMAGER","HEALER","HEALER","DAMAGER","TANK","DAMAGER","HEALER","HEALER","DAMAGER"}
local function testUpdate()
if inTestMode then
plugin:SimpleTimer(testUpdate, 3)
else
return
end
local amount = math.random(1,100)
display.bar:SetWidth((amount/100) * (200+(db.additionalWidth*2)))
display.title:SetFormattedText(L.yourPowerTest, amount)
local colorTbl = CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS
local sortedUnitListTest, powerListTest = {}, {}
local amount = db.expanded and 26 or 10
for i = 1, amount do
sortedUnitListTest[i] = i
powerListTest[i] = math.random(1, 99)
end
tsort(sortedUnitListTest, function(x,y)
local px, py = powerListTest[x], powerListTest[y]
if px == py then
return x > y
else
return px > py
end
end)
for i = 1, amount do
local unitNumber = sortedUnitListTest[i]
local tableSize = #classList
local tableEntry = unitNumber % tableSize
if tableEntry == 0 then tableEntry = tableSize end
local class = classList[tableEntry]
local role = roleList[tableEntry]
local power = powerListTest[unitNumber]
local r, g = colorize(power, 100)
local name = (L.player):format(unitNumber)
local classColorTbl = class and colorTbl[class] or GRAY_FONT_COLOR
display.text[i]:SetFormattedText("|cFF%02x%02x00[%d]|r %s|cFF%02x%02x%02x%s|r", r, g, power, roleIcons[role], classColorTbl.r*255, classColorTbl.g*255, classColorTbl.b*255, name)
end
end
function plugin:Test()
if not inTestMode then
self:Close()
display:Show()
inTestMode = true
testUpdate()
end
end
end
do
local function sortTbl(x,y)
local px, py = powerList[x], powerList[y]
if px == py then
return x > y
elseif sortDir == "AZ" then
return px > py
else
return px < py
end
end
function UpdateDisplay()
for i = 1, maxPlayers do
local unit = unitList[i]
-- If we don't have sync data (players not using BigWigs) use whatever (potentially incorrect) data Blizz gives us.
powerList[unit] = syncPowerList and syncPowerList[unit] or UnitPower(unit, 10) -- Enum.PowerType.Alternate = 10
powerMaxList[unit] = syncPowerMaxList and syncPowerMaxList[unit] or UnitPowerMax(unit, 10) -- Enum.PowerType.Alternate = 10
end
local power = UnitPower("player", 10)
local percent = power / UnitPowerMax("player", 10)
display.bar:SetWidth(percent * (200+(db.additionalWidth*2)))
display.title:SetFormattedText(L.yourAltPower, currentTitle, power)
tsort(sortedUnitList, sortTbl)
for i = 1, db.expanded and 26 or 10 do
local unit = sortedUnitList[i]
if unit then
local power = powerList[unit]
local powerMax = powerMaxList[unit]
local r, g = colorize(power, powerMax)
display.text[i]:SetFormattedText("|cFF%02x%02x00[%d]|r %s", r, g, power, roleColoredList[unit])
else
display.text[i]:SetText("")
end
end
end
end
function plugin:Expand()
db.expanded = true
display:SetHeight(210+(db.additionalHeight*13))
display.expand:SetNormalTexture("Interface\\AddOns\\BigWigs\\Media\\Icons\\arrows_up")
if not inTestMode then
UpdateDisplay()
end
end
function plugin:Contract()
db.expanded = false
display:SetHeight(82+(db.additionalHeight*5))
display.expand:SetNormalTexture("Interface\\AddOns\\BigWigs\\Media\\Icons\\arrows_down")
for i = 11, 26 do
display.text[i]:SetText("")
end
end
function plugin:Close()
if repeatSync then
self:UnregisterEvent("GROUP_ROSTER_UPDATE")
self:CancelTimer(repeatSync)
repeatSync = nil
end
if updater then self:CancelTimer(updater) end
updater = nil
display:UnregisterEvent("GROUP_ROSTER_UPDATE")
display:Hide()
self:UnregisterMessage("BigWigs_PluginComm")
for i = 1, 26 do
display.text[i]:SetText("")
end
powerList, powerMaxList, sortedUnitList, roleColoredList, syncPowerList, syncPowerMaxList = nil, nil, nil, nil, nil, nil
unitList, opener, inTestMode, currentTitle = nil, nil, nil, nil
end
function plugin:BigWigs_OnBossDisable(_, module)
if module == opener then
self:Close()
end
end
do
local power = -1
local powerMax = -1
local function sendPower()
local newPower = UnitPower("player", 10) -- Enum.PowerType.Alternate = 10
local newPowerMax = UnitPowerMax("player", 10) -- Enum.PowerType.Alternate = 10
if newPower ~= power then
power = newPower
plugin:Sync("AltPower", newPower)
end
if newPowerMax ~= powerMax then
powerMax = newPowerMax
plugin:Sync("AltPowerMax", newPowerMax)
end
end
function plugin:RosterUpdateForHiddenDisplay()
-- This is for people that don't show the AltPower display (event isn't registered to the display as it normally would be).
-- It will force sending the current power for those that do have the display shown but just had their power list reset by a
-- GROUP_ROSTER_UPDATE. Or someone DCd and is logging back on, so send an update.
if not IsInGroup() then plugin:Close() return end
self:CancelTimer(repeatSync)
power = -1
repeatSync = self:ScheduleRepeatingTimer(sendPower, 1)
end
function plugin:BigWigs_StartSyncingPower(_, module)
if not IsInGroup() then return end
power = -1
opener = module
if not repeatSync then
repeatSync = self:ScheduleRepeatingTimer(sendPower, 1)
if display:IsShown() then
syncPowerList = {}
syncPowerMaxList = {}
else
self:RegisterEvent("GROUP_ROSTER_UPDATE", "RosterUpdateForHiddenDisplay")
end
end
end
function plugin:BigWigs_PluginComm(_, msg, amount, sender)
if msg == "AltPower" then
local curPower = tonumber(amount)
if curPower then
for i = 1, maxPlayers do
local unit = unitList[i]
if sender == self:UnitName(unit) then
syncPowerList[unit] = curPower
break
end
end
end
elseif msg == "AltPowerMax" then
local curPowerMax = tonumber(amount)
if curPowerMax then
for i = 1, maxPlayers do
local unit = unitList[i]
if sender == self:UnitName(unit) then
syncPowerMaxList[unit] = curPowerMax
break
end
end
end
end
end
end
-- We run this last to prevent the AltPower module breaking if some addon listening to this event causes an error
plugin:SendMessage("BigWigs_FrameCreated", display, "AltPower")