Advertisement
Guest User

Lex auras

a guest
Jul 16th, 2017
800
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 150.70 KB | None | 0 0
  1. -- Lua APIs
  2. local tinsert, tconcat, tremove, tContains, wipe = table.insert, table.concat, table.remove, tContains, wipe
  3. local fmt, tostring, select, pairs, next, type = string.format, tostring, select, pairs, next, type
  4. local loadstring, assert, error = loadstring, assert, error
  5. local setmetatable, getmetatable = setmetatable, getmetatable
  6. local coroutine = coroutine
  7. local _G = _G
  8.  
  9. -- WoW APIs
  10. local GetTalentInfo, GetPvpTalentInfo, IsAddOnLoaded, InCombatLockdown = GetTalentInfo, GetPvpTalentInfo, IsAddOnLoaded, InCombatLockdown
  11. local LoadAddOn, setfenv, UnitName, GetRealmName, UnitGroupRolesAssigned, UnitRace, UnitFactionGroup, IsInRaid
  12. = LoadAddOn, setfenv, UnitName, GetRealmName, UnitGroupRolesAssigned, UnitRace, UnitFactionGroup, IsInRaid
  13. local UnitClass, UnitExists, UnitGUID, UnitAffectingCombat, GetInstanceInfo, IsInInstance
  14. = UnitClass, UnitExists, UnitGUID, UnitAffectingCombat, GetInstanceInfo, IsInInstance
  15. local GetNumGroupMembers, UnitIsUnit, GetRaidRosterInfo, GetSpecialization, GetSpecializationRole, UnitInVehicle, UnitHasVehicleUI, GetSpellInfo
  16. = GetNumGroupMembers, UnitIsUnit, GetRaidRosterInfo, GetSpecialization, GetSpecializationRole, UnitInVehicle, UnitHasVehicleUI, GetSpellInfo
  17. local SendChatMessage, GetChannelName, UnitInBattleground, UnitInRaid, UnitInParty, PlaySoundFile, PlaySoundKitID, GetTime, GetSpellLink, GetItemInfo
  18. = SendChatMessage, GetChannelName, UnitInBattleground, UnitInRaid, UnitInParty, PlaySoundFile, PlaySoundKitID, GetTime, GetSpellLink, GetItemInfo
  19. local CreateFrame, IsShiftKeyDown, GetScreenWidth, GetScreenHeight, GetCursorPosition, random, UpdateAddOnCPUUsage, GetFrameCPUUsage, debugprofilestop
  20. = CreateFrame, IsShiftKeyDown, GetScreenWidth, GetScreenHeight, GetCursorPosition, random, UpdateAddOnCPUUsage, GetFrameCPUUsage, debugprofilestop
  21. local debugstack, IsSpellKnown = debugstack, IsSpellKnown
  22.  
  23. local ADDON_NAME = "WeakAuras"
  24. local WeakAuras = WeakAuras
  25. local versionString = WeakAuras.versionString
  26. WeakAurasTimers = setmetatable({}, {__tostring=function() return "WeakAuras" end})
  27. LibStub("AceTimer-3.0"):Embed(WeakAurasTimers)
  28. local LDB = LibStub:GetLibrary("LibDataBroker-1.1")
  29. local HBD = LibStub("HereBeDragons-1.0")
  30.  
  31. local timer = WeakAurasTimers
  32. WeakAuras.timer = timer
  33.  
  34. local L = WeakAuras.L
  35.  
  36. -- luacheck: globals NamePlateDriverFrame CombatText_AddMessage COMBAT_TEXT_SCROLL_FUNCTION
  37. -- luacheck: globals Lerp Saturate KuiNameplatesPlayerAnchor KuiNameplatesCore ElvUIPlayerNamePlateAnchor GTFO
  38.  
  39. local queueshowooc;
  40.  
  41. function WeakAuras.LoadOptions(msg)
  42. if not(IsAddOnLoaded("WeakAurasOptions")) then
  43. if InCombatLockdown() then
  44. -- inform the user and queue ooc
  45. print("|cff9900FF".."WeakAuras Options"..FONT_COLOR_CODE_CLOSE.." will finish loading after combat.")
  46. queueshowooc = msg or "";
  47. WeakAuras.frames["Addon Initialization Handler"]:RegisterEvent("PLAYER_REGEN_ENABLED")
  48. return false;
  49. else
  50. local loaded, reason = LoadAddOn("WeakAurasOptions");
  51. if not(loaded) then
  52. print("|cff9900FF".."WeakAuras Options"..FONT_COLOR_CODE_CLOSE.." could not be loaded: "..RED_FONT_COLOR_CODE.._G["ADDON_"..reason]);
  53. return false;
  54. end
  55. end
  56. end
  57. return true;
  58. end
  59.  
  60. function WeakAuras.OpenOptions(msg)
  61. if (WeakAuras.LoadOptions(msg)) then
  62. WeakAuras.ToggleOptions(msg);
  63. end
  64. end
  65.  
  66. SLASH_WEAKAURAS1, SLASH_WEAKAURAS2 = "/weakauras", "/wa";
  67. function SlashCmdList.WEAKAURAS(msg)
  68. WeakAuras.OpenOptions(msg);
  69. end
  70.  
  71. -- An alias for WeakAurasSaved, the SavedVariables
  72. -- Noteable properties:
  73. -- debug: If set to true, WeakAura.debug() outputs messages to the chat frame
  74. -- displays: All aura settings, keyed on their id
  75. local db;
  76.  
  77. local registeredFromAddons;
  78. -- List of addons that registered displays
  79. WeakAuras.addons = {};
  80. local addons = WeakAuras.addons;
  81.  
  82. -- A list of tutorials, filled in by the WeakAuras_Tutorials addon by calling RegisterTutorial
  83. WeakAuras.tutorials = {};
  84. local tutorials = WeakAuras.tutorials;
  85.  
  86. -- used if an addon tries to register a display under an id that the user already has a display with that id
  87. WeakAuras.collisions = {};
  88. local collisions = WeakAuras.collisions;
  89.  
  90. -- While true no events are handled. E.g. WeakAuras is paused while the Options dialog is open
  91. local paused = true;
  92. local importing = false;
  93.  
  94. -- squelches actions and sounds from auras. is used e.g. to prevent lots of actions/sounds from triggering
  95. -- on login or after closing the options dialog
  96. local squelch_actions = true;
  97.  
  98. -- Load functions, keyed on id
  99. local loadFuncs = {};
  100.  
  101. -- Check Conditions Functions, keyed on id
  102. local checkConditions = {};
  103.  
  104. -- All regions keyed on id, has properties: region, regionType, also see clones
  105. WeakAuras.regions = {};
  106. local regions = WeakAuras.regions;
  107. WeakAuras.auras = {};
  108. local auras = WeakAuras.auras;
  109. WeakAuras.events = {};
  110. local events = WeakAuras.events;
  111.  
  112. -- keyed on id, contains bool indicating whether the aura is loaded
  113. WeakAuras.loaded = {};
  114. local loaded = WeakAuras.loaded;
  115.  
  116. WeakAuras.specificBosses = {};
  117. local specificBosses = WeakAuras.specificBosses;
  118. WeakAuras.specificUnits = {};
  119. local specificUnits = WeakAuras.specificUnits;
  120.  
  121. -- contains regions for clones
  122. WeakAuras.clones = {};
  123. local clones = WeakAuras.clones;
  124. -- Unused regions that are kept around for clones
  125. WeakAuras.clonePool = {};
  126. local clonePool = WeakAuras.clonePool;
  127.  
  128. -- One table per regionType, see RegisterRegionType, notable properties: create, modify and default
  129. WeakAuras.regionTypes = {};
  130. local regionTypes = WeakAuras.regionTypes;
  131.  
  132. -- One table per regionType, see RegisterRegionOptions
  133. WeakAuras.regionOptions = {};
  134. local regionOptions = WeakAuras.regionOptions;
  135.  
  136. -- Maps from trigger type to trigger system
  137. WeakAuras.triggerTypes = {};
  138. local triggerTypes = WeakAuras.triggerTypes;
  139.  
  140. -- Trigger State, updated by trigger systems, then applied to regions by UpdatedTriggerState
  141. -- keyed on id, triggernum, cloneid
  142. -- cloneid can be a empty string
  143.  
  144. -- Noteable properties:
  145. -- changed: Whether this trigger state was recently changed and its properties
  146. -- need to be applied to a region. The glue code resets this
  147. -- after syncing the region to the trigger state
  148. -- show: Whether the region for this trigger state should be shown
  149. -- progressType: Either "timed", "static"
  150. -- duration: The duration if the progressType is timed
  151. -- expirationTime: The expirationTime if the progressType is timed
  152. -- resort: Should be set to true by the trigger system the parent needs
  153. -- to be resorted. The glue code resets this.
  154. -- autoHide: If the aura should be hidden on expiring
  155. -- value: The value if the progressType is static
  156. -- total: The total if the progressType is static
  157. -- inverse: The static values should be interpreted inversely
  158. -- name: The name information
  159. -- icon: The icon information
  160. -- texture: The texture information
  161. -- stacks: The stacks information
  162. -- index: The index of the buff/debuff for the buff trigger system, used to set the tooltip
  163. -- spellId: spellId of the buff/debuff, used to set the tooltip
  164.  
  165. WeakAuras.triggerState = {}
  166. local triggerState = WeakAuras.triggerState;
  167.  
  168. -- Fallback states
  169. local fallbacksStates = {};
  170.  
  171. -- List of all trigger systems, contains each system once
  172. WeakAuras.triggerSystems = {}
  173. local triggerSystems = WeakAuras.triggerSystems;
  174.  
  175. WeakAuras.forceable_events = {};
  176.  
  177. local from_files = {};
  178.  
  179. local timers = {}; -- Timers for autohiding, keyed on id, triggernum, cloneid
  180. WeakAuras.timers = timers;
  181.  
  182. local loaded_events = {};
  183. WeakAuras.loaded_events = loaded_events;
  184. local loaded_auras = {};
  185. WeakAuras.loaded_auras = loaded_auras;
  186.  
  187. -- Animations
  188. WeakAuras.animations = {};
  189. local animations = WeakAuras.animations;
  190. WeakAuras.pending_controls = {};
  191. local pending_controls = WeakAuras.pending_controls;
  192.  
  193. WeakAuras.frames = {};
  194.  
  195. WeakAuras.raidUnits = {};
  196. WeakAuras.partyUnits = {};
  197. do
  198. for i=1,40 do
  199. WeakAuras.raidUnits[i] = "raid"..i
  200. end
  201. for i=1,4 do
  202. WeakAuras.partyUnits[i] = "party"..i
  203. end
  204. end
  205. local playerLevel = UnitLevel("player");
  206.  
  207. WeakAuras.currentInstanceType = "none"
  208.  
  209. -- Custom Action Functions, keyed on id, "init" / "start" / "finish"
  210. WeakAuras.customActionsFunctions = {};
  211.  
  212. -- Custom Functions used in conditions, keyed on id, condition number, "changes", property number
  213. WeakAuras.customConditionsFunctions = {};
  214.  
  215. local anim_function_strings = WeakAuras.anim_function_strings;
  216. local anim_presets = WeakAuras.anim_presets;
  217. local load_prototype = WeakAuras.load_prototype;
  218.  
  219. local levelColors = {
  220. [0] = "|cFFFFFFFF",
  221. [1] = "|cFF40FF40",
  222. [2] = "|cFF6060FF",
  223. [3] = "|cFFFF4040"
  224. };
  225.  
  226. function WeakAuras.debug(msg, level)
  227. if(db.debug) then
  228. level = (level and levelColors[level] and level) or 2;
  229. msg = (type(msg) == "string" and msg) or (msg and "Invalid debug message of type "..type(msg)) or "Debug message not specified";
  230. DEFAULT_CHAT_FRAME:AddMessage(levelColors[level]..msg);
  231. end
  232. end
  233. local debug = WeakAuras.debug;
  234.  
  235. function WeakAuras.validate(input, default)
  236. for field, defaultValue in pairs(default) do
  237. if(type(defaultValue) == "table" and type(input[field]) ~= "table") then
  238. input[field] = {};
  239. elseif(input[field] == nil) then
  240. input[field] = defaultValue;
  241. elseif(type(input[field]) ~= type(defaultValue)) then
  242. input[field] = defaultValue;
  243. end
  244. if(type(input[field]) == "table") then
  245. WeakAuras.validate(input[field], defaultValue);
  246. end
  247. end
  248. end
  249.  
  250. function WeakAuras.RegisterRegionType(name, createFunction, modifyFunction, default, properties)
  251. if not(name) then
  252. error("Improper arguments to WeakAuras.RegisterRegionType - name is not defined");
  253. elseif(type(name) ~= "string") then
  254. error("Improper arguments to WeakAuras.RegisterRegionType - name is not a string");
  255. elseif not(createFunction) then
  256. error("Improper arguments to WeakAuras.RegisterRegionType - creation function is not defined");
  257. elseif(type(createFunction) ~= "function") then
  258. error("Improper arguments to WeakAuras.RegisterRegionType - creation function is not a function");
  259. elseif not(modifyFunction) then
  260. error("Improper arguments to WeakAuras.RegisterRegionType - modification function is not defined");
  261. elseif(type(modifyFunction) ~= "function") then
  262. error("Improper arguments to WeakAuras.RegisterRegionType - modification function is not a function")
  263. elseif not(default) then
  264. error("Improper arguments to WeakAuras.RegisterRegionType - default options are not defined");
  265. elseif(type(default) ~= "table") then
  266. error("Improper arguments to WeakAuras.RegisterRegionType - default options are not a table");
  267. elseif(type(default) ~= "table" and type(default) ~= "nil") then
  268. error("Improper arguments to WeakAuras.RegisterRegionType - properties options are not a table");
  269. elseif(regionTypes[name]) then
  270. error("Improper arguments to WeakAuras.RegisterRegionType - region type \""..name.."\" already defined");
  271. else
  272. regionTypes[name] = {
  273. create = createFunction,
  274. modify = modifyFunction,
  275. default = default,
  276. properties = properties
  277. };
  278. end
  279. end
  280.  
  281. function WeakAuras.RegisterRegionOptions(name, createFunction, icon, displayName, createThumbnail, modifyThumbnail, description, templates)
  282. if not(name) then
  283. error("Improper arguments to WeakAuras.RegisterRegionOptions - name is not defined");
  284. elseif(type(name) ~= "string") then
  285. error("Improper arguments to WeakAuras.RegisterRegionOptions - name is not a string");
  286. elseif not(createFunction) then
  287. error("Improper arguments to WeakAuras.RegisterRegionOptions - creation function is not defined");
  288. elseif(type(createFunction) ~= "function") then
  289. error("Improper arguments to WeakAuras.RegisterRegionOptions - creation function is not a function");
  290. elseif not(icon) then
  291. error("Improper arguments to WeakAuras.RegisterRegionOptions - icon is not defined");
  292. elseif not(type(icon) == "string" or type(icon) == "function") then
  293. error("Improper arguments to WeakAuras.RegisterRegionOptions - icon is not a string or a function")
  294. elseif not(displayName) then
  295. error("Improper arguments to WeakAuras.RegisterRegionOptions - display name is not defined".." "..name);
  296. elseif(type(displayName) ~= "string") then
  297. error("Improper arguments to WeakAuras.RegisterRegionOptions - display name is not a string");
  298. elseif(regionOptions[name]) then
  299. error("Improper arguments to WeakAuras.RegisterRegionOptions - region type \""..name.."\" already defined");
  300. else
  301. regionOptions[name] = {
  302. create = createFunction,
  303. icon = icon,
  304. displayName = displayName,
  305. createThumbnail = createThumbnail,
  306. modifyThumbnail = modifyThumbnail,
  307. description = description,
  308. templates = templates
  309. };
  310. end
  311. end
  312.  
  313. -- This function is replaced in WeakAurasOptions.lua
  314. function WeakAuras.IsOptionsOpen()
  315. return false;
  316. end
  317.  
  318. local LBG = LibStub("LibButtonGlow-1.0")
  319. local function WeakAuras_ShowOverlayGlow(frame)
  320. LBG.ShowOverlayGlow(frame)
  321. end
  322.  
  323. local function WeakAuras_HideOverlayGlow(frame)
  324. LBG.HideOverlayGlow(frame)
  325. end
  326.  
  327. WeakAuras.ShowOverlayGlow = WeakAuras_ShowOverlayGlow;
  328. WeakAuras.HideOverlayGlow = WeakAuras_HideOverlayGlow;
  329.  
  330. local function forbidden()
  331. print("|cffffff00A WeakAura just tried to use a forbidden function but has been blocked from doing so. Please check your auras!|r")
  332. end
  333.  
  334. local blockedFunctions = {
  335. -- Lua functions that may allow breaking out of the environment
  336. getfenv = true,
  337. setfenv = true,
  338. loadstring = true,
  339. pcall = true,
  340. -- blocked WoW API
  341. SendMail = true,
  342. SetTradeMoney = true,
  343. AddTradeMoney = true,
  344. PickupTradeMoney = true,
  345. PickupPlayerMoney = true,
  346. TradeFrame = true,
  347. MailFrame = true,
  348. EnumerateFrames = true,
  349. RunScript = true,
  350. AcceptTrade = true,
  351. SetSendMailMoney = true,
  352. EditMacro = true,
  353. SlashCmdList = true,
  354. DevTools_DumpCommand = true,
  355. hash_SlashCmdList = true,
  356. CreateMacro = true,
  357. SetBindingMacro = true,
  358. GuildDisband = true,
  359. GuildUninvite = true,
  360. }
  361.  
  362. local overrideFunctions = {
  363. ActionButton_ShowOverlayGlow = WeakAuras_ShowOverlayGlow,
  364. ActionButton_HideOverlayGlow = WeakAuras_HideOverlayGlow,
  365. }
  366.  
  367. local aura_environments = {};
  368. local current_aura_env = nil;
  369. local aura_env_stack = {}; -- Stack of of aura environments, allows use of recursive aura activations through calls to WeakAuras.ScanEvents().
  370. function WeakAuras.ActivateAuraEnvironment(id, cloneId, state)
  371. if(not id or not db.displays[id]) then
  372. -- Pop the last aura_env from the stack, and update current_aura_env appropriately.
  373. tremove(aura_env_stack);
  374. current_aura_env = aura_env_stack[#aura_env_stack] or nil;
  375. else
  376. local data = db.displays[id];
  377. if data.init_started then
  378. -- Point the current environment to the correct table
  379. aura_environments[id] = aura_environments[id] or {};
  380. current_aura_env = aura_environments[id];
  381. current_aura_env.cloneId = cloneId;
  382. current_aura_env.state = state;
  383. -- Push the new environment onto the stack
  384. tinsert(aura_env_stack, current_aura_env);
  385. else
  386. -- Reset the environment if we haven't completed init, i.e. if we add/update/replace a WeakAura
  387. aura_environments[id] = {};
  388. current_aura_env = aura_environments[id];
  389. current_aura_env.cloneId = cloneId;
  390. current_aura_env.state = state;
  391. -- Push the new environment onto the stack
  392. tinsert(aura_env_stack, current_aura_env);
  393. -- Run the init function if supplied
  394. local actions = data.actions.init;
  395. data.init_started = 1;
  396. if(actions and actions.do_custom and actions.custom) then
  397. local func = WeakAuras.customActionsFunctions[id]["init"];
  398. if func then
  399. current_aura_env.id = id;
  400. func();
  401. end
  402. end
  403. end
  404. current_aura_env.id = id;
  405. end
  406. end
  407.  
  408. local env_getglobal
  409. local exec_env = setmetatable({}, { __index =
  410. function(t, k)
  411. if k == "_G" then
  412. return t
  413. elseif k == "getglobal" then
  414. return env_getglobal
  415. elseif k == "aura_env" then
  416. return current_aura_env;
  417. elseif blockedFunctions[k] then
  418. return forbidden
  419. elseif overrideFunctions[k] then
  420. return overrideFunctions[k]
  421. else
  422. return _G[k]
  423. end
  424. end
  425. })
  426.  
  427. function env_getglobal(k)
  428. return exec_env[k]
  429. end
  430.  
  431. local function_cache = {};
  432. function WeakAuras.LoadFunction(string, id, inTrigger)
  433. if function_cache[string] then
  434. return function_cache[string]
  435. else
  436. local loadedFunction, errorString = loadstring("--[[ Error in ' ".. (id or "Unknown") .. (inTrigger and ("':'".. inTrigger) or "") .."' ]]" .. string)
  437. if errorString then
  438. print(errorString)
  439. else
  440. setfenv(loadedFunction, exec_env)
  441. local success, func = pcall(assert(loadedFunction))
  442. if success then
  443. function_cache[string] = func
  444. return func
  445. end
  446. end
  447. end
  448. end
  449.  
  450. function WeakAuras.ParseNumber(numString)
  451. if not(numString and type(numString) == "string") then
  452. if(type(numString) == "number") then
  453. return numString, "notastring";
  454. else
  455. return nil;
  456. end
  457. elseif(numString:sub(-1) == "%") then
  458. local percent = tonumber(numString:sub(0, -2));
  459. if(percent) then
  460. return percent / 100, "percent";
  461. else
  462. return nil;
  463. end
  464. else
  465. -- Matches any string with two integers separated by a forward slash
  466. -- Captures the two integers
  467. local _, _, numerator, denominator = numString:find("(%d+)%s*/%s*(%d+)");
  468. numerator, denominator = tonumber(numerator), tonumber(denominator);
  469. if(numerator and denominator) then
  470. if(denominator == 0) then
  471. return nil;
  472. else
  473. return numerator / denominator, "fraction";
  474. end
  475. else
  476. local num = tonumber(numString)
  477. if(num) then
  478. if(math.floor(num) ~= num) then
  479. return num, "decimal";
  480. else
  481. return num, "whole";
  482. end
  483. else
  484. return nil;
  485. end
  486. end
  487. end
  488. end
  489.  
  490. -- Used for the load function, could be simplified a bit
  491. -- It used to be also used for the generic trigger system
  492. function WeakAuras.ConstructFunction(prototype, trigger)
  493. local input = {"event"};
  494. local required = {};
  495. local tests = {};
  496. local debug = {};
  497. local init;
  498. if(prototype.init) then
  499. init = prototype.init(trigger);
  500. else
  501. init = "";
  502. end
  503. for index, arg in pairs(prototype.args) do
  504. local enable = true;
  505. if(type(arg.enable) == "function") then
  506. enable = arg.enable(trigger);
  507. end
  508. if(enable) then
  509. local name = arg.name;
  510. if not(arg.name or arg.hidden) then
  511. tinsert(input, "_");
  512. else
  513. if(arg.init == "arg") then
  514. tinsert(input, name);
  515. end
  516. if(arg.hidden or arg.type == "tristate" or arg.type == "toggle" or (arg.type == "multiselect" and trigger["use_"..name] ~= nil) or ((trigger["use_"..name] or arg.required) and trigger[name])) then
  517. if(arg.init and arg.init ~= "arg") then
  518. init = init.."local "..name.." = "..arg.init.."\n";
  519. end
  520. local number = tonumber(trigger[name]);
  521. local test;
  522. if(arg.type == "tristate") then
  523. if(trigger["use_"..name] == false) then
  524. test = "(not "..name..")";
  525. elseif(trigger["use_"..name]) then
  526. if(arg.test) then
  527. test = "("..arg.test:format(trigger[name])..")";
  528. else
  529. test = name;
  530. end
  531. end
  532. elseif(arg.type == "multiselect") then
  533. if(trigger["use_"..name] == false) then -- multi selection
  534. test = "(";
  535. local any = false;
  536. for value, _ in pairs(trigger[name].multi) do
  537. if not arg.test then
  538. test = test..name.."=="..(tonumber(value) or "[["..value.."]]").." or ";
  539. else
  540. test = test..arg.test:format(tonumber(value) or "[["..value.."]]").." or ";
  541. end
  542. any = true;
  543. end
  544. if(any) then
  545. test = test:sub(0, -5);
  546. else
  547. test = "(false";
  548. end
  549. test = test..")";
  550. elseif(trigger["use_"..name]) then -- single selection
  551. local value = trigger[name].single;
  552. if not arg.test then
  553. test = trigger[name].single and "("..name.."=="..(tonumber(value) or "[["..value.."]]")..")";
  554. else
  555. test = trigger[name].single and "("..arg.test:format(tonumber(value) or "[["..value.."]]")..")";
  556. end
  557. end
  558. elseif(arg.type == "toggle") then
  559. if(trigger["use_"..name]) then
  560. if(arg.test) then
  561. test = "("..arg.test:format(trigger[name])..")";
  562. else
  563. test = name;
  564. end
  565. end
  566. elseif(arg.test) then
  567. test = "("..arg.test:format(trigger[name])..")";
  568. elseif(arg.type == "longstring" and trigger[name.."_operator"]) then
  569. if(trigger[name.."_operator"] == "==") then
  570. test = "("..name.."==[["..trigger[name].."]])";
  571. else
  572. test = "("..name..":"..trigger[name.."_operator"]:format(trigger[name])..")";
  573. end
  574. else
  575. if(type(trigger[name]) == "table") then
  576. trigger[name] = "error";
  577. end
  578. test = "("..name..(trigger[name.."_operator"] or "==")..(number or "[["..(trigger[name] or "").."]]")..")";
  579. end
  580. if(arg.required) then
  581. tinsert(required, test);
  582. else
  583. tinsert(tests, test);
  584. end
  585. if(arg.debug) then
  586. tinsert(debug, arg.debug:format(trigger[name]));
  587. end
  588. end
  589. end
  590. end
  591. end
  592.  
  593. local ret = "return function("..tconcat(input, ", ")..")\n";
  594. ret = ret..(init or "");
  595. ret = ret..(#debug > 0 and tconcat(debug, "\n") or "");
  596. ret = ret.."if(";
  597. ret = ret..((#required > 0) and tconcat(required, " and ").." and " or "");
  598. ret = ret..(#tests > 0 and tconcat(tests, " and ") or "true");
  599. ret = ret..") then\n";
  600. if(#debug > 0) then
  601. ret = ret.."print('ret: true');\n";
  602. end
  603. ret = ret.."return true else return false end end";
  604.  
  605. return ret;
  606. end
  607.  
  608. function WeakAuras.GetActiveConditions(id, cloneId)
  609. triggerState[id].activatedConditions[cloneId] = triggerState[id].activatedConditions[cloneId] or {};
  610. return triggerState[id].activatedConditions[cloneId];
  611. end
  612.  
  613.  
  614. local function formatValueForAssignment(vtype, value, pathToCustomFunction)
  615. if (value == nil) then
  616. value = false;
  617. end
  618. if (vtype == "bool" or vtype == "number") then
  619. return tostring(value);
  620. elseif (vtype == "list") then
  621. return type(value) == "string" and string.format("%q", value) or "nil";
  622. elseif(vtype == "color") then
  623. if (value and type(value) == "table") then
  624. return string.format("{%s, %s, %s, %s}", tostring(value[1]), tostring(value[2]), tostring(value[3]), tostring(value[4]));
  625. end
  626. elseif(vtype == "chat") then
  627. if (value and type(value) == "table") then
  628. return string.format("{message_type = %q, message = %q, message_dest = %q, message_channel = %q, message_custom = %s}",
  629. tostring(value.message_type), tostring(value.message or ""),
  630. tostring(value.message_dest), tostring(value.message_channel),
  631. pathToCustomFunction);
  632. end
  633. elseif(vtype == "sound") then
  634. if (value and type(value) == "table") then
  635. return string.format("{ sound = %q, sound_channel = %q, sound_path = %q, sound_kit_id = %q, sound_type = %q, %s}",
  636. tostring(value.sound or ""), tostring(value.sound_channel or ""), tostring(value.sound_path or ""),
  637. tostring(value.sound_kit_id or ""), tostring(value.sound_type or ""),
  638. value.sound_repeat and "sound_repeat = " .. tostring(value.sound_repeat) or "nil");
  639. end
  640. elseif(vtype == "customcode") then
  641. return string.format("%s", pathToCustomFunction);
  642. end
  643. return "nil";
  644. end
  645.  
  646. local function formatValueForCall(type, property)
  647. if (type == "bool" or type == "number" or type == "list") then
  648. return "propertyChanges." .. property;
  649. elseif (type == "color") then
  650. local pcp = "propertyChanges." .. property;
  651. return pcp .. "[1], " .. pcp .. "[2], " .. pcp .. "[3], " .. pcp .. "[4]";
  652. end
  653. return "nil";
  654. end
  655.  
  656. local conditionChecksTimers = {};
  657. conditionChecksTimers.recheckTime = {};
  658. conditionChecksTimers.recheckHandle = {};
  659.  
  660. function WeakAuras.scheduleConditionCheck(time, id, cloneId)
  661. conditionChecksTimers.recheckTime[id] = conditionChecksTimers.recheckTime[id] or {}
  662. conditionChecksTimers.recheckHandle[id] = conditionChecksTimers.recheckHandle[id] or {};
  663.  
  664. if (conditionChecksTimers.recheckTime[id][cloneId] and conditionChecksTimers.recheckTime[id][cloneId] > time) then
  665. timer:CancelTimer(conditionChecksTimers.recheckHandle);
  666. conditionChecksTimers.recheckHandle = nil;
  667. end
  668.  
  669. if (conditionChecksTimers.recheckTime[id][cloneId] == nil) then
  670. conditionChecksTimers.recheckHandle[id][cloneId] = timer:ScheduleTimer(function()
  671. local region;
  672. if(cloneId and cloneId ~= "") then
  673. region = clones[id] and clones[id][cloneId];
  674. else
  675. region = WeakAuras.regions[id].region;
  676. end
  677. if (region and region.state and region.state.show) then
  678. checkConditions[id](region);
  679. end
  680. end, time - GetTime())
  681. end
  682. end
  683.  
  684. local function CreateCheckCondition(ret, condition, conditionNumber, allConditionsTemplate, debug)
  685. local trigger = condition.check and condition.check.trigger;
  686. local variable = condition.check and condition.check.variable;
  687. local op = condition.check and condition.check.op;
  688. local value = condition.check and condition.check.value;
  689. if (trigger and variable and value) then
  690. local conditionTemplate = allConditionsTemplate[trigger] and allConditionsTemplate[trigger][variable];
  691. local type = conditionTemplate and conditionTemplate.type;
  692. local test = conditionTemplate and conditionTemplate.test;
  693.  
  694. local check = nil;
  695. local stateCheck = "state and state.show and ";
  696. local stateVariableCheck = "state." .. variable .. "~= nil and ";
  697. if (test) then
  698. if (value) then
  699. check = string.format(test, value);
  700. end
  701. elseif (type == "number" and op) then
  702. check = stateCheck .. stateVariableCheck .. "state." .. variable .. op .. value;
  703. elseif (type == "timer" and op) then
  704. if (op == "==") then
  705. check = stateCheck .. stateVariableCheck .. "abs(state." ..variable .. "- now -" .. value .. ") < 0.05";
  706. else
  707. check = stateCheck .. stateVariableCheck .. "state." .. variable .. "- now" .. op .. value;
  708. end
  709. elseif (type == "select" and op) then
  710. if (tonumber(value)) then
  711. check = stateCheck .. stateVariableCheck .. "state." .. variable .. op .. tonumber(value);
  712. else
  713. check = stateCheck .. stateVariableCheck .. "state." .. variable .. op .. "'" .. value .. "'";
  714. end
  715. elseif (type == "bool") then
  716. local rightSide = value == 0 and "false" or "true";
  717. check = stateCheck .. stateVariableCheck .. "state." .. variable .. "==" .. rightSide
  718. elseif (type == "string") then
  719. if(op == "==") then
  720. check = stateCheck .. stateVariableCheck .. "state." .. variable .. " == [[" .. value .. "]]";
  721. elseif (op == "find('%s')") then
  722. check = stateCheck .. stateVariableCheck .. "state." .. variable .. ":find([[" .. value .. "]], 1, true)";
  723. elseif (op == "match('%s')") then
  724. check = stateCheck .. stateVariableCheck .. "state." .. variable .. ":match([[" .. value .. "]], 1, true)";
  725. end
  726. end
  727.  
  728. if (check) then
  729. ret = ret .. " allStates = WeakAuras.GetTriggerStateForTrigger(id, " .. trigger .. ")\n";
  730. ret = ret .. " state = allStates[cloneId] or allStates['']\n";
  731. ret = ret .. " if (" .. check .. ") then\n";
  732. ret = ret .. " newActiveConditions[" .. conditionNumber .. "] = true;\n";
  733. ret = ret .. " end\n";
  734. end
  735.  
  736. if (type == "timer" and value) then
  737. ret = ret .. " local nextTime = state and state." .. variable .. " and (state." .. variable .. " -" .. value .. ")\n";
  738. ret = ret .. " if (nextTime and (not recheckTime or nextTime < recheckTime) and nextTime >= now) then\n"
  739. ret = ret .. " recheckTime = nextTime\n";
  740. ret = ret .. " end\n"
  741. end
  742. ret = ret .. "\n";
  743. end
  744. return ret;
  745. end
  746.  
  747. local function CreateDeactivateCondition(ret, condition, conditionNumber, data, properties, usedProperties, debug)
  748. if (condition.changes) then
  749. ret = ret .. " if (activatedConditions[".. conditionNumber .. "] and not newActiveConditions[" .. conditionNumber .. "]) then\n"
  750. if (debug) then ret = ret .. " print('Deactivating condition " .. conditionNumber .. "' )\n"; end
  751. for changeNum, change in ipairs(condition.changes) do
  752. if (change.property) then
  753. local propertyData = properties and properties[change.property]
  754. if (propertyData and propertyData.type and propertyData.setter) then
  755. usedProperties[change.property] = true;
  756. ret = ret .. " propertyChanges." .. change.property .. " = " .. formatValueForAssignment(propertyData.type, data[change.property]) .. "\n";
  757. if (debug) then ret = ret .. " print('- " .. change.property .. " " ..formatValueForAssignment(propertyData.type, data[change.property]) .. "')\n"; end
  758. end
  759. end
  760. end
  761. ret = ret .. " end\n"
  762. end
  763. return ret;
  764. end
  765.  
  766. local function CreateActivateCondition(ret, id, condition, conditionNumber, properties, debug)
  767. if (condition.changes) then
  768. ret = ret .. " if (newActiveConditions[" .. conditionNumber .. "]) then\n"
  769. ret = ret .. " if (not activatedConditions[".. conditionNumber .. "]) then\n"
  770. if (debug) then ret = ret .. " print('Activating condition " .. conditionNumber .. "' )\n"; end
  771. -- non active => active
  772. for changeNum, change in ipairs(condition.changes) do
  773. if (change.property) then
  774. local propertyData = properties and properties[change.property]
  775. if (propertyData and propertyData.type) then
  776. if (propertyData.setter) then
  777. ret = ret .. " propertyChanges." .. change.property .. " = " .. formatValueForAssignment(propertyData.type, change.value) .. "\n";
  778. if (debug) then ret = ret .. " print('- " .. change.property .. " " .. formatValueForAssignment(propertyData.type, change.value) .. "')\n"; end
  779. elseif (propertyData.action) then
  780. local pathToCustomFunction = "nil";
  781. if (WeakAuras.customConditionsFunctions[id]
  782. and WeakAuras.customConditionsFunctions[id][conditionNumber]
  783. and WeakAuras.customConditionsFunctions[id][conditionNumber].changes
  784. and WeakAuras.customConditionsFunctions[id][conditionNumber].changes[changeNum]) then
  785. pathToCustomFunction = string.format("WeakAuras.customConditionsFunctions[%q][%s].changes[%s]", id, conditionNumber, changeNum);
  786. end
  787. ret = ret .. " if (not skipActions) then\n";
  788. ret = ret .. " region:" .. propertyData.action .. "(" .. formatValueForAssignment(propertyData.type, change.value, pathToCustomFunction) .. ")" .. "\n";
  789. if (debug) then ret = ret .. " print('# " .. propertyData.action .. "(" .. formatValueForAssignment(propertyData.type, change.value, pathToCustomFunction) .. "')\n"; end
  790. ret = ret .. " end\n"
  791. end
  792. end
  793. end
  794. end
  795. ret = ret .. " else\n"
  796. -- active => active, only override properties
  797. for changeNum, change in ipairs(condition.changes) do
  798. if (change.property) then
  799. local propertyData = properties and properties[change.property]
  800. if (propertyData and propertyData.type and propertyData.setter) then
  801. ret = ret .. " if(propertyChanges.".. change.property .."~= nil) then\n"
  802. ret = ret .. " propertyChanges." .. change.property .. " = " .. formatValueForAssignment(propertyData.type, change.value) .. "\n";
  803. if (debug) then ret = ret .. " print('- " .. change.property .. " " .. formatValueForAssignment(propertyData.type, change.value) .. "')\n"; end
  804. ret = ret .. " end\n"
  805. end
  806. end
  807. end
  808. ret = ret .. " end\n"
  809. ret = ret .. " end\n"
  810. ret = ret .. "\n";
  811. ret = ret .. " activatedConditions[".. conditionNumber .. "] = newActiveConditions[" .. conditionNumber .. "]\n";
  812. end
  813.  
  814. return ret;
  815. end
  816.  
  817.  
  818. function WeakAuras.LoadCustomActionFunctions(data)
  819. local id = data.id;
  820. WeakAuras.customActionsFunctions[id] = {};
  821.  
  822. if (data.actions) then
  823. if (data.actions.init and data.actions.init.do_custom and data.actions.init.custom) then
  824. local func = WeakAuras.LoadFunction("return function() "..(data.actions.init.custom).."\n end", id);
  825. WeakAuras.customActionsFunctions[id]["init"] = func;
  826. end
  827.  
  828. if (data.actions.start) then
  829. if (data.actions.start.do_custom and data.actions.start.custom) then
  830. local func = WeakAuras.LoadFunction("return function() "..(data.actions.start.custom).."\n end", id);
  831. WeakAuras.customActionsFunctions[id]["start"] = func;
  832. end
  833.  
  834. if (data.actions.start.do_message and data.actions.start.message_custom) then
  835. local func = WeakAuras.LoadFunction("return "..(data.actions.start.message_custom), id);
  836. WeakAuras.customActionsFunctions[id]["start_message"] = func;
  837. end
  838. end
  839.  
  840. if (data.actions.finish) then
  841. if (data.actions.finish.do_custom and data.actions.finish.custom) then
  842. local func = WeakAuras.LoadFunction("return function() "..(data.actions.finish.custom).."\n end", id);
  843. WeakAuras.customActionsFunctions[id]["finish"] = func;
  844. end
  845.  
  846. if (data.actions.finish.do_message and data.actions.finish.message_custom) then
  847. local func = WeakAuras.LoadFunction("return "..(data.actions.finish.message_custom), id);
  848. WeakAuras.customActionsFunctions[id]["finish_message"] = func;
  849. end
  850. end
  851.  
  852. end
  853. end
  854.  
  855. function WeakAuras.LoadConditionPropertyFunctions(data)
  856. local id = data.id;
  857. if (data.conditions) then
  858. WeakAuras.customConditionsFunctions[id] = {};
  859. for conditionNumber, condition in ipairs(data.conditions) do
  860. if (condition.changes) then
  861. for changeIndex, change in ipairs(condition.changes) do
  862. if ( (change.property == "chat" or change.property == "customcode") and type(change.value) == "table" and change.value.custom) then
  863. local custom = change.value.custom;
  864. local prefix, suffix;
  865. if (change.property == "chat") then
  866. prefix, suffix = "return ", "";
  867. else
  868. prefix, suffix = "return function()", "\nend";
  869. end
  870. local customFunc = WeakAuras.LoadFunction(prefix .. custom .. suffix, id, "condition");
  871. if (customFunc) then
  872. WeakAuras.customConditionsFunctions[id][conditionNumber] = WeakAuras.customConditionsFunctions[id][conditionNumber] or {};
  873. WeakAuras.customConditionsFunctions[id][conditionNumber].changes = WeakAuras.customConditionsFunctions[id][conditionNumber].changes or {};
  874. WeakAuras.customConditionsFunctions[id][conditionNumber].changes[changeIndex] = customFunc;
  875. end
  876. end
  877. end
  878. end
  879. end
  880. end
  881. end
  882.  
  883. function WeakAuras.ConstructConditionFunction(data)
  884. local debug = false;
  885. if (not data.conditions or #data.conditions == 0) then
  886. return nil;
  887. end
  888.  
  889. local usedProperties = {};
  890.  
  891. local allConditionsTemplate = WeakAuras.GetTriggerConditions(data);
  892.  
  893. local ret = "";
  894. ret = ret .. "local newActiveConditions = {};\n"
  895. ret = ret .. "local propertyChanges = {}\n;"
  896. ret = ret .. "return function(region, skipActions)\n";
  897. if (debug) then ret = ret .. " print('check conditions for:', region.id, region.cloneId)\n"; end
  898. ret = ret .. " local id = region.id\n";
  899. ret = ret .. " local cloneId = region.cloneId or ''\n";
  900. ret = ret .. " local activatedConditions = WeakAuras.GetActiveConditions(id, cloneId)\n";
  901. ret = ret .. " wipe(newActiveConditions)\n";
  902. ret = ret .. " local allStates\n";
  903. ret = ret .. " local state\n";
  904. ret = ret .. " local recheckTime;\n"
  905. ret = ret .. " local now = GetTime();\n"
  906.  
  907. local normalConditionCount = data.conditions and #data.conditions;
  908. -- First Loop gather which conditions are active
  909. if (data.conditions) then
  910. for conditionNumber, condition in ipairs(data.conditions) do
  911. ret = CreateCheckCondition(ret, condition, conditionNumber, allConditionsTemplate, debug)
  912. end
  913. end
  914.  
  915. ret = ret .. " if (recheckTime) then\n"
  916. ret = ret .. " WeakAuras.scheduleConditionCheck(recheckTime, id, cloneId);\n"
  917. ret = ret .. " end\n"
  918.  
  919. local properties = WeakAuras.regionTypes[data.regionType] and WeakAuras.regionTypes[data.regionType].properties;
  920.  
  921. -- Now build a propety + change list
  922. -- Second Loop deals with conditions that are no longer active
  923. ret = ret .. " wipe(propertyChanges)\n"
  924. if (data.conditions) then
  925. for conditionNumber, condition in ipairs(data.conditions) do
  926. ret = CreateDeactivateCondition(ret, condition, conditionNumber, data, properties, usedProperties, debug)
  927. end
  928. end
  929. ret = ret .. "\n";
  930.  
  931. -- Third Loop deals with conditions that are newly active
  932. if (data.conditions) then
  933. for conditionNumber, condition in ipairs(data.conditions) do
  934. ret = CreateActivateCondition(ret, data.id, condition, conditionNumber, properties, debug)
  935. end
  936. end
  937.  
  938. -- Last apply changes to region
  939.  
  940. local allPotentialProperties = WeakAuras.regionTypes[data.regionType] and WeakAuras.regionTypes[data.regionType].properties;
  941. if (not allPotentialProperties) then return nil; end
  942.  
  943. for property, _ in pairs(usedProperties) do
  944. ret = ret .. " if( propertyChanges." .. property .. "~= nil) then\n"
  945. ret = ret .. " region:" .. allPotentialProperties[property].setter .. "(" .. formatValueForCall(allPotentialProperties[property].type, property) .. ")\n";
  946. if (debug) then ret = ret .. " print('Calling " .. allPotentialProperties[property].setter .. " with', " .. formatValueForCall(allPotentialProperties[property].type, property) .. ")\n"; end
  947. ret = ret .. " end\n";
  948. end
  949. ret = ret .. "end\n";
  950.  
  951. --print(ret);
  952.  
  953. return ret;
  954. end
  955.  
  956. WeakAuras.talent_types_specific = {}
  957. WeakAuras.pvp_talent_types_specific = {}
  958. function WeakAuras.CreateTalentCache()
  959. local _, player_class = UnitClass("player")
  960. WeakAuras.talent_types_specific[player_class] = WeakAuras.talent_types_specific[player_class] or {};
  961. WeakAuras.pvp_talent_types_specific[player_class] = WeakAuras.pvp_talent_types_specific[player_class] or {};
  962. local spec = GetSpecialization()
  963. WeakAuras.talent_types_specific[player_class][spec] = WeakAuras.talent_types_specific[player_class][spec] or {};
  964. WeakAuras.pvp_talent_types_specific[player_class][spec] = WeakAuras.pvp_talent_types_specific[player_class][spec] or {};
  965.  
  966. for tier = 1, MAX_TALENT_TIERS do
  967. for column = 1, NUM_TALENT_COLUMNS do
  968. -- Get name and icon info for the current talent of the current class and save it
  969. local _, talentName, talentIcon = GetTalentInfo(tier, column, 1)
  970. local talentId = (tier-1)*3+column
  971. -- Get the icon and name from the talent cache and record it in the table that will be used by WeakAurasOptions
  972. if (talentName and talentIcon) then
  973. WeakAuras.talent_types_specific[player_class][spec][talentId] = "|T"..talentIcon..":0|t "..talentName
  974. end
  975. end
  976. end
  977.  
  978. for tier = 1, MAX_PVP_TALENT_TIERS do
  979. for column = 1, MAX_PVP_TALENT_COLUMNS do
  980. local _, talentName, talentIcon = GetPvpTalentInfo(tier, column, 1);
  981. local talentId = (tier-1)*3+column
  982. if (talentName and talentIcon) then
  983. WeakAuras.pvp_talent_types_specific[player_class][spec][talentId] = "|T"..talentIcon..":0|t "..talentName
  984. end
  985. end
  986. end
  987. end
  988.  
  989. local frame = CreateFrame("FRAME", "WeakAurasFrame", UIParent);
  990. WeakAuras.frames["WeakAuras Main Frame"] = frame;
  991. frame:SetAllPoints(UIParent);
  992. local loadedFrame = CreateFrame("FRAME");
  993. WeakAuras.frames["Addon Initialization Handler"] = loadedFrame;
  994. loadedFrame:RegisterEvent("ADDON_LOADED");
  995. loadedFrame:RegisterEvent("PLAYER_LOGIN");
  996. loadedFrame:RegisterEvent("PLAYER_ENTERING_WORLD");
  997. loadedFrame:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED");
  998. loadedFrame:SetScript("OnEvent", function(self, event, addon)
  999. if(event == "ADDON_LOADED") then
  1000. if(addon == ADDON_NAME) then
  1001. WeakAurasSaved = WeakAurasSaved or {};
  1002. db = WeakAurasSaved;
  1003.  
  1004. -- Defines the action squelch period after login
  1005. -- Stored in SavedVariables so it can be changed by the user if they find it necessary
  1006. db.login_squelch_time = db.login_squelch_time or 10;
  1007.  
  1008. -- Deprecated fields with *lots* of data, clear them out
  1009. db.iconCache = nil;
  1010. db.iconHash = nil;
  1011. db.tempIconCache = nil;
  1012. db.dynamicIconCache = db.dynamicIconCache or {};
  1013.  
  1014. db.displays = db.displays or {};
  1015. db.registered = db.registered or {};
  1016.  
  1017. WeakAuras.UpdateCurrentInstanceType();
  1018. WeakAuras.SyncParentChildRelationships();
  1019. end
  1020. elseif(event == "PLAYER_LOGIN") then
  1021. local toAdd = {};
  1022. for id, data in pairs(db.displays) do
  1023. if(id ~= data.id) then
  1024. print("|cFF8800FFWeakAuras|r detected a corrupt entry in WeakAuras saved displays - '"..tostring(id).."' vs '"..tostring(data.id).."'" );
  1025. data.id = id;
  1026. end
  1027. tinsert(toAdd, data);
  1028. end
  1029. WeakAuras.AddMany(toAdd);
  1030. WeakAuras.AddManyFromAddons(from_files);
  1031. WeakAuras.RegisterDisplay = WeakAuras.AddFromAddon;
  1032.  
  1033. WeakAuras.ResolveCollisions(function() registeredFromAddons = true; end);
  1034. WeakAuras.FixGroupChildrenOrder();
  1035.  
  1036. for _, triggerSystem in pairs(triggerSystems) do
  1037. if (triggerSystem.AllAdded) then
  1038. triggerSystem.AllAdded();
  1039. end
  1040. end
  1041. -- check in case of a disconnect during an encounter.
  1042. if (db.CurrentEncounter) then
  1043. WeakAuras.CheckForPreviousEncounter()
  1044. end
  1045.  
  1046. WeakAuras.RegisterLoadEvents();
  1047. WeakAuras.Resume();
  1048. elseif(event == "PLAYER_ENTERING_WORLD") then
  1049. -- Schedule events that need to be handled some time after login
  1050. timer:ScheduleTimer(function() squelch_actions = false; end, db.login_squelch_time); -- No sounds while loading
  1051. WeakAuras.CreateTalentCache() -- It seems that GetTalentInfo might give info about whatever class was previously being played, until PLAYER_ENTERING_WORLD
  1052. WeakAuras.UpdateCurrentInstanceType();
  1053. elseif(event == "ACTIVE_TALENT_GROUP_CHANGED") then
  1054. WeakAuras.CreateTalentCache();
  1055. elseif(event == "PLAYER_REGEN_ENABLED") then
  1056. if (queueshowooc) then
  1057. WeakAuras.OpenOptions(queueshowooc)
  1058. queueshowooc = nil
  1059. WeakAuras.frames["Addon Initialization Handler"]:UnregisterEvent("PLAYER_REGEN_ENABLED")
  1060. end
  1061. end
  1062. end);
  1063.  
  1064. function WeakAuras.SetImporting(b)
  1065. importing = b;
  1066. end
  1067.  
  1068. function WeakAuras.IsImporting()
  1069. return importing;
  1070. end
  1071.  
  1072. function WeakAuras.IsPaused()
  1073. return paused;
  1074. end
  1075.  
  1076. function WeakAuras.Pause()
  1077. paused = true;
  1078. -- Forcibly hide all displays, and clear all trigger information (it will be restored on .Resume() due to forced events)
  1079. for id, region in pairs(regions) do
  1080. region.region:Collapse(); -- ticket 366
  1081. end
  1082.  
  1083. for id, cloneList in pairs(clones) do
  1084. for cloneId, clone in pairs(cloneList) do
  1085. clone:Collapse();
  1086. end
  1087. end
  1088. end
  1089.  
  1090. function WeakAuras.Resume()
  1091. paused = false;
  1092. squelch_actions = true;
  1093. WeakAuras.ScanAll();
  1094. squelch_actions = false;
  1095. end
  1096.  
  1097. function WeakAuras.Toggle()
  1098. if(paused) then
  1099. WeakAuras.Resume();
  1100. else
  1101. WeakAuras.Pause();
  1102. end
  1103. end
  1104.  
  1105. function WeakAuras.PauseAllDynamicGroups()
  1106. for id, region in pairs(regions) do
  1107. if (region.region.ControlChildren) then
  1108. region.region:Suspend();
  1109. end
  1110. end
  1111. end
  1112.  
  1113. function WeakAuras.ResumeAllDynamicGroups()
  1114. for id, region in pairs(regions) do
  1115. if (region.region.ControlChildren) then
  1116. region.region:Resume();
  1117. end
  1118. end
  1119. end
  1120.  
  1121. function WeakAuras.ScanAll()
  1122.  
  1123. WeakAuras.PauseAllDynamicGroups();
  1124.  
  1125. for id, region in pairs(regions) do
  1126. region.region:Collapse();
  1127. end
  1128.  
  1129. for id, cloneList in pairs(clones) do
  1130. for cloneId, clone in pairs(cloneList) do
  1131. clone:Collapse();
  1132. end
  1133. end
  1134.  
  1135. WeakAuras.ResumeAllDynamicGroups();
  1136.  
  1137. WeakAuras.ReloadAll();
  1138.  
  1139. for _, triggerSystem in pairs(triggerSystems) do
  1140. triggerSystem.ScanAll();
  1141. end
  1142. end
  1143.  
  1144. -- encounter stuff
  1145. function WeakAuras.StoreBossGUIDs()
  1146. if (WeakAuras.CurrentEncounter and WeakAuras.CurrentEncounter.boss_guids) then
  1147. for i = 1, 5 do
  1148. if (UnitExists ("boss" .. i)) then
  1149. local guid = UnitGUID ("boss" .. i)
  1150. if (guid) then
  1151. WeakAuras.CurrentEncounter.boss_guids [guid] = true
  1152. end
  1153. end
  1154. end
  1155. db.CurrentEncounter = WeakAuras.CurrentEncounter
  1156. end
  1157. end
  1158.  
  1159. function WeakAuras.CheckForPreviousEncounter()
  1160. if (UnitAffectingCombat ("player") or InCombatLockdown()) then
  1161. for i = 1, 5 do
  1162. if (UnitExists ("boss" .. i)) then
  1163. local guid = UnitGUID ("boss" .. i)
  1164. if (guid and db.CurrentEncounter.boss_guids [guid]) then
  1165. -- we are in the same encounter
  1166. WeakAuras.CurrentEncounter = db.CurrentEncounter
  1167. return true
  1168. end
  1169. end
  1170. end
  1171. db.CurrentEncounter = nil
  1172. else
  1173. db.CurrentEncounter = nil
  1174. end
  1175. end
  1176.  
  1177. function WeakAuras.DestroyEncounterTable()
  1178. if (WeakAuras.CurrentEncounter) then
  1179. wipe(WeakAuras.CurrentEncounter)
  1180. end
  1181. WeakAuras.CurrentEncounter = nil
  1182. db.CurrentEncounter = nil
  1183. end
  1184.  
  1185. function WeakAuras.CreateEncounterTable(encounter_id)
  1186. local _, _, _, _, _, _, _, ZoneMapID = GetInstanceInfo()
  1187. WeakAuras.CurrentEncounter = {
  1188. id = encounter_id,
  1189. zone_id = ZoneMapID,
  1190. boss_guids = {},
  1191. }
  1192. timer:ScheduleTimer(WeakAuras.StoreBossGUIDs, 2)
  1193.  
  1194. return WeakAuras.CurrentEncounter
  1195. end
  1196.  
  1197. function WeakAuras.LoadEncounterInitScripts(id)
  1198. if (WeakAuras.currentInstanceType ~= "raid") then
  1199. return
  1200. end
  1201. if (id) then
  1202. local data = db.displays[id]
  1203. if (data and data.load.use_encounterid and not data.init_started and data.actions.init and data.actions.init.do_custom) then
  1204. WeakAuras.ActivateAuraEnvironment(id)
  1205. WeakAuras.ActivateAuraEnvironment(nil)
  1206. end
  1207. else
  1208. for id, data in pairs(db.displays) do
  1209. if (data.load.use_encounterid and not data.init_started and data.actions.init and data.actions.init.do_custom) then
  1210. WeakAuras.ActivateAuraEnvironment(id)
  1211. WeakAuras.ActivateAuraEnvironment(nil)
  1212. end
  1213. end
  1214. end
  1215. end
  1216.  
  1217. function WeakAuras.UpdateCurrentInstanceType(instanceType)
  1218. if (not IsInInstance()) then
  1219. WeakAuras.currentInstanceType = "none"
  1220. else
  1221. WeakAuras.currentInstanceType = instanceType or select (2, GetInstanceInfo())
  1222. end
  1223. end
  1224.  
  1225. local pausedOptionsProcessing = false;
  1226. function WeakAuras.pauseOptionsProcessing(enable)
  1227. pausedOptionsProcessing = enable;
  1228. end
  1229.  
  1230. function WeakAuras.IsOptionsProcessingPaused()
  1231. return pausedOptionsProcessing;
  1232. end
  1233.  
  1234. function WeakAuras.ScanForLoads(self, event, arg1)
  1235. if (WeakAuras.IsOptionsProcessingPaused()) then
  1236. return;
  1237. end
  1238. -- PET_BATTLE_CLOSE fires twice at the end of a pet battle. IsInBattle evaluates to TRUE during the
  1239. -- first firing, and FALSE during the second. I am not sure if this check is necessary, but the
  1240. -- following IF statement limits the impact of the PET_BATTLE_CLOSE event to the second one.
  1241. if (event == "PET_BATTLE_CLOSE" and C_PetBattles.IsInBattle()) then return end
  1242.  
  1243. if(event == "PLAYER_LEVEL_UP") then
  1244. playerLevel = arg1;
  1245. end
  1246.  
  1247. -- encounter id stuff, we are holding the current combat id to further load checks.
  1248. -- there is three ways to unload: encounter_end / zone changed (hearthstone used) / reload or disconnect
  1249. -- regen_enabled isn't good due to combat drop abilities such invisibility, vanish, fake death, etc.
  1250. local encounter_id = WeakAuras.CurrentEncounter and WeakAuras.CurrentEncounter.id or 0
  1251.  
  1252. if (event == "ENCOUNTER_START") then
  1253. encounter_id = tonumber (arg1)
  1254. WeakAuras.CreateEncounterTable (encounter_id)
  1255. elseif (event == "ENCOUNTER_END") then
  1256. encounter_id = 0
  1257. WeakAuras.DestroyEncounterTable()
  1258. end
  1259.  
  1260. local player, realm, spec, role, zone = UnitName("player"), GetRealmName(), GetSpecialization(), UnitGroupRolesAssigned("player"), GetRealZoneText();
  1261. local zoneId = HBD:GetPlayerZone();
  1262. local _, race = UnitRace("player")
  1263. local faction = UnitFactionGroup("player")
  1264.  
  1265. if role == "NONE" then
  1266. if IsInRaid() then
  1267. for i=1,GetNumGroupMembers() do
  1268. if UnitIsUnit(WeakAuras.raidUnits[i],"player") then
  1269. local _, _, _, _, _, _, _, _, _, raid_role, _, spec_role = GetRaidRosterInfo(i)
  1270. if raid_role and raid_role == "MAINTANK" then role = "TANK" end
  1271. if role == "NONE" then
  1272. if spec and spec > 0 then
  1273. local tmprole = GetSpecializationRole(spec)
  1274. if type(tmprole) == "string" then
  1275. role = tmprole
  1276. end
  1277. end
  1278. end
  1279. break;
  1280. end
  1281. end
  1282. end
  1283. end
  1284.  
  1285. local _, class = UnitClass("player");
  1286. -- 0:none 1:5N 2:5H 3:10N 4:25N 5:10H 6:25H 7:LFR 8:5CH 9:40N
  1287. local inInstance, Type = IsInInstance()
  1288. local size, difficulty
  1289. local incombat = UnitAffectingCombat("player") -- or UnitAffectingCombat("pet");
  1290. local inpetbattle = C_PetBattles.IsInBattle()
  1291. local vehicle = UnitInVehicle('player');
  1292. local vehicleUi = UnitHasVehicleUI('player');
  1293.  
  1294. local _, instanceType, difficultyIndex, _, _, _, _, ZoneMapID = GetInstanceInfo()
  1295. if (inInstance) then
  1296. WeakAuras.UpdateCurrentInstanceType(instanceType)
  1297. size = Type
  1298. if difficultyIndex == 1 then
  1299. size = "party"
  1300. difficulty = "normal"
  1301. elseif difficultyIndex == 2 then
  1302. size = "party"
  1303. difficulty = "heroic"
  1304. elseif difficultyIndex == 3 then
  1305. size = "ten"
  1306. difficulty = "normal"
  1307. elseif difficultyIndex == 4 then
  1308. size = "twentyfive"
  1309. difficulty = "normal"
  1310. elseif difficultyIndex == 5 then
  1311. size = "ten"
  1312. difficulty = "heroic"
  1313. elseif difficultyIndex == 6 then
  1314. size = "twentyfive"
  1315. difficulty = "heroic"
  1316. elseif difficultyIndex == 7 then
  1317. size = "twentyfive"
  1318. difficulty = "lfr"
  1319. elseif difficultyIndex == 8 then
  1320. size = "party"
  1321. difficulty = "challenge"
  1322. elseif difficultyIndex == 9 then
  1323. size = "fortyman"
  1324. difficulty = "normal"
  1325. elseif difficultyIndex == 11 then
  1326. size = "scenario"
  1327. difficulty = "heroic"
  1328. elseif difficultyIndex == 12 then
  1329. size = "scenario"
  1330. difficulty = "normal"
  1331. elseif difficultyIndex == 14 then
  1332. size = "flexible"
  1333. difficulty = "normal"
  1334. elseif difficultyIndex == 15 then
  1335. size = "flexible"
  1336. difficulty = "heroic"
  1337. elseif difficultyIndex == 16 then
  1338. size = "twenty"
  1339. difficulty = "mythic"
  1340. elseif difficultyIndex == 17 then
  1341. size = "flexible"
  1342. difficulty = "lfr"
  1343. elseif difficultyIndex == 23 then
  1344. size = "party"
  1345. difficulty = "mythic"
  1346. elseif difficultyIndex == 24 then
  1347. size = "party"
  1348. difficulty = "timewalking"
  1349. end
  1350. else
  1351. WeakAuras.UpdateCurrentInstanceType();
  1352. size = "none"
  1353. difficulty = "none"
  1354. end
  1355.  
  1356. if (WeakAuras.CurrentEncounter) then
  1357. if (ZoneMapID ~= WeakAuras.CurrentEncounter.zone_id and not incombat) then
  1358. encounter_id = 0
  1359. WeakAuras.DestroyEncounterTable()
  1360. end
  1361. end
  1362.  
  1363. if (event == "ZONE_CHANGED_NEW_AREA") then
  1364. WeakAuras.LoadEncounterInitScripts();
  1365. end
  1366.  
  1367. local changed = 0;
  1368. local shouldBeLoaded, couldBeLoaded;
  1369. for id, data in pairs(db.displays) do
  1370. if (data and not data.controlledChildren) then
  1371. local loadFunc = loadFuncs[id];
  1372. shouldBeLoaded = loadFunc and loadFunc("ScanForLoads_Auras", incombat, IsInGroup(), inpetbattle, vehicle, vehicleUi, player, realm, class, spec, race, faction, playerLevel, zone, zoneId, encounter_id, size, difficulty, role);
  1373. couldBeLoaded = loadFunc and loadFunc("ScanForLoads_Auras", true, true, true, vehicle, vehicleUi, player, realm, class, spec, race, faction, playerLevel, zone, zoneId, encounter_id, size, difficulty, role);
  1374.  
  1375. if(shouldBeLoaded and not loaded[id]) then
  1376. WeakAuras.LoadDisplay(id);
  1377. changed = changed + 1;
  1378. end
  1379.  
  1380. if(loaded[id] and not shouldBeLoaded) then
  1381. WeakAuras.UnloadDisplay(id);
  1382. local region = WeakAuras.regions[id].region;
  1383. if not(paused) then
  1384. region:Collapse();
  1385. WeakAuras.CollapseAllClones(id);
  1386. end
  1387. end
  1388. if(shouldBeLoaded) then
  1389. loaded[id] = true;
  1390. elseif(couldBeLoaded) then
  1391. loaded[id] = false;
  1392. else
  1393. loaded[id] = nil;
  1394. end
  1395. end
  1396. end
  1397. for id, data in pairs(db.displays) do
  1398. if(data.controlledChildren) then
  1399. if(#data.controlledChildren > 0) then
  1400. local any_loaded;
  1401. for index, childId in pairs(data.controlledChildren) do
  1402. if(loaded[childId] ~= nil) then
  1403. any_loaded = true;
  1404. end
  1405. end
  1406. loaded[id] = any_loaded;
  1407. else
  1408. loaded[id] = true;
  1409. end
  1410. end
  1411. end
  1412. if(changed > 0 and not paused) then
  1413. for _, triggerSystem in pairs(triggerSystems) do
  1414. triggerSystem.ScanAll();
  1415. end
  1416. end
  1417.  
  1418. if (WeakAuras.afterScanForLoads) then -- Hook for Options
  1419. WeakAuras.afterScanForLoads();
  1420. end
  1421. end
  1422.  
  1423. local loadFrame = CreateFrame("FRAME");
  1424. WeakAuras.loadFrame = loadFrame;
  1425. WeakAuras.frames["Display Load Handling"] = loadFrame;
  1426.  
  1427. loadFrame:RegisterEvent("ENCOUNTER_START");
  1428. loadFrame:RegisterEvent("ENCOUNTER_END");
  1429.  
  1430. loadFrame:RegisterEvent("PLAYER_TALENT_UPDATE");
  1431. loadFrame:RegisterEvent("PLAYER_PVP_TALENT_UPDATE");
  1432. loadFrame:RegisterEvent("ZONE_CHANGED");
  1433. loadFrame:RegisterEvent("ZONE_CHANGED_INDOORS");
  1434. loadFrame:RegisterEvent("ZONE_CHANGED_NEW_AREA");
  1435. loadFrame:RegisterEvent("PLAYER_LEVEL_UP");
  1436. loadFrame:RegisterEvent("PLAYER_REGEN_DISABLED");
  1437. loadFrame:RegisterEvent("PLAYER_REGEN_ENABLED");
  1438.  
  1439. loadFrame:RegisterEvent("PLAYER_ROLES_ASSIGNED");
  1440. loadFrame:RegisterEvent("PLAYER_DIFFICULTY_CHANGED");
  1441. loadFrame:RegisterEvent("PET_BATTLE_OPENING_START");
  1442. loadFrame:RegisterEvent("PET_BATTLE_CLOSE");
  1443. loadFrame:RegisterEvent("UNIT_ENTERED_VEHICLE");
  1444. loadFrame:RegisterEvent("UNIT_EXITED_VEHICLE");
  1445. loadFrame:RegisterEvent("SPELLS_CHANGED");
  1446. loadFrame:RegisterEvent("GROUP_JOINED");
  1447. loadFrame:RegisterEvent("GROUP_LEFT");
  1448.  
  1449. function WeakAuras.RegisterLoadEvents()
  1450. loadFrame:SetScript("OnEvent", WeakAuras.ScanForLoads);
  1451. end
  1452.  
  1453. function WeakAuras.ReloadAll()
  1454. WeakAuras.UnloadAll();
  1455. WeakAuras.ScanForLoads();
  1456. end
  1457.  
  1458. function WeakAuras.UnloadAll()
  1459. for _, v in pairs(triggerState) do
  1460. for i = 0, v.numTriggers - 1 do
  1461. if (v[i]) then
  1462. wipe(v[i]);
  1463. end
  1464. end
  1465. end
  1466.  
  1467. for _, aura in pairs(timers) do
  1468. for _, trigger in pairs(aura) do
  1469. for _, record in pairs(trigger) do
  1470. if (record.handle) then
  1471. timer:CancelTimer(record.handle);
  1472. end
  1473. end
  1474. end
  1475. end
  1476. wipe(timers);
  1477.  
  1478. for _, id in pairs(conditionChecksTimers.recheckTime) do
  1479. if (conditionChecksTimers.recheckHandle[id]) then
  1480. for _, v in pairs(conditionChecksTimers.recheckHandle[id]) do
  1481. timer:CancelTimer(v);
  1482. end
  1483. end
  1484. end
  1485. wipe(conditionChecksTimers.recheckTime);
  1486. wipe(conditionChecksTimers.recheckHandle);
  1487.  
  1488. for _, triggerSystem in pairs(triggerSystems) do
  1489. triggerSystem.UnloadAll();
  1490. end
  1491. wipe(loaded);
  1492. end
  1493.  
  1494. do
  1495. function WeakAuras.LoadDisplay(id)
  1496. triggerState[id].triggers = {};
  1497. triggerState[id].triggerCount = 0;
  1498. triggerState[id].show = false;
  1499. triggerState[id].activeTrigger = nil;
  1500. for _, triggerSystem in pairs(triggerSystems) do
  1501. triggerSystem.LoadDisplay(id);
  1502. end
  1503. end
  1504.  
  1505. function WeakAuras.UnloadDisplay(id)
  1506. for i = 0, triggerState[id].numTriggers - 1 do
  1507. if (triggerState[id][i]) then
  1508. wipe(triggerState[id][i]);
  1509. end
  1510. end
  1511. triggerState[id].show = nil;
  1512. triggerState[id].activeTrigger = nil;
  1513.  
  1514. if (timers[id]) then
  1515. for _, trigger in pairs(timers[id]) do
  1516. for _, record in pairs(trigger) do
  1517. if (record.handle) then
  1518. timer:CancelTimer(record.handle);
  1519. end
  1520. end
  1521. end
  1522. timers[id] = nil;
  1523. end
  1524.  
  1525. conditionChecksTimers.recheckTime[id] = nil;
  1526. if (conditionChecksTimers.recheckHandle[id]) then
  1527. for _, v in pairs(conditionChecksTimers.recheckHandle[id]) do
  1528. timer:CancelTimer(v);
  1529. end
  1530. end
  1531. conditionChecksTimers.recheckHandle[id] = nil;
  1532.  
  1533. for _, triggerSystem in pairs(triggerSystems) do
  1534. triggerSystem.UnloadDisplay(id);
  1535. end
  1536. end
  1537. end
  1538.  
  1539. function WeakAuras.Delete(data)
  1540. local id = data.id;
  1541.  
  1542. if(data.parent) then
  1543. local parentData = db.displays[data.parent];
  1544. if(parentData and parentData.controlledChildren) then
  1545. for index, childId in pairs(parentData.controlledChildren) do
  1546. if(childId == id) then
  1547. tremove(parentData.controlledChildren, index);
  1548. end
  1549. end
  1550. end
  1551. end
  1552.  
  1553. if(data.controlledChildren) then
  1554. for index, childId in pairs(data.controlledChildren) do
  1555. local childData = db.displays[childId];
  1556. if(childData) then
  1557. childData.parent = nil;
  1558. WeakAuras.Add(childData);
  1559. end
  1560. end
  1561. end
  1562.  
  1563. animations[tostring(regions[id].region)] = nil
  1564.  
  1565. WeakAuras.UnregisterCustomTextUpdates(regions[id].region)
  1566. regions[id].region:SetScript("OnUpdate", nil);
  1567. regions[id].region:SetScript("OnShow", nil);
  1568. regions[id].region:SetScript("OnHide", nil);
  1569. regions[id].region:Hide();
  1570.  
  1571. WeakAuras.CollapseAllClones(id);
  1572.  
  1573. db.registered[id] = nil;
  1574. if(WeakAuras.importDisplayButtons and WeakAuras.importDisplayButtons[id]) then
  1575. local button = WeakAuras.importDisplayButtons[id];
  1576. button.checkbox:SetChecked(false);
  1577. if(button.updateChecked) then
  1578. button.updateChecked();
  1579. end
  1580. end
  1581.  
  1582. for _, triggerSystem in pairs(triggerSystems) do
  1583. triggerSystem.Delete(id);
  1584. end
  1585.  
  1586. regions[id].region = nil;
  1587. regions[id] = nil;
  1588. loaded[id] = nil;
  1589. loadFuncs[id] = nil;
  1590. checkConditions[id] = nil;
  1591. conditionChecksTimers.recheckTime[id] = nil;
  1592. if (conditionChecksTimers.recheckHandle[id]) then
  1593. for _, v in pairs(conditionChecksTimers.recheckHandle[id]) do
  1594. timer:CancelTimer(v);
  1595. end
  1596. end
  1597. conditionChecksTimers.recheckHandle[id] = nil;
  1598.  
  1599. db.displays[id] = nil;
  1600.  
  1601. aura_environments[id] = nil;
  1602. triggerState[id] = nil;
  1603.  
  1604. if (WeakAuras.personalRessourceDisplayFrame) then
  1605. WeakAuras.personalRessourceDisplayFrame:delete(id);
  1606. end
  1607.  
  1608. if (WeakAuras.mouseFrame) then
  1609. WeakAuras.mouseFrame:delete(id);
  1610. end
  1611. end
  1612.  
  1613. function WeakAuras.Rename(data, newid)
  1614. local oldid = data.id;
  1615. if(data.parent) then
  1616. local parentData = db.displays[data.parent];
  1617. if(parentData.controlledChildren) then
  1618. for index, childId in pairs(parentData.controlledChildren) do
  1619. if(childId == data.id) then
  1620. parentData.controlledChildren[index] = newid;
  1621. end
  1622. end
  1623. end
  1624. end
  1625.  
  1626. regions[newid] = regions[oldid];
  1627. regions[oldid] = nil;
  1628. regions[newid].region.id = newid;
  1629.  
  1630. for _, triggerSystem in pairs(triggerSystems) do
  1631. triggerSystem.Rename(oldid, newid);
  1632. end
  1633.  
  1634. loaded[newid] = loaded[oldid];
  1635. loaded[oldid] = nil;
  1636. loadFuncs[newid] = loadFuncs[oldid];
  1637. loadFuncs[oldid] = nil;
  1638.  
  1639. checkConditions[newid] = checkConditions[oldid];
  1640. checkConditions[oldid] = nil;
  1641.  
  1642. conditionChecksTimers.recheckTime[newid] = conditionChecksTimers.recheckTime[oldid];
  1643. conditionChecksTimers.recheckTime[oldid] = nil;
  1644.  
  1645. conditionChecksTimers.recheckHandle[newid] = conditionChecksTimers.recheckHandle[oldid];
  1646. conditionChecksTimers.recheckHandle[oldid] = nil;
  1647.  
  1648. timers[newid] = timers[oldid];
  1649. timers[oldid] = nil;
  1650.  
  1651. triggerState[newid] = triggerState[oldid];
  1652. triggerState[oldid] = nil;
  1653.  
  1654.  
  1655. db.displays[newid] = db.displays[oldid];
  1656. db.displays[oldid] = nil;
  1657.  
  1658. if(clones[oldid]) then
  1659. clones[newid] = clones[oldid];
  1660. clones[oldid] = nil;
  1661. for cloneid, clone in pairs(clones[newid]) do
  1662. clone.id = newid;
  1663. end
  1664. end
  1665.  
  1666. db.displays[newid].id = newid;
  1667.  
  1668. if(data.controlledChildren) then
  1669. for index, childId in pairs(data.controlledChildren) do
  1670. local childData = db.displays[childId];
  1671. if(childData) then
  1672. childData.parent = data.id;
  1673. end
  1674. end
  1675. end
  1676.  
  1677. for key, animation in pairs(animations) do
  1678. if animation.name == oldid then
  1679. animation.name = newid;
  1680. end
  1681. end
  1682.  
  1683. aura_environments[newid] = aura_environments[oldid] or {};
  1684. aura_environments[newid].id = newid;
  1685. aura_environments[oldid] = nil;
  1686.  
  1687. if (WeakAuras.personalRessourceDisplayFrame) then
  1688. WeakAuras.personalRessourceDisplayFrame:rename(oldid, newid);
  1689. end
  1690.  
  1691. if (WeakAuras.mouseFrame) then
  1692. WeakAuras.mouseFrame:rename(oldid, newid);
  1693. end
  1694. end
  1695.  
  1696. function WeakAuras.Convert(data, newType)
  1697. local id = data.id;
  1698. regions[id].region:SetScript("OnUpdate", nil);
  1699. regions[id].region:Hide();
  1700. WeakAuras.EndEvent(id, 0, true);
  1701.  
  1702. regions[id].region = nil;
  1703. regions[id] = nil;
  1704.  
  1705. data.regionType = newType;
  1706. WeakAuras.Add(data);
  1707. end
  1708.  
  1709. function WeakAuras.DeepCopy(source, dest)
  1710. local function recurse(source, dest)
  1711. for i,v in pairs(source) do
  1712. if(type(v) == "table") then
  1713. dest[i] = type(dest[i]) == "table" and dest[i] or {};
  1714. recurse(v, dest[i]);
  1715. else
  1716. dest[i] = v;
  1717. end
  1718. end
  1719. end
  1720. recurse(source, dest);
  1721. end
  1722.  
  1723. function WeakAuras.Copy(sourceid, destid)
  1724. local sourcedata = db.displays[sourceid];
  1725. local destdata = db.displays[destid];
  1726. if(sourcedata and destdata) then
  1727. local oldParent = destdata.parent;
  1728. local oldChildren = destdata.controlledChildren;
  1729. wipe(destdata);
  1730. WeakAuras.DeepCopy(sourcedata, destdata);
  1731. destdata.id = destid;
  1732. destdata.parent = oldParent;
  1733. destdata.controlledChildren = oldChildren;
  1734. WeakAuras.Add(destdata);
  1735. end
  1736. end
  1737.  
  1738. function WeakAuras.RegisterAddon(addon, displayName, description, icon)
  1739. if(addons[addon]) then
  1740. addons[addon].displayName = displayName;
  1741. addons[addon].description = description;
  1742. addons[addon].icon = icon;
  1743. addons[addon].displays = addons[addon].displays or {};
  1744. else
  1745. addons[addon] = {
  1746. displayName = displayName,
  1747. description = description,
  1748. icon = icon,
  1749. displays = {}
  1750. };
  1751. end
  1752. end
  1753.  
  1754. function WeakAuras.RegisterDisplay(addon, data, force)
  1755. tinsert(from_files, {addon, data, force});
  1756. end
  1757.  
  1758. function WeakAuras.AddManyFromAddons(table)
  1759. for _, addData in ipairs(table) do
  1760. WeakAuras.AddFromAddon(addData[1], addData[2], addData[3]);
  1761. end
  1762. end
  1763.  
  1764. function WeakAuras.AddFromAddon(addon, data, force)
  1765. local id = data.id;
  1766. if(id and addons[addon]) then
  1767. addons[addon].displays[id] = data;
  1768. if(db.registered[id]) then
  1769. -- This display was already registered
  1770. -- It is unnecessary to add it again
  1771. elseif(force and not db.registered[id] == false) then
  1772. if(db.displays[id]) then
  1773. -- ID collision
  1774. collisions[id] = {addon, data};
  1775. else
  1776. db.registered[id] = addon;
  1777. WeakAuras.Add(data);
  1778. end
  1779. end
  1780. end
  1781. end
  1782.  
  1783. function WeakAuras.CollisionResolved(addon, data, force)
  1784. WeakAuras.AddFromAddon(addon, data, force);
  1785. end
  1786.  
  1787. function WeakAuras.IsDefinedByAddon(id)
  1788. return db.registered[id];
  1789. end
  1790.  
  1791. function WeakAuras.ResolveCollisions(onFinished)
  1792. local num = 0;
  1793. for id, _ in pairs(collisions) do
  1794. num = num + 1;
  1795. end
  1796.  
  1797. if(num > 0) then
  1798. local baseText;
  1799. local buttonText;
  1800. if(registeredFromAddons) then
  1801. if(num == 1) then
  1802. baseText = L["Resolve collisions dialog singular"];
  1803. buttonText = L["Done"];
  1804. else
  1805. baseText = L["Resolve collisions dialog"];
  1806. buttonText = L["Next"];
  1807. end
  1808. else
  1809. if(num == 1) then
  1810. baseText = L["Resolve collisions dialog startup singular"];
  1811. buttonText = L["Done"];
  1812. else
  1813. baseText = L["Resolve collisions dialog startup"];
  1814. buttonText = L["Next"];
  1815. end
  1816. end
  1817.  
  1818. local numResolved = 0;
  1819. local currentId = next(collisions);
  1820.  
  1821. local function UpdateText(popup)
  1822. popup.text:SetText(baseText..(numResolved or "error").."/"..(num or "error"));
  1823. end
  1824.  
  1825. StaticPopupDialogs["WEAKAURAS_RESOLVE_COLLISIONS"] = {
  1826. text = baseText,
  1827. button1 = buttonText,
  1828. OnAccept = function(self)
  1829. -- Do the collision resolution
  1830. local newId = self.editBox:GetText();
  1831. if(WeakAuras.OptionsFrame and WeakAuras.OptionsFrame() and WeakAuras.displayButtons and WeakAuras.displayButtons[currentId]) then
  1832. WeakAuras.displayButtons[currentId].callbacks.OnRenameAction(newId)
  1833. else
  1834. local data = WeakAuras.GetData(currentId);
  1835. if(data) then
  1836. WeakAuras.Rename(data, newId);
  1837. else
  1838. print("|cFF8800FFWeakAuras|r: Data not found");
  1839. end
  1840. end
  1841.  
  1842. WeakAuras.CollisionResolved(collisions[currentId][1], collisions[currentId][2], true);
  1843. numResolved = numResolved + 1;
  1844.  
  1845. -- Get the next id to resolve
  1846. currentId = next(collisions, currentId);
  1847. if(currentId) then
  1848. -- There is another conflict to resolve - hook OnHide to reshow the dialog as soon as it hides
  1849. self:SetScript("OnHide", function(self)
  1850. self:Show();
  1851. UpdateText(self);
  1852. self.editBox:SetText(currentId);
  1853. self:SetScript("OnHide", nil);
  1854. if not(next(collisions, currentId)) then
  1855. self.button1:SetText(L["Done"]);
  1856. end
  1857. end);
  1858. else
  1859. self.editBox:SetScript("OnTextChanged", nil);
  1860. wipe(collisions);
  1861. if(onFinished) then
  1862. onFinished();
  1863. end
  1864. end
  1865. end,
  1866. hasEditBox = true,
  1867. hasWideEditBox = true,
  1868. hideOnEscape = true,
  1869. whileDead = true,
  1870. showAlert = true,
  1871. timeout = 0,
  1872. preferredindex = STATICPOPUP_NUMDIALOGS
  1873. };
  1874.  
  1875. local popup = StaticPopup_Show("WEAKAURAS_RESOLVE_COLLISIONS");
  1876. popup.editBox:SetScript("OnTextChanged", function(self)
  1877. local newid = self:GetText();
  1878. if(collisions[newid] or db.displays[newid]) then
  1879. popup.button1:Disable();
  1880. else
  1881. popup.button1:Enable();
  1882. end
  1883. end);
  1884. popup.editBox:SetText(currentId);
  1885. popup.text:SetJustifyH("left");
  1886. popup.icon:SetTexture("Interface\\Addons\\WeakAuras\\Media\\Textures\\icon.blp");
  1887. popup.icon:SetVertexColor(0.833, 0, 1);
  1888.  
  1889. UpdateText(popup);
  1890. elseif(onFinished) then
  1891. onFinished();
  1892. end
  1893. end
  1894.  
  1895. local function ModernizeAnimation(animation)
  1896. if (type(animation) ~= "string") then
  1897. return nil;
  1898. end
  1899. return animation:gsub("^%s*return%s*", "");
  1900. end
  1901.  
  1902. local function ModernizeAnimations(animations)
  1903. if (not animations) then
  1904. return;
  1905. end
  1906. animations.alphaFunc = ModernizeAnimation(animations.alphaFunc);
  1907. animations.translateFunc = ModernizeAnimation(animations.translateFunc);
  1908. animations.scaleFunc = ModernizeAnimation(animations.scaleFunc);
  1909. animations.rotateFunc = ModernizeAnimation(animations.rotateFunc);
  1910. animations.colorFunc = ModernizeAnimation(animations.colorFunc);
  1911. end
  1912.  
  1913. -- Takes as input a table of display data and attempts to update it to be compatible with the current version
  1914. function WeakAuras.Modernize(data)
  1915. -- Add trigger count
  1916. if not data.numTriggers then
  1917. data.numTriggers = 1 + (data.additional_triggers and #data.additional_triggers or 0)
  1918. end
  1919.  
  1920. local load = data.load;
  1921. -- Convert load options into single/multi format
  1922. for index, prototype in pairs(WeakAuras.load_prototype.args) do
  1923. local protoname = prototype.name;
  1924. if(prototype.type == "multiselect") then
  1925. if(not load[protoname] or type(load[protoname]) ~= "table") then
  1926. local value = load[protoname];
  1927. load[protoname] = {};
  1928. if(value) then
  1929. load[protoname].single = value;
  1930. end
  1931. end
  1932. load[protoname].multi = load[protoname].multi or {};
  1933. elseif(load[protoname] and type(load[protoname]) == "table") then
  1934. load[protoname] = nil;
  1935. end
  1936. end
  1937.  
  1938. -- upgrade from singleselecting talents to multi select, see ticket 52
  1939. if (type(load.talent) == "number") then
  1940. local talent = load.talent;
  1941. load.talent = {};
  1942. load.talent.single = talent;
  1943. load.talent.multi = {}
  1944. end
  1945.  
  1946. --upgrade to support custom trigger combination logic
  1947. if (data.disjunctive == true) then
  1948. data.disjunctive = "any";
  1949. end
  1950. if(data.disjunctive == false) then
  1951. data.disjunctive = "all";
  1952. end
  1953.  
  1954. for _, triggerSystem in pairs(triggerSystems) do
  1955. triggerSystem.Modernize(data);
  1956. end
  1957.  
  1958. -- Change English-language class tokens to locale-agnostic versions
  1959. local class_agnosticize = {
  1960. ["Death Knight"] = "DEATHKNIGHT",
  1961. ["Druid"] = "DRUID",
  1962. ["Hunter"] = "HUNTER",
  1963. ["Mage"] = "MAGE",
  1964. ["Monk"] = "MONK",
  1965. ["Paladin"] = "PALADIN",
  1966. ["Priest"] = "PRIEST",
  1967. ["Rogue"] = "ROGUE",
  1968. ["Shaman"] = "SHAMAN",
  1969. ["Warlock"] = "WARLOCK",
  1970. ["Warrior"] = "WARRIOR"
  1971. };
  1972. if(load.class.single) then
  1973. load.class.single = class_agnosticize[load.class.single] or load.class.single;
  1974. end
  1975. if(load.class.multi) then
  1976. for i,v in pairs(load.class.multi) do
  1977. if(class_agnosticize[i]) then
  1978. load.class.multi[class_agnosticize[i]] = true;
  1979. load.class.multi[i] = nil;
  1980. end
  1981. end
  1982. end
  1983.  
  1984. -- Add dynamic text info to Progress Bars
  1985. -- Also convert custom displayText to new displayText
  1986. if(data.regionType == "aurabar") then
  1987. data.displayTextLeft = data.displayTextLeft or (not data.auto and data.displayText) or "%n";
  1988. data.displayTextRight = data.displayTextRight or "%p";
  1989. end
  1990.  
  1991.  
  1992. if(data.regionType == "icon") then
  1993. if (data.cooldownTextEnabled == nil) then
  1994. data.cooldownTextEnabled = true;
  1995. end
  1996. if (data.displayStacks) then
  1997. data.text1Enabled = true;
  1998. data.text1 = data.displayStacks;
  1999. data.displayStacks = nil;
  2000. data.text1Color = data.textColor;
  2001. data.textColor = nil;
  2002. data.text1Point = data.stacksPoint;
  2003. data.stacksPoint = nil;
  2004. data.text1Containment = data.stacksContainment;
  2005. data.stacksContainment = nil;
  2006. data.text1Font = data.font;
  2007. data.font = nil;
  2008. data.text1FontSize = data.fontSize;
  2009. data.fontSize = nil;
  2010. data.text1FontFlags = data.fontFlags;
  2011. data.fontFlags = nil;
  2012.  
  2013. data.text2Enabled = false;
  2014. data.text2 = "%p";
  2015. data.text2Color = {1, 1, 1, 1};
  2016. data.text2Point = "CENTER";
  2017. data.text2Containment = "INSIDE";
  2018. data.text2Font = "Friz Quadrata TT";
  2019. data.text2FontSize = 24;
  2020. data.text2FontFlags = "OUTLINE";
  2021. end
  2022. end
  2023.  
  2024. -- Upgrade some old variables
  2025. if data.regionType == "aurabar" then
  2026. -- "border" changed to "borderEdge"
  2027. if data.border and type(data.border) ~= "boolean" then
  2028. data.borderEdge = data.border;
  2029. data.border = data.borderEdge ~= "None";
  2030. end
  2031. -- Multiple text settings
  2032. if data.textColor then
  2033. if not data.timerColor then
  2034. data.timerColor = {};
  2035. data.timerColor[1] = data.textColor[1];
  2036. data.timerColor[2] = data.textColor[2];
  2037. data.timerColor[3] = data.textColor[3];
  2038. data.timerColor[4] = data.textColor[4];
  2039. end
  2040. if not data.stacksColor then
  2041. data.stacksColor = {};
  2042. data.stacksColor[1] = data.textColor[1];
  2043. data.stacksColor[2] = data.textColor[2];
  2044. data.stacksColor[3] = data.textColor[3];
  2045. data.stacksColor[4] = data.textColor[4];
  2046. end
  2047. end
  2048. -- Multiple text settings
  2049. if data.font then
  2050. if not data.textFont then
  2051. data.textFont = data.font;
  2052. end
  2053. if not data.timerFont then
  2054. data.timerFont = data.font;
  2055. end
  2056. if not data.stacksFont then
  2057. data.stacksFont = data.font;
  2058. end
  2059.  
  2060. data.font = nil;
  2061. end
  2062. -- Multiple text settings
  2063. if data.fontSize then
  2064. if not data.textSize then
  2065. data.textSize = data.fontSize;
  2066. end
  2067. if not data.timerSize then
  2068. data.timerSize = data.fontSize;
  2069. end
  2070. if not data.stacksSize then
  2071. data.stacksSize = data.fontSize;
  2072. end
  2073.  
  2074. data.fontSize = nil;
  2075. end
  2076.  
  2077. -- fontFlags (outline)
  2078. if not data.fontFlags then
  2079. data.fontFlags = "OUTLINE";
  2080. end
  2081. end
  2082.  
  2083. if data.regionType == "text" then
  2084. if (type(data.outline) == "boolean") then
  2085. data.outline = data.outline and "OUTLINE" or "None";
  2086. end
  2087. end
  2088.  
  2089. if data.regionType == "model" then
  2090. if (data.api == nil) then
  2091. data.api = false;
  2092. end
  2093. end
  2094.  
  2095. if (data.regionType == "progresstexture") then
  2096. if (not data.version or data.version < 2) then
  2097. if (data.orientation == "CLOCKWISE") then
  2098. if (data.inverse) then
  2099. data.startAngle, data.endAngle = 360 - data.endAngle, 360 - data.startAngle;
  2100. data.orientation = (data.orientation == "CLOCKWISE") and "ANTICLOCKWISE" or "CLOCKWISE";
  2101. end
  2102. elseif (data.orientation == "ANTICLOCKWISE") then
  2103. data.startAngle, data.endAngle = 360 - data.endAngle, 360 - data.startAngle;
  2104. if (data.inverse) then
  2105. data.orientation = (data.orientation == "CLOCKWISE") and "ANTICLOCKWISE" or "CLOCKWISE";
  2106. end
  2107. end
  2108. data.version = 2;
  2109. end
  2110. end
  2111.  
  2112. if (not data.activeTriggerMode) then
  2113. data.activeTriggerMode = 0;
  2114. end
  2115.  
  2116. if (data.sort == "hybrid") then
  2117. if (not data.hybridPosition) then
  2118. data.hybridPosition = "hybridLast";
  2119. end
  2120. if (not data.hybridSortMode) then
  2121. data.hybridSortMode = "descending";
  2122. end
  2123. end
  2124.  
  2125. if (data.conditions) then
  2126. for conditionIndex, condition in ipairs(data.conditions) do
  2127. if (not condition.check) then
  2128. condition.check = {
  2129. ["trigger"] = condition.trigger,
  2130. ["variable"] = condition.condition,
  2131. ["op"] = condition.op,
  2132. ["value"] = condition.value
  2133. };
  2134. condition.trigger = nil;
  2135. condition.condition = nil;
  2136. condition.op = nil;
  2137. condition.value = nil;
  2138. end
  2139. end
  2140. end
  2141.  
  2142. ModernizeAnimations(data.animation and data.animation.start);
  2143. ModernizeAnimations(data.animation and data.animation.main);
  2144. ModernizeAnimations(data.animation and data.animation.finish);
  2145.  
  2146. end
  2147.  
  2148. function WeakAuras.SyncParentChildRelationships(silent)
  2149. local childToParent = {};
  2150. local parentToChild = {};
  2151. for id, data in pairs(db.displays) do
  2152. if(data.parent) then
  2153. if(data.controlledChildren) then
  2154. if not(silent) then
  2155. print("|cFF8800FFWeakAuras|r detected desynchronization in saved variables:", id, "has both child and parent");
  2156. end
  2157. -- A display cannot have both children and a parent
  2158. data.parent = nil;
  2159. elseif(db.displays[data.parent] and db.displays[data.parent].controlledChildren) then
  2160. childToParent[id] = data.parent;
  2161. parentToChild[data.parent] = parentToChild[data.parent] or {};
  2162. parentToChild[data.parent][id] = true;
  2163. else
  2164. if not(silent) then
  2165. print("|cFF8800FFWeakAuras|r detected desynchronization in saved variables:", id, "has a nonexistent parent");
  2166. end
  2167. data.parent = nil;
  2168. end
  2169. end
  2170. end
  2171.  
  2172. for id, data in pairs(db.displays) do
  2173. if(data.controlledChildren) then
  2174. for index, childId in pairs(data.controlledChildren) do
  2175. if not(childToParent[childId] and childToParent[childId] == id) then
  2176. if not(silent) then
  2177. print("|cFF8800FFWeakAuras|r detected desynchronization in saved variables:", id, "thinks it controls", childId, "but does not");
  2178. end
  2179. tremove(data.controlledChildren, index);
  2180. end
  2181. end
  2182.  
  2183. if(parentToChild[id]) then
  2184. for childId, _ in pairs(parentToChild[id]) do
  2185. if not(tContains(data.controlledChildren, childId)) then
  2186. if not(silent) then
  2187. print("|cFF8800FFWeakAuras|r detected desynchronization in saved variables:", id, "does not control", childId, "but should");
  2188. end
  2189. tinsert(data.controlledChildren, childId);
  2190. end
  2191. end
  2192. end
  2193. end
  2194. end
  2195. end
  2196.  
  2197. function WeakAuras.AddMany(table)
  2198. local idtable = {};
  2199. for _, data in ipairs(table) do
  2200. idtable[data.id] = data;
  2201. end
  2202. local loaded = {};
  2203. local function load(id, depends)
  2204. local data = idtable[id];
  2205. if(data.parent) then
  2206. if(idtable[data.parent]) then
  2207. if(tContains(depends, data.parent)) then
  2208. error("Circular dependency in WeakAuras.AddMany between "..tconcat(depends, ", "));
  2209. else
  2210. if not(loaded[data.parent]) then
  2211. local dependsOut = {};
  2212. for i,v in pairs(depends) do
  2213. dependsOut[i] = v;
  2214. end
  2215. tinsert(dependsOut, data.parent);
  2216. load(data.parent, dependsOut);
  2217. end
  2218. end
  2219. else
  2220. data.parent = nil;
  2221. end
  2222. end
  2223. if not(loaded[id]) then
  2224. WeakAuras.Add(data);
  2225. loaded[id] = true;
  2226. end
  2227. end
  2228. for id, data in pairs(idtable) do
  2229. load(id, {});
  2230. end
  2231. for id, data in pairs(idtable) do
  2232. if(data.regionType == "dynamicgroup") then
  2233. WeakAuras.Add(data);
  2234. regions[id].region:ControlChildren();
  2235. end
  2236. end
  2237. end
  2238.  
  2239. -- Dummy add function to protect errors from propagating out of the real add function
  2240. function WeakAuras.Add(data)
  2241. WeakAuras.Modernize(data);
  2242. WeakAuras.pAdd(data);
  2243. -- local status, err = pcall(WeakAuras.pAdd, data);
  2244. -- if not(status) then
  2245. -- local id = type(data.id) == "string" and data.id or "WeakAurasOptions tempGroup";
  2246. -- print("|cFFFF0000WeakAuras "..id..": "..err);
  2247. -- debug(id..": "..err, 3);
  2248. -- debug(debugstack(1, 6));
  2249. -- WeakAurasFrame:Hide();
  2250. -- error(err);
  2251. -- end
  2252. end
  2253.  
  2254. local function removeSpellNames(data)
  2255. local trigger
  2256. for triggernum=0,(data.numTriggers or 9) do
  2257. if(triggernum == 0) then
  2258. trigger = data.trigger;
  2259. elseif(data.additional_triggers and data.additional_triggers[triggernum]) then
  2260. trigger = data.additional_triggers[triggernum].trigger;
  2261. end
  2262. if (trigger.spellId) then
  2263. trigger.name = GetSpellInfo(trigger.spellId) or trigger.name;
  2264. end
  2265. if (trigger.spellIds) then
  2266. for i = 1, 10 do
  2267. if (trigger.spellIds[i]) then
  2268. trigger.names[i] = GetSpellInfo(trigger.spellIds[i]) or trigger.names[i];
  2269. end
  2270. end
  2271. end
  2272. end
  2273. end
  2274.  
  2275. function WeakAuras.pAdd(data)
  2276. local id = data.id;
  2277. if not(id) then
  2278. error("Improper arguments to WeakAuras.Add - id not defined");
  2279. elseif (data.controlledChildren) then
  2280. WeakAuras.SetRegion(data);
  2281. else
  2282. for _, triggerSystem in pairs(triggerSystems) do
  2283. triggerSystem.Add(data);
  2284. end
  2285. local region = WeakAuras.SetRegion(data);
  2286. if (WeakAuras.clones[id]) then
  2287. for cloneId, _ in pairs(WeakAuras.clones[id]) do
  2288. WeakAuras.SetRegion(data, cloneId);
  2289. end
  2290. end
  2291.  
  2292. data.init_started = nil;
  2293. data.load = data.load or {};
  2294. data.actions = data.actions or {};
  2295. data.actions.init = data.actions.init or {};
  2296. data.actions.start = data.actions.start or {};
  2297. data.actions.finish = data.actions.finish or {};
  2298. local loadFuncStr = WeakAuras.ConstructFunction(load_prototype, data.load);
  2299. local loadFunc = WeakAuras.LoadFunction(loadFuncStr);
  2300. local triggerLogicFunc = WeakAuras.LoadFunction("return "..(data.customTriggerLogic or ""), id);
  2301. WeakAuras.LoadCustomActionFunctions(data);
  2302. WeakAuras.LoadConditionPropertyFunctions(data);
  2303. local checkConditionsFuncStr = WeakAuras.ConstructConditionFunction(data);
  2304. local checkCondtionsFunc = checkConditionsFuncStr and WeakAuras.LoadFunction(checkConditionsFuncStr);
  2305. WeakAuras.debug(id.." - Load", 1);
  2306. WeakAuras.debug(loadFuncStr);
  2307.  
  2308. loadFuncs[id] = loadFunc;
  2309. checkConditions[id] = checkCondtionsFunc;
  2310. clones[id] = clones[id] or {};
  2311.  
  2312. if (timers[id]) then
  2313. for _, trigger in pairs(timers[id]) do
  2314. for _, record in pairs(trigger) do
  2315. if (record.handle) then
  2316. timer:CancelTimer(record.handle);
  2317. end
  2318. end
  2319. end
  2320. timers[id] = nil;
  2321. end
  2322.  
  2323. if (data.activeTriggerMode >= data.numTriggers) then
  2324. data.activeTriggerMode = WeakAuras.trigger_modes.first_active;
  2325. end
  2326. triggerState[id] = {};
  2327. triggerState[id].disjunctive = data.numTriggers > 1 and data.disjunctive or "all";
  2328. triggerState[id].numTriggers = data.numTriggers;
  2329. triggerState[id].activeTriggerMode = data.activeTriggerMode or 0;
  2330. triggerState[id].triggerLogicFunc = triggerLogicFunc;
  2331. triggerState[id].triggers = {};
  2332. triggerState[id].triggerCount = 0;
  2333. triggerState[id].activatedConditions = {};
  2334.  
  2335. WeakAuras.LoadEncounterInitScripts(id);
  2336.  
  2337. if not(paused) then
  2338. region:Collapse();
  2339. WeakAuras.ScanForLoads();
  2340. end
  2341. end
  2342.  
  2343. removeSpellNames(data);
  2344. db.displays[id] = data;
  2345. end
  2346.  
  2347. function WeakAuras.SetRegion(data, cloneId)
  2348. local regionType = data.regionType;
  2349. if not(regionType) then
  2350. error("Improper arguments to WeakAuras.SetRegion - regionType not defined");
  2351. else
  2352. if(not regionTypes[regionType]) then
  2353. regionType = "fallback";
  2354. print("Improper arguments to WeakAuras.CreateRegion - regionType \""..data.regionType.."\" is not supported");
  2355. end
  2356.  
  2357. local id = data.id;
  2358. if not(id) then
  2359. error("Improper arguments to WeakAuras.SetRegion - id not defined");
  2360. else
  2361. local region;
  2362. if(cloneId) then
  2363. region = clones[id][cloneId];
  2364. else
  2365. if((not regions[id]) or (not regions[id].region) or regions[id].regionType ~= regionType) then
  2366. region = regionTypes[regionType].create(frame, data);
  2367. region.toShow = true;
  2368. regions[id] = {
  2369. regionType = regionType,
  2370. region = region
  2371. };
  2372. else
  2373. region = regions[id].region;
  2374. end
  2375. region.id = id;
  2376. region.cloneId = "";
  2377. end
  2378. WeakAuras.validate(data, regionTypes[regionType].default);
  2379.  
  2380. local parent = frame;
  2381. if(data.parent) then
  2382. if(regions[data.parent]) then
  2383. parent = regions[data.parent].region;
  2384. else
  2385. data.parent = nil;
  2386. end
  2387. end
  2388.  
  2389. local anim_cancelled = WeakAuras.CancelAnimation(region, true, true, true, true, true);
  2390. local pSelfPoint, pAnchor, pAnchorPoint, pX, pY = region:GetPoint(1);
  2391.  
  2392. regionTypes[regionType].modify(parent, region, data);
  2393. WeakAuras.regionPrototype.AddSetDurationInfo(region);
  2394. local parentRegionType = data.parent and db.displays[data.parent] and db.displays[data.parent].regionType;
  2395. WeakAuras.regionPrototype.AddExpandFunction(data, region, id, cloneId, parent, parentRegionType)
  2396.  
  2397.  
  2398. if(data.parent and db.displays[data.parent] and db.displays[data.parent].regionType == "dynamicgroup" and pSelfPoint and pAnchor and pAnchorPoint and pX and pY) then
  2399. region:ClearAllPoints();
  2400. region:SetPoint(pSelfPoint, pAnchor, pAnchorPoint, pX, pY);
  2401. end
  2402.  
  2403. data.animation = data.animation or {};
  2404. data.animation.start = data.animation.start or {type = "none"};
  2405. data.animation.main = data.animation.main or {type = "none"};
  2406. data.animation.finish = data.animation.finish or {type = "none"};
  2407. if(WeakAuras.CanHaveDuration(data)) then
  2408. data.animation.start.duration_type = data.animation.start.duration_type or "seconds";
  2409. data.animation.main.duration_type = data.animation.main.duration_type or "seconds";
  2410. data.animation.finish.duration_type = data.animation.finish.duration_type or "seconds";
  2411. else
  2412. data.animation.start.duration_type = "seconds";
  2413. data.animation.main.duration_type = "seconds";
  2414. data.animation.finish.duration_type = "seconds";
  2415. end
  2416.  
  2417. if(cloneId) then
  2418. clonePool[regionType] = clonePool[regionType] or {};
  2419. end
  2420.  
  2421. if(anim_cancelled) then
  2422. WeakAuras.Animate("display", data, "main", data.animation.main, region, false, nil, true, cloneId);
  2423. end
  2424. return region;
  2425. end
  2426. end
  2427. end
  2428.  
  2429. function WeakAuras.EnsureClone(id, cloneId)
  2430. clones[id] = clones[id] or {};
  2431. if not(clones[id][cloneId]) then
  2432. local data = WeakAuras.GetData(id);
  2433. if(clonePool[data.regionType] and clonePool[data.regionType][1]) then
  2434. clones[id][cloneId] = tremove(clonePool[data.regionType]);
  2435. else
  2436. local clone = regionTypes[data.regionType].create(frame, data);
  2437. clone:Hide();
  2438. clones[id][cloneId] = clone;
  2439. end
  2440. WeakAuras.SetRegion(data, cloneId);
  2441. clones[id][cloneId].justCreated = true;
  2442. clones[id][cloneId].id = id;
  2443. clones[id][cloneId].cloneId = cloneId;
  2444. end
  2445. return clones[id][cloneId];
  2446. end
  2447.  
  2448. function WeakAuras.GetRegion(id, cloneId)
  2449. if(cloneId and cloneId ~= "") then
  2450. return WeakAuras.EnsureClone(id, cloneId);
  2451. end
  2452. return WeakAuras.regions[id] and WeakAuras.regions[id].region;
  2453. end
  2454.  
  2455. function WeakAuras.CollapseAllClones(id, triggernum)
  2456. if(clones[id]) then
  2457. for i,v in pairs(clones[id]) do
  2458. v:Collapse();
  2459. end
  2460. end
  2461. end
  2462.  
  2463. function WeakAuras.SetAllStatesHidden(id, triggernum)
  2464. local triggerState = WeakAuras.GetTriggerStateForTrigger(id, triggernum);
  2465. for id, state in pairs(triggerState) do
  2466. state.show = false;
  2467. state.changed = true;
  2468. end
  2469. end
  2470.  
  2471. function WeakAuras.SetAllStatesHiddenExcept(id, triggernum, list)
  2472. local triggerState = WeakAuras.GetTriggerStateForTrigger(id, triggernum);
  2473. for cloneId, state in pairs(triggerState) do
  2474. if (not (list[cloneId])) then
  2475. state.show = false;
  2476. state.changed = true;
  2477. end
  2478. end
  2479. end
  2480.  
  2481. function WeakAuras.ReleaseClone(id, cloneId, regionType)
  2482. if (not clones[id]) then
  2483. return;
  2484. end
  2485. local region = clones[id][cloneId];
  2486. clones[id][cloneId] = nil;
  2487. clonePool[regionType][#clonePool[regionType] + 1] = region;
  2488. end
  2489.  
  2490. function WeakAuras.HandleChatAction(message_type, message, message_dest, message_channel, r, g, b, region, customFunc)
  2491. if (message:find('%%')) then
  2492. message = WeakAuras.ReplacePlaceHolders(message, region, customFunc);
  2493. end
  2494. if(message_type == "PRINT") then
  2495. DEFAULT_CHAT_FRAME:AddMessage(message, r or 1, g or 1, b or 1);
  2496. elseif(message_type == "COMBAT") then
  2497. if(CombatText_AddMessage) then
  2498. CombatText_AddMessage(message, COMBAT_TEXT_SCROLL_FUNCTION, r or 1, g or 1, b or 1);
  2499. end
  2500. elseif(message_type == "WHISPER") then
  2501. if(message_dest) then
  2502. if(message_dest == "target" or message_dest == "'target'" or message_dest == "\"target\"" or message_dest == "%t" or message_dest == "'%t'" or message_dest == "\"%t\"") then
  2503. pcall(function() SendChatMessage(message, "WHISPER", nil, UnitName("target")) end);
  2504. else
  2505. pcall(function() SendChatMessage(message, "WHISPER", nil, message_dest) end);
  2506. end
  2507. end
  2508. elseif(message_type == "CHANNEL") then
  2509. local channel = message_channel and tonumber(message_channel);
  2510. if(GetChannelName(channel)) then
  2511. pcall(function() SendChatMessage(message, "CHANNEL", nil, channel) end);
  2512. end
  2513. elseif(message_type == "SMARTRAID") then
  2514. local isInstanceGroup = IsInGroup(LE_PARTY_CATEGORY_INSTANCE)
  2515. if UnitInBattleground("player") then
  2516. pcall(function() SendChatMessage(message, "INSTANCE_CHAT") end)
  2517. elseif UnitInRaid("player") then
  2518. pcall(function() SendChatMessage(message, "RAID") end)
  2519. elseif UnitInParty("player") then
  2520. if isInstanceGroup then
  2521. pcall(function() SendChatMessage(message, "INSTANCE_CHAT") end)
  2522. else
  2523. pcall(function() SendChatMessage(message, "PARTY") end)
  2524. end
  2525. else
  2526. pcall(function() SendChatMessage(message, "SAY") end)
  2527. end
  2528. else
  2529. pcall(function() SendChatMessage(message, message_type, nil, nil) end);
  2530. end
  2531. end
  2532.  
  2533. function WeakAuras.PerformActions(data, type, region)
  2534. if (paused) then
  2535. return;
  2536. end;
  2537. local actions;
  2538. if(type == "start") then
  2539. actions = data.actions.start;
  2540. elseif(type == "finish") then
  2541. actions = data.actions.finish;
  2542. else
  2543. return;
  2544. end
  2545.  
  2546. if(actions.do_message and actions.message_type and actions.message and not squelch_actions) then
  2547. local customFunc = WeakAuras.customActionsFunctions[data.id][type .. "_message"];
  2548. WeakAuras.HandleChatAction(actions.message_type, actions.message, actions.message_dest, actions.message_channel, actions.r, actions.g, actions.b, region, customFunc);
  2549. end
  2550.  
  2551. if (actions.stop_sound) then
  2552. if (region.SoundStop) then
  2553. region:SoundStop();
  2554. end
  2555. end
  2556.  
  2557. if(actions.do_sound and actions.sound and not squelch_actions) then
  2558. if (region.SoundPlay) then
  2559. region:SoundPlay(actions);
  2560. end
  2561. end
  2562.  
  2563. if(actions.do_custom and actions.custom and not squelch_actions) then
  2564. local func = WeakAuras.customActionsFunctions[data.id][type]
  2565. if func then
  2566. WeakAuras.ActivateAuraEnvironment(region.id, region.cloneId, region.state);
  2567. func();
  2568. WeakAuras.ActivateAuraEnvironment(nil);
  2569. end
  2570. end
  2571.  
  2572. -- Apply start glow actions even if squelch_actions is true, but don't apply finish glow actions
  2573. local squelch_glow = squelch_actions and (type == "finish");
  2574. if(actions.do_glow and actions.glow_action and actions.glow_frame and not squelch_glow) then
  2575. local glow_frame
  2576. local original_glow_frame
  2577. if(actions.glow_frame:sub(1, 10) == "WeakAuras:") then
  2578. local frame_name = actions.glow_frame:sub(11);
  2579. if(regions[frame_name]) then
  2580. glow_frame = regions[frame_name].region;
  2581. end
  2582. else
  2583. glow_frame = _G[actions.glow_frame];
  2584. original_glow_frame = glow_frame
  2585. end
  2586.  
  2587. if (glow_frame) then
  2588. if (not glow_frame.__WAGlowFrame) then
  2589. glow_frame.__WAGlowFrame = CreateFrame("Frame", nil, glow_frame);
  2590. glow_frame.__WAGlowFrame:SetAllPoints(glow_frame);
  2591. glow_frame.__WAGlowFrame:SetSize(glow_frame:GetSize());
  2592. end
  2593. glow_frame = glow_frame.__WAGlowFrame;
  2594. end
  2595.  
  2596. if(glow_frame) then
  2597. if(actions.glow_action == "show") then
  2598. WeakAuras_ShowOverlayGlow(glow_frame);
  2599. elseif(actions.glow_action == "hide") then
  2600. WeakAuras_HideOverlayGlow(glow_frame);
  2601. if original_glow_frame then
  2602. WeakAuras_HideOverlayGlow(original_glow_frame);
  2603. end
  2604. end
  2605. end
  2606. end
  2607. end
  2608.  
  2609. local updatingAnimations;
  2610. local last_update = GetTime();
  2611. function WeakAuras.UpdateAnimations()
  2612. for groupId, groupRegion in pairs(pending_controls) do
  2613. pending_controls[groupId] = nil;
  2614. groupRegion:DoControlChildren();
  2615. end
  2616. local time = GetTime();
  2617. local elapsed = time - last_update;
  2618. last_update = time;
  2619. local num = 0;
  2620. for id, anim in pairs(animations) do
  2621. num = num + 1;
  2622. local finished = false;
  2623. if(anim.duration_type == "seconds") then
  2624. if anim.duration > 0 then
  2625. anim.progress = anim.progress + (elapsed / anim.duration);
  2626. else
  2627. anim.progress = anim.progress + (elapsed / 1);
  2628. end
  2629. if(anim.progress >= 1) then
  2630. anim.progress = 1;
  2631. finished = true;
  2632. end
  2633. elseif(anim.duration_type == "relative") then
  2634. local state = anim.region.state;
  2635. if (not state
  2636. or (state.progressType == "timed" and state.duration < 0.01)
  2637. or (state.progressType == "static" and state.value < 0.01)) then
  2638. anim.progress = 0;
  2639. if(anim.type == "start" or anim.type == "finish") then
  2640. finished = true;
  2641. end
  2642. else
  2643. local relativeProgress = 0;
  2644. if(state.progressType == "static") then
  2645. relativeProgress = state.value / state.total;
  2646. elseif (state.progressType == "timed") then
  2647. relativeProgress = 1 - ((state.expirationTime - time) / state.duration);
  2648. end
  2649. relativeProgress = state.inverseDirection and (1 - relativeProgress) or relativeProgress;
  2650. anim.progress = relativeProgress / anim.duration
  2651. local iteration = math.floor(anim.progress);
  2652. --anim.progress = anim.progress - iteration;
  2653. if not(anim.iteration) then
  2654. anim.iteration = iteration;
  2655. elseif(anim.iteration ~= iteration) then
  2656. anim.iteration = nil;
  2657. finished = true;
  2658. end
  2659. end
  2660. else
  2661. anim.progress = 1;
  2662. end
  2663. local progress = anim.inverse and (1 - anim.progress) or anim.progress;
  2664. WeakAuras.ActivateAuraEnvironment(anim.name, anim.cloneId, anim.region.state);
  2665. if(anim.translateFunc) then
  2666. anim.region:ClearAllPoints();
  2667. anim.region:SetPoint(anim.selfPoint, anim.anchor, anim.anchorPoint, anim.translateFunc(progress, anim.startX, anim.startY, anim.dX, anim.dY));
  2668. end
  2669. if(anim.alphaFunc) then
  2670. anim.region:SetAlpha(anim.alphaFunc(progress, anim.startAlpha, anim.dAlpha));
  2671. end
  2672. if(anim.scaleFunc) then
  2673. local scaleX, scaleY = anim.scaleFunc(progress, 1, 1, anim.scaleX, anim.scaleY);
  2674. if(anim.region.Scale) then
  2675. anim.region:Scale(scaleX, scaleY);
  2676. else
  2677. anim.region:SetWidth(anim.startWidth * scaleX);
  2678. anim.region:SetHeight(anim.startHeight * scaleY);
  2679. end
  2680. end
  2681. if(anim.rotateFunc and anim.region.Rotate) then
  2682. anim.region:Rotate(anim.rotateFunc(progress, anim.startRotation, anim.rotate));
  2683. end
  2684. if(anim.colorFunc and anim.region.Color) then
  2685. anim.region:Color(anim.colorFunc(progress, anim.startR, anim.startG, anim.startB, anim.startA, anim.colorR, anim.colorG, anim.colorB, anim.colorA));
  2686. end
  2687. WeakAuras.ActivateAuraEnvironment(nil);
  2688. if(finished) then
  2689. if not(anim.loop) then
  2690. if(anim.startX) then
  2691. anim.region:SetPoint(anim.selfPoint, anim.anchor, anim.anchorPoint, anim.startX, anim.startY);
  2692. end
  2693. if(anim.startAlpha) then
  2694. anim.region:SetAlpha(anim.startAlpha);
  2695. end
  2696. if(anim.startWidth) then
  2697. if(anim.region.Scale) then
  2698. anim.region:Scale(1, 1);
  2699. else
  2700. anim.region:SetWidth(anim.startWidth);
  2701. anim.region:SetHeight(anim.startHeight);
  2702. end
  2703. end
  2704. if(anim.startRotation) then
  2705. if(anim.region.Rotate) then
  2706. anim.region:Rotate(anim.startRotation);
  2707. end
  2708. end
  2709. if(anim.startR and anim.startG and anim.startB and anim.startA) then
  2710. if(anim.region.Color) then
  2711. anim.region:Color(anim.startR, anim.startG, anim.startB, anim.startA);
  2712. end
  2713. end
  2714. animations[id] = nil;
  2715. end
  2716.  
  2717. if(anim.loop) then
  2718. WeakAuras.Animate(anim.namespace, anim.data, anim.type, anim.anim, anim.region, anim.inverse, anim.onFinished, anim.loop, anim.cloneId);
  2719. elseif(anim.onFinished) then
  2720. anim.onFinished();
  2721. end
  2722. end
  2723. end
  2724. -- XXX: I tried to have animations only update if there are actually animation data to animate upon.
  2725. -- This caused all start animations to break, and I couldn't figure out why.
  2726. -- May revisit at a later time.
  2727. --[[
  2728. if(num == 0) then
  2729. WeakAuras.debug("Animation stopped", 3);
  2730. frame:SetScript("OnUpdate", nil);
  2731. updatingAnimations = nil;
  2732. updatingAnimations = nil;
  2733. end
  2734. ]]--
  2735. end
  2736.  
  2737. function WeakAuras.Animate(namespace, data, type, anim, region, inverse, onFinished, loop, cloneId)
  2738. local id = data.id;
  2739. local key = tostring(region);
  2740. local valid;
  2741. if(anim and anim.type == "custom" and (anim.use_translate or anim.use_alpha or (anim.use_scale and region.Scale) or (anim.use_rotate and region.Rotate) or (anim.use_color and region.Color))) then
  2742. valid = true;
  2743. elseif(anim and anim.type == "preset" and anim.preset and anim_presets[anim.preset]) then
  2744. anim = anim_presets[anim.preset];
  2745. valid = true;
  2746. end
  2747. if(valid) then
  2748. local progress, duration, selfPoint, anchor, anchorPoint, startX, startY, startAlpha, startWidth, startHeight, startRotation;
  2749. local startR, startG, startB, startA, translateFunc, alphaFunc, scaleFunc, rotateFunc, colorFunc;
  2750. if(animations[key]) then
  2751. if(animations[key].type == type and not loop) then
  2752. return "no replace";
  2753. end
  2754. anim.x = anim.x or 0;
  2755. anim.y = anim.y or 0;
  2756. selfPoint, anchor, anchorPoint, startX, startY = animations[key].selfPoint, animations[key].anchor, animations[key].anchorPoint, animations[key].startX, animations[key].startY;
  2757. anim.alpha = anim.alpha or 0;
  2758. startAlpha = animations[key].startAlpha;
  2759. anim.scalex = anim.scalex or 1;
  2760. anim.scaley = anim.scaley or 1;
  2761. startWidth, startHeight = animations[key].startWidth, animations[key].startHeight;
  2762. anim.rotate = anim.rotate or 0;
  2763. startRotation = animations[key].startRotation;
  2764. anim.colorR = anim.colorR or 1;
  2765. anim.colorG = anim.colorG or 1;
  2766. anim.colorB = anim.colorB or 1;
  2767. anim.colorA = anim.colorA or 1;
  2768. startR = animations[key].startR;
  2769. startG = animations[key].startG;
  2770. startB = animations[key].startB;
  2771. startA = animations[key].startA;
  2772. else
  2773. anim.x = anim.x or 0;
  2774. anim.y = anim.y or 0;
  2775. selfPoint, anchor, anchorPoint, startX, startY = region:GetPoint(1);
  2776. anim.alpha = anim.alpha or 0;
  2777. startAlpha = region:GetAlpha();
  2778. anim.scalex = anim.scalex or 1;
  2779. anim.scaley = anim.scaley or 1;
  2780. startWidth, startHeight = region:GetWidth(), region:GetHeight();
  2781. anim.rotate = anim.rotate or 0;
  2782. startRotation = region.GetRotation and region:GetRotation() or 0;
  2783. anim.colorR = anim.colorR or 1;
  2784. anim.colorG = anim.colorG or 1;
  2785. anim.colorB = anim.colorB or 1;
  2786. anim.colorA = anim.colorA or 1;
  2787. if(region.GetColor) then
  2788. startR, startG, startB, startA = region:GetColor();
  2789. else
  2790. startR, startG, startB, startA = 1, 1, 1, 1;
  2791. end
  2792. end
  2793.  
  2794. if(anim.use_translate) then
  2795. if not(anim.translateType == "custom" and anim.translateFunc) then
  2796. anim.translateType = anim.translateType or "straightTranslate";
  2797. anim.translateFunc = anim_function_strings[anim.translateType] or anim_function_strings.straightTranslate;
  2798. end
  2799. translateFunc = WeakAuras.LoadFunction("return " .. anim.translateFunc, id);
  2800. else
  2801. region:SetPoint(selfPoint, anchor, anchorPoint, startX, startY);
  2802. end
  2803. if(anim.use_alpha) then
  2804. if not(anim.alphaType == "custom" and anim.alphaFunc) then
  2805. anim.alphaType = anim.alphaType or "straight";
  2806. anim.alphaFunc = anim_function_strings[anim.alphaType] or anim_function_strings.straight;
  2807. end
  2808. alphaFunc = WeakAuras.LoadFunction("return " .. anim.alphaFunc, id);
  2809. else
  2810. region:SetAlpha(startAlpha);
  2811. end
  2812. if(anim.use_scale) then
  2813. if not(anim.scaleType == "custom" and anim.scaleFunc) then
  2814. anim.scaleType = anim.scaleType or "straightScale";
  2815. anim.scaleFunc = anim_function_strings[anim.scaleType] or anim_function_strings.straightScale;
  2816. end
  2817. scaleFunc = WeakAuras.LoadFunction("return " .. anim.scaleFunc, id);
  2818. elseif(region.Scale) then
  2819. region:Scale(1, 1);
  2820. end
  2821. if(anim.use_rotate) then
  2822. if not(anim.rotateType == "custom" and anim.rotateFunc) then
  2823. anim.rotateType = anim.rotateType or "straight";
  2824. anim.rotateFunc = anim_function_strings[anim.rotateType] or anim_function_strings.straight;
  2825. end
  2826. rotateFunc = WeakAuras.LoadFunction("return " .. anim.rotateFunc, id);
  2827. elseif(region.Rotate) then
  2828. region:Rotate(startRotation);
  2829. end
  2830. if(anim.use_color) then
  2831. if not(anim.colorType == "custom" and anim.colorFunc) then
  2832. anim.colorType = anim.colorType or "straightColor";
  2833. anim.colorFunc = anim_function_strings[anim.colorType] or anim_function_strings.straightColor;
  2834. end
  2835. colorFunc = WeakAuras.LoadFunction("return " .. anim.colorFunc, id);
  2836. elseif(region.Color) then
  2837. region:Color(startR, startG, startB, startA);
  2838. end
  2839.  
  2840. duration = WeakAuras.ParseNumber(anim.duration) or 0;
  2841. progress = 0;
  2842. if(namespace == "display" and type == "main" and not onFinished and not anim.duration_type == "relative") then
  2843. local data = WeakAuras.GetData(id);
  2844. if(data and data.parent) then
  2845. local parentRegion = regions[data.parent].region;
  2846. if(parentRegion and parentRegion.controlledRegions) then
  2847. for index, regionData in pairs(parentRegion.controlledRegions) do
  2848. local childRegion = regionData.region;
  2849. local childKey = regionData.key;
  2850. if(childKey and childKey ~= tostring(region) and animations[childKey] and animations[childKey].type == "main" and duration == animations[childKey].duration) then
  2851. progress = animations[childKey].progress;
  2852. break;
  2853. end
  2854. end
  2855. end
  2856. end
  2857. end
  2858.  
  2859. animations[key] = animations[key] or {};
  2860. animations[key].progress = progress
  2861. animations[key].startX = startX
  2862. animations[key].startY = startY
  2863. animations[key].startAlpha = startAlpha
  2864. animations[key].startWidth = startWidth
  2865. animations[key].startHeight = startHeight
  2866. animations[key].startRotation = startRotation
  2867. animations[key].startR = startR
  2868. animations[key].startG = startG
  2869. animations[key].startB = startB
  2870. animations[key].startA = startA
  2871. animations[key].dX = (anim.use_translate and anim.x)
  2872. animations[key].dY = (anim.use_translate and anim.y)
  2873. animations[key].dAlpha = (anim.use_alpha and (anim.alpha - startAlpha))
  2874. animations[key].scaleX = (anim.use_scale and anim.scalex)
  2875. animations[key].scaleY = (anim.use_scale and anim.scaley)
  2876. animations[key].rotate = anim.rotate
  2877. animations[key].colorR = (anim.use_color and anim.colorR)
  2878. animations[key].colorG = (anim.use_color and anim.colorG)
  2879. animations[key].colorB = (anim.use_color and anim.colorB)
  2880. animations[key].colorA = (anim.use_color and anim.colorA)
  2881. animations[key].translateFunc = translateFunc
  2882. animations[key].alphaFunc = alphaFunc
  2883. animations[key].scaleFunc = scaleFunc
  2884. animations[key].rotateFunc = rotateFunc
  2885. animations[key].colorFunc = colorFunc
  2886. animations[key].region = region
  2887. animations[key].selfPoint = selfPoint
  2888. animations[key].anchor = anchor
  2889. animations[key].anchorPoint = anchorPoint
  2890. animations[key].duration = duration
  2891. animations[key].duration_type = anim.duration_type or "seconds"
  2892. animations[key].inverse = inverse
  2893. animations[key].type = type
  2894. animations[key].loop = loop
  2895. animations[key].onFinished = onFinished
  2896. animations[key].name = id
  2897. animations[key].cloneId = cloneId or ""
  2898. animations[key].namespace = namespace;
  2899. animations[key].data = data;
  2900. animations[key].anim = anim;
  2901.  
  2902. if not(updatingAnimations) then
  2903. frame:SetScript("OnUpdate", WeakAuras.UpdateAnimations);
  2904. updatingAnimations = true;
  2905. end
  2906. return true;
  2907. else
  2908. if(animations[key]) then
  2909. if(animations[key].type ~= type or loop) then
  2910. WeakAuras.CancelAnimation(region, true, true, true, true, true);
  2911. end
  2912. end
  2913. return false;
  2914. end
  2915. end
  2916.  
  2917. function WeakAuras.IsAnimating(region)
  2918. local key = tostring(region);
  2919. local anim = animations[key];
  2920. if(anim) then
  2921. return anim.type;
  2922. else
  2923. return nil;
  2924. end
  2925. end
  2926.  
  2927. function WeakAuras.CancelAnimation(region, resetPos, resetAlpha, resetScale, resetRotation, resetColor, doOnFinished)
  2928. local key = tostring(region);
  2929. local anim = animations[key];
  2930.  
  2931. if(anim) then
  2932. if(resetPos) then
  2933. anim.region:ClearAllPoints();
  2934. anim.region:SetPoint(anim.selfPoint, anim.anchor, anim.anchorPoint, anim.startX, anim.startY);
  2935. end
  2936. if(resetAlpha) then
  2937. anim.region:SetAlpha(anim.startAlpha);
  2938. end
  2939. if(resetScale) then
  2940. if(anim.region.Scale) then
  2941. anim.region:Scale(1, 1);
  2942. else
  2943. anim.region:SetWidth(anim.startWidth);
  2944. anim.region:SetHeight(anim.startHeight);
  2945. end
  2946. end
  2947. if(resetRotation and anim.region.Rotate) then
  2948. anim.region:Rotate(anim.startRotation);
  2949. end
  2950. if(resetColor and anim.region.Color) then
  2951. anim.region:Color(anim.startR, anim.startG, anim.startB, anim.startA);
  2952. end
  2953.  
  2954. animations[key] = nil;
  2955. if(doOnFinished and anim.onFinished) then
  2956. anim.onFinished();
  2957. end
  2958. return true;
  2959. else
  2960. return false;
  2961. end
  2962. end
  2963.  
  2964. function WeakAuras.GetData(id)
  2965. return id and db.displays[id];
  2966. end
  2967.  
  2968. function WeakAuras.GetTriggerSystem(data, triggernum)
  2969. if (triggernum == 0) then
  2970. return triggerTypes[data.trigger.type];
  2971. elseif (data.additional_triggers and data.additional_triggers[triggernum]) then
  2972. return triggerTypes[data.additional_triggers[triggernum].trigger.type];
  2973. end
  2974. return nil;
  2975. end
  2976.  
  2977. local function wrapTriggerSystemFunction(functionName, mode)
  2978. local func;
  2979. func = function(data, triggernum)
  2980. if (not triggernum) then
  2981. return func(data, data.activeTriggerMode);
  2982. elseif (triggernum < 0) then
  2983. local result;
  2984. if (mode == "or") then
  2985. result = false;
  2986. for i = 0, data.numTriggers - 1 do
  2987. result = result or func(data, i);
  2988. end
  2989. elseif (mode == "and") then
  2990. result = true;
  2991. for i = 0, data.numTriggers - 1 do
  2992. result = result and func(data, i);
  2993. end
  2994. elseif (mode == "firstValue") then
  2995. result = false;
  2996. for i = 0, data.numTriggers - 1 do
  2997. local tmp = func(data, i);
  2998. if (tmp) then
  2999. result = tmp;
  3000. break;
  3001. end
  3002. end
  3003. elseif (mode == "nameAndIcon") then
  3004. for i = 0, data.numTriggers - 1 do
  3005. local tmp1, tmp2 = func(data, i);
  3006. if (tmp1) then
  3007. return tmp1, tmp2;
  3008. end
  3009. end
  3010. end
  3011. return result;
  3012. else -- triggernum >= 0
  3013. local triggerSystem = WeakAuras.GetTriggerSystem(data, triggernum);
  3014. if (not triggerSystem) then
  3015. return false;
  3016. end
  3017. return triggerSystem[functionName](data, triggernum);
  3018. end
  3019. end
  3020. return func;
  3021. end
  3022.  
  3023. WeakAuras.CanHaveDuration = wrapTriggerSystemFunction("CanHaveDuration", "firstValue");
  3024. WeakAuras.CanHaveAuto = wrapTriggerSystemFunction("CanHaveAuto", "or");
  3025. WeakAuras.CanGroupShowWithZero = wrapTriggerSystemFunction("CanGroupShowWithZero", "or");
  3026. WeakAuras.CanHaveClones = wrapTriggerSystemFunction("CanHaveClones", "or");
  3027. WeakAuras.CanHaveTooltip = wrapTriggerSystemFunction("CanHaveTooltip", "or");
  3028. WeakAuras.GetNameAndIcon = wrapTriggerSystemFunction("GetNameAndIcon", "nameAndIcon");
  3029. WeakAuras.GetAdditionalProperties = wrapTriggerSystemFunction("GetAdditionalProperties", "firstValue");
  3030.  
  3031. function WeakAuras.GetTriggerConditions(data)
  3032. local conditions = {};
  3033. for i = 0, data.numTriggers - 1 do
  3034. local triggerSystem = WeakAuras.GetTriggerSystem(data, i);
  3035. if (triggerSystem) then
  3036. conditions[i] = triggerSystem.GetTriggerConditions(data, i);
  3037. conditions[i] = conditions[i] or {};
  3038. conditions[i].show = {
  3039. display = L["Active"],
  3040. type = "bool",
  3041. test = "(state and state.show or false) == (%s == 1)"
  3042. }
  3043. end
  3044. end
  3045. return conditions;
  3046. end
  3047.  
  3048. function WeakAuras.CreateFallbackState(id, triggernum, state)
  3049. local data = db.displays[id];
  3050. local triggerSystem = WeakAuras.GetTriggerSystem(data, triggernum);
  3051. if (not triggerSystem) then
  3052. return false;
  3053. end
  3054.  
  3055. triggerSystem.CreateFallbackState(data, triggernum, state);
  3056. if (triggernum == 0) then
  3057. state.trigger = data.trigger;
  3058. state.triggernum = 0;
  3059. state.id = id;
  3060. else
  3061. state.trigger = data.additional_triggers[triggernum].trigger;
  3062. state.triggernum = triggernum;
  3063. state.id = id;
  3064. end
  3065. end
  3066.  
  3067. function WeakAuras.CanShowNameInfo(data)
  3068. if(data.regionType == "aurabar" or data.regionType == "icon" or data.regionType == "text") then
  3069. return true;
  3070. else
  3071. return false;
  3072. end
  3073. end
  3074.  
  3075. function WeakAuras.CanShowStackInfo(data)
  3076. if(data.regionType == "aurabar" or data.regionType == "icon" or data.regionType == "text") then
  3077. return true;
  3078. else
  3079. return false;
  3080. end
  3081. end
  3082.  
  3083. function WeakAuras.CorrectSpellName(input)
  3084. local inputId = tonumber(input);
  3085. if(inputId) then
  3086. local name = GetSpellInfo(inputId);
  3087. if(name) then
  3088. return inputId;
  3089. else
  3090. return nil;
  3091. end
  3092. elseif(input) then
  3093. local link;
  3094. if(input:sub(1,1) == "\124") then
  3095. link = input;
  3096. else
  3097. link = GetSpellLink(input);
  3098. end
  3099. if(link) then
  3100. local itemId = link:match("spell:(%d+)");
  3101. return tonumber(itemId);
  3102. else
  3103. return nil;
  3104. end
  3105. end
  3106. end
  3107.  
  3108. function WeakAuras.CorrectItemName(input)
  3109. local inputId = tonumber(input);
  3110. if(inputId) then
  3111. local name = GetItemInfo(inputId);
  3112. if(name) then
  3113. return inputId;
  3114. else
  3115. return nil;
  3116. end
  3117. elseif(input) then
  3118. local _, link = GetItemInfo(input);
  3119. if(link) then
  3120. local itemId = link:match("item:(%d+)");
  3121. return tonumber(itemId);
  3122. else
  3123. return nil;
  3124. end
  3125. end
  3126. end
  3127.  
  3128.  
  3129. local currentTooltipRegion;
  3130. local currentTooltipOwner;
  3131. function WeakAuras.UpdateMouseoverTooltip(region)
  3132. if(region == currentTooltipRegion) then
  3133. WeakAuras.ShowMouseoverTooltip(currentTooltipRegion, currentTooltipOwner);
  3134. end
  3135. end
  3136.  
  3137. function WeakAuras.ShowMouseoverTooltip(region, owner)
  3138. currentTooltipRegion = region;
  3139. currentTooltipOwner = owner;
  3140.  
  3141. GameTooltip:SetOwner(owner, "ANCHOR_NONE");
  3142. GameTooltip:SetPoint("LEFT", owner, "RIGHT");
  3143. GameTooltip:ClearLines();
  3144.  
  3145. local triggerType;
  3146. if (region.state) then
  3147. triggerType = region.state.trigger.type;
  3148. end
  3149.  
  3150. local triggerSystem = triggerType and triggerTypes[triggerType];
  3151. if (not triggerSystem) then
  3152. GameTooltip:Hide();
  3153. return;
  3154. end
  3155.  
  3156. triggerSystem.SetToolTip(region.state.trigger, region.state);
  3157. GameTooltip:Show();
  3158. end
  3159.  
  3160. function WeakAuras.HideTooltip()
  3161. currentTooltipRegion = nil;
  3162. currentTooltipOwner = nil;
  3163. GameTooltip:Hide();
  3164. end
  3165.  
  3166. do
  3167. local hiddenTooltip;
  3168. function WeakAuras.GetHiddenTooltip()
  3169. if not(hiddenTooltip) then
  3170. hiddenTooltip = CreateFrame("GameTooltip", "WeakAurasTooltip", nil, "GameTooltipTemplate");
  3171. hiddenTooltip:SetOwner(WorldFrame, "ANCHOR_NONE");
  3172. hiddenTooltip:AddFontStrings(
  3173. hiddenTooltip:CreateFontString("$parentTextLeft1", nil, "GameTooltipText"),
  3174. hiddenTooltip:CreateFontString("$parentTextRight1", nil, "GameTooltipText")
  3175. );
  3176. end
  3177. return hiddenTooltip;
  3178. end
  3179. end
  3180.  
  3181. function WeakAuras.GetAuraTooltipInfo(unit, index, filter)
  3182. local tooltip = WeakAuras.GetHiddenTooltip();
  3183. tooltip:SetUnitAura(unit, index, filter);
  3184. local debuffTypeLine, tooltipTextLine = select(11, tooltip:GetRegions())
  3185. local tooltipText = tooltipTextLine and tooltipTextLine:GetObjectType() == "FontString" and tooltipTextLine:GetText() or "";
  3186. local debuffType = debuffTypeLine and debuffTypeLine:GetObjectType() == "FontString" and debuffTypeLine:GetText() or "";
  3187. local found = false;
  3188. for i,v in pairs(WeakAuras.debuff_class_types) do
  3189. if(v == debuffType) then
  3190. found = true;
  3191. debuffType = i;
  3192. break;
  3193. end
  3194. end
  3195. if not(found) then
  3196. debuffType = "none";
  3197. end
  3198. local tooltipSize,_;
  3199. if(tooltipText) then
  3200. local n2
  3201. _, _, tooltipSize, n2 = tooltipText:find("(%d+),(%d%d%d)") -- Blizzard likes american digit grouping, e.g. "9123="9,123" /mikk
  3202. if tooltipSize then
  3203. tooltipSize = tooltipSize..n2
  3204. else
  3205. _, _, tooltipSize = tooltipText:find("(%d+)")
  3206. end
  3207. end
  3208. return tooltipText, debuffType, tonumber(tooltipSize) or 0;
  3209. end
  3210.  
  3211. local function tooltip_draw()
  3212. GameTooltip:ClearLines();
  3213. GameTooltip:AddDoubleLine("WeakAuras", versionString, 0.5333, 0, 1, 1, 1, 1);
  3214. GameTooltip:AddLine(" ");
  3215. if(WeakAuras.IsOptionsOpen()) then
  3216. GameTooltip:AddLine(L["Click to close configuration"], 0, 1, 1);
  3217. else
  3218. GameTooltip:AddLine(L["Click to open configuration"], 0, 1, 1);
  3219. if(paused) then
  3220. GameTooltip:AddLine("|cFFFF0000"..L["Paused"].." - |cFF00FFFF"..L["Shift-Click to resume"], 0, 1, 1);
  3221. else
  3222. GameTooltip:AddLine(L["Shift-Click to pause"], 0, 1, 1);
  3223. end
  3224. end
  3225. GameTooltip:Show();
  3226. end
  3227.  
  3228. local colorFrame = CreateFrame("frame");
  3229. WeakAuras.frames["LDB Icon Recoloring"] = colorFrame;
  3230. local colorElapsed = 0;
  3231. local colorDelay = 2;
  3232. local r, g, b = 0.8, 0, 1;
  3233. local r2, g2, b2 = random(2)-1, random(2)-1, random(2)-1;
  3234. local tooltip_update_frame = CreateFrame("FRAME");
  3235. WeakAuras.frames["LDB Tooltip Updater"] = tooltip_update_frame;
  3236. local Broker_WeakAuras;
  3237. Broker_WeakAuras = LDB:NewDataObject("WeakAuras", {
  3238. type = "data source",
  3239. text = "WeakAuras",
  3240. icon = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\icon.blp",
  3241. OnClick = function(self, button)
  3242. if(IsShiftKeyDown()) then
  3243. if not(WeakAuras.IsOptionsOpen()) then
  3244. WeakAuras.Toggle();
  3245. end
  3246. else
  3247. WeakAuras.OpenOptions();
  3248. end
  3249. end,
  3250. OnEnter = function(self)
  3251. colorFrame:SetScript("OnUpdate", function(self, elaps)
  3252. colorElapsed = colorElapsed + elaps;
  3253. if(colorElapsed > colorDelay) then
  3254. colorElapsed = colorElapsed - colorDelay;
  3255. r, g, b = r2, g2, b2;
  3256. r2, g2, b2 = random(2)-1, random(2)-1, random(2)-1;
  3257. end
  3258. Broker_WeakAuras.iconR = r + (r2 - r) * colorElapsed / colorDelay;
  3259. Broker_WeakAuras.iconG = g + (g2 - g) * colorElapsed / colorDelay;
  3260. Broker_WeakAuras.iconB = b + (b2 - b) * colorElapsed / colorDelay;
  3261. end);
  3262. local elapsed = 0;
  3263. local delay = 1;
  3264. tooltip_update_frame:SetScript("OnUpdate", function(self, elap)
  3265. elapsed = elapsed + elap;
  3266. if(elapsed > delay) then
  3267. elapsed = 0;
  3268. tooltip_draw();
  3269. end
  3270. end);
  3271. -- Section the screen into 6 sextants and define the tooltip anchor position based on which sextant the cursor is in
  3272. local max_x = GetScreenWidth();
  3273. local max_y = GetScreenHeight();
  3274. local x, y = GetCursorPosition();
  3275. local horizontal = (x < (max_x/3) and "LEFT") or ((x >= (max_x/3) and x < ((max_x/3)*2)) and "") or "RIGHT";
  3276. local tooltip_vertical = (y < (max_y/2) and "BOTTOM") or "TOP";
  3277. local anchor_vertical = (y < (max_y/2) and "TOP") or "BOTTOM";
  3278. GameTooltip:SetOwner(self, "ANCHOR_NONE");
  3279. GameTooltip:SetPoint(tooltip_vertical..horizontal, self, anchor_vertical..horizontal);
  3280. tooltip_draw();
  3281. end,
  3282. OnLeave = function(self)
  3283. colorFrame:SetScript("OnUpdate", nil);
  3284. tooltip_update_frame:SetScript("OnUpdate", nil);
  3285. GameTooltip:Hide();
  3286. end,
  3287. iconR = 0.6,
  3288. iconG = 0,
  3289. iconB = 1
  3290. });
  3291.  
  3292. local FrameTimes = {};
  3293. function WeakAuras.ProfileFrames(all)
  3294. UpdateAddOnCPUUsage();
  3295. for name, frame in pairs(WeakAuras.frames) do
  3296. local FrameTime = GetFrameCPUUsage(frame);
  3297. FrameTimes[name] = FrameTimes[name] or 0;
  3298. if(all or FrameTime > FrameTimes[name]) then
  3299. print("|cFFFF0000"..name.."|r -", FrameTime, "-", FrameTime - FrameTimes[name]);
  3300. end
  3301. FrameTimes[name] = FrameTime;
  3302. end
  3303. end
  3304.  
  3305. local DisplayTimes = {};
  3306. function WeakAuras.ProfileDisplays(all)
  3307. UpdateAddOnCPUUsage();
  3308. for id, regionData in pairs(WeakAuras.regions) do
  3309. local DisplayTime = GetFrameCPUUsage(regionData.region, true);
  3310. DisplayTimes[id] = DisplayTimes[id] or 0;
  3311. if(all or DisplayTime > DisplayTimes[id]) then
  3312. print("|cFFFF0000"..id.."|r -", DisplayTime, "-", DisplayTime - DisplayTimes[id]);
  3313. end
  3314. DisplayTimes[id] = DisplayTime;
  3315. end
  3316. end
  3317.  
  3318. function WeakAuras.RegisterTutorial(name, displayName, description, icon, steps, order)
  3319. tutorials[name] = {
  3320. name = name,
  3321. displayName = displayName,
  3322. description = description,
  3323. icon = icon,
  3324. steps = steps,
  3325. order = order
  3326. };
  3327. end
  3328.  
  3329. do
  3330. local customTextUpdateFrame;
  3331. local updateRegions = {};
  3332.  
  3333. local function DoCustomTextUpdates()
  3334. for region, _ in pairs(updateRegions) do
  3335. if(region.UpdateCustomText) then
  3336. if(region:IsVisible()) then
  3337. region.UpdateCustomText();
  3338. end
  3339. else
  3340. updateRegions[region] = nil;
  3341. end
  3342. end
  3343. end
  3344.  
  3345. function WeakAuras.InitCustomTextUpdates()
  3346. if not(customTextUpdateFrame) then
  3347. customTextUpdateFrame = CreateFrame("frame");
  3348. customTextUpdateFrame:SetScript("OnUpdate", DoCustomTextUpdates);
  3349. end
  3350. end
  3351.  
  3352. function WeakAuras.RegisterCustomTextUpdates(region)
  3353. WeakAuras.InitCustomTextUpdates();
  3354. updateRegions[region] = true;
  3355. end
  3356.  
  3357. function WeakAuras.UnregisterCustomTextUpdates(region)
  3358. updateRegions[region] = nil;
  3359. end
  3360.  
  3361. function WeakAuras.IsRegisteredForCustomTextUpdates(region)
  3362. return updateRegions[region];
  3363. end
  3364. end
  3365.  
  3366. function WeakAuras.ValueFromPath(data, path)
  3367. if(#path == 1) then
  3368. return data[path[1]];
  3369. else
  3370. local reducedPath = {};
  3371. for i=2,#path do
  3372. reducedPath[i-1] = path[i];
  3373. end
  3374. return WeakAuras.ValueFromPath(data[path[1]], reducedPath);
  3375. end
  3376. end
  3377.  
  3378. function WeakAuras.ValueToPath(data, path, value)
  3379. if(#path == 1) then
  3380. data[path[1]] = value;
  3381. else
  3382. local reducedPath = {};
  3383. for i=2,#path do
  3384. reducedPath[i-1] = path[i];
  3385. end
  3386. WeakAuras.ValueToPath(data[path[1]], reducedPath, value);
  3387. end
  3388. end
  3389.  
  3390. function WeakAuras.FixGroupChildrenOrder()
  3391. for id, data in pairs(db.displays) do
  3392. if(data.controlledChildren) then
  3393. local frameLevel = 1;
  3394. for i=1, #data.controlledChildren do
  3395. local childRegion = WeakAuras.regions[data.controlledChildren[i]] and WeakAuras.regions[data.controlledChildren[i]].region;
  3396. if(childRegion) then
  3397. frameLevel = frameLevel + 4
  3398. childRegion:SetFrameLevel(frameLevel);
  3399. end
  3400. end
  3401. end
  3402. end
  3403. end
  3404.  
  3405. function WeakAuras.EnsureString(input)
  3406. if (input == nil) then
  3407. return "";
  3408. end
  3409. return tostring(input);
  3410. end
  3411.  
  3412. -- Handle coroutines
  3413. local dynFrame = {};
  3414. do
  3415. -- Internal data
  3416. dynFrame.frame = CreateFrame("frame");
  3417. dynFrame.update = {};
  3418. dynFrame.size = 0;
  3419.  
  3420. -- Add an action to be resumed via OnUpdate
  3421. function dynFrame.AddAction(self, name, func)
  3422. if not name then
  3423. name = fmt("NIL", dynFrame.size+1);
  3424. end
  3425.  
  3426. if not dynFrame.update[name] then
  3427. dynFrame.update[name] = func;
  3428. dynFrame.size = dynFrame.size + 1
  3429. dynFrame.frame:Show();
  3430. end
  3431. end
  3432.  
  3433. -- Remove an action from OnUpdate
  3434. function dynFrame.RemoveAction(self, name)
  3435. if dynFrame.update[name] then
  3436. dynFrame.update[name] = nil;
  3437. dynFrame.size = dynFrame.size - 1
  3438. if dynFrame.size == 0 then
  3439. dynFrame.frame:Hide();
  3440. end
  3441. end
  3442. end
  3443.  
  3444. -- Setup frame
  3445. dynFrame.frame:Hide();
  3446. dynFrame.frame:SetScript("OnUpdate", function(self, elapsed)
  3447. -- Start timing
  3448. local start = debugprofilestop();
  3449. local hasData = true;
  3450.  
  3451. -- Resume as often as possible (Limit to 16ms per frame -> 60 FPS)
  3452. while (debugprofilestop() - start < 16 and hasData) do
  3453. -- Stop loop without data
  3454. hasData = false;
  3455.  
  3456. -- Resume all coroutines
  3457. for name, func in pairs(dynFrame.update) do
  3458. -- Loop has data
  3459. hasData = true;
  3460.  
  3461. -- Resume or remove
  3462. if coroutine.status(func) ~= "dead" then
  3463. local err,ret1,ret2 = assert(coroutine.resume(func))
  3464. if err then
  3465. WeakAuras.debug(debugstack(func))
  3466. end
  3467. else
  3468. dynFrame:RemoveAction(name);
  3469. end
  3470. end
  3471. end
  3472. end);
  3473. end
  3474.  
  3475. WeakAuras.dynFrame = dynFrame;
  3476.  
  3477. function WeakAuras.ControlChildren(childid)
  3478. local parent = db.displays[childid].parent;
  3479. if (parent and db.displays[parent] and db.displays[parent].regionType == "dynamicgroup") then
  3480. regions[parent].region:ControlChildren();
  3481. end
  3482. end
  3483.  
  3484. function WeakAuras.SetDynamicIconCache(name, spellId, icon)
  3485. db.dynamicIconCache[name] = db.dynamicIconCache[name] or {};
  3486. db.dynamicIconCache[name][spellId] = icon;
  3487. end
  3488.  
  3489. function WeakAuras.GetDynamicIconCache(name)
  3490. if (db.dynamicIconCache[name]) then
  3491. for spellId, icon in pairs(db.dynamicIconCache[name]) do
  3492. if (IsSpellKnown(spellId)) then -- TODO save this information?
  3493. return db.dynamicIconCache[name][spellId];
  3494. end
  3495. end
  3496. end
  3497.  
  3498. if WeakAuras.spellCache then
  3499. return WeakAuras.spellCache.GetIcon(name);
  3500. end
  3501. return nil;
  3502. end
  3503.  
  3504. function WeakAuras.RegisterTriggerSystem(types, triggerSystem)
  3505. for _, v in ipairs(types) do
  3506. triggerTypes[v] = triggerSystem;
  3507. end
  3508. tinsert(triggerSystems, triggerSystem);
  3509. end
  3510.  
  3511. function WeakAuras.GetTriggerStateForTrigger(id, triggernum)
  3512. triggerState[id][triggernum] = triggerState[id][triggernum] or {}
  3513. return triggerState[id][triggernum];
  3514. end
  3515.  
  3516. local function startStopTimers(id, cloneId, triggernum, state)
  3517. if (state.show) then
  3518. if (state.autoHide and state.duration and state.duration > 0) then -- autohide, update timer
  3519. timers[id] = timers[id] or {};
  3520. timers[id][triggernum] = timers[id][triggernum] or {};
  3521. timers[id][triggernum][cloneId] = timers[id][triggernum][cloneId] or {};
  3522. local record = timers[id][triggernum][cloneId];
  3523. if (state.expirationTime == nil) then
  3524. state.expirationTime = GetTime() + state.duration;
  3525. state.resort = true;
  3526. end
  3527. if (record.expirationTime ~= state.expirationTime) then
  3528. if (record.handle ~= nil) then
  3529. timer:CancelTimer(record.handle);
  3530. end
  3531.  
  3532. record.handle = timer:ScheduleTimer(
  3533. function()
  3534. if (state.show ~= false and state.show ~= nil) then
  3535. state.show = false;
  3536. state.changed = true;
  3537. WeakAuras.UpdatedTriggerState(id);
  3538. end
  3539. end,
  3540. state.expirationTime - GetTime());
  3541. record.expirationTime = state.expirationTime;
  3542. end
  3543. else -- no auto hide, delete timer
  3544. if (timers[id] and timers[id][triggernum] and timers[id][triggernum][cloneId]) then
  3545. local record = timers[id][triggernum][cloneId];
  3546. if (record.handle) then
  3547. timer:CancelTimer(record.handle);
  3548. end
  3549. record.handle = nil;
  3550. record.expirationTime = nil;
  3551. end
  3552. end
  3553. else -- not shown
  3554. if(timers[id] and timers[id][triggernum] and timers[id][triggernum][cloneId]) then
  3555. local record = timers[id][triggernum][cloneId];
  3556. if (record.handle) then
  3557. timer:CancelTimer(record.handle);
  3558. end
  3559. record.handle = nil;
  3560. record.expirationTime = nil;
  3561. end
  3562. end
  3563. end
  3564.  
  3565. local function ApplyStateToRegion(id, region, state)
  3566. region.state = state;
  3567. if(region.SetDurationInfo) then
  3568. if (state.progressType == "timed") then
  3569. local now = GetTime();
  3570. local value = math.huge - now;
  3571. if (state.expirationTime and state.expirationTime > 0) then
  3572. value = state.expirationTime - now;
  3573. end
  3574. local total = state.duration or 0
  3575. local func = nil;
  3576.  
  3577. region:SetDurationInfo(total, now + value, func, state.inverseDirection);
  3578. elseif (state.progressType == "static") then
  3579. local value = state.value or 0;
  3580. local total = state.total or 0;
  3581. local durationFunc = state.durationFunc or true;
  3582.  
  3583. region:SetDurationInfo(value, total, durationFunc or true, state.inverseDirection);
  3584. else
  3585. -- Do nothing, should ideally clear duration info on region
  3586. end
  3587. end
  3588. local controlChidren = state.resort;
  3589. if (state.resort) then
  3590. state.resort = false;
  3591. end
  3592. if(region.SetName) then
  3593. region:SetName(state.name);
  3594. end
  3595. if(region.SetIcon) then
  3596. region:SetIcon(state.icon or "Interface\\Icons\\INV_Misc_QuestionMark");
  3597. end
  3598. if(region.SetStacks) then
  3599. region:SetStacks(state.stacks);
  3600. end
  3601. if(region.UpdateCustomText and not WeakAuras.IsRegisteredForCustomTextUpdates(region)) then
  3602. region.UpdateCustomText();
  3603. end
  3604.  
  3605. if(state.texture and region.SetTexture) then
  3606. region:SetTexture(state.texture);
  3607. end
  3608.  
  3609. WeakAuras.UpdateMouseoverTooltip(region);
  3610.  
  3611. region:Expand();
  3612. if (controlChidren) then
  3613. WeakAuras.ControlChildren(region.id);
  3614. end
  3615. end
  3616.  
  3617. -- Fallbacks if the states are empty
  3618. local emptyState = {};
  3619. emptyState[""] = {};
  3620.  
  3621. local function applyToTriggerStateTriggers(stateShown, id, triggernum)
  3622. if (stateShown and not triggerState[id].triggers[triggernum + 1]) then
  3623. triggerState[id].triggers[triggernum + 1] = true;
  3624. triggerState[id].triggerCount = triggerState[id].triggerCount + 1;
  3625. return true;
  3626. elseif (not stateShown and triggerState[id].triggers[triggernum + 1]) then
  3627. -- Check if any other clone is shown
  3628. local anyCloneShown = false;
  3629. for _, state in pairs(triggerState[id][triggernum]) do
  3630. if (state.show) then
  3631. anyCloneShown = true;
  3632. break;
  3633. end
  3634. end
  3635. if (not anyCloneShown) then
  3636. triggerState[id].triggers[triggernum + 1] = false;
  3637. triggerState[id].triggerCount = triggerState[id].triggerCount - 1;
  3638. return true;
  3639. end
  3640. end
  3641. return false;
  3642. end
  3643.  
  3644. local function evaluateTriggerStateTriggers(id)
  3645. local result = false;
  3646. WeakAuras.ActivateAuraEnvironment(id);
  3647. if(triggerState[id].disjunctive == "any" and triggerState[id].triggerCount > 0
  3648. or (triggerState[id].disjunctive == "all" and triggerState[id].triggerCount == triggerState[id].numTriggers)
  3649. or (triggerState[id].disjunctive == "custom"
  3650. and triggerState[id].triggerLogicFunc
  3651. and triggerState[id].triggerLogicFunc(triggerState[id].triggers))
  3652. ) then
  3653. result = true;
  3654. end
  3655. WeakAuras.ActivateAuraEnvironment(nil);
  3656. return result;
  3657. end
  3658.  
  3659. local function ApplyStatesToRegions(id, triggernum, states)
  3660. -- Show new clones
  3661. local visibleRegion = false;
  3662. for cloneId, state in pairs(states) do
  3663. local region = WeakAuras.GetRegion(id, cloneId);
  3664. if (state.show) then
  3665. visibleRegion = true;
  3666. if (not region.toShow or state.changed or region.state ~= state) then
  3667. ApplyStateToRegion(id, region, state);
  3668. end
  3669. end
  3670. if (checkConditions[id]) then -- Even if this state has not changed
  3671. checkConditions[id](region);
  3672. end
  3673. end
  3674.  
  3675. if (visibleRegion) then
  3676. if (not states[""] or not states[""].show) then
  3677. WeakAuras.regions[id].region:Collapse();
  3678. end
  3679. else
  3680. -- no visible region, fallback to a fallback state
  3681. fallbacksStates[id] = fallbacksStates[id] or {};
  3682. fallbacksStates[id][triggernum] = fallbacksStates[id][triggernum] or {};
  3683. WeakAuras.CreateFallbackState(id, triggernum, fallbacksStates[id][triggernum])
  3684. ApplyStateToRegion(id, WeakAuras.regions[id].region, fallbacksStates[id][triggernum]);
  3685. if (checkConditions[id]) then
  3686. checkConditions[id](WeakAuras.regions[id].region);
  3687. end
  3688. end
  3689. end
  3690.  
  3691. local toRemove = {};
  3692. function WeakAuras.UpdatedTriggerState(id)
  3693. if (not triggerState[id]) then
  3694. return;
  3695. end
  3696.  
  3697. local changed = false;
  3698. for triggernum = 0, triggerState[id].numTriggers - 1 do
  3699. triggerState[id][triggernum] = triggerState[id][triggernum] or {};
  3700. for cloneId, state in pairs(triggerState[id][triggernum]) do
  3701. if (triggernum == 0) then
  3702. state.trigger = db.displays[id].trigger;
  3703. state.triggernum = 0;
  3704. state.id = id;
  3705. else
  3706. state.trigger = db.displays[id].additional_triggers[triggernum].trigger;
  3707. state.triggernum = triggernum;
  3708. state.id = id;
  3709. end
  3710.  
  3711. if (state.changed) then
  3712. startStopTimers(id, cloneId, triggernum, state);
  3713. local stateShown = triggerState[id][triggernum][cloneId] and triggerState[id][triggernum][cloneId].show;
  3714. -- Update triggerState.triggers
  3715. changed = applyToTriggerStateTriggers(stateShown, id, triggernum) or changed;
  3716. end
  3717. end
  3718. end
  3719.  
  3720. -- Figure out whether we should be shown or not
  3721. local show = triggerState[id].show;
  3722. if (changed or show == nil) then
  3723. show = evaluateTriggerStateTriggers(id);
  3724. end
  3725.  
  3726. -- Figure out which subtrigger is active, and if it changed
  3727. local newActiveTrigger = triggerState[id].activeTriggerMode;
  3728. if (newActiveTrigger == WeakAuras.trigger_modes.first_active) then
  3729. -- Mode is first active trigger, so find a active trigger
  3730. for i = 0, triggerState[id].numTriggers - 1 do
  3731. if (triggerState[id].triggers[i + 1]) then
  3732. newActiveTrigger = i;
  3733. break;
  3734. end
  3735. end
  3736. end
  3737.  
  3738. local oldShow = triggerState[id].show;
  3739. triggerState[id].activeTrigger = newActiveTrigger;
  3740. triggerState[id].show = show;
  3741.  
  3742. local activeTriggerState = WeakAuras.GetTriggerStateForTrigger(id, newActiveTrigger);
  3743. if (not next(activeTriggerState)) then
  3744. activeTriggerState = emptyState;
  3745. end
  3746.  
  3747. local region;
  3748. -- Now apply
  3749. if (show and not oldShow) then -- Hide => Show
  3750. ApplyStatesToRegions(id, newActiveTrigger, activeTriggerState);
  3751. elseif (not show and oldShow) then -- Show => Hide
  3752. WeakAuras.CollapseAllClones(id);
  3753. WeakAuras.regions[id].region:Collapse();
  3754. elseif (show and oldShow) then -- Already shown, update regions
  3755. -- Hide old clones
  3756. for cloneId, clone in pairs(clones[id]) do
  3757. if (not activeTriggerState[cloneId] or not activeTriggerState[cloneId].show) then
  3758. clone:Collapse();
  3759. end
  3760. end
  3761. -- Show new states
  3762. ApplyStatesToRegions(id, newActiveTrigger, activeTriggerState);
  3763. end
  3764.  
  3765. for cloneId, state in pairs(activeTriggerState) do
  3766. local region = WeakAuras.GetRegion(id, cloneId);
  3767. if (checkConditions[id]) then
  3768. checkConditions[id](region, not state.show);
  3769. end
  3770. end
  3771.  
  3772.  
  3773. for triggernum = 0, triggerState[id].numTriggers - 1 do
  3774. triggerState[id][triggernum] = triggerState[id][triggernum] or {};
  3775. for cloneId, state in pairs(triggerState[id][triggernum]) do
  3776. if (not state.show) then
  3777. if (cloneId ~= "") then -- Keep "" state around, it's likely to be reused
  3778. tinsert(toRemove, cloneId)
  3779. else
  3780. for k, v in pairs(state) do
  3781. if (k ~= "trigger" and k ~= "triggernum" and k ~= "id") then
  3782. state[k] = nil;
  3783. end
  3784. end
  3785. end
  3786. end
  3787. state.changed = false;
  3788. end
  3789. for _, cloneId in ipairs(toRemove) do
  3790. triggerState[id][triggernum][cloneId] = nil;
  3791. end
  3792. wipe(toRemove);
  3793. end
  3794. end
  3795.  
  3796. local replaceStringCache = {};
  3797. function WeakAuras.ContainsPlaceHolders(textStr, toCheck)
  3798. if (textStr == nil or toCheck == nil) then
  3799. return false;
  3800. end
  3801. for i = 1, #toCheck do
  3802. if (textStr:find("%%" .. toCheck:sub(i, i))) then
  3803. return true;
  3804. end
  3805. end
  3806. return false;
  3807. end
  3808.  
  3809. local function ReplaceValuePlaceHolders(textStr, region, customFunc)
  3810. local regionValues = region.values;
  3811. local value;
  3812. if (textStr == "%c" and customFunc) then
  3813. WeakAuras.ActivateAuraEnvironment(region.id, region.cloneId, region.state);
  3814. local value = customFunc(region.expirationTime, region.duration, regionValues.progress, regionValues.duration, regionValues.name, regionValues.icon, regionValues.stacks);
  3815. WeakAuras.ActivateAuraEnvironment(nil);
  3816. value = value or "";
  3817. else
  3818. local variable = WeakAuras.dynamic_texts[textStr];
  3819. variable = variable and variable.value;
  3820. value = variable and regionValues[variable] or "";
  3821. end
  3822. return value;
  3823. end
  3824.  
  3825. function WeakAuras.ReplacePlaceHolders(textStr, region, customFunc)
  3826. local regionValues = region.values;
  3827. local regionState = region.state;
  3828. if (not regionState and not regionValues) then
  3829. return;
  3830. end
  3831.  
  3832. -- We look backwards through the string
  3833. -- Invariant currentPos - endPos is a ascii "alphabetic" string
  3834. -- So on finding a "%" we extract currentPos - endPos and depending
  3835. -- on how long the string is look it up in regionState or in regionValues
  3836. -- textStr is in UTF-8 encoding. We assume all state variables are pure alphabetic strings
  3837. local endPos = textStr:len();
  3838. if (endPos < 2) then
  3839. return textStr;
  3840. end
  3841.  
  3842. if (endPos == 2) then
  3843. local value = ReplaceValuePlaceHolders(textStr, region, customFunc);
  3844. if (value) then
  3845. return tostring(value);
  3846. else
  3847. return textStr;
  3848. end
  3849. end
  3850.  
  3851. local currentPos = endPos;
  3852. -- Look backwards
  3853. while (currentPos > 0) do
  3854. local char = string.byte(textStr, currentPos);
  3855. if (char == 37) then --%
  3856. if (endPos - currentPos == 1 and regionValues) then
  3857. local symbol = string.sub(textStr, currentPos, endPos)
  3858. local value = ReplaceValuePlaceHolders(symbol, region, customFunc);
  3859. if (value) then
  3860. textStr = string.sub(textStr, 1, currentPos - 1) .. value .. string.sub(textStr, endPos + 1);
  3861. end
  3862. elseif (endPos > currentPos and regionState) then
  3863. local symbol = string.sub(textStr, currentPos + 1, endPos);
  3864. local value = regionState[symbol] and tostring(regionState[symbol]);
  3865. if (value) then
  3866. textStr = string.sub(textStr, 1, currentPos - 1) .. value .. string.sub(textStr, endPos + 1);
  3867. else
  3868. value = ReplaceValuePlaceHolders(string.sub(textStr, currentPos, currentPos + 1), region, customFunc);
  3869. value = value or "";
  3870. textStr = string.sub(textStr, 1, currentPos - 1) .. value .. string.sub(textStr, currentPos + 2);
  3871. end
  3872. end
  3873. endPos = currentPos - 1;
  3874. elseif (char >= 65 and char <= 90) or (char >= 97 and char <= 122) then
  3875. -- a-zA-Z character
  3876. else
  3877. endPos = currentPos - 1;
  3878. end
  3879. currentPos = currentPos - 1;
  3880. end
  3881.  
  3882. textStr = textStr:gsub("\\n", "\n");
  3883. return textStr;
  3884. end
  3885.  
  3886. function WeakAuras.IsTriggerActive(id)
  3887. local active = triggerState[id];
  3888. return active and active.show;
  3889. end
  3890.  
  3891. -- Attach to Cursor/Frames code
  3892. -- Very simple function to convert a hsv angle to a color with
  3893. -- value hardcoded to 1 and saturation hardcoded to 0.75
  3894. local function colorWheel(angle)
  3895. local hh = angle / 60;
  3896. local i = floor(hh);
  3897. local ff = hh - i;
  3898. local p = 0;
  3899. local q = 0.75 * (1.0 - ff);
  3900. local t = 0.75 * ff;
  3901. if (i == 0) then
  3902. return 0.75, t, p;
  3903. elseif (i == 1) then
  3904. return q, 0.75, p;
  3905. elseif (i == 2) then
  3906. return p, 0.75, t;
  3907. elseif (i == 3) then
  3908. return p, q, 0.75;
  3909. elseif (i == 4) then
  3910. return t, p, 0.75;
  3911. else
  3912. return 0.75, p, q;
  3913. end
  3914. end
  3915.  
  3916. local function xPositionNextToOptions()
  3917. local xOffset;
  3918. local optionsFrame = WeakAuras.OptionsFrame();
  3919. local centerX = (optionsFrame:GetLeft() + optionsFrame:GetRight()) / 2;
  3920. if (centerX > GetScreenWidth() / 2) then
  3921. if (optionsFrame:GetLeft() > 400) then
  3922. xOffset = optionsFrame:GetLeft() - 200;
  3923. else
  3924. xOffset = optionsFrame:GetLeft() / 2;
  3925. end
  3926. else
  3927. if (GetScreenWidth() - optionsFrame:GetRight() > 400 ) then
  3928. xOffset = optionsFrame:GetRight() + 200;
  3929. else
  3930. xOffset = (GetScreenWidth() + optionsFrame:GetRight()) / 2;
  3931. end
  3932. end
  3933. return xOffset;
  3934. end
  3935.  
  3936. local mouseFrame;
  3937. local function ensureMouseFrame()
  3938. if (mouseFrame) then
  3939. return;
  3940. end
  3941. mouseFrame = CreateFrame("FRAME", "WeakAurasAttachToMouseFrame", UIParent);
  3942. mouseFrame.attachedVisibleFrames = {};
  3943. mouseFrame:SetWidth(1);
  3944. mouseFrame:SetHeight(1);
  3945.  
  3946. local moverFrame = CreateFrame("FRAME", "WeakAurasMousePointerFrame", mouseFrame);
  3947. mouseFrame.moverFrame = moverFrame;
  3948. moverFrame:SetPoint("TOPLEFT", mouseFrame, "CENTER");
  3949. moverFrame:SetWidth(32);
  3950. moverFrame:SetHeight(32);
  3951. moverFrame:SetFrameStrata("FULLSCREEN"); -- above settings dialog
  3952.  
  3953. moverFrame:EnableMouse(true)
  3954. moverFrame:SetScript("OnMouseDown", function()
  3955. mouseFrame:SetMovable(true);
  3956. mouseFrame:StartMoving()
  3957. end);
  3958. moverFrame:SetScript("OnMouseUp", function()
  3959. mouseFrame:StopMovingOrSizing();
  3960. mouseFrame:SetMovable(false);
  3961. local xOffset = mouseFrame:GetRight() - GetScreenWidth();
  3962. local yOffset = mouseFrame:GetTop() - GetScreenHeight();
  3963. db.mousePointerFrame = db.mousePointerFrame or {};
  3964. db.mousePointerFrame.xOffset = xOffset;
  3965. db.mousePointerFrame.yOffset = yOffset;
  3966. end);
  3967. moverFrame.colorWheelAnimation = function()
  3968. local angle = ((GetTime() - moverFrame.startTime) % 5) / 5 * 360;
  3969. moverFrame.texture:SetVertexColor(colorWheel(angle));
  3970. end;
  3971. local texture = moverFrame:CreateTexture(nil, "BACKGROUND");
  3972. moverFrame.texture = texture;
  3973. texture:SetAllPoints(moverFrame);
  3974. texture:SetTexture("Interface\\Cursor\\Point");
  3975.  
  3976. local label = moverFrame:CreateFontString(nil, "BACKGROUND", "GameFontHighlightSmall")
  3977. label:SetJustifyH("LEFT")
  3978. label:SetJustifyV("TOP")
  3979. label:SetPoint("TOPLEFT", moverFrame, "BOTTOMLEFT");
  3980. label:SetText("WeakAuras Anchor");
  3981.  
  3982. moverFrame:Hide();
  3983.  
  3984. mouseFrame.OptionsOpened = function()
  3985. if (db.mousePointerFrame) then
  3986. -- Restore from settings
  3987. mouseFrame:ClearAllPoints();
  3988. mouseFrame:SetPoint("TOPRIGHT", UIParent, "TOPRIGHT", db.mousePointerFrame.xOffset, db.mousePointerFrame.yOffset);
  3989. else
  3990. -- Fnd a suitable position
  3991. local optionsFrame = WeakAuras.OptionsFrame();
  3992. local yOffset = (optionsFrame:GetTop() + optionsFrame:GetBottom()) / 2;
  3993. local xOffset = xPositionNextToOptions();
  3994. -- We use the top right, because the main frame usees the top right as the reference too
  3995. mouseFrame:ClearAllPoints();
  3996. mouseFrame:SetPoint("TOPRIGHT", UIParent, "TOPRIGHT", xOffset - GetScreenWidth(), yOffset - GetScreenHeight());
  3997. end
  3998. -- Change the color of the mouse cursor
  3999. moverFrame.startTime = GetTime();
  4000. moverFrame:SetScript("OnUpdate", moverFrame.colorWheelAnimation);
  4001. mouseFrame:SetScript("OnUpdate", nil);
  4002. end
  4003.  
  4004. mouseFrame.moveWithMouse = function()
  4005. local scale = 1 / UIParent:GetEffectiveScale();
  4006. local x, y = GetCursorPosition();
  4007. mouseFrame:SetPoint("CENTER", UIParent, "BOTTOMLEFT", x * scale, y * scale);
  4008. end
  4009.  
  4010. mouseFrame.OptionsClosed = function()
  4011. moverFrame:Hide();
  4012. mouseFrame:ClearAllPoints();
  4013. mouseFrame:SetScript("OnUpdate", mouseFrame.moveWithMouse);
  4014. moverFrame:SetScript("OnUpdate", nil);
  4015. wipe(mouseFrame.attachedVisibleFrames);
  4016. end
  4017.  
  4018. mouseFrame.expand = function(self, id)
  4019. local data = WeakAuras.GetData(id);
  4020. if (data.anchorFrameType == "MOUSE") then
  4021. self.attachedVisibleFrames[id] = true;
  4022. self:updateVisible();
  4023. end
  4024. end
  4025.  
  4026. mouseFrame.collapse = function(self, id)
  4027. self.attachedVisibleFrames[id] = nil;
  4028. self:updateVisible();
  4029. end
  4030.  
  4031. mouseFrame.rename = function(self, oldid, newid)
  4032. self.attachedVisibleFrames[newid] = self.attachedVisibleFrames[oldid];
  4033. self.attachedVisibleFrames[oldid] = nil;
  4034. self:updateVisible();
  4035. end
  4036.  
  4037. mouseFrame.delete = function(self, id)
  4038. self.attachedVisibleFrames[id] = nil;
  4039. self:updateVisible();
  4040. end
  4041.  
  4042. mouseFrame.anchorFrame = function(self, id, anchorFrameType)
  4043. if (anchorFrameType == "MOUSE") then
  4044. self.attachedVisibleFrames[id] = true;
  4045. else
  4046. self.attachedVisibleFrames[id] = nil;
  4047. end
  4048. self:updateVisible();
  4049. end
  4050.  
  4051. mouseFrame.updateVisible = function(self)
  4052. if (not WeakAuras.IsOptionsOpen()) then
  4053. return;
  4054. end
  4055.  
  4056. if (next(self.attachedVisibleFrames)) then
  4057. mouseFrame.moverFrame:Show();
  4058. else
  4059. mouseFrame.moverFrame:Hide();
  4060. end
  4061. end
  4062.  
  4063. if (WeakAuras.IsOptionsOpen()) then
  4064. mouseFrame:OptionsOpened();
  4065. else
  4066. mouseFrame:OptionsClosed();
  4067. end
  4068.  
  4069. WeakAuras.mouseFrame = mouseFrame;
  4070. end
  4071.  
  4072. local personalRessourceDisplayFrame;
  4073. local function ensurePRDFrame()
  4074. if (personalRessourceDisplayFrame) then
  4075. return;
  4076. end
  4077. personalRessourceDisplayFrame = CreateFrame("FRAME", "WeakAurasAttachToPRD", UIParent);
  4078. personalRessourceDisplayFrame:Hide();
  4079. personalRessourceDisplayFrame.attachedVisibleFrames = {};
  4080. WeakAuras.personalRessourceDisplayFrame = personalRessourceDisplayFrame;
  4081.  
  4082. local moverFrame = CreateFrame("FRAME", "WeakAurasPRDMoverFrame", personalRessourceDisplayFrame);
  4083. personalRessourceDisplayFrame.moverFrame = moverFrame;
  4084. moverFrame:SetPoint("TOPLEFT", personalRessourceDisplayFrame, "TOPLEFT", -2, 2);
  4085. moverFrame:SetPoint("BOTTOMRIGHT", personalRessourceDisplayFrame, "BOTTOMRIGHT", 2, -2);
  4086. moverFrame:SetFrameStrata("FULLSCREEN"); -- above settings dialog
  4087.  
  4088. moverFrame:EnableMouse(true)
  4089. moverFrame:SetScript("OnMouseDown", function()
  4090. personalRessourceDisplayFrame:SetMovable(true);
  4091. personalRessourceDisplayFrame:StartMoving()
  4092. end);
  4093. moverFrame:SetScript("OnMouseUp", function()
  4094. personalRessourceDisplayFrame:StopMovingOrSizing();
  4095. personalRessourceDisplayFrame:SetMovable(false);
  4096. local xOffset = personalRessourceDisplayFrame:GetRight();
  4097. local yOffset = personalRessourceDisplayFrame:GetTop();
  4098.  
  4099. db.personalRessourceDisplayFrame = db.personalRessourceDisplayFrame or {};
  4100. local scale = UIParent:GetEffectiveScale() / personalRessourceDisplayFrame:GetEffectiveScale();
  4101. db.personalRessourceDisplayFrame.xOffset = xOffset / scale - GetScreenWidth();
  4102. db.personalRessourceDisplayFrame.yOffset = yOffset / scale - GetScreenHeight();
  4103. end);
  4104. moverFrame:Hide();
  4105.  
  4106. local texture = moverFrame:CreateTexture(nil, "BACKGROUND");
  4107. personalRessourceDisplayFrame.texture = texture;
  4108. texture:SetAllPoints(moverFrame);
  4109. texture:SetTexture("Interface\\AddOns\\WeakAuras\\Media\\Textures\\PRDFrame");
  4110.  
  4111. local label = moverFrame:CreateFontString(nil, "BACKGROUND", "GameFontHighlight")
  4112. label:SetPoint("CENTER", moverFrame, "CENTER");
  4113. label:SetText("WeakAuras Anchor");
  4114.  
  4115. personalRessourceDisplayFrame:RegisterEvent('NAME_PLATE_UNIT_ADDED');
  4116. personalRessourceDisplayFrame:RegisterEvent('NAME_PLATE_UNIT_REMOVED');
  4117.  
  4118. personalRessourceDisplayFrame.Attach = function(self, frame, frameTL, frameBR)
  4119. self:SetParent(frame);
  4120. self:ClearAllPoints();
  4121. self:SetPoint("TOPLEFT", frameTL, "TOPLEFT");
  4122. self:SetPoint("BOTTOMRIGHT", frameBR, "BOTTOMRIGHT");
  4123. end
  4124.  
  4125. personalRessourceDisplayFrame.Detach = function(self, frame)
  4126. self:ClearAllPoints();
  4127. self:SetParent(UIParent);
  4128. end
  4129.  
  4130. personalRessourceDisplayFrame.OptionsOpened = function()
  4131. personalRessourceDisplayFrame:Detach();
  4132. personalRessourceDisplayFrame:SetScript("OnEvent", nil);
  4133. personalRessourceDisplayFrame:ClearAllPoints();
  4134. local xOffset, yOffset;
  4135. if (db.personalRessourceDisplayFrame) then
  4136. xOffset = db.personalRessourceDisplayFrame.xOffset;
  4137. yOffset = db.personalRessourceDisplayFrame.yOffset;
  4138. end
  4139.  
  4140. -- Calculate size of self nameplate
  4141. local prdWidth;
  4142. local prdHeight;
  4143.  
  4144. if (KuiNameplatesCore and KuiNameplatesCore.profile) then
  4145. prdWidth = KuiNameplatesCore.profile.frame_width_personal;
  4146. prdHeight = KuiNameplatesCore.profile.frame_height_personal;
  4147. if (KuiNameplatesCore.profile.ignore_uiscale) then
  4148. local _, screenWidth = GetPhysicalScreenSize();
  4149. local uiScale = 1;
  4150. if (screenWidth) then
  4151. uiScale = 768 / screenWidth;
  4152. end
  4153. personalRessourceDisplayFrame:SetScale(uiScale / UIParent:GetEffectiveScale());
  4154. else
  4155. personalRessourceDisplayFrame:SetScale(1);
  4156. end
  4157. personalRessourceDisplayFrame.texture:SetTexture("Interface\\AddOns\\WeakAuras\\Media\\Textures\\PRDFrameKui");
  4158. else
  4159. local namePlateVerticalScale = tonumber(GetCVar("NamePlateVerticalScale"));
  4160. local zeroBasedScale = namePlateVerticalScale - 1.0;
  4161. local clampedZeroBasedScale = Saturate(zeroBasedScale);
  4162. local horizontalScale = tonumber(GetCVar("NamePlateHorizontalScale"));
  4163. local baseNamePlateWidth = NamePlateDriverFrame.baseNamePlateWidth;
  4164. prdWidth = baseNamePlateWidth * horizontalScale * Lerp(1.1, 1.0, clampedZeroBasedScale) - 24;
  4165. prdHeight = 4 * namePlateVerticalScale * Lerp(1.2, 1.0, clampedZeroBasedScale) * 2 + 1;
  4166. personalRessourceDisplayFrame:SetScale(1 / UIParent:GetEffectiveScale());
  4167. personalRessourceDisplayFrame.texture:SetTexture("Interface\\AddOns\\WeakAuras\\Media\\Textures\\PRDFrame");
  4168. end
  4169.  
  4170. local scale = UIParent:GetEffectiveScale() / personalRessourceDisplayFrame:GetEffectiveScale();
  4171. if (not xOffset or not yOffset) then
  4172. local optionsFrame = WeakAuras.OptionsFrame();
  4173. yOffset = optionsFrame:GetBottom() + prdHeight / scale - GetScreenHeight();
  4174. xOffset = xPositionNextToOptions() + prdWidth / 2 / scale - GetScreenWidth();
  4175. end
  4176.  
  4177. xOffset = xOffset * scale;
  4178. yOffset = yOffset * scale;
  4179.  
  4180. personalRessourceDisplayFrame:SetPoint("TOPRIGHT", UIParent, "TOPRIGHT", xOffset, yOffset);
  4181. personalRessourceDisplayFrame:SetPoint("BOTTOMLEFT", UIParent, "TOPRIGHT", xOffset - prdWidth, yOffset - prdHeight);
  4182. end
  4183.  
  4184. personalRessourceDisplayFrame.OptionsClosed = function()
  4185. personalRessourceDisplayFrame:SetScale(1);
  4186. local frame = C_NamePlate.GetNamePlateForUnit("player");
  4187. if (frame) then
  4188. if (frame.kui and frame.kui.bg) then
  4189. personalRessourceDisplayFrame:Attach(frame.kui, frame.kui.bg, frame.kui.bg);
  4190. elseif (ElvUIPlayerNamePlateAnchor) then
  4191. personalRessourceDisplayFrame:Attach(ElvUIPlayerNamePlateAnchor, ElvUIPlayerNamePlateAnchor, ElvUIPlayerNamePlateAnchor);
  4192. else
  4193. personalRessourceDisplayFrame:Attach(frame, frame.UnitFrame.healthBar, NamePlateDriverFrame.nameplateManaBar);
  4194. end
  4195. else
  4196. personalRessourceDisplayFrame:Detach();
  4197. personalRessourceDisplayFrame:Hide();
  4198. end
  4199.  
  4200. personalRessourceDisplayFrame:SetScript("OnEvent", personalRessourceDisplayFrame.eventHandler);
  4201. personalRessourceDisplayFrame.texture:Hide();
  4202. personalRessourceDisplayFrame.moverFrame:Hide();
  4203. wipe(personalRessourceDisplayFrame.attachedVisibleFrames);
  4204. end
  4205.  
  4206. personalRessourceDisplayFrame.eventHandler = function(self, event, nameplate)
  4207. if (event == "NAME_PLATE_UNIT_ADDED") then
  4208. if (UnitIsUnit(nameplate, "player")) then
  4209. local frame = C_NamePlate.GetNamePlateForUnit("player");
  4210. if (frame) then
  4211. if (frame.kui and frame.kui.bg) then
  4212. personalRessourceDisplayFrame:Attach(frame.kui, frame.kui.bg, frame.kui.bg);
  4213. elseif (ElvUIPlayerNamePlateAnchor) then
  4214. personalRessourceDisplayFrame:Attach(ElvUIPlayerNamePlateAnchor, ElvUIPlayerNamePlateAnchor, ElvUIPlayerNamePlateAnchor);
  4215. else
  4216. personalRessourceDisplayFrame:Attach(frame, frame.UnitFrame.healthBar, NamePlateDriverFrame.nameplateManaBar);
  4217. end
  4218. personalRessourceDisplayFrame:Show();
  4219. db.personalRessourceDisplayFrame = db.personalRessourceDisplayFrame or {};
  4220. else
  4221. personalRessourceDisplayFrame:Detach();
  4222. personalRessourceDisplayFrame:Hide();
  4223. end
  4224. end
  4225. elseif (event == "NAME_PLATE_UNIT_REMOVED") then
  4226. if (UnitIsUnit(nameplate, "player")) then
  4227. personalRessourceDisplayFrame:Detach();
  4228. personalRessourceDisplayFrame:Hide();
  4229. end
  4230. end
  4231. end
  4232.  
  4233. personalRessourceDisplayFrame.expand = function(self, id)
  4234. local data = WeakAuras.GetData(id);
  4235. if (data.anchorFrameType == "PRD") then
  4236. self.attachedVisibleFrames[id] = true;
  4237. self:updateVisible();
  4238. end
  4239. end
  4240.  
  4241. personalRessourceDisplayFrame.collapse = function(self, id)
  4242. self.attachedVisibleFrames[id] = nil;
  4243. self:updateVisible();
  4244. end
  4245.  
  4246. personalRessourceDisplayFrame.rename = function(self, oldid, newid)
  4247. self.attachedVisibleFrames[newid] = self.attachedVisibleFrames[oldid];
  4248. self.attachedVisibleFrames[oldid] = nil;
  4249. self:updateVisible();
  4250. end
  4251.  
  4252. personalRessourceDisplayFrame.delete = function(self, id)
  4253. self.attachedVisibleFrames[id] = nil;
  4254. self:updateVisible();
  4255. end
  4256.  
  4257. personalRessourceDisplayFrame.anchorFrame = function(self, id, anchorFrameType)
  4258. if (anchorFrameType == "PRD") then
  4259. self.attachedVisibleFrames[id] = true;
  4260. else
  4261. self.attachedVisibleFrames[id] = nil;
  4262. end
  4263. self:updateVisible();
  4264. end
  4265.  
  4266. personalRessourceDisplayFrame.updateVisible = function(self)
  4267. if (not WeakAuras.IsOptionsOpen()) then
  4268. return;
  4269. end
  4270.  
  4271. if (next(self.attachedVisibleFrames)) then
  4272. personalRessourceDisplayFrame.texture:Show();
  4273. personalRessourceDisplayFrame.moverFrame:Show();
  4274. personalRessourceDisplayFrame:Show();
  4275. else
  4276. personalRessourceDisplayFrame.texture:Hide();
  4277. personalRessourceDisplayFrame.moverFrame:Hide();
  4278. personalRessourceDisplayFrame:Hide();
  4279. end
  4280. end
  4281.  
  4282. if (WeakAuras.IsOptionsOpen()) then
  4283. personalRessourceDisplayFrame.OptionsOpened();
  4284. else
  4285. personalRessourceDisplayFrame.OptionsClosed();
  4286. end
  4287. end
  4288.  
  4289. local postPonedAnchors = {};
  4290. local anchorTimer
  4291.  
  4292. local function tryAnchorAgain()
  4293. local delayed = postPonedAnchors;
  4294. postPonedAnchors = {};
  4295. anchorTimer = nil;
  4296.  
  4297. for id, _ in pairs(delayed) do
  4298. local data = WeakAuras.GetData(id);
  4299. local region = WeakAuras.GetRegion(id);
  4300. if (data and region) then
  4301. local parent = frame;
  4302. if (data.parent and regions[data.parent]) then
  4303. parent = regions[data.parent].region;
  4304. end
  4305. WeakAuras.AnchorFrame(data, region, parent);
  4306. end
  4307. end
  4308. end
  4309.  
  4310. local function postponeAnchor(id)
  4311. postPonedAnchors[id] = true;
  4312. if (not anchorTimer) then
  4313. anchorTimer = timer:ScheduleTimer(tryAnchorAgain, 5);
  4314. end
  4315. end
  4316.  
  4317. function WeakAuras.GetAnchorFrame(id, anchorFrameType, parent, anchorFrameFrame)
  4318. if (personalRessourceDisplayFrame) then
  4319. personalRessourceDisplayFrame:anchorFrame(id, anchorFrameType);
  4320. end
  4321.  
  4322. if (mouseFrame) then
  4323. mouseFrame:anchorFrame(id, anchorFrameType);
  4324. end
  4325.  
  4326. if (anchorFrameType == "SCREEN") then
  4327. return parent;
  4328. end
  4329.  
  4330. if (anchorFrameType == "PRD") then
  4331. ensurePRDFrame();
  4332. personalRessourceDisplayFrame:anchorFrame(id, anchorFrameType);
  4333. return personalRessourceDisplayFrame;
  4334. end
  4335.  
  4336. if (anchorFrameType == "MOUSE") then
  4337. ensureMouseFrame();
  4338. mouseFrame:anchorFrame(id, anchorFrameType);
  4339. return mouseFrame;
  4340. end
  4341.  
  4342. if (anchorFrameType == "SELECTFRAME" and anchorFrameFrame) then
  4343. if(anchorFrameFrame:sub(1, 10) == "WeakAuras:") then
  4344. local frame_name = anchorFrameFrame:sub(11);
  4345. if (frame == id) then
  4346. return parent;
  4347. end
  4348. if(regions[frame_name]) then
  4349. return regions[frame_name].region;
  4350. end
  4351. else
  4352. if (_G[anchorFrameFrame]) then
  4353. return _G[anchorFrameFrame];
  4354. end
  4355. postponeAnchor(id);
  4356. return parent;
  4357. end
  4358. end
  4359. -- Fallback
  4360. return parent;
  4361. end
  4362.  
  4363. function WeakAuras.AnchorFrame(data, region, parent)
  4364. local anchorParent = WeakAuras.GetAnchorFrame(data.id, data.anchorFrameType, parent, data.anchorFrameFrame);
  4365. if (data.anchorFrameParent or data.anchorFrameParent == nil) then
  4366. region:SetParent(anchorParent);
  4367. else
  4368. region:SetParent(frame);
  4369. end
  4370. region:SetPoint(data.selfPoint, anchorParent, data.anchorPoint, data.xOffset, data.yOffset);
  4371. if(data.frameStrata == 1) then
  4372. region:SetFrameStrata(region:GetParent():GetFrameStrata());
  4373. else
  4374. region:SetFrameStrata(WeakAuras.frame_strata_types[data.frameStrata]);
  4375. end
  4376. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement