changeset 4:a30285f8191e

Units: make sure unit frames are SecureUnitButton derivatives remove of unnecessary target/focus events Stats: resolve GUID after event handlers have fired keep frame manipulation in uf.ui, get needed values from wrapper functions
author Nenue
date Tue, 21 Jun 2016 11:56:14 -0400
parents 07293831dd7b
children 9ac29fe77455
files SkeletonStats/DamageMeter.lua SkeletonStats/SkeletonStats.toc SkeletonStats/Views/DamageDone.lua SkeletonUnit/UnitFrame.lua SkeletonUnit/UnitFrame.xml
diffstat 5 files changed, 306 insertions(+), 179 deletions(-) [+]
line wrap: on
line diff
--- a/SkeletonStats/DamageMeter.lua	Tue Jun 21 08:14:22 2016 -0400
+++ b/SkeletonStats/DamageMeter.lua	Tue Jun 21 11:56:14 2016 -0400
@@ -13,23 +13,27 @@
 local KT = LibKT.register(DamageMeter)
 local dm = DamageMeter
 local db
-local segments = {}
-local actors = {}
-local ordered = {}
-local actorsOrdered = {}
-local prototypes = {}
 
+local addon, core = ...
+core.segments = {}
+core.actors = {}
+core.pets = {}
+core.ordered = {}
+core.actorsOrdered = {}
+core.current = {}
+core.prototypes = {}
+
+local GROUP_SIZE = 4
+local GROUP_TOKEN = 'party'
+local myGUID
+local segments, actors, pets, ordered = core.segments, core.actors, core.pets, core.ordered
+local prototypes = core.prototypes
 local segmentUID
 local viewPos
-local view
 local handler
 local viewType = 'damageDone'
-local sortType
 local meterWidth = 230
 
-local SortByDamage = function(a, b)
-end
-
 dm.init = function()
   dm:RegisterEvent('COMBAT_LOG_EVENT_UNFILTERED')
   dm:RegisterEvent('PLAYER_REGEN_DISABLED')
@@ -50,21 +54,20 @@
   db.actors = db.actors or {}
   db.viewType = db.viewType or 'damageDone'
 
