view BuffButton.lua @ 7:5301c68f28d8

TrackerBlock - use IsModifiedClick function to determine appropriate OnClick actions - handle 'CHATLINK' modifier - handle 'TOGGLEQUESTWATCH' modifier TrackerBlockObjectives - use a generic framework to manage frame creation for various criteria tracker types: - ProgressBar when Blizzard flag data indicates so - skip when Blizzard flag data indicates so - DynamicText otherwise - events related to the criteria are registered in the criteria frame, and unregistered when the frame is hidden, either by destruction of its parent or completion
author Nenue
date Fri, 01 Apr 2016 12:27:05 -0400
parents b0447b382f36
children
line wrap: on
line source
--- Actual BlizzUI modifications are applied here
-- @file-author@
-- @project-revision@ @project-hash@
-- @file-revision@ @file-hash@
-- Created: 3/12/2016 12:47 AM
local MODULE = 'BuffFrame'
local _, A = ...
local B, _G = A.frame, _G
local type, unpack, select, pairs, ipairs = _G.type, _G.unpack, _G.select, _G.pairs, _G.ipairs
local min, ceil, mod, tonumber, tostring = _G.min, _G.ceil, _G.mod, _G.tonumber, _G.tostring
local floor, wipe, max = _G.math.floor, _G.table.wipe, _G.math.max
local CreateFrame, IsInGroup, GetCVarBool = _G.CreateFrame, _G.IsInGroup, _G.GetCVarBool
local BuffFrame, ConsolidatedBuffs = _G.BuffFrame, _G.ConsolidatedBuffs
local print, gprint, aprint, fprint = B.print('Buff'), B.print('SetGuides'), B.print('SetAnchors'), B.fprint
local displays, anchors, guides, decors, positioned, drawn, zoom = B.displays, {}, {}, {}, {}, {}, {}
local UnitAura, UnitName, RegisterStateDriver = _G.UnitAura, _G.UnitName, _G.RegisterStateDriver

local M = B:RegisterModule(MODULE)

M.GetBuffZoom = function(buffName)
  local zoom = tonumber(B.displays[buffName].conf['Zoom']) / 100 / 2
  local zoomL, zoomU, zoomR, zoomD = zoom, zoom, 1-zoom, 1-zoom
  print(buffName, zoom)
  return function(self, ...)
    if select('#',...) == 4 then
      zoomL, zoomR, zoomU, zoomD = ...
    end
    self:SetTexCoord(zoomL, zoomR, zoomU, zoomD)
    return zoomL, zoomR, zoomU, zoomD
  end
end



M.UpdateButtonAlpha = function(self)
  if not self.parent.timeLeft or not self:IsVisible() then
    self:SetScript('OnUpdate', nil)
    return
  end

  if self.parent.timeLeft < _G.BUFF_WARNING_TIME then
    self:SetAlpha(BuffFrame.BuffAlphaValue)
  else
    self:SetAlpha(1)
  end
end

