Compare with Previous | Blame | View Log
-- ZAM_BuffDisplay © ZAM Network LLC
-- All Rights Reserved
local zbdVersion = "1.2"
local wm = WINDOW_MANAGER
local em = EVENT_MANAGER
local playerPool, reticlePool, db, _
local pbc = wm:CreateTopLevelWindow("ZAM_BuffDisplayPlayerContainer")
local rbc = wm:CreateTopLevelWindow("ZAM_BuffDisplayReticleoverContainer")
local GetNumBuffs = GetNumBuffs
local GetUnitBuffInfo = GetUnitBuffInfo
local GetBuffColor = GetBuffColor
local GetControl = GetControl
local zo_ceil = zo_ceil
local LAM = LibStub("LibAddonMenu-2.0")
local LMP = LibStub("LibMediaProvider-1.0")
--add option later to change text color (maybe also separate bar colors if buff/debuff?)
local defaults = {
isLocked = true,
player = {
anchor = {
a = TOPLEFT,
b = TOPLEFT,
x = 5,
y = 100,
},
growUp = false,
},
reticleover = {
anchor = {
a = TOPLEFT,
b = TOP,
x = 150,
y = 85,
},
growUp = false,
},
displayBars = true,
font = "Arial Narrow",
barTex = "ESO Basic",
barColor = {
r = .35,
g = .35,
b = .35,
a = 1,
},
defaultTextColor = true,
customTextColors = {
[BUFF_EFFECT_TYPE_NOT_AN_EFFECT] = { r = 1, g = 1, b = 1}, --[0]
[BUFF_EFFECT_TYPE_BUFF] = { r = 0, g = 1, b = 0}, --[1]
[BUFF_EFFECT_TYPE_DEBUFF] = { r = 1, g = 0, b = 0}, --[2]
},
cmbtShow = false,
attackShow = false,
menuHide = false,
}
local function HandleAnchors(buff, buffID, unit)
buff:ClearAnchors()
local bc = unit == "player" and pbc or rbc
local pool = unit == "player" and playerPool or reticlePool
local anchorBuff = buffID == 1 and bc or pool:AcquireObject(buffID-1)
if db[unit].growUp then
buff:SetAnchor(BOTTOMLEFT, anchorBuff, anchorBuff == bc and BOTTOMLEFT or TOPLEFT)
buff:SetAnchor(BOTTOMRIGHT, anchorBuff, anchorBuff == bc and BOTTOMRIGHT or TOPRIGHT)
else
buff:SetAnchor(TOPLEFT, anchorBuff, anchorBuff == bc and TOPLEFT or BOTTOMLEFT)
buff:SetAnchor(TOPRIGHT, anchorBuff, anchorBuff == bc and TOPRIGHT or BOTTOMRIGHT)
end
end
local function SetFonts(buff)
buff.name:SetFont(LMP:Fetch("font", db.font).."|18|soft-shadow-thin")
buff.time:SetFont(LMP:Fetch("font", db.font).."|18|soft-shadow-thin")
end
local function SetTexture(buff)
buff.bar:SetTexture(LMP:Fetch("statusbar", db.barTex))
end
local function SetTextColors(unit, pool)
--local pool = unit == "player" and playerPool or reticlePool
for i = 1, GetNumBuffs(unit) do
local buffName, timeStarted, timeEnding, buffSlot, stackCount, iconFilename, buffType, effectType, abilityType, statusEffectType = GetUnitBuffInfo(unit, i)
local myBuff = pool:AcquireObject(i)
if db.defaultTextColor then
myBuff.name:SetColor(GetBuffColor(effectType):UnpackRGBA())
else
local color = db.customTextColors[effectType]
myBuff.name:SetColor(color.r, color.g, color.b)
end
end
end
local function CreateBuff(pool)
local forPlayer = pool == playerPool
local buff = ZO_ObjectPool_CreateControl(forPlayer and "ZAM_BuffDisplay_Player" or "ZAM_BuffDisplay_Reticleover", pool, forPlayer and pbc or rbc)
buff.icon = GetControl(buff, "Icon")
buff.time = GetControl(buff, "Time")
buff.name = GetControl(buff, "Name")
--GetControl(myBuff, "Name"):SetWidth(170)
buff.bar = GetControl(buff, "Statusbar")
buff.bar:SetColor(db.barColor.r, db.barColor.g, db.barColor.b, db.barColor.a)
buff.bar:SetHidden(not db.displayBars)
local buffID = pool.m_NextControlId
HandleAnchors(buff, buffID, forPlayer and "player" or "reticleover")
SetFonts(buff)
SetTexture(buff)
buff.timeLastRun = 0
buff:SetHandler("OnUpdate", function(self, updateTime)
if (updateTime - self.timeLastRun) >= .5 then
self.timeLastRun = updateTime
--if self.endTime == "\195\236" then
if self.endTime == "--" then
return
else
local timeLeft = (self.endTime-updateTime)
if timeLeft < 60 then
self.time:SetText(zo_ceil(timeLeft).."s")
else
self.time:SetText(zo_ceil(timeLeft/60).."m")
end
end
end
end)
buff.bar.timeLastRun = 0
buff.bar:SetHandler("OnUpdate", function(self,updateTime)
if (updateTime - self.timeLastRun) >= .01 then
self.timeLastRun = updateTime
if self.noDur then
return
else
self:SetValue((buff.endTime - updateTime) + self.min)
end
end
end)
return buff
end
local function RemoveBuff(buffFrame)
buffFrame:SetHidden(true)
end
local function UpdateBuffs(unit)
unit = unit or "player"
if unit ~= "player" and db.attackShow then
if IsUnitAttackable(unit) then
rbc:SetHidden(false)
return
else
rbc:SetHidden(true)
end
end
local pool = unit == "player" and playerPool or reticlePool
local numBuffs = GetNumBuffs(unit)
for i = 1, numBuffs do
local buffName, timeStarted, timeEnding, buffSlot, stackCount, iconFilename, buffType, effectType, abilityType, statusEffectType = GetUnitBuffInfo(unit, i)
local myBuff = pool:AcquireObject(i)
myBuff.name:SetText(buffName)
myBuff.name:SetColor(GetBuffColor(effectType):UnpackRGBA())
--myBuff.bar:SetColor(GetBuffColor(effectType):UnpackRGBA())
myBuff.icon:SetTexture(iconFilename)
myBuff:SetHidden(false)
local noDur = timeStarted == timeEnding
myBuff.bar.noDur = noDur
myBuff.endTime = noDur and "--" or timeEnding
if noDur then
myBuff.time:SetText(myBuff.endTime)
end
myBuff.bar.min = timeStarted
myBuff.bar.max = timeEnding
myBuff.bar:SetMinMax(myBuff.bar.min, myBuff.bar.max)
end
local activeBuffs = pool:GetActiveObjectCount()
if activeBuffs > numBuffs then
for i = numBuffs+1, activeBuffs do
pool:ReleaseObject(i)
end
end
end
local function SetUpContainer(unit)
local bc = unit == "player" and pbc or rbc
bc:SetDimensions(250,30)
local anchors = db[unit].anchor
bc:SetAnchor(anchors.a, GuiRoot, anchors.b, anchors.x, anchors.y)
bc:SetDrawLayer(DL_BACKGROUND)
bc:SetMouseEnabled(true)
bc:SetMovable(not db.isLocked)
bc:SetClampedToScreen(true)
bc:SetHandler("OnReceiveDrag", function(self)
if not db.isLocked then
self:StartMoving()
end
end)
bc:SetHandler("OnMouseUp", function(self)
self:StopMovingOrResizing()
_, anchors.a, _, anchors.b, anchors.x, anchors.y = self:GetAnchor()
end)
bc.bg = wm:CreateControl("ZAM_BuffDisplay"..unit.."ContainerBG", bc, CT_TEXTURE)
bc.bg:SetAnchor(TOPLEFT, bc, TOPLEFT, -3, -3)
bc.bg:SetAnchor(BOTTOMRIGHT, bc, BOTTOMRIGHT, 3, 3)
bc.bg:SetColor(1,1,1,.5)
bc.bg:SetAlpha(db.isLocked and 0 or .5)
end
local function CombatVisibility(event, inCombat)
rbc:SetHidden(not inCombat)
end
local changevisibility = EVENT_ACTION_LAYER_PUSHED
--popped & pushed can fire 2-3 times in a row...
local function MenuVisibility(event)
if event == EVENT_ACTION_LAYER_PUSHED and changevisibility == event then
pbc:SetHidden(true)
changevisibility = EVENT_ACTION_LAYER_POPPED
elseif event == EVENT_ACTION_LAYER_POPPED and changevisibility == event then
pbc:SetHidden(false)
changevisibility = EVENT_ACTION_LAYER_PUSHED
end
end
local panelData = {
type = "panel",
name = "ZAM BuffDisplay",
displayName = "|t72:36:ZAM_BuffDisplay\\ZAM_Logo.dds|t BuffDisplay",
slashCommand = "/zambd",
registerForRefresh = true,
registerForDefaults = true,
resetFunc = function()
db.player.anchor = defaults.player.anchor
pbc:ClearAnchors()
local anchor = db.player.anchor
pbc:SetAnchor(anchor.a, GuiRoot, anchor.b, anchor.x, anchor.y)
db.reticleover.anchor = defaults.reticleover.anchor
pbc:ClearAnchors()
local anchor = db.reticleover.anchor
pbc:SetAnchor(anchor.a, GuiRoot, anchor.b, anchor.x, anchor.y)
end,
author = "Seerah",
version = zbdVersion,
}
local optionsData = {
[1] = {
type = "checkbox",
name = "Lock Buff Displays",
tooltip = "Lock or unlock the buff anchors to move them.",
getFunc = function() return db.isLocked end,
setFunc = function(value)
db.isLocked = value
pbc:SetMovable(not db.isLocked)
pbc.bg:SetAlpha(db.isLocked and 0 or 1)
rbc:SetMovable(not db.isLocked)
rbc.bg:SetAlpha(db.isLocked and 0 or 1)
end,
default = defaults.isLocked,
},
[2] = {
type = "header",
name = "Skinning Options",
},
[3] = {
type = "dropdown",
name = "Font",
tooltip = "The font to use for the text.",
choices = LMP:List("font"),
getFunc = function() return db.font end,
setFunc = function(val)
db.font = val
for i = 1, playerPool:GetTotalObjectCount() do
SetFonts(playerPool:AcquireObject(i))
end
for i = 1, reticlePool:GetTotalObjectCount() do
SetFonts(reticlePool:AcquireObject(i))
end
end,
default = defaults.font,
},
[4] = {
type = "dropdown",
name = "Bar Texture",
tooltip = "The texture used for the bars.",
choices = LMP:List("statusbar"),
getFunc = function() return db.barTex end,
setFunc = function(val)
db.barTex = val
for i = 1, playerPool:GetTotalObjectCount() do
SetTexture(playerPool:AcquireObject(i))
end
for i = 1, reticlePool:GetTotalObjectCount() do
SetTexture(reticlePool:AcquireObject(i))
end
end,
default = defaults.barTex,
},
[5] = {
type = "colorpicker",
name = "Statusbar Color",
tooltip = "The color of the statusbar.",
getFunc = function() return db.barColor.r, db.barColor.g, db.barColor.b, db.barColor.a end,
setFunc = function(r,g,b,a)
db.barColor.r = r
db.barColor.g = g
db.barColor.b = b
db.barColor.a = a
for i = 1, playerPool:GetTotalObjectCount() do
(playerPool:AcquireObject(i)).bar:SetColor(r, g, b, a)
end
for i = 1, reticlePool:GetTotalObjectCount() do
(reticlePool:AcquireObject(i)).bar:SetColor(r, g, b, a)
end
end,
default = defaults.barColor,
},
[6] = {
type = "checkbox",
name = "Grow Player Buffs Upward",
tooltip = "When enabled, new buffs will be added above the anchor instead of below.",
getFunc = function() return db.player.growUp end,
setFunc = function(value) --setFunc
db.player.growUp = value
for i = 1, playerPool:GetTotalObjectCount() do
HandleAnchors(playerPool:AcquireObject(i), i, "player")
end
end,
default = defaults.player.growUp,
},
[7] = {
type = "checkbox",
name = "Grow Reticleover Buffs Upward",
tooltip = "When enabled, new buffs will be added above the anchor instead of below.",
getFunc = function() return db.reticleover.growUp end,
setFunc = function(value) --setFunc
db.reticleover.growUp = value
for i = 1, reticlePool:GetTotalObjectCount() do
HandleAnchors(reticlePool:AcquireObject(i), i, "reticleover")
end
end,
default = defaults.reticleover.growUp,
},
[8] = {
type = "checkbox",
name = "Display Statusbar",
tooltip = "Display the statusbar counting down the duration of the buff.",
getFunc = function() return db.displayBars end,
setFunc = function(value) --setFunc
db.displayBars = value
for i = 1, playerPool:GetTotalObjectCount() do
(playerPool:AcquireObject(i)).bar:SetHidden(not db.displayBars)
end
for i = 1, reticlePool:GetTotalObjectCount() do
(reticlePool:AcquireObject(i)).bar:SetHidden(not db.displayBars)
end
end,
default = defaults.displayBars,
},
[9] = {
type = "checkbox",
name = "Use Default Colors for Buff/Debuff Text",
tooltip = "Use the default UI values for buffs (bright green) and debuffs (bright red).",
getFunc = function() return db.defaultTextColor end,
setFunc = function(value)
db.defaultTextColor = value
SetTextColors("player", playerPool)
SetTextColors("reticleover", reticlePool)
end,
default = defaults.defaultTextColor,
},
[10] = {
type = "colorpicker",
name = "Buff Text Color",
tooltip = "The color of the text on a buff bar.",
getFunc = function() return db.customTextColors[BUFF_EFFECT_TYPE_BUFF].r, db.customTextColors[BUFF_EFFECT_TYPE_BUFF].g, db.customTextColors[BUFF_EFFECT_TYPE_BUFF].b end,
setFunc = function(r,g,b)
db.customTextColors[BUFF_EFFECT_TYPE_BUFF].r = r
db.customTextColors[BUFF_EFFECT_TYPE_BUFF].g = g
db.customTextColors[BUFF_EFFECT_TYPE_BUFF].b = b
SetTextColors("player", playerPool)
SetTextColors("reticleover", reticlePool)
end,
width = "half",
disabled = function() return db.defaultTextColor end,
default = defaults.customTextColors[BUFF_EFFECT_TYPE_BUFF],
},
[11] = {
type = "colorpicker",
name = "Debuff Text Color",
tooltip = "The color of the text on a debuff bar.",
getFunc = function() return db.customTextColors[BUFF_EFFECT_TYPE_DEBUFF].r, db.customTextColors[BUFF_EFFECT_TYPE_DEBUFF].g, db.customTextColors[BUFF_EFFECT_TYPE_DEBUFF].b end,
setFunc = function(r,g,b)
db.customTextColors[BUFF_EFFECT_TYPE_DEBUFF].r = r
db.customTextColors[BUFF_EFFECT_TYPE_DEBUFF].g = g
db.customTextColors[BUFF_EFFECT_TYPE_DEBUFF].b = b
SetTextColors("player", playerPool)
SetTextColors("reticleover", reticlePool)
end,
width = "half",
disabled = function() return db.defaultTextColor end,
default = defaults.customTextColors[BUFF_EFFECT_TYPE_DEBUFF],
},
[12] = {
type = "header",
name = "Visibility Options",
},
[13] = {
type = "checkbox",
name = "Only Show If Attackable",
tooltip = "Display the target's buffs only if you can attack the unit.",
getFunc = function() return db.attackShow end,
setFunc = function(value) --setFunc
db.attackShow = value
rbc:SetHidden(db.attackShow)
end,
default = defaults.attackShow,
},
[14] = {
type = "checkbox",
name = "Only Show In Combat",
tooltip = "Display the target's buffs only when you are in combat.",
getFunc = function() return db.cmbtShow end,
setFunc = function(value) --setFunc
db.cmbtShow = value
if db.cmbtShow then
em:RegisterForEvent("ZAM_BuffDisplay", EVENT_PLAYER_COMBAT_STATE, CombatVisibility)
else
em:UnregisterForEvent("ZAM_BuffDisplay", EVENT_PLAYER_COMBAT_STATE)
end
rbc:SetHidden(db.cmbtShow)
end,
default = defaults.cmbtShow,
},
[15] = {
type = "checkbox",
name = "Hide In Menus",
tooltip = "Hide the player buffs when in game menus and UI screens.",
getFunc = function() return db.menuHide end,
setFunc = function(value) --setFunc
db.menuHide = value
if db.menuHide then
em:RegisterForEvent("ZAM_BuffDisplay", EVENT_ACTION_LAYER_POPPED, MenuVisibility)
em:RegisterForEvent("ZAM_BuffDisplay", EVENT_ACTION_LAYER_PUSHED, MenuVisibility)
else
em:UnregisterForEvent("ZAM_BuffDisplay", EVENT_ACTION_LAYER_POPPED, MenuVisibility)
em:UnregisterForEvent("ZAM_BuffDisplay", EVENT_ACTION_LAYER_PUSHED, MenuVisibility)
end
pbc:SetHidden(db.menuHide)
changevisibility = EVENT_ACTION_LAYER_POPPED
end,
default = defaults.menuHide,
},
}
local function CreateOptions()
LAM:RegisterAddonPanel("ZAMBuffDisplayOptions", panelData)
LAM:RegisterOptionControls("ZAMBuffDisplayOptions", optionsData)
end
local function Initialize()
ZAM_BuffDisplayDB = ZAM_BuffDisplayDB or {}
for k,v in pairs(defaults) do
if type(ZAM_BuffDisplayDB[k]) == "nil" then
ZAM_BuffDisplayDB[k] = v
end
end
db = ZAM_BuffDisplayDB
SetUpContainer("player")
SetUpContainer("reticleover")
CreateOptions()
playerPool = ZO_ObjectPool:New(CreateBuff, RemoveBuff)
reticlePool = ZO_ObjectPool:New(CreateBuff, RemoveBuff)
UpdateBuffs("player")
if db.cmbtShow then
em:RegisterForEvent("ZAM_BuffDisplay", EVENT_PLAYER_COMBAT_STATE, CombatVisibility)
rbc:SetHidden(true)
end
if db.menuHide then
em:RegisterForEvent("ZAM_BuffDisplay", EVENT_ACTION_LAYER_POPPED, MenuVisibility)
em:RegisterForEvent("ZAM_BuffDisplay", EVENT_ACTION_LAYER_PUSHED, MenuVisibility)
end
em:RegisterForEvent("ZAM_BuffDisplay", EVENT_EFFECT_CHANGED, function(event, changeType, effectSlot, effectName, unitTag, beginTime, endTime, stackCount, iconName, buffType, effectType, abilityType, statusEffectType)
if unitTag =="player" or unitTag == "reticleover" then
UpdateBuffs(unitTag)
end
if effectType == 5 then print(effectName.." = passive?") return end
end)
em:UnregisterForEvent("ZAM_BuffDisplay", EVENT_PLAYER_ACTIVATED) --the next event handler was adding to instead of replacing the old one ><
em:RegisterForEvent("ZAM_BuffDisplay", EVENT_PLAYER_ACTIVATED, function()
UpdateBuffs("player")
end)
end
em:RegisterForEvent("ZAM_BuffDisplay", EVENT_PLAYER_ACTIVATED, function()
--em:RegisterForEvent("ZAM_BuffDisplay", EVENT_ADD_ON_LOADED, function(event, addon)
-- if addon == "ZAM_BuffDisplay" then
Initialize()
-- em:UnregisterForEvent("ZAM_BuffDisplay", EVENT_ADD_ON_LOADED)
-- end
end)
em:RegisterForEvent("ZAM_BuffDisplay", EVENT_RETICLE_TARGET_CHANGED, function()
UpdateBuffs("reticleover")
end)
--em:RegisterForEvent("ZAM_BuffDisplay", EVENT_EFFECTS_FULL_UPDATE, function() print("full update") endem