-    actors = db.actors
-    segments = db.segments
-    viewPos = db.currentSegment
-    viewType = prototypes[viewType] and viewType or 'damageDone'
+
+
+  myGUID = UnitGUID('player')
+  core.actor(myGUID)
+  segments = db.segments
+  viewPos = db.currentSegment
+  viewType = prototypes[viewType] and viewType or 'damageDone'
+
+  if not segments[#segments] then
+    dm.new()
+  end
 
   dm.handler(viewType)
-
-  -- resolve segment
-  if not segments[viewPos] then
-    viewPos = dm.new()
-  else
-    view = segments[viewPos]
-    print('Using segment #', viewPos)
-  end
-  db.currentSegment = viewPos
+  dm.view(viewPos)
 
   dm.ui()
 end
@@ -75,15 +78,35 @@
 end
 
 dm.new = function()
+  if core.active then
+    core.active.finish = GetTime()
+  end
+
   segmentUID = (db.segUID or 0) + 1
   db.segUID = segmentUID
-  view = {}
-  viewPos = #segments+1
-  segments[viewPos] = view
-  db.currentSegment = viewPos
-  print('Starting new segment #', viewPos)
+
+  local segPos = #segments+1
+  core.segments[segPos] = {
+    view = {},
+    start = GetTime(),
+    uid = segmentUID
+  }
+  core.active = core.segments[viewPos].view
+  print('Starting new segment #', segPos, 'UID', segmentUID, core.active, core.segments[viewPos])
+
+  dm.view(segPos)
+
   return viewPos
 end
+
+dm.view = function(pos)
+  if not segments[pos] then
+    pos = 1
+  end
+  core.current = segments[pos]
+  core.current.view = core.current.view or {}
+end
+
 local dummyColor = {
   r = 0,
   g = 0.5,
@@ -100,30 +123,63 @@
   realm = ''
 }
 
-dm.actor = function (guid, name, flags)
-  --if not actors[guid] then
-    local class, classFilename, race, raceFilename, sex, name, realm = GetPlayerInfoByGUID(guid)
+local UNKNOWN_ACTOR = {
+  name = 'Unknown',
+  flags = 0,
+  class = 'NADA'
+}
+--- Pull stored actor info if possible
+core.actor = function (guid, name, flags)
+  local class, classFilename, race, raceFilename, sex, name, realm
+  local actor = UNKNOWN_ACTOR
+  if actors[guid] then
+    print('cached GUID', guid)
+    return actors[guid]
+  end
 
-    --print('|cFFFFFF00guid|r =', name, flags, 'class', class, classFilename)
-    actors[guid] = {
+  if guid:match('^Player') then
+    if db.actors[guid] then
+      actor = db.actors[guid]
+      print('using saved player GUID')
+    else
+
+      class, classFilename, race, raceFilename, sex, name, realm = GetPlayerInfoByGUID(guid)
+      actor = {
+        name = name,
+        flags = flags,
+        class = class,
+        classFilename = classFilename,
+        race = race,
+        raceFilename = raceFilename,
+        sex = sex,
+        realm = realm
+      }
+      print('saving player GUID')
+      db.actors[guid] = actor
+
+    end
+
+    actors[guid] = actor
+  elseif guid:match('^Pet') then
+    print('analyzing pet GUID')
+    actor = {
       name = name,
-      flags = flags,
-      class = class,
-      classFilename = classFilename,
-      race = race,
-      raceFilename = raceFilename,
-      sex = sex,
-      realm = realm
+      class = 'PET',
     }
-  --end
-  return actors[guid]
+    if not pets[guid] then
+      if bit.band(flags, COMBATLOG_OBJECT_AFFILIATION_MINE) > 0 then
+        pets[guid] = myGUID
+        actors[myGUID].pets = actors[myGUID].pets or {}
+        actors[myGUID].pets[guid] = actor
+      elseif bit.band(flags, COMBATLOG_OBJECT_AFFILIATION_PARTY+COMBATLOG_OBJECT_AFFILIATION_RAID > 0) then
+        -- todo: handle party/raid pets
+      end
+    end
+    actors[guid] = actor
+  end
+  return actor
 end
 
-
-prototypes.damageDone = {
-  header = 'Damage Done',
-}
-
 dm.PLAYER_REGEN_DISABLED = function()
   dm.new()
 end
@@ -131,16 +187,22 @@
 dm.COMBAT_LOG_EVENT_UNFILTERED = function(self, event, timeStemp, subEvent, u1, ...)
   local sourceGUID, sourceName, sourceFlags, _, destGUID, destName, destFlags = ...
 
-
+  local storeActor = false
   local args = {...}
   for key, prototype in pairs(prototypes) do
     --print(subEvent)
     if prototype[subEvent] then
-       prototype[subEvent](subEvent, ...)
+       local result = prototype[subEvent](subEvent, ...)
+       storeActor = (storeActor or result)
        --print('|cFFFFFF00' .. key .. '|r.|cFF00FFFF'..subEvent, '|r', ...)
     end
   end
 
+  if storeActor then
+    core.actor(sourceGUID, sourceName, sourceFlags)
+    core.actor(destGUID, destName, destFlags)
+  end
+
   dm.ui()
   return true
 end
@@ -167,7 +229,7 @@
 
 dm.ui = function()
   --table.sort(view, sortType)
-
+  local view = core.current.view
 
   table.wipe(ordered)
   for k,v in pairs(view) do
@@ -197,14 +259,47 @@
         dm.bars[i].headerRight = dm:CreateFontString('MeterHeaderRight'..i, 'OVERLAY', 'MeterHeaderRight')
         dm.bars[i].headerRight:SetPoint('TOP', dm.bars[i], 'TOP', 0, -6)
       end
-      handler.calculate(dm.bars[i], ordered[i], ordered[i].actor)
+
+      local actor = actors[ordered[i].guid] or UNKNOWN_ACTOR
+      handler.calculate(dm.bars[i], ordered[i], ordered[i], actor)
     end
   end
 
   for i, bar in ipairs(dm.bars) do
     if ordered[i] then
-      handler.refresh(bar, ordered[i], ordered[i].actor)
-      dm.showbar(bar)
+
+      local actor = actors[ordered[i].guid] or UNKNOWN_ACTOR
+      local icon, textLeft, textRight, r, g, b, a, percent = handler.refresh(bar, ordered[i], actor)
+
+      bar:SetColorTexture(r, g, b, a)
+
+      if icon then
+        bar.icon:Show()
+        bar.icon:SetTexture(icon[1])
+        if icon[2] then
+          bar.icon:SetTexCoord(unpack(icon[2]))
+        else
+          bar.icon:SetTexCoord(1,0,1,0)
+        end
+      else
+        bar.icon:Hide()
+      end
+
+      if textLeft then
+        bar.header:Show()
+        bar.header:SetText(textLeft)
+      else
+        bar.header:Hide()
+      end
+      if textRight then
+        bar.headerRight:Show()
+        bar.headerRight:SetText(textRight)
+      else
+        bar.headerRight:Hide()
+      end
+
+      bar:SetWidth(meterWidth*percent)
+      bar:Show()
     else
       dm.hidebar(bar)
     end
@@ -214,72 +309,6 @@
 end
 ---------------------------------------------------------
 -- DAMAGE DONE
-local dd = prototypes.damageDone
-dd.SPELL_DAMAGE = function(subEvent, ...)
-  local sourceGUID, sourceName, sourceFlags, _, destGUID, destName, destFlags, _, spellID, spellName, spellSchool, amount, overkill, school, resisted, blocked, absorbed, critical, glancing, crushing = ...
-
-  if not sourceName then
-    return
-  end
-
-  view[sourceName] = view[sourceName] or {}
-  local p = view[sourceName]
-
-  p.actor = dm.actor(sourceGUID, sourceName, sourceFlags)
-  dm.actor(destGUID, destName, destFlags)
-
-  p.last = amount
-  p.damage = (view[sourceName].damage or 0) + amount
-  p.name = (sourceName or 'Unknown')
-
-  p.child = p.child or {}
-  p.child[spellName] = p.child[spellName] or {}
-  p.child[spellName].hit = (p.child[spellName].hit or 0) + 1
-end
-dd.SPELL_DAMAGE_PERIODIC = dd.SPELL_DAMAGE
-dd.RANGE_DAMAGE = dd.SPELL_DAMAGE
-
-dd.SWING_DAMAGE = function(subEvent, ...)
-  local sourceGUID, sourceName, sourceFlags, _, destGUID, destName, destFlags, _, amount, overkill, school, resisted, blocked, absorbed, critical, glancing, crushing = ...
-  local spellID, spellName, spellSchool = -1, 'Attack', 1
-  dd.SPELL_DAMAGE(subEvent, sourceGUID, sourceName, sourceFlags, _, destGUID, destName, destFlags, _, spellID, spellName, spellSchool, amount, overkill, school, resisted, blocked, absorbed, critical, glancing, crushing)
-end
-
-dd.init = function()
-  dd.maxDamage = 0
-end
-
-dd.sort =  function(a, b)
-  return a.damage > b.damage
-end
-
-dd.calculate = function(bar, data, actor)
-  if dd.maxDamage < data.damage then
-    dd.maxDamage = data.damage
-  end
-
-end
-
-dd.refresh = function(bar, data, actor)
-  if actor.class and CLASS_ICON_TCOORDS[actor.classFilename] then
-    bar.icon:Show()
-    bar.icon:SetTexture("Interface\\TargetingFrame\\UI-Classes-Circles")
-    bar.icon:SetTexCoord(unpack(CLASS_ICON_TCOORDS[actor.classFilename]))
-  else
-    bar.icon:Hide()
-  end
-  bar.header:SetText(data.name)
-  bar.headerRight:SetText(data.damage .. ' ('..data.last..')')
-
-  local color = dummyColor
-  if actor.class and  RAID_CLASS_COLORS[actor.classFilename] then
-    color = RAID_CLASS_COLORS[actor.classFilename]
-  end
-
-  bar:SetColorTexture(color.r, color.g, color.b, 1)
-
-  bar:SetWidth(meterWidth * (data.damage / dd.maxDamage))
-end
 
 
 ---------------------------------------------------------
--- a/SkeletonStats/SkeletonStats.toc	Tue Jun 21 08:14:22 2016 -0400
+++ b/SkeletonStats/SkeletonStats.toc	Tue Jun 21 11:56:14 2016 -0400
@@ -12,4 +12,5 @@
 libKT-1.0\libKT-1.0.xml
 
 DamageMeter.xml
-DamageMeter.lua
\ No newline at end of file
+DamageMeter.lua
+Views\DamageDone.lua
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SkeletonStats/Views/DamageDone.lua	Tue Jun 21 11:56:14 2016 -0400
@@ -0,0 +1,83 @@
+
+local core = select(2, ...)
+local prototypes = core.prototypes
+local SOURCE_MASK = COMBATLOG_OBJECT_AFFILIATION_RAID+COMBATLOG_OBJECT_AFFILIATION_PARTY+COMBATLOG_OBJECT_AFFILIATION_MINE
+
+local dd = {
+  header = 'Damage Done',
+}
+dd.SPELL_DAMAGE = function(subEvent, ...)
+  local sourceGUID, sourceName, sourceFlags, _, destGUID, destName, destFlags, _, spellID, spellName, spellSchool, amount, overkill, school, resisted, blocked, absorbed, critical, glancing, crushing = ...
+
+  if not sourceName then
+    return false
+  end
+
+  if bit.band(sourceFlags, SOURCE_MASK) == 0 then
+    --print('discarded non-raid damage event', sourceFlags, sourceName)
+    return false
+  end
+
+  local view = core.current.view
+  view[sourceName] = view[sourceName] or {}
+  local p = view[sourceName]
+
+
+  p.guid = sourceGUID
+  p.last = amount
+  p.damage = (p.damage or 0) + amount
+  p.name = (sourceName or 'Unknown')
+
+  p.child = p.child or {}
+  p.child[spellName] = p.child[spellName] or {}
+  p.child[spellName].hit = (p.child[spellName].hit or 0) + 1
+
+  -- true = store this event's actor
+  return true
+end
+
+dd.SPELL_DAMAGE_PERIODIC = dd.SPELL_DAMAGE
+dd.RANGE_DAMAGE = dd.SPELL_DAMAGE
+
+dd.SWING_DAMAGE = function(subEvent, ...)
+  local sourceGUID, sourceName, sourceFlags, _, destGUID, destName, destFlags, _, amount, overkill, school, resisted, blocked, absorbed, critical, glancing, crushing = ...
+  local spellID, spellName, spellSchool = -1, 'Attack', 1
+  dd.SPELL_DAMAGE(subEvent, sourceGUID, sourceName, sourceFlags, _, destGUID, destName, destFlags, _, spellID, spellName, spellSchool, amount, overkill, school, resisted, blocked, absorbed, critical, glancing, crushing)
+end
+
+dd.init = function()
+  dd.maxDamage = 0
+end
+
+dd.sort =  function(a, b)
+  return a.damage > b.damage
+end
+
+dd.calculate = function(bar, data, actor)
+  if dd.maxDamage < data.damage then
+    dd.maxDamage = data.damage
+  end
+
+end
+
+dd.refresh = function(bar, data, actor)
+  local icon, textLeft, textRight
+  local r, g, b, a, percent = 1,1,1,1,1
+  if actor.class and CLASS_ICON_TCOORDS[actor.classFilename] then
+    icon = { "Interface\\TargetingFrame\\UI-Classes-Circles" , CLASS_ICON_TCOORDS[actor.classFilename] }
+  end
+  textLeft = data.name
+  textRight = data.damage .. ' ('..data.last..')'
+
+
+  if actor.class and RAID_CLASS_COLORS[actor.classFilename] then
+    r = RAID_CLASS_COLORS[actor.classFilename].r
+    g = RAID_CLASS_COLORS[actor.classFilename].g
+    b = RAID_CLASS_COLORS[actor.classFilename].b
+  end
+  percent = (data.damage / dd.maxDamage)
+
+  return icon, textLeft, textRight, r, g, b, a, percent
+end
+
+prototypes.damageDone = dd
\ No newline at end of file
--- a/SkeletonUnit/UnitFrame.lua	Tue Jun 21 08:14:22 2016 -0400
+++ b/SkeletonUnit/UnitFrame.lua	Tue Jun 21 11:56:14 2016 -0400
@@ -19,10 +19,11 @@
 local params = setmetatable({}, {
   __index = function(t, k)
     print('get', k)
-    return rawget(t.player, k)
+    return t.player
   end,
   __newindex = function(t, k, v)
     if type(v) == 'table' then
+      print('branch', k)
       v = setmetatable(v, {
         __index  = function(tt, kk)
           print('get', k, kk)
@@ -30,6 +31,7 @@
         end,
         __newindex = function(tt, kk, vv)
           if type(vv) == 'table' then
+            print('_branch', k, kk)
             vv = setmetatable(vv, {
               __index = function(_tt, _kk)
                 print('_get', k, kk, _kk)
@@ -50,12 +52,23 @@
   height = 30,
     health = { height = 24 },
     power = { height = 6},
-  }
+  position = {'CENTER', nil, 'CENTER',0,-120 }
+}
 params.pet = {
   width = 180,
-  height = 25
+  height = 25,
+
+  position = {'TOPLEFT', 'player', 'BOTTOMLEFT',0,-4}
 }
-params.focus = params.pet
+params.target = {
+
+  position = {'TOPLEFT', 'player', 'TOPRIGHT',4,0}
+}
+params.focus = {
+  width = 180,
+  height = 25,
+  position = {'BOTTOMRIGHT', 'target', 'TOPRIGHT',0,4}
+}
 
 uf.handler = uf -- so uf.event can work
 
@@ -77,7 +90,6 @@
     buttons[unit][index] = CreateFrame('Frame', 'KT'..unit..'Buff'..index, self, 'KTAuraButton')
     buttons[unit][index]:SetSize(BUFF_SIZE, BUFF_SIZE)
     buttons[unit][index].cooldown:SetHideCountdownNumbers(true)
-
   end
   return buttons[unit][index]
 end
@@ -88,34 +100,36 @@
   end
   print('|cFFFFFF00unit|r:', unit)
 
+  local c = params[unit]
+
   if not _G['KT'..unit..'Frame'] then
-    CreateFrame('Frame', 'KT'.. unit .. 'Frame', uf)
+    local new = CreateFrame('Button', 'KT'.. unit .. 'Frame', uf, 'KTUnitFrameTemplate')
+    new.unit = unit
+    new.anchorPoint = {new:GetPoint(1)}
+    new.handler = prototypes[unit]
+    new.params = c
+
+    --if not db.position[unit] then
+      db.position[unit] = c.position
+    --end
+    new.position = db.position[unit]
+
+
+    buttons[unit] = {}
+    new.buttons = buttons[unit]
+    new.refresh = prototypes[unit].refresh
+    new.init = prototypes[unit].init
+
+    new:EnableMouse(true)
+    new:SetScript('OnMouseUp', prototypes[unit].OnMouseUp)
+
+    new:SetAttribute("type", "target")
+    new:SetAttribute("unit", unit)
+    RegisterUnitWatch(new)
+    units[unit] = new
   end
 
-  local frame = _G['KT'..unit..'Frame']
-  frame.unit = unit
-  frame.anchorPoint = {frame:GetPoint(1)}
-  frame.handler = prototypes[unit]
-
-  buttons[unit] = {}
-  units[unit] = frame
-
-  local width = params.player.width
-  local height = params.player.height
-
-  if params[unit] then
-    if params[unit].width then
-      width = params[unit].width
-    end
-    if params[unit].height then
-      height = params[unit].height
-    end
-  end
-
-  frame:SetWidth(width)
-  frame:SetHeight(height)
-
-  return frame
+  return _G['KT'..unit..'Frame']
 end
 
 uf.ui = function ()
@@ -126,6 +140,7 @@
 
 uf.init = function()
   uf:RegisterEvent('PLAYER_TARGET_CHANGED')
+  uf:RegisterEvent('PLAYER_FOCUS_CHANGED')
   uf:RegisterUnitEvent("UNIT_AURA")
 end
 
@@ -156,7 +171,12 @@
 end
 
 uf.PLAYER_TARGET_CHANGED = function()
-  prototypes.player.refresh(units.target)
+  print('caught target change')
+  units.target:refresh()
+end
+
+uf.PLAYER_FOCUS_CHANGED = function()
+  units.focus:refresh()
 end
 
 prototypes.player = {}
@@ -168,16 +188,30 @@
   self:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
   self:RegisterEvent("UNIT_TARGET")
   self:SetScript('OnEvent', uf.event)
+
+  local anchor, parent, relative, x, y = unpack(self.position)
+  if parent and units[parent] then
+    parent = units[parent]
+  else
+    parent = UIParent
+  end
+
+  self:SetPoint(anchor, parent, relative, x, y)
+  self:SetSize(self.params.width, self.params.height)
+
 end
 
 
 --- Runs when event handler decides so
 player.refresh = function(self)
+  print(self.unit, UnitExists(self.unit))
   if not UnitExists(self.unit) then
+    print('hiding unit')
     self:Hide()
     return
   end
 
+  self:Show()
   if UnitIsPlayer(self.unit) then
     local class, classFile = UnitClass(self.unit)
     if classFile then
@@ -198,12 +232,12 @@
   else
     self.healthbar:SetHeight(params[self.unit].health.height + params[self.unit].power.height)
   end
-
-
+  uf.UNIT_AURA(self, 'UNIT_AURA', self.unit)
 
   self.handler.UNIT_HEALTH_FREQUENT(self)
 end
 
+
 uf.UNIT_AURA = function(self, event, unit)
   if not units[unit] then return true end
 
@@ -240,7 +274,7 @@
 
 player.UNIT_HEALTH_FREQUENT = function(self, ...)
   if UnitHealthMax(self.unit) > 0 then
-    self.healthbar:SetWidth(PLAYER_WIDTH * UnitHealth(self.unit) / UnitHealthMax(self.unit))
+    self.healthbar:SetWidth(self.params.width * UnitHealth(self.unit) / UnitHealthMax(self.unit))
     self.healthtext:SetText(UnitHealth(self.unit))
   else
     self.healthbar:SetWidth(PLAYER_WIDTH)
@@ -251,7 +285,7 @@
 
 player.UNIT_POWER_FREQUENT = function(self)
   if UnitPowerMax(self.unit) > 0 then
-    self.powerbar:SetWidth(PLAYER_WIDTH * UnitPower(self.unit) / UnitPowerMax(self.unit))
+    self.powerbar:SetWidth(self.params.width * UnitPower(self.unit) / UnitPowerMax(self.unit))
     self.powertext:SetText(UnitPower(self.unit))
   else
     self.powerbar:Hide()
@@ -271,4 +305,5 @@
 end
 
 prototypes.pet = player
-prototypes.target = player
\ No newline at end of file
+prototypes.target = player
+prototypes.focus = player
\ No newline at end of file
--- a/SkeletonUnit/UnitFrame.xml	Tue Jun 21 08:14:22 2016 -0400
+++ b/SkeletonUnit/UnitFrame.xml	Tue Jun 21 11:56:14 2016 -0400
@@ -4,7 +4,7 @@
   <Texture name="KTStatusBar" virtual="true" />
   <Frame name="SkeletonUnits" parent="UIParent"></Frame>
 
-  <Frame name="KTUnitFrame" parent="UIParent" virtual="true">
+  <Button name="KTUnitFrameTemplate" inherits="SecureUnitButtonTemplate" parent="UIParent" virtual="true">
     <Size x="250" y="50" />
     <Layers>
       <Layer level="BACKGROUND">
@@ -47,28 +47,7 @@
         </FontString>
       </Layer>
     </Layers>
-  </Frame>
+  </Button>
 
-  <Frame name="KTplayerFrame" inherits="KTUnitFrame">
-
-    <Anchors>
-      <Anchor point="BOTTOM" x="0"  y="260" />
-    </Anchors>
-  </Frame>
-  <Frame name="KTpetFrame" inherits="KTUnitFrame">
-
-    <Anchors>
-      <Anchor point="TOPRIGHT" relativePoint="TOPLEFT" relativeTo="KTplayerFrame" x="-2" />
-    </Anchors>
-  </Frame>
-  <Frame name="KTtargetFrame" inherits="KTUnitFrame">
-
-
-    <Anchors>
-      <Anchor point="TOPLEFT" relativePoint="TOPRIGHT" x="4"  y="0" relativeTo="KTplayerFrame" />
-    </Anchors>
-  </Frame>
-
-  <Frame name="KTfocusFrame" inherits="KTUnitFrame" />
 
 </Ui>
\ No newline at end of file