--- Called infrequently to align stencil frames
local refreshCount = 0
M.UpdateGuideFrames = function(buffName)
  refreshCount = refreshCount + 1
  local print = fprint()


  local anchor = anchors[buffName]
  local c, g, d = displays[buffName].conf, guides[buffName], decors[buffName]
  local perRow = c['PerRow']
  local buffSpacing, buffSize, buffBorder, buffDurationSize, buffCountSize, relativeX, relativeY = c['Spacing'], c['Size'], c['Border'], c['DurationSize'], c['CountSize'], c['RelativeX'], c['RelativeY']
  local consolidated = (anchors[buffName].contains and IsInGroup())
  local consolidatedPosition = (consolidated and anchors[buffName].containPosition or 0)


  print('|cFF00FF00Setting Guides ('..refreshCount..'):|r', buffName, 'user max:',c['Max'], 'hard max:', displays[buffName].maxIcons)

  local buffMax = min(c['Max'], displays[buffName].maxIcons)
  local anchorFrom, anchorTo = c.Point[1], c.Point[2]
  anchor.Zoom = M.GetBuffZoom(buffName)



  if consolidated then
    buffMax = buffMax + 1
  end

  local legend = {}
  legend.r, legend.g, legend.b, legend.a = unpack(displays[buffName].legendColor)
  local horizFrom = (relativeX < 0) and 'RIGHT' or 'LEFT'
  local horizTo = (relativeX < 0) and 'LEFT' or 'RIGHT'
  local vertFrom = (relativeY < 0) and 'TOP' or 'BOTTOM'
  local vertTo = (relativeY < 0) and 'BOTTOM' or 'TOP'
  local previous, up
  local bottom_extent = 0
  for i = 1, buffMax do
    print('update idx', i)
    if not g[i] then
      g[i] = CreateFrame('Frame', buffName..'Guide'..i, anchor, displays[buffName].template or 'VeneerGuideTemplate')
      RegisterStateDriver(g[i], "visibility", "[petbattle] [vehicleui] hide; show")
    end

    local guide = g[i]

    local row = ceil(i / perRow)
    local  col = mod(i, perRow)
    if col == 0 then
      col = perRow
    end

    guide.previous = previous
    guide.up = up
    local x, y, parent = 0, 0, anchor
    if i == 1 then
      parent = anchor
      up = guide
    elseif col == 1 then
      parent = g[i-perRow]
      y = (buffSpacing + bottom_extent)  * relativeY
      up = guide
      anchorFrom = vertFrom .. horizFrom
      anchorTo = vertFrom .. horizFrom
      bottom_extent = 0
    else
      parent = g[i-1]
      x = buffSpacing * relativeX
      anchorFrom = vertFrom .. horizFrom
      anchorTo = vertFrom .. horizTo
    end
    previous = guide
    guide.parent = parent

    ---------------------------------
    -- Positioning layer
    if i ~= consolidatedPosition or not consolidated then
      guide:SetSize(buffSize, buffSize + buffDurationSize)
      -- RaidBuffTray will fix the sizing
    end
    bottom_extent = max(bottom_extent, guide:GetHeight())

    guide.info = {} -- UnitAura cache

    if i == consolidatedPosition then
      guide.legend:SetTexture(1,1,0,0.5)
    else
      guide.legend:SetTexture(legend.r, legend.g, legend.b, legend.a)
    end

    guide.idText:SetText(i) -- needs to reflect the current position

    guide:ClearAllPoints()
    guide:SetPoint(anchorFrom, parent, anchorTo, x, y)
    print(anchorFrom, parent, anchorTo, x, y)

    guide.icon:SetSize(buffSize - buffBorder * 2, buffSize - buffBorder * 2)
    guide.icon:ClearAllPoints()
    guide.icon:SetPoint('TOPLEFT', guide, 'TOPLEFT', buffBorder, -buffBorder )

    local anchorTo, anchorFrom, x, y = unpack(c.DurationPoint)
    guide.duration:ClearAllPoints()
    guide.duration:SetPoint(anchorTo, guide, anchorFrom, x, y)
    --guide.duration:SetSize(buffSize, buffDurationSize)
    print('  duration ->', anchorFrom, anchorTo, x, y)

    local anchorTo, anchorFrom, x, y = unpack(c.CountPoint)
    guide.count:ClearAllPoints()
    guide.count:SetPoint(anchorTo, guide.icon, anchorFrom, x, y)
    --guide.count:SetSize(buffSize, c.CountSize)
    print('  count    ->', anchorFrom, anchorTo, x, y)

    -----------------------------------
    -- Background decorations layer
    if not d[i] then
      d[i] = CreateFrame('Frame', buffName..i..'Decor', _G.UIParent, 'VeneerDecorTemplate')
      -- todo: sort out a way to fix this without creating taint issues
      RegisterStateDriver(d[i], "visibility", "[petbattle] [vehicleui] hide")
    end

    d[i]:SetPoint('BOTTOMLEFT', guide.icon, 'BOTTOMLEFT', -buffBorder, -buffBorder)
    d[i]:SetPoint('TOPRIGHT', guide.icon, 'TOPRIGHT', buffBorder, buffBorder)


    guide:Show()
    B.SetConfigLayers(guide)
  end


  if #guides[buffName] > buffMax then
    local lim = #guides[buffName]
    for i = buffMax+1, lim do

      local g = guides[buffName][i]
      if g:IsVisible() then
        print('cleaning up #', i, buffName)
        g:Hide()
        B.RemoveConfigLayers(g)
      end

    end
  end

  anchor.last = previous
  anchor.up = up

  print(anchor:GetName(), anchor:GetSize())
end

