changeset 157:e77f716af1b7

Pushed state rule logic from State.lua to Bar.lua
author Flick <flickerstreak@gmail.com>
date Fri, 12 Jun 2009 21:44:44 +0000
parents 611e6ce08717
children d2f289c3bae6
files classes/Bar.lua modules/State.lua
diffstat 2 files changed, 204 insertions(+), 170 deletions(-) [+]
line wrap: on
line diff
--- a/classes/Bar.lua	Mon May 18 23:08:34 2009 +0000
+++ b/classes/Bar.lua	Fri Jun 12 21:44:44 2009 +0000
@@ -127,6 +127,60 @@
   end
 ]] .. _reaction_refresh
 
+-- For reference
+-- the option field names must match the field names of the options table, below
+local stateProperties = { 
+  hide = true,
+  --keybindState = true, TODO: broken
+  anchorEnable = true,
+  anchorFrame = true,
+  anchorPoint = true,
+  anchorRelPoint = true,
+  anchorX = true,
+  anchorY = true,
+  enableScale = true,
+  scale = true,
+  enableAlpha = true,
+  alpha = true,
+}
+
+
+---- Utility functions ----
+
+-- traverse a table tree by key list and fetch the result or first nil
+local function tfetch(t, ...)
+  for i = 1, select('#', ...) do
+    t = t and t[select(i, ...)]
+  end
+  return t
+end
+
+-- traverse a table tree by key list and build tree as necessary
+local function tbuild(t, ...)
+  for i = 1, select('#', ...) do
+    local key = select(i, ...)
+    if not t[key] then t[key] = { } end
+    t = t[key]
+  end
+  return t
+end
+
+-- return a new array of keys of table 't', sorted by comparing 
+-- sub-fields (obtained via tfetch) of the table values
+local function fieldsort( t, ... )
+  local r = { }
+  for k in pairs(t) do
+    table.insert(r,k)
+  end
+  local path = { ... }
+  table.sort(r, function(lhs, rhs)
+     local olhs = tfetch(t[lhs], unpack(path)) or 0
+     local orhs = tfetch(t[rhs], unpack(path)) or 0
+     return olhs < orhs
+    end)
+  return r
+end
+
 
 ---- Bar class ----
 local Bar   = { }
@@ -564,13 +618,29 @@
   end
 end
 
-function Bar:SetStateDriver( rule )
+function Bar:SetStateDriver( states )
+  if states then
+    for state, props in pairs(states) do
+      self:SetSecureStateData(state, "active_", true) -- make sure there's a 'settings' field for this state
+      for propname, value in pairs(props) do
+        if propname == "anchorFrame" then
+          self:SetFrameRef("anchor-"..state, _G[value])
+        elseif propname == "rule" then
+          -- do nothing
+        else
+          self:SetSecureStateData(state, propname, value)
+        end
+      end
+    end
+  end
+  local rule = states and self:BuildStateRule(states)
   if rule then
     RegisterStateDriver(self:GetFrame(),"reaction",rule)
   elseif self.statedriver then
     UnregisterStateDriver(self:GetFrame(),"reaction")
   end
   self.statedriver = rule
+  self:BuildStateKeybinds(states)
   self:RefreshSecureState()
 end
 
@@ -589,7 +659,6 @@
   self:RefreshSecureState()
 end
 
--- set a keybind to push a value into "state-reaction" attribute
 function Bar:SetStateKeybind( key, state )
   local f = self:GetFrame()
   local binds = self.statebinds
@@ -604,7 +673,7 @@
   end
 
   if key then
-    SetOverrideBinding(f, false, key, state, nil) -- state name is virtual mouse button
+    SetOverrideBindingClick(f, false, key, f:GetName(), state) -- state name is virtual mouse button
   end
   binds[state] = key
 end
@@ -638,3 +707,129 @@
     defaultAlpha = self:GetAttribute("defaultAlpha")
   ]])
 end
