Mercurial > wow > reaction
comparison State.lua @ 69:a785d6708388
moved State.lua to a top level file
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Tue, 03 Jun 2008 23:05:16 +0000 |
parents | modules/ReAction_State/ReAction_State.lua@fcb5dad031f9 |
children | 2c12e2b1752e |
comparison
equal
deleted
inserted
replaced
68:fcb5dad031f9 | 69:a785d6708388 |
---|---|
1 --[[ | |
2 ReAction bar state driver interface | |
3 | |
4 --]] | |
5 | |
6 -- local imports | |
7 local ReAction = ReAction | |
8 local L = ReAction.L | |
9 local _G = _G | |
10 local InCombatLockdown = InCombatLockdown | |
11 | |
12 -- module declaration | |
13 local moduleID = "State" | |
14 local module = ReAction:NewModule( moduleID, "AceEvent-3.0" ) | |
15 | |
16 -- Utility -- | |
17 | |
18 -- traverse a table tree by key list and fetch the result or first nil | |
19 local function tfetch(t, ...) | |
20 for i = 1, select('#', ...) do | |
21 t = t and t[select(i, ...)] | |
22 end | |
23 return t | |
24 end | |
25 | |
26 -- traverse a table tree by key list and build tree as necessary | |
27 local function tbuild(t, ...) | |
28 for i = 1, select('#', ...) do | |
29 local key = select(i, ...) | |
30 if not t[key] then t[key] = { } end | |
31 t = t[key] | |
32 end | |
33 return t | |
34 end | |
35 | |
36 -- PRIVATE -- | |
37 | |
38 local InitRules, ApplyStates, SetProperty, GetProperty | |
39 do | |
40 -- As far as I can tell the macro clauses are NOT locale-specific. | |
41 local ruleformats = { | |
42 stealth = "stealth", | |
43 nostealth = "nostealth", | |
44 shadowform = "form:1", | |
45 noshadowform = "noform", | |
46 pet = "pet", | |
47 nopet = "nopet", | |
48 harm = "target=target,harm", | |
49 help = "target=target,help", | |
50 notarget = "target=target,noexists", | |
51 focusharm = "target=focus,harm", | |
52 focushelp = "target=focus,help", | |
53 nofocus = "target=focus,noexists", | |
54 raid = "group:raid", | |
55 party = "group:party", | |
56 solo = "nogroup", | |
57 combat = "combat", | |
58 nocombat = "nocombat", | |
59 } | |
60 | |
61 -- 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 | |
63 -- 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 | |
65 -- until well past when they get cat form, and stance 5 can be flight, tree, or moonkin | |
66 -- depending on talents. | |
67 function InitRules() | |
68 local forms = { } | |
69 -- sort by icon since it's locale-independent | |
70 for i = 1, GetNumShapeshiftForms() do | |
71 local icon = GetShapeshiftFormInfo(i) | |
72 forms[icon] = i; | |
73 end | |
74 -- use 9 if not found since 9 is never a valid stance/form | |
75 local defensive = forms["Interface\\Icons\\Ability_Warrior_DefensiveStance"] or 9 | |
76 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 | |
78 local aquatic = forms["Interface\\Icons\\Ability_Druid_AquaticForm"] or 9 | |
79 local cat = forms["Interface\\Icons\\Ability_Druid_CatForm"] or 9 | |
80 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 | |
82 local flight = forms["Interface\\Icons\\Ability_Druid_FlightForm"] or 9 -- flight and swift flight share the same icon | |
83 | |
84 ruleformats.battle = "stance:1" | |
85 ruleformats.defensive = ("stance:%d"):format(defensive) | |
86 ruleformats.berserker = ("stance:%d"):format(berserker) | |
87 ruleformats.caster = ("form:0/%d/%d/%d"):format(aquatic, travel, flight) | |
88 ruleformats.bear = ("form:%d"):format(bear) | |
89 ruleformats.cat = ("form:%d"):format(cat) | |
90 ruleformats.treeOrMoonkin = ("form:%d"):format(treekin) | |
91 end | |
92 | |
93 -- return a new array of keys of table 't', sorted by comparing | |
94 -- sub-fields (obtained via tfetch) of the table values | |
95 local function fieldsort( t, ... ) | |
96 local r = { } | |
97 for k in pairs(t) do | |
98 table.insert(r,k) | |
99 end | |
100 local dotdotdot = { ... } | |
101 table.sort(r, function(lhs, rhs) | |
102 local olhs = tfetch(t[lhs], unpack(dotdotdot)) or 0 | |
103 local orhs = tfetch(t[rhs], unpack(dotdotdot)) or 0 | |
104 return olhs < orhs | |
105 end) | |
106 return r | |
107 end | |
108 | |
109 local function BuildRuleString(states) | |
110 local s = "" | |
111 local default | |
112 local sorted = fieldsort(states, "rule", "order") | |
113 for idx, name in ipairs(sorted) do | |
114 local state = states[name] | |
115 local semi = #s > 0 and "; " or "" | |
116 local mode = tfetch(state,"rule","type") | |
117 if mode == "default" then | |
118 default = name | |
119 elseif mode == "custom" then | |
120 if state.rule.custom then | |
121 -- strip out all spaces from the custom rule | |
122 s = ("%s%s%s %s"):format(s, semi, state.rule.custom:gsub("%s",""), name) | |
123 end | |
124 elseif mode == "any" then | |
125 if state.rule.values then | |
126 local clause = "" | |
127 for key, value in pairs(state.rule.values) do | |
128 clause = ("%s[%s]"):format(clause,ruleformats[key]) | |
129 end | |
130 if #clause > 0 then | |
131 s = ("%s%s%s %s"):format(s, semi, clause, name) | |
132 end | |
133 end | |
134 elseif mode == "all" then | |
135 if state.rule.values then | |
136 local clause = "" | |
137 for key, value in pairs(state.rule.values) do | |
138 clause = ("%s%s%s"):format(clause,#clause > 0 and "," or "", ruleformats[key]) | |
139 end | |
140 if #clause > 0 then | |
141 s = ("%s%s[%s] %s"):format(s, semi, clause, name) | |
142 end | |
143 end | |
144 end | |
145 end | |
146 if default then | |
147 s = ("%s%s%s"):format(s, #s > 0 and "; " or "", default) | |
148 end | |
149 return s, default | |
150 end | |
151 | |
152 local drivers = setmetatable({},{__mode="k"}) | |
153 local propertyFuncs = { } | |
154 | |
155 function ApplyStates( bar ) | |
156 local states = tfetch(module.db.profile.bars, bar:GetName(), "states") | |
157 if states then | |
158 local frame = bar:GetFrame() | |
159 local string, default = BuildRuleString(states) | |
160 if string and #string > 0 then | |
161 drivers[bar] = true | |
162 -- register a map for each "statemap-reaction-XXX" to set 'state' to 'XXX' | |
163 -- UNLESS we're in a keybound state AND there's a default state, in which case | |
164 -- all keybound states go back to themselves. | |
165 local keybindprefix | |
166 if default then | |
167 local tmp = { } | |
168 for state, config in pairs(states) do | |
169 if tfetch(config, "rule", "type") == "keybind" then | |
170 bar:SetStateKeybind(tfetch(config,"rule","keybind"), state, tfetch(config,"rule","keybindreturn") or default or 0) | |
171 table.insert(tmp, ("%s:%s"):format(state,state)) | |
172 end | |
173 end | |
174 if #tmp > 0 then | |
175 table.insert(tmp,"") -- to get a final ';' | |
176 end | |
177 keybindprefix = table.concat(tmp,";") | |
178 end | |
179 for state in pairs(states) do | |
180 frame:SetAttribute(("statemap-reaction-%s"):format(state), ("%s%s"):format(keybindprefix or "",state)) | |
181 end | |
182 -- register a handler to set the value of attribute "state-reaction" | |
183 -- in response to events as per the rule string | |
184 RegisterStateDriver(frame, "reaction", string) | |
185 SecureStateHeader_Refresh(frame) | |
186 elseif drivers[bar] then | |
187 UnregisterStateDriver(frame, "reaction") | |
188 drivers[bar] = nil | |
189 end | |
190 for k, f in pairs(propertyFuncs) do | |
191 f(bar, states) | |
192 end | |
193 end | |
194 end | |
195 | |
196 function GetProperty( bar, state, propname ) | |
197 return tfetch(module.db.profile.bars, bar:GetName(), "states", state, propname) | |
198 end | |
199 | |
200 function SetProperty( bar, state, propname, value ) | |
201 local states = tbuild(module.db.profile.bars, bar:GetName(), "states") | |
202 tbuild(states, state)[propname] = value | |
203 local f = propertyFuncs[propname] | |
204 if f then | |
205 f(bar, states) | |
206 end | |
207 end | |
208 | |
209 -- state property functions | |
210 function propertyFuncs.hide( bar, states ) | |
211 local tmp = { } | |
212 for state, config in pairs(states) do | |
213 if config.hide then | |
214 table.insert(tmp, state) | |
215 end | |
216 end | |
217 local s = table.concat(tmp,",") | |
218 bar:SetHideStates(s) | |
219 end | |
220 | |
221 function propertyFuncs.page( bar, states ) | |
222 local map = { } | |
223 for state, config in pairs(states) do | |
224 map[state] = config.page | |
225 end | |
226 bar:SetStatePageMap(state, map) | |
227 end | |
228 | |
229 function propertyFuncs.keybindstate( bar, states ) | |
230 local map = { } | |
231 for state, config in pairs(states) do | |
232 if config.keybindstate then | |
233 table.insert(map,state) | |
234 end | |
235 end | |
236 bar:SetStateKeybindOverrideMap(map) | |
237 end | |
238 | |
239 function propertyFuncs.enableanchor( bar, states ) | |
240 | |
241 end | |
242 | |
243 function propertyFuncs.anchorPoint( bar, states ) | |
244 | |
245 end | |
246 | |
247 function propertyFuncs.anchorRelPoint( bar, states ) | |
248 | |
249 end | |
250 | |
251 function propertyFuncs.anchorX( bar, states ) | |
252 | |
253 end | |
254 | |
255 function propertyFuncs.anchorY( bar, states ) | |
256 | |
257 end | |
258 | |
259 function propertyFuncs.enablescale( bar, states ) | |
260 | |
261 end | |
262 | |
263 function propertyFuncs.scale( bar, states ) | |
264 | |
265 end | |
266 | |
267 end | |
268 | |
269 | |
270 | |
271 -- module event handlers -- | |
272 | |
273 function module:OnInitialize() | |
274 self.db = ReAction.db:RegisterNamespace( moduleID, | |
275 { | |
276 profile = { | |
277 bars = { }, | |
278 } | |
279 } | |
280 ) | |
281 | |
282 InitRules() | |
283 self:RegisterEvent("PLAYER_AURAS_CHANGED") | |
284 | |
285 ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") | |
286 | |
287 ReAction.RegisterCallback(self, "OnCreateBar","OnRefreshBar") | |
288 ReAction.RegisterCallback(self, "OnRefreshBar") | |
289 ReAction.RegisterCallback(self, "OnEraseBar") | |
290 ReAction.RegisterCallback(self, "OnRenameBar") | |
291 ReAction.RegisterCallback(self, "OnConfigModeChanged") | |
292 end | |
293 | |
294 function module:PLAYER_AURAS_CHANGED() | |
295 self:UnregisterEvent("PLAYER_AURAS_CHANGED") | |
296 -- on login the number of stances is 0 until this event fires during the init sequence. | |
297 -- however if you reload just the UI the number of stances is correct immediately | |
298 -- and this event won't fire until you gain/lose buffs/debuffs, at which point you might | |
299 -- be in combat. | |
300 if not InCombatLockdown() then | |
301 InitRules() | |
302 for name, bar in ReAction:IterateBars() do | |
303 self:OnRefreshBar(nil,bar,name) | |
304 end | |
305 end | |
306 end | |
307 | |
308 function module:OnRefreshBar(event, bar, name) | |
309 local c = self.db.profile.bars[name] | |
310 if c then | |
311 ApplyStates(bar) | |
312 end | |
313 end | |
314 | |
315 function module:OnEraseBar(event, bar, name) | |
316 self.db.profile.bars[name] = nil | |
317 end | |
318 | |
319 function module:OnRenameBar(event, bar, oldname, newname) | |
320 local b = self.db.profile.bars | |
321 bars[newname], bars[oldname] = bars[oldname], nil | |
322 end | |
323 | |
324 function module:OnConfigModeChanged(event, mode) | |
325 -- TODO: unregister all state drivers (temporarily) and hidestates | |
326 end | |
327 | |
328 | |
329 | |
330 -- Options -- | |
331 | |
332 local CreateBarOptions | |
333 do | |
334 local function ClassCheck(...) | |
335 for i = 1, select('#',...) do | |
336 local _, c = UnitClass("player") | |
337 if c == select(i,...) then | |
338 return false | |
339 end | |
340 end | |
341 return true | |
342 end | |
343 | |
344 -- pre-sorted by the order they should appear in | |
345 local rules = { | |
346 -- rule hidden fields | |
347 { "stance", ClassCheck("WARRIOR"), { {battle = L["Battle Stance"]}, {defensive = L["Defensive Stance"]}, {berserker = L["Berserker Stance"]} } }, | |
348 { "form", ClassCheck("DRUID"), { {caster = L["Caster Form"]}, {bear = L["Bear Form"]}, {cat = L["Cat Form"]}, {treeOrMoonkin = L["Tree/Moonkin"]} } }, | |
349 { "stealth", ClassCheck("ROGUE","DRUID"), { {stealth = L["Stealth"]}, {nostealth = L["No Stealth"]} } }, | |
350 { "shadow", ClassCheck("PRIEST"), { {shadowform = L["Shadowform"]}, {noshadowform = L["No Shadowform"]} } }, | |
351 { "pet", ClassCheck("HUNTER","WARLOCK"), { {pet = L["With Pet"]}, {nopet = L["Without Pet"]} } }, | |
352 { "target", false, { {harm = L["Hostile Target"]}, {help = L["Friendly Target"]}, {notarget = L["No Target"]} } }, | |
353 { "focus", false, { {focusharm = L["Hostile Focus"]}, {focushelp = L["Friendly Focus"]}, {nofocus = L["No Focus"]} } }, | |
354 { "group", false, { {raid = L["Raid"]}, {party = L["Party"]}, {solo = L["Solo"]} } }, | |
355 { "combat", false, { {combat = L["In Combat"]}, {nocombat = L["Out of Combat"]} } }, | |
356 } | |
357 | |
358 local ruleSelect = { } | |
359 local ruleMap = { } | |
360 local optionMap = setmetatable({},{__mode="k"}) | |
361 | |
362 local pointTable = { | |
363 NONE = " ", | |
364 CENTER = L["Center"], | |
365 LEFT = L["Left"], | |
366 RIGHT = L["Right"], | |
367 TOP = L["Top"], | |
368 BOTTOM = L["Bottom"], | |
369 TOPLEFT = L["Top Left"], | |
370 TOPRIGHT = L["Top Right"], | |
371 BOTTOMLEFT = L["Bottom Left"], | |
372 BOTTOMRIGHT = L["Bottom Right"], | |
373 } | |
374 | |
375 -- unpack rules table into ruleSelect and ruleMap | |
376 for _, c in ipairs(rules) do | |
377 local rule, hidden, fields = unpack(c) | |
378 if not hidden then | |
379 for _, field in ipairs(fields) do | |
380 local key, label = next(field) | |
381 table.insert(ruleSelect, label) | |
382 table.insert(ruleMap, key) | |
383 end | |
384 end | |
385 end | |
386 | |
387 local function CreateStateOptions(bar, name) | |
388 local opts = { | |
389 type = "group", | |
390 name = name, | |
391 childGroups = "tab", | |
392 } | |
393 | |
394 local states = tbuild(module.db.profile.bars, bar:GetName(), "states") | |
395 | |
396 local function update() | |
397 ApplyStates(bar) | |
398 end | |
399 | |
400 local function setrule( key, value, ... ) | |
401 tbuild(states, opts.name, "rule", ...)[key] = value | |
402 end | |
403 | |
404 local function getrule( ... ) | |
405 return tfetch(states, opts.name, "rule", ...) | |
406 end | |
407 | |
408 local function setprop(info, value) | |
409 SetProperty(bar, opts.name, info[#info], value) | |
410 end | |
411 | |
412 local function getprop(info) | |
413 return GetProperty(bar, opts.name, info[#info]) | |
414 end | |
415 | |
416 local function fixall(setkey) | |
417 -- if multiple selections in the same group are chosen when 'all' is selected, | |
418 -- keep only one of them. If changing the mode, the first in the fields list will | |
419 -- be chosen arbitrarily. Otherwise, if selecting a new checkbox from the field-set, | |
420 -- it will be retained. | |
421 local notified = false | |
422 for _, c in ipairs(rules) do | |
423 local rule, hidden, fields = unpack(c) | |
424 local found = false | |
425 for key in ipairs(fields) do | |
426 if getrule("values",key) then | |
427 if (found or setkey) and key ~= setkey then | |
428 setrule(key,false,"values") | |
429 if not setkey and not notified then | |
430 ReAction:UserError(L["Warning: one or more incompatible rules were turned off"]) | |
431 notified = true | |
432 end | |
433 end | |
434 found = true | |
435 end | |
436 end | |
437 end | |
438 end | |
439 | |
440 local function getNeighbors() | |
441 local before, after | |
442 for k, v in pairs(states) do | |
443 local o = tonumber(tfetch(v, "rule", "order")) | |
444 if o and k ~= opts.name then | |
445 local obefore = tfetch(states,before,"rule","order") | |
446 local oafter = tfetch(states,after,"rule","order") | |
447 if o < opts.order and (not obefore or obefore < o) then | |
448 before = k | |
449 end | |
450 if o > opts.order and (not oafter or oafter > o) then | |
451 after = k | |
452 end | |
453 end | |
454 end | |
455 return before, after | |
456 end | |
457 | |
458 local function swapOrder( a, b ) | |
459 -- do options table | |
460 local args = optionMap[bar].args | |
461 args[a].order, args[b].order = args[b].order, args[a].order | |
462 -- do profile | |
463 a = tbuild(states, a, "rule") | |
464 b = tbuild(states, b, "rule") | |
465 a.order, b.order = b.order, a.order | |
466 end | |
467 | |
468 local function anchordisable() | |
469 return not GetProperty(bar, opts.name, "enableanchor") | |
470 end | |
471 | |
472 tbuild(states, name) | |
473 | |
474 opts.order = getrule("order") | |
475 if opts.order == nil then | |
476 -- add after the highest | |
477 opts.order = 100 | |
478 for _, state in pairs(states) do | |
479 local x = tonumber(tfetch(state, "rule", "order")) | |
480 if x and x >= opts.order then | |
481 opts.order = x + 1 | |
482 end | |
483 end | |
484 setrule("order",opts.order) | |
485 end | |
486 | |
487 opts.args = { | |
488 ordering = { | |
489 name = L["Info"], | |
490 order = 1, | |
491 type = "group", | |
492 args = { | |
493 delete = { | |
494 name = L["Delete this State"], | |
495 order = -1, | |
496 type = "execute", | |
497 func = function(info) | |
498 if states[opts.name] then | |
499 states[opts.name] = nil | |
500 ApplyStates(bar) | |
501 end | |
502 optionMap[bar].args[opts.name] = nil | |
503 end, | |
504 }, | |
505 rename = { | |
506 name = L["Name"], | |
507 order = 1, | |
508 type = "input", | |
509 get = function() return opts.name end, | |
510 set = function(info, value) | |
511 -- check for existing state name | |
512 if states[value] then | |
513 L["State named '%s' already exists"]:format(value) | |
514 end | |
515 local args = optionMap[bar].args | |
516 states[value], args[value], states[opts.name], args[opts.name] = states[opts.name], args[opts.name], nil, nil | |
517 opts.name = value | |
518 update() | |
519 end, | |
520 pattern = "^%w*$", | |
521 usage = L["State names must be alphanumeric without spaces"], | |
522 }, | |
523 ordering = { | |
524 name = L["Evaluation Order"], | |
525 desc = L["State transitions are evaluated in the order listed:\nMove a state up or down to change the order"], | |
526 order = 2, | |
527 type = "group", | |
528 inline = true, | |
529 args = { | |
530 up = { | |
531 name = L["Up"], | |
532 order = 1, | |
533 type = "execute", | |
534 width = "half", | |
535 func = function() | |
536 local before, after = getNeighbors() | |
537 if before then | |
538 swapOrder(before, opts.name) | |
539 update() | |
540 end | |
541 end, | |
542 }, | |
543 down = { | |
544 name = L["Down"], | |
545 order = 2, | |
546 type = "execute", | |
547 width = "half", | |
548 func = function() | |
549 local before, after = getNeighbors() | |
550 if after then | |
551 swapOrder(opts.name, after) | |
552 update() | |
553 end | |
554 end, | |
555 } | |
556 } | |
557 } | |
558 } | |
559 }, | |
560 properties = { | |
561 name = L["Properties"], | |
562 order = 2, | |
563 type = "group", | |
564 args = { | |
565 desc = { | |
566 name = L["Set the properties for the bar when in this state"], | |
567 order = 1, | |
568 type = "description" | |
569 }, | |
570 hide = { | |
571 name = L["Hide Bar"], | |
572 order = 2, | |
573 type = "toggle", | |
574 set = setprop, | |
575 get = getprop, | |
576 }, | |
577 page = { | |
578 name = L["Show Page #"], | |
579 order = 3, | |
580 type = "select", | |
581 disabled = function() | |
582 return bar:GetNumPages() < 2 | |
583 end, | |
584 hidden = function() | |
585 return bar:GetNumPages() < 2 | |
586 end, | |
587 values = function() | |
588 local pages = { none = " " } | |
589 for i = 1, bar:GetNumPages() do | |
590 pages[i] = i | |
591 end | |
592 return pages | |
593 end, | |
594 set = function(info, value) | |
595 if value == "none" then | |
596 setprop(info, nil) | |
597 else | |
598 setprop(info, value) | |
599 end | |
600 end, | |
601 get = function(info) | |
602 return getprop(info) or "none" | |
603 end, | |
604 }, | |
605 keybindstate = { | |
606 name = L["Override Keybinds"], | |
607 desc = L["Set this state to maintain its own set of keybinds which override the defaults when active"], | |
608 order = 4, | |
609 type = "toggle", | |
610 set = setprop, | |
611 get = getprop, | |
612 }, | |
613 position = { | |
614 name = L["Position"], | |
615 order = 5, | |
616 type = "group", | |
617 inline = true, | |
618 args = { | |
619 enableanchor = { | |
620 name = L["Set New Position"], | |
621 order = 1, | |
622 type = "toggle", | |
623 set = setprop, | |
624 get = getprop, | |
625 }, | |
626 anchorPoint = { | |
627 name = L["Point"], | |
628 order = 2, | |
629 type = "select", | |
630 values = pointTable, | |
631 set = function(info, value) setprop(info, value ~= "NONE" and value or nil) end, | |
632 get = function(info) return getprop(info) or "NONE" end, | |
633 disabled = anchordisable, | |
634 hidden = anchordisable, | |
635 }, | |
636 anchorRelPoint = { | |
637 name = L["Relative Point"], | |
638 order = 3, | |
639 type = "select", | |
640 values = pointTable, | |
641 set = function(info, value) setprop(info, value ~= "NONE" and value or nil) end, | |
642 get = function(info) return getprop(info) or "NONE" end, | |
643 disabled = anchordisable, | |
644 hidden = anchordisable, | |
645 }, | |
646 anchorX = { | |
647 name = L["X Offset"], | |
648 order = 4, | |
649 type = "range", | |
650 min = -100, | |
651 max = 100, | |
652 step = 1, | |
653 set = setprop, | |
654 get = getprop, | |
655 disabled = anchordisable, | |
656 hidden = anchordisable, | |
657 }, | |
658 anchorY = { | |
659 name = L["Y Offset"], | |
660 order = 5, | |
661 type = "range", | |
662 min = -100, | |
663 max = 100, | |
664 step = 1, | |
665 set = setprop, | |
666 get = getprop, | |
667 disabled = anchordisable, | |
668 hidden = anchordisable, | |
669 }, | |
670 }, | |
671 }, | |
672 scale = { | |
673 name = L["Scale"], | |
674 order = 6, | |
675 type = "group", | |
676 inline = true, | |
677 args = { | |
678 enablescale = { | |
679 name = L["Set New Scale"], | |
680 order = 1, | |
681 type = "toggle", | |
682 set = setprop, | |
683 get = getprop, | |
684 }, | |
685 scale = { | |
686 name = L["Scale"], | |
687 order = 2, | |
688 type = "range", | |
689 min = 0.1, | |
690 max = 2.5, | |
691 step = 0.05, | |
692 isPercent = true, | |
693 set = setprop, | |
694 get = function(info) return getprop(info) or 1 end, | |
695 disabled = function() return not GetProperty(bar, opts.name, "enablescale") end, | |
696 hidden = function() return not GetProperty(bar, opts.name, "enablescale") end, | |
697 }, | |
698 }, | |
699 }, | |
700 }, | |
701 }, | |
702 rules = { | |
703 name = L["Selection Rule"], | |
704 order = 3, | |
705 type = "group", | |
706 args = { | |
707 mode = { | |
708 name = L["Select this state"], | |
709 order = 2, | |
710 type = "select", | |
711 style = "radio", | |
712 values = { | |
713 default = L["by default"], | |
714 any = L["when ANY of these"], | |
715 all = L["when ALL of these"], | |
716 custom = L["via custom rule"], | |
717 keybind = L["via keybinding"], | |
718 }, | |
719 set = function( info, value ) | |
720 setrule("type", value) | |
721 fixall() | |
722 update() | |
723 end, | |
724 get = function( info ) | |
725 return getrule("type") | |
726 end, | |
727 }, | |
728 clear = { | |
729 name = L["Clear All"], | |
730 order = 3, | |
731 type = "execute", | |
732 hidden = function() | |
733 local t = getrule("type") | |
734 return t ~= "any" and t ~= "all" | |
735 end, | |
736 disabled = function() | |
737 local t = getrule("type") | |
738 return t ~= "any" and t ~= "all" | |
739 end, | |
740 func = function() | |
741 local type = getrule("type") | |
742 if type == "custom" then | |
743 setrule("custom","") | |
744 elseif type == "any" or type == "all" then | |
745 setrule("values", {}) | |
746 end | |
747 update() | |
748 end, | |
749 }, | |
750 inputs = { | |
751 name = L["Conditions"], | |
752 order = 4, | |
753 type = "multiselect", | |
754 hidden = function() | |
755 local t = getrule("type") | |
756 return t ~= "any" and t ~= "all" | |
757 end, | |
758 disabled = function() | |
759 local t = getrule("type") | |
760 return t ~= "any" and t ~= "all" | |
761 end, | |
762 values = ruleSelect, | |
763 set = function(info, key, value ) | |
764 setrule(ruleMap[key], value or nil, "values") | |
765 if value then | |
766 fixall(ruleMap[key]) | |
767 end | |
768 update() | |
769 end, | |
770 get = function(info, key) | |
771 return getrule("values", ruleMap[key]) or false | |
772 end, | |
773 }, | |
774 custom = { | |
775 name = L["Custom Rule"], | |
776 order = 5, | |
777 type = "input", | |
778 multiline = true, | |
779 hidden = function() | |
780 return getrule("type") ~= "custom" | |
781 end, | |
782 disabled = function() | |
783 return getrule("type") ~= "custom" | |
784 end, | |
785 desc = L["Syntax like macro rules: see preset rules for examples"], | |
786 set = function(info, value) | |
787 setrule("custom",value) | |
788 update() | |
789 end, | |
790 get = function(info) | |
791 return getrule("custom") or "" | |
792 end, | |
793 validate = function (info, rule) | |
794 local s = rule:gsub("%s","") -- remove all spaces | |
795 -- unfortunately %b and captures don't support the '+' notation, or this would be considerably simpler | |
796 repeat | |
797 if s == "" then | |
798 return true | |
799 end | |
800 local c, r = s:match("(%b[])(.*)") | |
801 if c == nil and s and #s > 0 then | |
802 return L["Invalid custom rule '%s': each clause must appear within [brackets]"]:format(rule) | |
803 end | |
804 s = r | |
805 until c == nil | |
806 return true | |
807 end, | |
808 }, | |
809 keybind = { | |
810 name = L["Keybinding"], | |
811 order = 6, | |
812 inline = true, | |
813 hidden = function() return getrule("type") ~= "keybind" end, | |
814 disabled = function() return getrule("type") ~= "keybind" end, | |
815 type = "group", | |
816 args = { | |
817 desc = { | |
818 name = L["Invoking a state keybind overrides all other transition rules. Toggle the keybind again to remove the override and return to the specified toggle-off state."], | |
819 order = 1, | |
820 type = "description", | |
821 }, | |
822 keybind = { | |
823 name = L["State Hotkey"], | |
824 desc = L["Define an override toggle keybind"], | |
825 order = 2, | |
826 type = "keybinding", | |
827 set = function(info, value) | |
828 setrule("keybind",value) | |
829 update() | |
830 end, | |
831 get = function() return getrule("keybind") end, | |
832 }, | |
833 default = { | |
834 name = L["Toggle Off State"], | |
835 desc = L["Select a state to return to when the keybind override is toggled off"], | |
836 order = 3, | |
837 type = "select", | |
838 values = function() | |
839 local t = { } | |
840 for k in pairs(states) do | |
841 if k ~= opts.name then | |
842 t[k] = k | |
843 end | |
844 end | |
845 return t | |
846 end, | |
847 set = function(info, value) | |
848 setrule("keybindreturn",value) | |
849 update() | |
850 end, | |
851 get = function() return getrule("keybindreturn") end, | |
852 }, | |
853 }, | |
854 }, | |
855 }, | |
856 }, | |
857 } | |
858 return opts | |
859 end | |
860 | |
861 | |
862 CreateBarOptions = function(bar) | |
863 local private = { } | |
864 local options = { | |
865 type = "group", | |
866 name = L["Dynamic State"], | |
867 childGroups = "tree", | |
868 disabled = InCombatLockdown, | |
869 args = { | |
870 __desc__ = { | |
871 name = L["States are evaluated in the order they are listed"], | |
872 order = 1, | |
873 type = "description", | |
874 }, | |
875 __new__ = { | |
876 name = L["New State..."], | |
877 order = 2, | |
878 type = "group", | |
879 args = { | |
880 name = { | |
881 name = L["State Name"], | |
882 desc = L["Set a name for the new state"], | |
883 order = 1, | |
884 type = "input", | |
885 get = function() return private.newstatename or "" end, | |
886 set = function(info,value) private.newstatename = value end, | |
887 pattern = "^%w*$", | |
888 usage = L["State names must be alphanumeric without spaces"], | |
889 }, | |
890 create = { | |
891 name = L["Create State"], | |
892 order = 2, | |
893 type = "execute", | |
894 func = function () | |
895 local name = private.newstatename | |
896 if states[name] then | |
897 ReAction:UserError(L["State named '%s' already exists"]:format(name)) | |
898 else | |
899 -- TODO: select default state options and pass as final argument | |
900 states[name] = { } | |
901 optionMap[bar].args[name] = CreateStateOptions(bar,name) | |
902 private.newstatename = "" | |
903 end | |
904 end, | |
905 disabled = function() | |
906 local name = private.newstatename or "" | |
907 return #name == 0 or name:find("%W") | |
908 end, | |
909 } | |
910 } | |
911 } | |
912 } | |
913 } | |
914 local states = tfetch(module.db.profile.bars, bar:GetName(), "states") | |
915 if states then | |
916 for name, config in pairs(states) do | |
917 options.args[name] = CreateStateOptions(bar,name) | |
918 end | |
919 end | |
920 optionMap[bar] = options | |
921 return options | |
922 end | |
923 end | |
924 | |
925 function module:GetBarOptions(bar) | |
926 return CreateBarOptions(bar) | |
927 end |