comparison State.lua @ 75:06cd74bdc7da

- Cleaned up Bar interface - Move all attribute setting from Bar into State - Separated Moonkin and Tree of Life - Removed PossessBar module - Added some infrastructure for paged/mind control support to Action
author Flick <flickerstreak@gmail.com>
date Mon, 16 Jun 2008 18:46:08 +0000
parents aa88aed52124
children da8ba8783924
comparison
equal deleted inserted replaced
74:00e28094e1a3 75:06cd74bdc7da
6 -- local imports 6 -- local imports
7 local ReAction = ReAction 7 local ReAction = ReAction
8 local L = ReAction.L 8 local L = ReAction.L
9 local _G = _G 9 local _G = _G
10 local InCombatLockdown = InCombatLockdown 10 local InCombatLockdown = InCombatLockdown
11 local format = string.format
11 12
12 -- module declaration 13 -- module declaration
13 local moduleID = "State" 14 local moduleID = "State"
14 local module = ReAction:NewModule( moduleID, "AceEvent-3.0" ) 15 local module = ReAction:NewModule( moduleID, "AceEvent-3.0" )
15 16
31 t = t[key] 32 t = t[key]
32 end 33 end
33 return t 34 return t
34 end 35 end
35 36
37 -- return a new array of keys of table 't', sorted by comparing
38 -- sub-fields (obtained via tfetch) of the table values
39 local function fieldsort( t, ... )
40 local r = { }
41 for k in pairs(t) do
42 table.insert(r,k)
43 end
44 local path = { ... }
45 table.sort(r, function(lhs, rhs)
46 local olhs = tfetch(t[lhs], unpack(path)) or 0
47 local orhs = tfetch(t[rhs], unpack(path)) or 0
48 return olhs < orhs
49 end)
50 return r
51 end
52
53
54 local InitRules, ApplyStates, SetProperty, GetProperty
55
36 -- PRIVATE -- 56 -- PRIVATE --
37
38 local InitRules, ApplyStates, SetProperty, GetProperty
39 do 57 do
40 -- As far as I can tell the macro clauses are NOT locale-specific. 58 -- As far as I can tell the macro clauses are NOT locale-specific.
41 local ruleformats = { 59 local ruleformats = {
42 stealth = "stealth", 60 stealth = "stealth",
43 nostealth = "nostealth", 61 nostealth = "nostealth",
54 raid = "group:raid", 72 raid = "group:raid",
55 party = "group:party", 73 party = "group:party",
56 solo = "nogroup", 74 solo = "nogroup",
57 combat = "combat", 75 combat = "combat",
58 nocombat = "nocombat", 76 nocombat = "nocombat",
77 possess = "bonusbar:5",
59 } 78 }
60 79
61 -- Have to do these shenanigans instead of hardcoding the stances/forms because 80 -- Have to do these shenanigans instead of hardcoding the stances/forms because
62 -- the ordering varies if the character is missing a form. For warriors 81 -- the ordering varies if the character is missing a form. For warriors
63 -- this is rarely a problem (c'mon, who actually skips the level 10 def stance quest?) 82 -- this is rarely a problem (c'mon, who actually skips the level 10 def stance quest?)
64 -- but for druids it can be. Some people never bother to do the aquatic form quest 83 -- but for druids it can be. Some people never bother to do the aquatic form quest
65 -- until well past when they get cat form, and stance 5 can be flight, tree, or moonkin 84 -- until well past when they get cat form, and stance 5/6 can be flight, tree, or moonkin
66 -- depending on talents. 85 -- depending on talents.
67 function InitRules() 86 function InitRules()
68 local forms = { } 87 local forms = { }
69 -- sort by icon since it's locale-independent 88 -- sort by icon since it's locale-independent
70 for i = 1, GetNumShapeshiftForms() do 89 for i = 1, GetNumShapeshiftForms() do
76 local berserker = forms["Interface\\Icons\\Ability_Racial_Avatar"] or 9 95 local berserker = forms["Interface\\Icons\\Ability_Racial_Avatar"] or 9
77 local bear = forms["Interface\\Icons\\Ability_Racial_BearForm"] or 9 -- bear and dire bear share the same icon 96 local bear = forms["Interface\\Icons\\Ability_Racial_BearForm"] or 9 -- bear and dire bear share the same icon
78 local aquatic = forms["Interface\\Icons\\Ability_Druid_AquaticForm"] or 9 97 local aquatic = forms["Interface\\Icons\\Ability_Druid_AquaticForm"] or 9
79 local cat = forms["Interface\\Icons\\Ability_Druid_CatForm"] or 9 98 local cat = forms["Interface\\Icons\\Ability_Druid_CatForm"] or 9
80 local travel = forms["Interface\\Icons\\Ability_Druid_TravelForm"] or 9 99 local travel = forms["Interface\\Icons\\Ability_Druid_TravelForm"] or 9
81 local treekin = forms["Interface\\Icons\\Ability_Druid_TreeofLife"] or forms["Interface\\Icons\\Spell_Nature_ForceOfNature"] or 9 100 local tree = forms["Interface\\Icons\\Ability_Druid_TreeofLife"] or 9
101 local moonkin = forms["Interface\\Icons\\Spell_Nature_ForceOfNature"] or 9
82 local flight = forms["Interface\\Icons\\Ability_Druid_FlightForm"] or 9 -- flight and swift flight share the same icon 102 local flight = forms["Interface\\Icons\\Ability_Druid_FlightForm"] or 9 -- flight and swift flight share the same icon
83 103
84 ruleformats.battle = "stance:1" 104 ruleformats.battle = "stance:1"
85 ruleformats.defensive = ("stance:%d"):format(defensive) 105 ruleformats.defensive = format("stance:%d",defensive)
86 ruleformats.berserker = ("stance:%d"):format(berserker) 106 ruleformats.berserker = format("stance:%d",berserker)
87 ruleformats.caster = ("form:0/%d/%d/%d"):format(aquatic, travel, flight) 107 ruleformats.caster = format("form:0/%d/%d/%d",aquatic, travel, flight)
88 ruleformats.bear = ("form:%d"):format(bear) 108 ruleformats.bear = format("form:%d",bear)
89 ruleformats.cat = ("form:%d"):format(cat) 109 ruleformats.cat = format("form:%d",cat)
90 ruleformats.treeOrMoonkin = ("form:%d"):format(treekin) 110 ruleformats.tree = format("form:%d",tree)
91 end 111 ruleformats.moonkin = format("form:%d",moonkin)
92 112 end
93 -- return a new array of keys of table 't', sorted by comparing 113
94 -- sub-fields (obtained via tfetch) of the table values 114
95 local function fieldsort( t, ... ) 115 -- state property functions
96 local r = { } 116 local ofskeys = {
97 for k in pairs(t) do 117 anchorPoint = "point",
98 table.insert(r,k) 118 anchorRelPoint = "relpoint",
99 end 119 anchorX = "x",
100 local dotdotdot = { ... } 120 anchorY = "y"
101 table.sort(r, function(lhs, rhs) 121 }
102 local olhs = tfetch(t[lhs], unpack(dotdotdot)) or 0 122
103 local orhs = tfetch(t[rhs], unpack(dotdotdot)) or 0 123 local barofsidx = {
104 return olhs < orhs 124 anchorPoint = 1,
105 end) 125 anchorRelPoint = 3,
106 return r 126 anchorX = 4,
107 end 127 anchorY = 5
108 128 }
109 local function BuildRules(states) 129
130 local function UpdatePartialAnchor(bar, states, ckey)
131 local map = { }
132 local bc = bar.config
133 for state, c in pairs(states) do
134 if c.enableAnchor then
135 map[state] = c[ckey]
136 end
137 end
138 local ofskey = ofskeys[ckey]
139 local default = select(barofsidx[ckey], bar:GetAnchor())
140 bar:SetStateAttribute(format("headofs%s",ofskeys[ckey]), map, default)
141 end
142
143 -- the name of the function maps to the name of the config element
144 local propertyFuncs = {
145 hide = function( bar, states )
146 local hs = { }
147 for state, config in pairs(states) do
148 if config.hide then
149 table.insert(hs, state)
150 end
151 end
152 bar:SetStateAttribute("hidestates", nil, table.concat(hs,","), true) -- pass to buttons
153 end,
154
155 page = function( bar, states )
156 local map = { }
157 for state, config in pairs(states) do
158 if config.page then
159 map[state] = format("page%d",config.page)
160 end
161 end
162 bar:SetStateAttribute("statebutton", map)
163 end,
164
165 keybindstate = function( bar, states )
166 local map = { }
167 for state, config in pairs(states) do
168 local kbset = config.keybindstate and state
169 map[state] = kbset
170 for button in bar:IterateButtons() do
171 -- TODO: inform children they should maintain multiple binding sets
172 -- ?? button:UpdateBindingSet(kbset)
173 end
174 end
175 bar:SetStateAttribute("statebindings", map)
176 end,
177
178 enableAnchor = function( bar, states )
179 for ckey in pairs(ofskeys) do
180 UpdatePartialAnchor(bar, states, ckey)
181 end
182 end,
183
184 enableScale = function( bar, states )
185 local map = { }
186 for state, c in pairs(states) do
187 if c.enableScale then
188 map[state] = c.scale
189 end
190 end
191 bar:SetStateAttribute("headscale", map, 1.0)
192 end,
193 }
194
195 -- generate some table entries
196 propertyFuncs.scale = propertyFuncs.enableScale
197 for ckey in pairs(ofskeys) do
198 propertyFuncs[ckey] = function( bar, states )
199 UpdatePartialAnchor(bar, states, ckey)
200 end
201 end
202
203
204 function GetProperty( bar, state, propname )
205 return tfetch(module.db.profile.bars, bar:GetName(), "states", state, propname)
206 end
207
208 function SetProperty( bar, state, propname, value )
209 local states = tbuild(module.db.profile.bars, bar:GetName(), "states")
210 tbuild(states, state)[propname] = value
211 local f = propertyFuncs[propname]
212 if f then
213 f(bar, states)
214 end
215 end
216
217
218
219 --
220 -- Build a state-transition spec string and statemap to be passed to
221 -- Bar:SetStateDriver().
222 --
223 -- The statemap building is complex: keybound states override all
224 -- other transitions, so must remain in their current state, but must
225 -- also remember other transitions that happen while they're stuck there
226 -- so that when the binding is toggled off it can return to the proper state
227 --
228 local function BuildStateMap(states)
110 local rules = { } 229 local rules = { }
230 local statemap = { }
111 local keybinds = { } 231 local keybinds = { }
112 local default 232 local default
113 local fmt = "%s %s" 233
234 -- first grab all the keybind override states
235 -- and construct an override template
236 local override
237 do
238 local overrides = { }
239 for name, state in pairs(states) do
240 local type = tfetch(state, "rule", "type")
241 if type == "keybind" then
242 -- use the state-stack to remember the current transition
243 -- use $s as a marker for a later call to gsub()
244 table.insert(overrides, format("%s:$s set() %s", name, name))
245 end
246 end
247 if #overrides > 0 then
248 table.insert(overrides, "") -- for a trailing ';'
249 end
250 override = table.concat(overrides, ";") or ""
251 end
252
253 -- now iterate the rules in order
114 for idx, state in ipairs(fieldsort(states, "rule", "order")) do 254 for idx, state in ipairs(fieldsort(states, "rule", "order")) do
115 local c = states[state].rule 255 local c = states[state].rule
116 local type = tfetch(c,"type") 256 local type = c.type
117 if type == "default" then 257 if type == "default" then
118 default = default or state 258 default = default or state
119 elseif type == "keybind" then
120 keybinds[state] = c.keybind or false
121 elseif type == "custom" then 259 elseif type == "custom" then
122 if c.custom then 260 if c.custom then
123 -- strip out all spaces from the custom rule 261 -- strip out all spaces from the custom rule
124 table.insert(rules, fmt:format(c.custom:gsub("%s",""), state)) 262 table.insert(rules, format("%s %s", c.custom:gsub("%s",""), state))
125 end 263 end
126 elseif type == "any" then 264 elseif type == "any" then
127 if c.values then 265 if c.values then
128 local clauses = { } 266 local clauses = { }
129 for key, value in pairs(c.values) do 267 for key, value in pairs(c.values) do
130 table.insert(clauses, ("[%s]"):format(ruleformats[key])) 268 table.insert(clauses, format("[%s]", ruleformats[key]))
131 end 269 end
132 if #clauses > 0 then 270 if #clauses > 0 then
133 table.insert(rules, fmt:format(table.concat(clauses), state)) 271 table.insert(rules, format("%s %s", table.concat(clauses), state))
134 end 272 end
135 end 273 end
136 elseif type == "all" then 274 elseif type == "all" then
137 if c.values then 275 if c.values then
138 local clauses = { } 276 local clauses = { }
139 for key, value in pairs(c.values) do 277 for key, value in pairs(c.values) do
140 table.insert(clauses, ruleformats[key]) 278 table.insert(clauses, ruleformats[key])
141 end 279 end
142 if #clauses > 0 then 280 if #clauses > 0 then
143 table.insert(rules, fmt:format(("[%s]"):format(table.concat(clauses, ",")), state)) 281 table.insert(rules, format("%s %s", format("[%s]", table.concat(clauses, ",")), state))
144 end 282 end
145 end 283 end
146 end 284 end
147 end 285
286 -- use a different virtual button for the actual keybind transition,
287 -- to implement a toggle. You have to clear it regardless of the type
288 -- (which is usually a no-op) to unbind state transitions when switching
289 -- transition types.
290 local bindbutton = format("%s_binding",state)
291 if type == "keybind" then
292 keybinds[bindbutton] = c.keybind or false
293 statemap[bindbutton] = format("%s:pop();*:set(%s)", state, state)
294 else
295 keybinds[bindbutton] = false
296 end
297
298 -- construct the statemap. gsub() the state name into the override template.
299 statemap[state] = format("%s%s", override:gsub("%$s",state), state)
300 end
301 -- make sure that the default, if any, is last
148 if default then 302 if default then
149 table.insert(rules, default) 303 table.insert(rules, default)
150 end 304 end
151 return rules, keybinds 305 return table.concat(rules,";"), statemap, keybinds
152 end 306 end
153
154 local propertyFuncs = { }
155 307
156 function ApplyStates( bar ) 308 function ApplyStates( bar )
157 local states = tfetch(module.db.profile.bars, bar:GetName(), "states") 309 local states = tfetch(module.db.profile.bars, bar:GetName(), "states")
158 if states then 310 if states then
159 local rules, keybinds = BuildRules(states) 311 local rule, statemap, keybinds = BuildStateMap(states)
160 bar:SetStateDriver(table.concat(rules,";"), states, keybinds) 312 bar:SetStateDriver("reaction", rule, statemap)
313 for state, key in pairs(keybinds) do
314 bar:SetAttributeBinding(state, key, "state-reaction", state)
315 end
161 for k, f in pairs(propertyFuncs) do 316 for k, f in pairs(propertyFuncs) do
162 f(bar, states) 317 f(bar, states)
163 end 318 end
164 end 319 end
165 end 320 end
166
167 function GetProperty( bar, state, propname )
168 return tfetch(module.db.profile.bars, bar:GetName(), "states", state, propname)
169 end
170
171 function SetProperty( bar, state, propname, value )
172 local states = tbuild(module.db.profile.bars, bar:GetName(), "states")
173 tbuild(states, state)[propname] = value
174 local f = propertyFuncs[propname]
175 if f then
176 f(bar, states)
177 end
178 end
179
180 -- state property functions
181 function propertyFuncs.hide( bar, states )
182 local tmp = { }
183 for state, config in pairs(states) do
184 if config.hide then
185 table.insert(tmp, state)
186 end
187 end
188 local s = table.concat(tmp,",")
189 bar:SetHideStates(s)
190 end
191
192 function propertyFuncs.page( bar, states )
193 local map = { }
194 for state, config in pairs(states) do
195 map[state] = config.page
196 end
197 bar:SetStatePageMap(state, map)
198 end
199
200 function propertyFuncs.keybindstate( bar, states )
201 local map = { }
202 for state, config in pairs(states) do
203 if config.keybindstate then
204 table.insert(map,state)
205 end
206 end
207 bar:SetStateKeybindOverrideMap(map)
208 end
209
210 local function updateAnchor(bar, states)
211 local map = { }
212 for state, c in pairs(states) do
213 if c.enableAnchor then
214 map[state] = { point = c.anchorPoint, relpoint = c.anchorRelPoint, x = c.anchorX, y = c.anchorY }
215 end
216 end
217 bar:SetStateAnchorMap(map)
218 end
219
220 propertyFuncs.enableAnchor = updateAnchor
221 propertyFuncs.anchorPoint = updateAnchor
222 propertyFuncs.anchorRelPoint = updateAnchor
223 propertyFuncs.anchorX = updateAnchor
224 propertyFuncs.anchorY = updateAnchor
225
226 local function updateScale( bar, states )
227 local map = { }
228 for state, c in pairs(states) do
229 if c.enablescale then
230 map[state] = c.scale
231 end
232 end
233 bar:SetStateScaleMap(map)
234 end
235
236 propertyFuncs.enablescale = updateScale
237 propertyFuncs.scale = updateScale
238 321
239 end 322 end
240 323
241 324
242 325
287 function module:OnEraseBar(event, bar, name) 370 function module:OnEraseBar(event, bar, name)
288 self.db.profile.bars[name] = nil 371 self.db.profile.bars[name] = nil
289 end 372 end
290 373
291 function module:OnRenameBar(event, bar, oldname, newname) 374 function module:OnRenameBar(event, bar, oldname, newname)
292 local b = self.db.profile.bars 375 local bars = self.db.profile.bars
293 bars[newname], bars[oldname] = bars[oldname], nil 376 bars[newname], bars[oldname] = bars[oldname], nil
294 end 377 end
295 378
296 function module:OnConfigModeChanged(event, mode) 379 function module:OnConfigModeChanged(event, mode)
297 -- TODO: unregister all state drivers (temporarily) and hidestates 380 -- TODO: unregister all state drivers (temporarily) and hidestates
315 398
316 -- pre-sorted by the order they should appear in 399 -- pre-sorted by the order they should appear in
317 local rules = { 400 local rules = {
318 -- rule hidden fields 401 -- rule hidden fields
319 { "stance", ClassCheck("WARRIOR"), { {battle = L["Battle Stance"]}, {defensive = L["Defensive Stance"]}, {berserker = L["Berserker Stance"]} } }, 402 { "stance", ClassCheck("WARRIOR"), { {battle = L["Battle Stance"]}, {defensive = L["Defensive Stance"]}, {berserker = L["Berserker Stance"]} } },
320 { "form", ClassCheck("DRUID"), { {caster = L["Caster Form"]}, {bear = L["Bear Form"]}, {cat = L["Cat Form"]}, {treeOrMoonkin = L["Tree/Moonkin"]} } }, 403 { "form", ClassCheck("DRUID"), { {caster = L["Caster Form"]}, {bear = L["Bear Form"]}, {cat = L["Cat Form"]}, {tree = L["Tree of Life"]}, {moonkin = L["Moonkin Form"]} } },
321 { "stealth", ClassCheck("ROGUE","DRUID"), { {stealth = L["Stealth"]}, {nostealth = L["No Stealth"]} } }, 404 { "stealth", ClassCheck("ROGUE","DRUID"), { {stealth = L["Stealth"]}, {nostealth = L["No Stealth"]} } },
322 { "shadow", ClassCheck("PRIEST"), { {shadowform = L["Shadowform"]}, {noshadowform = L["No Shadowform"]} } }, 405 { "shadow", ClassCheck("PRIEST"), { {shadowform = L["Shadowform"]}, {noshadowform = L["No Shadowform"]} } },
323 { "pet", ClassCheck("HUNTER","WARLOCK"), { {pet = L["With Pet"]}, {nopet = L["Without Pet"]} } }, 406 { "pet", ClassCheck("HUNTER","WARLOCK"), { {pet = L["With Pet"]}, {nopet = L["Without Pet"]} } },
324 { "target", false, { {harm = L["Hostile Target"]}, {help = L["Friendly Target"]}, {notarget = L["No Target"]} } }, 407 { "target", false, { {harm = L["Hostile Target"]}, {help = L["Friendly Target"]}, {notarget = L["No Target"]} } },
325 { "focus", false, { {focusharm = L["Hostile Focus"]}, {focushelp = L["Friendly Focus"]}, {nofocus = L["No Focus"]} } }, 408 { "focus", false, { {focusharm = L["Hostile Focus"]}, {focushelp = L["Friendly Focus"]}, {nofocus = L["No Focus"]} } },
409 { "possess", false, { {possess = L["Mind Control"]} } },
326 { "group", false, { {raid = L["Raid"]}, {party = L["Party"]}, {solo = L["Solo"]} } }, 410 { "group", false, { {raid = L["Raid"]}, {party = L["Party"]}, {solo = L["Solo"]} } },
327 { "combat", false, { {combat = L["In Combat"]}, {nocombat = L["Out of Combat"]} } }, 411 { "combat", false, { {combat = L["In Combat"]}, {nocombat = L["Out of Combat"]} } },
328 } 412 }
329 413
330 local ruleSelect = { } 414 local ruleSelect = { }
480 type = "input", 564 type = "input",
481 get = function() return opts.name end, 565 get = function() return opts.name end,
482 set = function(info, value) 566 set = function(info, value)
483 -- check for existing state name 567 -- check for existing state name
484 if states[value] then 568 if states[value] then
485 L["State named '%s' already exists"]:format(value) 569 format(L["State named '%s' already exists"],value)
486 end 570 end
487 local args = optionMap[bar].args 571 local args = optionMap[bar].args
488 states[value], args[value], states[opts.name], args[opts.name] = states[opts.name], args[opts.name], nil, nil 572 states[value], args[value], states[opts.name], args[opts.name] = states[opts.name], args[opts.name], nil, nil
489 opts.name = value 573 opts.name = value
490 update() 574 update()
549 page = { 633 page = {
550 name = L["Show Page #"], 634 name = L["Show Page #"],
551 order = 3, 635 order = 3,
552 type = "select", 636 type = "select",
553 disabled = function() 637 disabled = function()
554 return bar:GetNumPages() < 2 638 --return bar:GetNumPages() < 2
639 return true
555 end, 640 end,
556 hidden = function() 641 hidden = function()
557 return bar:GetNumPages() < 2 642 --return bar:GetNumPages() < 2
643 return true
558 end, 644 end,
559 values = function() 645 values = function()
560 local pages = { none = " " } 646 -- use off-by-one ordering to put (none) first in the list
561 for i = 1, bar:GetNumPages() do 647 local pages = { [1] = L["(none)"] }
562 pages[i] = i 648 --for i = 1, bar:GetNumPages() do
563 end 649 -- pages[i+1] = i
650 --end
564 return pages 651 return pages
565 end, 652 end,
566 set = function(info, value) 653 set = function(info, value)
567 if value == "none" then 654 value = value - 1
568 setprop(info, nil) 655 setprop(info, value > 0 and value or nil)
569 else
570 setprop(info, value)
571 end
572 end, 656 end,
573 get = function(info) 657 get = function(info)
574 return getprop(info) or "none" 658 return getprop(info) or L["(none)"]
575 end, 659 end,
576 }, 660 },
577 keybindstate = { 661 keybindstate = {
578 name = L["Override Keybinds"], 662 name = L["Override Keybinds"],
579 desc = L["Set this state to maintain its own set of keybinds which override the defaults when active"], 663 desc = L["Set this state to maintain its own set of keybinds which override the defaults when active"],
645 name = L["Scale"], 729 name = L["Scale"],
646 order = 6, 730 order = 6,
647 type = "group", 731 type = "group",
648 inline = true, 732 inline = true,
649 args = { 733 args = {
650 enablescale = { 734 enableScale = {
651 name = L["Set New Scale"], 735 name = L["Set New Scale"],
652 order = 1, 736 order = 1,
653 type = "toggle", 737 type = "toggle",
654 set = setprop, 738 set = setprop,
655 get = getprop, 739 get = getprop,
662 max = 2.5, 746 max = 2.5,
663 step = 0.05, 747 step = 0.05,
664 isPercent = true, 748 isPercent = true,
665 set = setprop, 749 set = setprop,
666 get = function(info) return getprop(info) or 1 end, 750 get = function(info) return getprop(info) or 1 end,
667 disabled = function() return not GetProperty(bar, opts.name, "enablescale") end, 751 disabled = function() return not GetProperty(bar, opts.name, "enableScale") end,
668 hidden = function() return not GetProperty(bar, opts.name, "enablescale") end, 752 hidden = function() return not GetProperty(bar, opts.name, "enableScale") end,
669 }, 753 },
670 }, 754 },
671 }, 755 },
672 }, 756 },
673 }, 757 },
769 if s == "" then 853 if s == "" then
770 return true 854 return true
771 end 855 end
772 local c, r = s:match("(%b[])(.*)") 856 local c, r = s:match("(%b[])(.*)")
773 if c == nil and s and #s > 0 then 857 if c == nil and s and #s > 0 then
774 return L["Invalid custom rule '%s': each clause must appear within [brackets]"]:format(rule) 858 return format(L["Invalid custom rule '%s': each clause must appear within [brackets]"],rule)
775 end 859 end
776 s = r 860 s = r
777 until c == nil 861 until c == nil
778 return true 862 return true
779 end, 863 end,
814 end 898 end
815 899
816 900
817 CreateBarOptions = function(bar) 901 CreateBarOptions = function(bar)
818 local private = { } 902 local private = { }
903 local states = tbuild(module.db.profile.bars, bar:GetName(), "states")
819 local options = { 904 local options = {
820 type = "group", 905 type = "group",
821 name = L["Dynamic State"], 906 name = L["Dynamic State"],
822 childGroups = "tree", 907 childGroups = "tree",
823 disabled = InCombatLockdown, 908 disabled = InCombatLockdown,
847 order = 2, 932 order = 2,
848 type = "execute", 933 type = "execute",
849 func = function () 934 func = function ()
850 local name = private.newstatename 935 local name = private.newstatename
851 if states[name] then 936 if states[name] then
852 ReAction:UserError(L["State named '%s' already exists"]:format(name)) 937 ReAction:UserError(format(L["State named '%s' already exists"],name))
853 else 938 else
854 -- TODO: select default state options and pass as final argument 939 -- TODO: select default state options and pass as final argument
855 states[name] = { } 940 states[name] = { }
856 optionMap[bar].args[name] = CreateStateOptions(bar,name) 941 optionMap[bar].args[name] = CreateStateOptions(bar,name)
857 private.newstatename = "" 942 private.newstatename = ""