+
+---- secure state driver rules ----
+
+local playerClass = select(2, UnitClass("player"))
+local function ClassFilter(...)
+  for i = 1, select('#',...) do
+    if playerClass == select(i,...) then
+      return false
+    end
+  end
+  return true
+end
+
+local ruleformats = { 
+  stealth       = { format = "stealth",        filter = ClassFilter("ROGUE","DRUID") },
+  nostealth     = { format = "nostealth",      filter = ClassFilter("ROGUE","DRUID") },
+  shadowdance   = { format = "bonusbar:2",     filter = ClassFilter("ROGUE") },
+  shadowform    = { format = "form:1",         filter = ClassFilter("PRIEST") },
+  noshadowform  = { format = "noform",         filter = ClassFilter("PRIEST") },
+  battle        = { format = "stance:1",       filter = ClassFilter("WARRIOR") },
+  defensive     = { format = "stance:2",       filter = ClassFilter("WARRIOR") },
+  berserker     = { format = "stance:3",       filter = ClassFilter("WARRIOR") },
+  caster        = { format = "form:0/2/4/5/6", filter = ClassFilter("DRUID") },
+  bear          = { format = "form:1",         filter = ClassFilter("DRUID") },
+  cat           = { format = "form:3",         filter = ClassFilter("DRUID") },
+  tree          = { format = "form:5",         filter = ClassFilter("DRUID") },
+  moonkin       = { format = "form:5",         filter = ClassFilter("DRUID") },
+  pet           = { format = "pet" },
+  nopet         = { format = "nopet" },
+  harm          = { format = "target=target,harm" },
+  help          = { format = "target=target,help" },
+  notarget      = { format = "target=target,noexists" },
+  focusharm     = { format = "target=focus,harm" },
+  focushelp     = { format = "target=focus,help" },
+  nofocus       = { format = "target=focus,noexists" },
+  raid          = { format = "group:raid" },
+  party         = { format = "group:party" },
+  solo          = { format = "nogroup" },
+  combat        = { format = "combat" },
+  nocombat      = { format = "nocombat" },
+  possess       = { format = "target=vehicle,noexists,bonusbar:5" },
+  vehicle       = { format = "target=vehicle,exists,bonusbar:5" },
+}
+
+function Bar.InitRuleFormats()
+  local forms = { }
+  for i = 1, GetNumShapeshiftForms() do
+    local _, name = GetShapeshiftFormInfo(i)
+    forms[name] = i;
+  end
+    -- use 9 if not found since 9 is never a valid stance/form
+  local defensive = forms[GetSpellInfo(71)] or 9
+  local berserker = forms[GetSpellInfo(2458)] or 9
+  local bear      = forms[GetSpellInfo(9634)] or forms[GetSpellInfo(5487)] or 9
+  local aquatic   = forms[GetSpellInfo(1066)] or 9
+  local cat       = forms[GetSpellInfo(768)] or 9
+  local travel    = forms[GetSpellInfo(783)] or 9
+  local tree      = forms[GetSpellInfo(33891)] or 9
+  local moonkin   = forms[GetSpellInfo(24858)] or 9
+  local flight    = forms[GetSpellInfo(40120)] or forms[GetSpellInfo(33943)] or 9
+
+  ruleformats.defensive.format = "stance:"..defensive
+  ruleformats.berserker.format = "stance:"..berserker
+  ruleformats.caster.format    = format("form:0/%d/%d/%d", aquatic, travel, flight)
+  ruleformats.bear.format      = "form:"..bear
+  ruleformats.cat.format       = "form:"..cat
+  ruleformats.tree.format      = "form:"..tree
+  ruleformats.moonkin.format   = "form:"..moonkin
+end
+
+function Bar:BuildStateRule(states)
+  -- states is a table :
+  --   states[statename].rule = {
+  --     order = #,
+  --     type = "default"/"custom"/"any"/"all",
+  --     values = { ... }, -- keys of ruleformats[]
+  --     custom = "...",
+  --   }
+  local rules = { }
+  local default
+
+  for idx, state in ipairs(fieldsort(states, "rule", "order")) do
+    local c = states[state].rule
+    local type = c.type
+    if type == "default" then
+      default = default or state
+    elseif type == "custom" then
+      if c.custom then
+        -- strip out all spaces from the custom rule
+        table.insert(rules, format("%s %s", c.custom:gsub("%s",""), state))
+      end
+    elseif type == "any" or type == "all" then
+      if c.values then
+        local clauses = { }
+        for key, value in pairs(c.values) do
+          if ruleformats[key] and not ruleformats[key].filter then
+            table.insert(clauses, ruleformats[key].format)
+          end
+        end
+        if #clauses > 0 then
+          local sep = (type == "any") and "][" or ","
+          table.insert(rules, format("[%s] %s", table.concat(clauses,sep), state))
+        end
+      end
+    end
+  end
+  -- make sure that the default, if any, is last
+  if default then
+    table.insert(rules, default)
+  end
+  return table.concat(rules,";")
+end
+
+function Bar:BuildStateKeybinds( states )
+  if states then
+    for name, state in pairs(states) do
+      local rule = tfetch(state, "rule")
+      if rule and rule.type == "keybind" then
+        self:SetStateKeybind(rule.keybind, name)
+      else
+        self:SetStateKeybind(nil, name) -- this clears an existing keybind
+      end
+    end
+  end
+end
+
--- a/modules/State.lua	Mon May 18 23:08:34 2009 +0000
+++ b/modules/State.lua	Fri Jun 12 21:44:44 2009 +0000
@@ -54,148 +54,10 @@
 end
 
 
