view modules/ReAction_PetAction/ReAction_PetAction.lua @ 53:7e09c02ae620

Pet Action support
author Flick <flickerstreak@gmail.com>
date Fri, 25 Apr 2008 20:35:55 +0000
parents 21bcaf8215ff
children 88283658fec4
line wrap: on
line source
--[[
  ReAction Pet Action button module

  The button module implements standard action button functionality by wrapping Blizzard's 
  PetActionButton frame and associated functions. It also provides some button layout
  modification tools.

--]]

-- local imports
local ReAction = ReAction
local L = ReAction.L
local _G = _G
local CreateFrame = CreateFrame

-- module declaration
local moduleID = "PetAction"
local module = ReAction:NewModule( moduleID )

-- module methods
function module:OnInitialize()
  self.db = ReAction.db:RegisterNamespace( moduleID,
    { 
      profile = {
        buttons = { }
      }
    }
  )
  self.buttons = { }

  ReAction:RegisterOptions("global", self, {
  })
end

function module:OnEnable()
  ReAction:RegisterBarType(L["Pet Action Bar"], 
    { 
      type = moduleID ,
      defaultButtonSize = 30,
      defaultBarRows = 1,
      defaultBarCols = 10,
      defaultBarSpacing = 8
    })
end

function module:OnDisable()
  ReAction:UnregisterBarType(L["Pet Action Bar"])
end

function module:ApplyToBar(bar)
  if bar.config.type == moduleID then
    -- auto show/hide when pet exists
    bar:GetFrame():SetAttribute("unit","pet")
    RegisterUnitWatch(bar:GetFrame())
    self:RefreshBar(bar)
  end
end

function module:RefreshBar(bar)
  if bar.config.type == moduleID then
    if self.buttons[bar] == nil then
      self.buttons[bar] = { }
    end
    local btns = self.buttons[bar]
    local profile = self.db.profile
    local barName = bar:GetName()
    if profile.buttons[barName] == nil then
      profile.buttons[barName] = {}
    end
    local btnCfg = profile.buttons[barName]

    local r, c = bar:GetButtonGrid()
    local n = r*c
    for i = 1, n do
      if btnCfg[i] == nil then
        btnCfg[i] = {}
      end
      if btns[i] == nil then
        local ok, b = pcall(self.BtnClass.new, self.BtnClass, bar, i, btnCfg[i])
        if ok and b then
          btns[i] = b
        end
      else
        btns[i]:Refresh(bar,i)
      end
    end
    for i = n+1, #btns do
      if btns[i] then
        btns[i] = btns[i]:Destroy()
        if btnCfg[i] then
          btnCfg[i] = nil
        end
      end
    end
  end
end

function module:RemoveFromBar(bar)
  if self.buttons[bar] then
    local btns = self.buttons[bar]
    for _,b in pairs(btns) do
      if b then
        b:Destroy()
      end
    end
    self.buttons[bar] = nil
  end
end

function module:EraseBarConfig(barName)
  self.db.profile.buttons[barName] = nil
end

function module:RenameBarConfig(oldname, newname)
  local b = self.db.profile.buttons
  b[newname], b[oldname] = b[oldname], nil
end


function module:ApplyConfigMode(mode,bars)
  for _, bar in pairs(bars) do
    if bar and self.buttons[bar] then
      for _, b in pairs(self.buttons[bar]) do
        if b then
          if mode then
            self:showActionIDLabel(b)
          else
            ReAction:Print("Hiding action id "..b:GetActionID())
            self:hideActionIDLabel(b)
          end
        end
      end
      local f = bar:GetFrame()
      if mode then
        UnregisterUnitWatch(f)
        f:Show()
      else
        RegisterUnitWatch(f)
      end
    end
  end
end

function module:showActionIDLabel(button)
  -- store the action ID label in the frame due to frame recycling
  if not button:GetFrame().actionIDLabel and button:GetActionID() then
    local label = button:GetFrame():CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
    label:SetAllPoints()
    label:SetJustifyH("CENTER")
    label:SetShadowColor(0,0,0,1)
    label:SetShadowOffset(2,-2)
    label:SetText(tostring(button:GetActionID()))
    button:GetFrame().actionIDLabel = label
  end
  button:GetFrame().actionIDLabel:Show()
end

function module:hideActionIDLabel(button)
  if button:GetFrame().actionIDLabel then
    button:GetFrame().actionIDLabel:Hide()
  else
    ReAction:Print("actionIDLabel not found")
  end
end



-- use-count of action IDs
local nActionIDs = NUM_PET_ACTION_SLOTS
local ActionIDList = setmetatable( {}, {
  __index = function(self, idx)
    if idx == nil then
      for i = 1, nActionIDs do
        if rawget(self,i) == nil then
          rawset(self,i,1)
          return i
        end
      end
      error("ran out of pet action IDs")
    else
      local c = rawget(self,idx) or 0
      rawset(self,idx,c+1)
      return idx
    end
  end,
  __newindex = function(self,idx,value)
    if value == nil then
      value = rawget(self,idx)
      if value == 1 then
        value = nil
      elseif value then
        value = value - 1
      end
    end
    rawset(self,idx,value)
  end
})

local frameRecycler = {}


------ Button class ------
local Button = { }

local function Constructor( self, bar, idx, config )
  self.bar, self.idx, self.config = bar, idx, config

  local barFrame = bar:GetFrame()

  local name = config.name or "ReAction_"..bar:GetName().."_Pet_"..idx
  config.name = name
  self.name = config.name
  config.actionID = ActionIDList[config.actionID] -- gets a free one if none configured
  
  -- have to recycle frames with the same name:
  -- otherwise you either get references to old textures because named CreateFrame()
  -- doesn't overwrite existing globals (below)
  -- or, if you set them to nil in the global table, you get taint because of the
  -- crappy PetActionBar code.
  local f = frameRecycler[name]
  if f then
    f:SetParent(barFrame)
    f:Show()
  else
    f = CreateFrame("CheckButton", name, barFrame, "PetActionButtonTemplate")
  end
  if config.actionID then
    f:SetID(config.actionID) -- PetActionButtonTemplate isn't a proper SecureActionButton
  end
  f:SetFrameStrata("MEDIUM")

  barFrame:SetAttribute("addchild",f)

  self.frame = f
  self.icon = _G[("%sIcon"):format(name)]
  self.acTex = _G[("%sAutoCastable"):format(name)]
  self.acModel = _G[("%sAutoCast"):format(name)]
  self.cooldown = _G[("%sCooldown"):format(name)]
  self.hotkey = _G[("%sHotKey"):format(name)]

  f:HookScript("OnDragStart", function() self:Update() end)
  f:HookScript("OnReceiveDrag", function() self:Update() end)

	f:RegisterEvent("PLAYER_CONTROL_LOST");
	f:RegisterEvent("PLAYER_CONTROL_GAINED");
	f:RegisterEvent("PLAYER_FARSIGHT_FOCUS_CHANGED");
	f:RegisterEvent("UNIT_PET");
	f:RegisterEvent("UNIT_FLAGS");
	f:RegisterEvent("UNIT_AURA");
	f:RegisterEvent("PET_BAR_UPDATE");
	f:RegisterEvent("PET_BAR_UPDATE_COOLDOWN");

  f:SetScript("OnEvent",
    function(event,arg1)
      if event =="PET_BAR_UPDATE_COOLDOWN" then
        self:UpdateCooldown()
      elseif event == "UPDATE_BINDINGS" then
        self:UpdateHotkey()
      else
        self:Update()
      end
    end)

  self:Refresh(bar,idx)
end

function Button:Destroy()
  local f = self.frame
  f:UnregisterAllEvents()
  f:Hide()
  f:SetParent(UIParent)
  f:ClearAllPoints()
  if self.name then
    frameRecycler[self.name] = f
    _G[self.name] = nil
  end
  if self.config.actionID then
    ActionIDList[self.config.actionID] = nil
  end
  self.frame = nil
  self.config = nil
  self.bar = nil
end

function Button:Refresh(bar,idx)
  bar:PlaceButton(self.frame, idx, 30, 30)
  self:Update()
  self:UpdateHotkey()
end

function Button:GetFrame()
  return self.frame
end

function Button:GetName()
  return self.name
end

function Button:GetActionID()
  return self.config.actionID
end

function Button:Update()
  local id = self.frame:GetID()
  local name, subtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled = GetPetActionInfo(id);
  local f = self.frame
  --ReAction:Print(("id %d: '%s', '%s', '%s', '%s', '%s', '%s', '%s'"):format(tostring(id), tostring(name),tostring(subtext),tostring(texture),tostring(isToken),tostring(isActive),tostring(autoCastAllowed),tostring(autoCastEnabled)))

  if isToken then
    self.icon:SetTexture(_G[texture]);
    f.tooltipName = _G[name];
  else
    self.icon:SetTexture(texture);
    f.tooltipName = name;
  end

  f.isToken = isToken;
	f.tooltipSubtext = subtext;
  f:SetChecked( isActive and 1 or 0);

  if autoCastAllowed then
    self.acTex:Show();
  else
    self.acTex:Hide();
  end

  if autoCastEnabled then
    self.acModel:Show();
  else
    self.acModel:Hide();
  end

  if texture then
    if GetPetActionsUsable() then
      SetDesaturation(self.icon,nil)
    else
      SetDesaturation(self.icon,1)
    end
    self.icon:Show();
    f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2");
  else
    self.icon:Hide();
    f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot");
  end

  self:UpdateCooldown()
end

function Button:UpdateCooldown()
	local start, duration, enable = GetPetActionCooldown(self.frame:GetID());
  CooldownFrame_SetTimer(self.cooldown, start, duration, enable);
end

function Button:UpdateHotkey()

end

-- export as a class-factory to module
module.BtnClass = {
  new = function(self, ...)
    local x = { }
    for k,v in pairs(Button) do
      x[k] = v
    end
    Constructor(x, ...)
    return x
  end
}