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 |