-local InitRules, ApplyStates, CleanupStates, SetProperty, GetProperty, RegisterProperty
+local ApplyStates, CleanupStates, SetProperty, GetProperty, RegisterProperty
 
 -- PRIVATE --
 do
-
-  -- the field names must match the field names of the options table, below
-  -- the field values are secure snippets or 'true' to skip the snippet for that property.
-  local properties = { 
-    hide = true,
-    --keybindState = true, TODO: broken
-    anchorEnable = true,
-    anchorFrame = true,
-    anchorPoint = true,
-    anchorRelPoint = true,
-    anchorX = true,
-    anchorY = true,
-    enableScale = true,
-    scale = true,
-    enableAlpha = true,
-    alpha = true,
-  }
-
-  local playerClass = select(2, UnitClass("player"))
-  local function ClassFilter(...)
-    for i = 1, select('#',...) do
-      if playerClass == select(i,...) then
-        return false
-      end
-    end
-    return true
-  end
-
-  -- As far as I can tell the macro clauses are NOT locale-specific.
-  -- 'filter' specifies whether rules should be omitted from execution.
-  -- 'true' indicates they should be filtered out.
-  local ruleformats = { 
-    stealth       = { format = "stealth",        filter = ClassFilter("ROGUE","DRUID") },
-    nostealth     = { format = "nostealth",      filter = ClassFilter("ROGUE","DRUID") },
-    shadowform    = { format = "form:1",         filter = ClassFilter("PRIEST") },
-    noshadowform  = { format = "noform",         filter = ClassFilter("PRIEST") },
-    battle        = { format = "stance:1",       filter = ClassFilter("WARRIOR") },
-    defensive     = { format = "stance:2",       filter = ClassFilter("WARRIOR") },
-    berserker     = { format = "stance:3",       filter = ClassFilter("WARRIOR") },
-    caster        = { format = "form:0/2/4/5/6", filter = ClassFilter("DRUID") },
-    bear          = { format = "form:1",         filter = ClassFilter("DRUID") },
-    cat           = { format = "form:3",         filter = ClassFilter("DRUID") },
-    tree          = { format = "form:5",         filter = ClassFilter("DRUID") },
-    moonkin       = { format = "form:5",         filter = ClassFilter("DRUID") },
-    pet           = { format = "pet" },
-    nopet         = { format = "nopet" },
-    harm          = { format = "target=target,harm" },
-    help          = { format = "target=target,help" },
-    notarget      = { format = "target=target,noexists" },
-    focusharm     = { format = "target=focus,harm" },
-    focushelp     = { format = "target=focus,help" },
-    nofocus       = { format = "target=focus,noexists" },
-    raid          = { format = "group:raid" },
-    party         = { format = "group:party" },
-    solo          = { format = "nogroup" },
-    combat        = { format = "combat" },
-    nocombat      = { format = "nocombat" },
-    possess       = { format = "bonusbar:5" },
-    vehicle       = { format = "target=vehicle,exists,bonusbar:5" },
-  }
-
-  -- Determine the stance #'s programmatically: they can vary if for some reason the
-  -- player is missing a stance/form (due to not training it). Also moonkin/flight/tree form
-  -- can be stance 5 or 6, depending.
-  function InitRules()
-    local forms = { }
-    for i = 1, GetNumShapeshiftForms() do
-      local _, name = GetShapeshiftFormInfo(i)
-      forms[name] = i;
-    end
-      -- use 9 if not found since 9 is never a valid stance/form
-    local defensive = forms[GetSpellInfo(71)] or 9
-    local berserker = forms[GetSpellInfo(2458)] or 9
-    local bear      = forms[GetSpellInfo(9634)] or forms[GetSpellInfo(5487)] or 9
-    local aquatic   = forms[GetSpellInfo(1066)] or 9
-    local cat       = forms[GetSpellInfo(768)] or 9
-    local travel    = forms[GetSpellInfo(783)] or 9
-    local tree      = forms[GetSpellInfo(33891)] or 9
-    local moonkin   = forms[GetSpellInfo(24858)] or 9
-    local flight    = forms[GetSpellInfo(40120)] or forms[GetSpellInfo(33943)] or 9
-
-    ruleformats.defensive.format = format("stance:%d",defensive)
-    ruleformats.berserker.format = format("stance:%d",berserker)
-    ruleformats.caster.format    = format("form:0/%d/%d/%d", aquatic, travel, flight)
-    ruleformats.bear.format      = format("form:%d",bear)
-    ruleformats.cat.format       = format("form:%d",cat)
-    ruleformats.tree.format      = format("form:%d",tree)
-    ruleformats.moonkin.format   = format("form:%d",moonkin)
-  end
-
-  local function BuildRule(states)
-    local rules = { }
-    local default
-
-    for idx, state in ipairs(fieldsort(states, "rule", "order")) do
-      local c = states[state].rule
-      local type = c.type
-      if type == "default" then
-        default = default or state
-      elseif type == "custom" then
-        if c.custom then
-          -- strip out all spaces from the custom rule
-          table.insert(rules, format("%s %s", c.custom:gsub("%s",""), state))
-        end
-      elseif type == "any" or type == "all" then
-        if c.values then
-          local clauses = { }
-          for key, value in pairs(c.values) do
-            if ruleformats[key] and not ruleformats[key].filter then
-              table.insert(clauses, ruleformats[key].format)
-            end
-          end
-          if #clauses > 0 then
-            local sep = (type == "any") and "][" or ","
-            table.insert(rules, format("[%s] %s", table.concat(clauses,sep), state))
-          end
-        end
-      end
-    end
-    -- make sure that the default, if any, is last
-    if default then
-      table.insert(rules, default)
-    end
-    return table.concat(rules,";")
-  end
-
-  local function BuildKeybinds( bar, states )
-    for name, state in pairs(states) do
-      local type = tfetch(state, "rule", "type")
-      if type == "keybind" then
-        local key = tfetch(state, "rule", "keybind")
-        bar:SetStateKeybind(key, name)
-      else
-        bar:SetStateKeybind(nil, name) -- this clears an existing keybind
-      end
-    end
-  end
-
   function GetProperty( bar, state, propname )
     return tfetch(module.db.profile.bars, bar:GetName(), "states", state, propname)
   end
@@ -207,49 +69,25 @@
   end
 
   function RegisterProperty( propname, snippet )
-    properties[propname] = true
     for _, bar in ReAction:IterateBars() do
-      local states = tfetch(module.db.profile.bars, bar:GetName(), "states")
-      if states then
-        for name, s in pairs(states) do
-          bar:SetSecureStateData(name, propname, s[propname])
-        end
-        bar:SetStateDriver(BuildRule(states))
-      end
       if type(snippet) == "string" then
         bar:SetSecureStateExtension(propname,snippet)
       end
+      ApplyStates(bar)
     end
   end
 
   function UnregisterProperty( propname )
-    properties[propname] = nil
     for _, bar in ReAction:IterateBars() do
-      local states = tfetch(module.db.profile.bars, bar:GetName(), "states")
-      if states then
-        for name, s in pairs(states) do
-          bar:SetSecureStateData(name, propname, nil)
-        end
-      end
-      bar:SetStateDriver(BuildRule(states))
       bar:SetSecureStateExtension(propname,nil)
+      ApplyStates(bar)
     end
   end
 
   function ApplyStates( bar )
     local states = tfetch(module.db.profile.bars, bar:GetName(), "states")
     if states then
-      for propname in pairs(properties) do
-        for name, s in pairs(states) do
-          if propname == "anchorFrame" then
-            bar:SetFrameRef("anchor-"..name, _G[s.anchorFrame])
-          else
-            bar:SetSecureStateData(name, propname, s[propname])
-          end
-        end
-      end
-      BuildKeybinds(bar, states)
-      bar:SetStateDriver(BuildRule(states))
+      bar:SetStateDriver(states)
     end
   end
 
@@ -290,7 +128,7 @@
   -- Re-parse the rules table according to the new form list.
   -- This happens both at initial login (after PLAYER_ENTERING_WORLD)
   -- as well as when gaining new abilities. 
-  InitRules()
+  ReAction.Bar.InitRuleFormats()
   for _, bar in ReAction:IterateBars() do
     ApplyStates(bar)
   end
@@ -855,7 +693,7 @@
   function StateHandler:SetAnchorFrame(info, value)
     local f = _G[self._anchorframes[value]]
     if f then
-      bar:SetFrameRef("anchor-"..self:GetName(), f)
+      self.bar:SetFrameRef("anchor-"..self:GetName(), f)
       self:SetProp(info, f:GetName())
     end
   end