M.UpdateButtonPositions = function(buffName, auraType)
  local print = fprint()
  local c = auraType.conf
  local numBuffs = 0
  local actualIcons = auraType.actualIcons()
  local maxIcons = auraType.maxIcons
  local anchor = anchors[buffName]
  local buffMax = c['Max']
  local consolidated = (anchor.contains and IsInGroup())
  local consolidatedPosition = (consolidated and anchor.containPosition or 0)

  for k,v in pairs(decors[buffName]) do
    print(v)
  end

  if consolidated then
    decors[buffName][1]:Hide()
    numBuffs = numBuffs + 1
    buffMax = buffMax + 1
  end

  print(' ', 'frame count:', auraType.actualIcons(), 'hardmax:', maxIcons)
  if auraType.actualIcons() > 0 then
    for i = 1, actualIcons do


      local buff = _G[buffName .. i]
      local buffIcon = _G[buffName .. i .. 'Icon']
      local buffBorder = c['Border']
      local buffDuration = _G[buffName .. i .. 'Duration']
      local buffCount = _G[buffName .. i .. 'Count']
      local buffDurationSize = c['DurationSize']
      local debuffBorder = _G[buffName .. i .. 'Border']


      if buff and not buff.consolidated then
        numBuffs = numBuffs + 1
        local guide = guides[buffName][numBuffs]
        local deco = decors[buffName][numBuffs]
        if numBuffs > buffMax then
          -- if a limit is reached, start hiding
          if guide then
            guide.info = nil
          end
          if deco then
            deco:Hide()
          end
          buff:Hide()
        else
          local buffData = guide.info
          buffData.name,  buffData.rank,  buffData.icon,  buffData.count,  buffData.dispelType,  buffData.duration,  buffData.expires,  buffData.caster,  buffData.isStealable,  buffData.shouldConsolidate,  buffData.spellID,  buffData.canApplyAura,  buffData.isBossDebuff,  buffData.value1,  buffData.value2,  buffData.value3
          = UnitAura(buff.unit, buff:GetID(), nil, buff.filters)

          if guide.caster and buffData.caster then
            if (buffData.caster ~= 'player' or c.ShowSelfCast) then
              guide.caster:SetText(UnitName(buffData.caster))
            else
              guide.caster:SetText(nil)
            end
          end


          print(numBuffs, i, buff:GetName(), buff:GetID(), decors[buffName][numBuffs]:GetName())

          buff:SetAllPoints(guide)
          buffIcon:ClearAllPoints()
          buffIcon:SetPoint('TOPLEFT', guide.icon, 'TOPLEFT', 0, 0)
          buffIcon:SetPoint('BOTTOMRIGHT', guide.icon, 'BOTTOMRIGHT', 0, 0)

          deco.parent = buff
          -- make sure so they aren't re-shown in pet battle
          if not C_PetBattles.IsInBattle() then
            deco:Show()
            deco:SetAlpha(1)
          end

          if debuffBorder then
            deco.background:SetTexture(debuffBorder:GetVertexColor())
            debuffBorder:Hide()
          else
            if guide.info.caster == 'player' then
              print(guide.info.caster)
              deco.background:SetTexture(unpack(c.PlayerColor))
            elseif buffData.isBossDebuff then
              print(guide.info.isBossDebuff)
              deco.background:SetTexture(unpack(c.BossColor))
            else
              print(guide.info.caster)
              deco.background:SetTexture(unpack(c.Color))
            end
          end


          buffDuration:ClearAllPoints()
          local from, to = unpack(c.DurationPoint)
          buffDuration:SetPoint(from, guide.duration, to)
          buffDuration:SetText('WHAT')

          if buff.timeLeft and c.WarningFade then
            deco:SetScript('OnUpdate', M.UpdateButtonAlpha)
          else
            deco:SetScript('OnUpdate', nil)
            deco:SetAlpha(1.0)
          end

          buffCount:ClearAllPoints()
          local from, to = unpack(c.CountPoint)
          buffCount:SetPoint(from, guide.count, to)

          if not drawn[buffName][numBuffs] then
            anchors[buffName].Zoom(buffIcon)

            if buffDuration then
              local font = buffDuration:GetFont()
              buffDuration:SetFont(font, c.DurationSize, 'OUTLINE')

            end

            if buffCount then
              local font = buffCount:GetFont()
              buffCount:SetFont(font, c.CountSize, 'OUTLINE')
            end
            drawn[buffName][numBuffs] = true
          end
        end
      end

    end
  end
  -- clear any outliers
  for i = numBuffs+1, buffMax do
    if guides[buffName][i].caster then
    guides[buffName][i].caster:SetText(nil)
    end
    --if not decors[buffName][i].parent or

    decors[buffName][i].parent = nil
    decors[buffName][i]:SetAlpha(1.0)
    decors[buffName][i]:SetScript('OnUpdate', nil)
    decors[buffName][i]:Hide()
  end

  -- parametric occlusion data for compacted anchor points
  if numBuffs == 0 then
    anchor.cutout_X = 0
    anchor.cutout_Y = 0
    anchor.outer_X = 0
    anchor.outer_Y = 0
  elseif numBuffs <= buffMax then
    local sX, sY = guides[buffName][numBuffs]:GetWidth(), guides[buffName][numBuffs]:GetHeight()
    local p = c.PerRow
    local lX = mod(numBuffs, p)
    local lY = floor(numBuffs / p)
    local oX = min(numBuffs, c.PerRow)
    local oY = ceil(numBuffs / p)
    anchor.cutout_X = lX * sX + lX * c.Spacing -- max clearance to fit alongside the row
    anchor.cutout_Y = lY * sY + lY * c.Spacing
    anchor.outer_Y  = oY * sY + oY * c.Spacing -- distance of farthest row
    anchor.outer_X  = oX * sX + oX * c.Spacing


    print('|cFF0088FF', 'inner corner', lX, lY, 'outer corners', oX, oY)
    print('cutout delta =', anchor.cutout_X, anchor.cutout_Y, 'out of', floor(anchor:GetWidth()), floor(anchor:GetHeight()))
    print('extent delta =', anchor.outer_X, anchor.outer_Y)
  else
    anchor.cutout_X = 0
    anchor.cutout_Y = 0
    anchor.outer_X = 0
    anchor.outer_Y = 0
  end

  if anchor.attached then
    M.UpdateAnchorChild(anchor, anchor.attached, anchor.attachmentConf)
  end

end

M.PostBuffAnchors  = function()
  local print = fprint()
  if M.ShowConsolidatedBuffs then
    M.UpdateRaidBuffs()
  end
  for buttonName, auraType in pairs(displays) do
    print('sending', buttonName, auraType)
    -- if waiting for anchors
    if not anchors[buttonName] then
      return
    end

    --if positioned[buttonName] == 0 then
      print('possibly reloaded UI, check positions')
      M.UpdateGuideFrames(buttonName)
    --end

    M.UpdateButtonPositions(buttonName, auraType)
  end
end

M.UpdateBuffs = function(buttonName, forced)
  local print = B.fprint(buttonName)
  local c = displays[buttonName].conf
  if drawn[buttonName] then
    wipe(drawn[buttonName])
  else
    drawn[buttonName] = {}
  end

  M.UpdateAnchorFrames(buttonName)
  M.UpdateGuideFrames(buttonName)
  M.UpdateButtonPositions(buttonName, displays[buttonName])
end

--- should only be called from user input
print('init def')
function M:OnInitialize ()
  drawn = B.Abstract(B.displays, 'drawn')
  -- Lesser extent of guide frames that have been positioned
  positioned = B.Abstract(B.displays, 'positioned', positioned)
  -- Backdrop style frame
  decors = B.Abstract(B.displays, 'decorator', decors)
  -- Static positioning frames
  guides = B.Abstract(B.displays, 'guides', guides)
  -- Anchor points for guides
  anchors = B.Abstract(B.displays, 'anchor')
  -- Stored functions for doing icon texture adjustments
  zoom = B.Abstract(B.displays, 'zoom', zoom)

  B:RegisterUnitEvent("UNIT_AURA", "player", "vehicle")
  B:RegisterEvent("GROUP_ROSTER_UPDATE")
  B:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED")
  hooksecurefunc("BuffFrame_UpdateAllBuffAnchors", M.PostBuffAnchors)
  hooksecurefunc("RaidBuffTray_Update", M.UpdateRaidBuffs)
end
print('update def')
function M:OnUpdate ()
  M.ShowConsolidated = (IsInGroup() and GetCVarBool("consolidateBuffs"))
  M.ShowMissingBuffs = (IsInGroup() and B.Conf.RaidShowMissing)

  for name, auraType in pairs(displays) do
    print(name, auraType)
    M.UpdateBuffs(auraType.buffName, true)
  end

  M.UpdateAnchorAnchors()
  M.UpdateRaidBuffs()
  M.UpdateBuffsTodo()
end