Advertisement
Hekili

EventHorizon.lua Totem Handler (WiP)

May 22nd, 2016
224
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 123.94 KB | None | 0 0
  1. local EHN,ns = ...
  2. EventHorizon = ns
  3.  
  4. local class = select(2,UnitClass('player'))
  5. local playername = UnitName('player')..' - '..GetRealmName('player')
  6. local Cataclysm = select(4,GetBuildInfo()) >= 40000
  7. local Mop = select(4,GetBuildInfo()) >= 50000
  8. local Wod = select(4,GetBuildInfo()) >= 60000
  9. local Legion = select(4,GetBuildInfo()) >= 70000
  10.  
  11. local DEBUG = true -- Comment out this line by prefixing it with "--" if you want to see the load-spam and debug messages
  12. local spellIDsEnabled = DEBUG
  13.  
  14. -- Wod Changes
  15. local _GetSpellInfo, _GetTalentInfo, _GetNumTalents, _GetAddOnInfo = GetSpellInfo, GetTalentInfo, GetNumTalents, GetAddOnInfo
  16. local GetSpellInfo, GetTalentInfo, GetNumTalents, GetAddOnInfo = _GetSpellInfo, _GetTalentInfo, _GetNumTalents, _GetAddOnInfo
  17. if Wod then
  18. GetSpellInfo = function(...)
  19. local name, rank, icon, castingTime, minRange, maxRange = _GetSpellInfo(...)
  20. return name, rank, icon, nil, nil, nil, castingTime, minRange, maxRange
  21. end
  22.  
  23. GetTalentInfo = function(talentIndex, isInspect, talentGroup, inspectedUnit, classID)
  24. local tier = math.floor((talentIndex-1)/3 + 1)
  25. local column = (talentIndex-1)%3 + 1
  26. -- nameTalent, icon, tier, column, active
  27. local _, name, icon, selected, selectable = _GetTalentInfo(tier, column, talentGroup or GetActiveSpecGroup(), inspectedUnit, nil)
  28. return name, icon, tier, column, selected
  29. end
  30.  
  31. GetNumTalents = function()
  32. if Wod then
  33. return 21
  34. else
  35. return 18
  36. end
  37. end
  38.  
  39. GetAddOnInfo = function(id_or_name)
  40. if type(id_or_name) == "number" then
  41. return _GetAddOnInfo(id_or_name)
  42. else
  43. for i=1, GetNumAddOns() do
  44. local name, title, notes, enabled, loadable, reason, security_clearance = _GetAddOnInfo(i)
  45. if name == id_or_name then
  46. return name, title, notes, enabled, loadable, reason, security_clearance
  47. end
  48. end
  49. end
  50. return
  51. end
  52. end
  53.  
  54. ns.wodf = {
  55. ["GetSpellInfo"] = GetSpellInfo,
  56. ["GetTalentInfo"] = GetTalentInfo,
  57. ["GetNumTalents"] = GetNumTalents,
  58. ["GetAddOnInfo"] = GetAddOnInfo,
  59. }
  60.  
  61. local WodStatusText = ""
  62. if Wod then
  63. WodStatusText = WodStatusText .. "This is a beta release of the WoD version of EventHorizon. As such, expect there to be bugs. "
  64. else
  65. WodStatusText = WodStatusText .. "This is the backwards compatible version of the WoD beta version of EventHorizon. "
  66. end
  67. WodStatusText = WodStatusText .. "If you encounter a bug, please copy the whole error message including stack trace and tell me about it on EventHorizon's WowInterface page. \n\n"
  68. WodStatusText = WodStatusText .. "I only play a few specs, so most of these configs are my best guess given a few hours on beta and training dummies. \n"
  69. WodStatusText = WodStatusText .. "If you're interested in helping me out with improving class configurations, please don't hesitate to let me know on EventHorizon's WowInterface page. \n\n Thanks! ^.^ \n - Brusalk \n\n"
  70.  
  71. -- Legion API Changes
  72.  
  73. local _GetSpellInfo, _GetTalentInfo, _GetNumTalents, _GetAddOnInfo = GetSpellInfo, GetTalentInfo, GetNumTalents, GetAddOnInfo
  74. local GetSpellInfo, GetTalentInfo, GetNumTalents, GetAddOnInfo = _GetSpellInfo, _GetTalentInfo, _GetNumTalents, _GetAddOnInfo
  75. if Legion then
  76. GetGlyphSocketInfo = function(...) -- Doesn't really exist anymore, so just disable all glyphs always
  77. -- local enabled,_,_,gsID,_ = GetGlyphSocketInfo(i)
  78. return false, nil, nil, nil, nil
  79. end
  80. end
  81.  
  82. ns.legionf = {
  83. ["GetGlyphSocketInfo"] = GetGlyphSocketInfo,
  84. }
  85.  
  86. local LegionStatusText = ""
  87. if Legion then
  88. LegionStatusText = LegionStatusText .. "This is a beta release of the Legion version of EventHorizon. As such, expect there to be bugs. "
  89. else
  90. LegionStatusText = LegionStatusText .. "This is the backwards compatible version of the Legion beta version of EventHorizon. Class Configs likely won't work, so you'll need to customize it yourself."
  91. end
  92. LegionStatusText = LegionStatusText .. "If you encounter a bug, please copy the whole error message including stack trace and tell me about it on EventHorizon's WowInterface page. \n\n"
  93. LegionStatusText = LegionStatusText .. "I don't plan on updating every individual class config for every spec. It takes me weeks to play every class and spec to a level I feel comfortable with enough to set the default config for, and I don't have time for that anymore. \n"
  94. LegionStatusText = LegionStatusText .. "If you have a class config that you think is good enough for your spec or class, please post it to EventHorizon's WowInterface page so I can add it! \n\n Thanks! ^.^ \n - Brusalk \n\n"
  95.  
  96.  
  97.  
  98. local LegionSpecIDMapping = {
  99. [62] = "Mage: Arcane",
  100. [63] = "Mage: Fire",
  101. [64] = "Mage: Frost",
  102. [65] = "Paladin: Holy",
  103. [66] = "Paladin: Protection",
  104. [70] = "Paladin: Retribution",
  105. [71] = "Warrior: Arms",
  106. [72] = "Warrior: Fury",
  107. [73] = "Warrior: Protection",
  108. [102] = "Druid: Balance",
  109. [103] = "Druid: Feral",
  110. [104] = "Druid: Guardian",
  111. [105] = "Druid: Restoration",
  112. [250] = "Death Knight: Blood",
  113. [251] = "Death Knight: Frost",
  114. [252] = "Death Knight: Unholy",
  115. [253] = "Hunter: Beast Mastery",
  116. [254] = "Hunter: Marksmanship",
  117. [255] = "Hunter: Survival",
  118. [256] = "Priest: Discipline",
  119. [257] = "Priest: Holy",
  120. [258] = "Priest: Shadow",
  121. [259] = "Rogue: Assassination",
  122. [260] = "Rogue: Combat",
  123. [261] = "Rogue: Subtlety",
  124. [262] = "Shaman: Elemental",
  125. [263] = "Shaman: Enhancement",
  126. [264] = "Shaman: Restoration",
  127. [265] = "Warlock: Affliction",
  128. [266] = "Warlock: Demonology",
  129. [267] = "Warlock: Destruction",
  130. [268] = "Monk: Brewmaster",
  131. [269] = "Monk: Windwalker",
  132. [270] = "Monk: Mistweaver",
  133. [577] = "Demon Hunter: Havoc",
  134. [581] = "Demon Hunter: Vengeance",
  135. }
  136.  
  137. local LegionClassConfigStatus = {
  138. [262] = true,
  139. [267] = true,
  140. }
  141.  
  142. local BuildLegionClassConfigStatusText = function()
  143. local ret = "EventHorizon\nCurrent Class Status \n\n"
  144. for specID, classname in pairs(LegionSpecIDMapping) do
  145. local id, name, desc, icon, background, role, class = GetSpecializationInfoByID(specID)
  146. local status = LegionClassConfigStatus[specID] and "Implemented" or "NYI"
  147. ret = ret .. name .. " | " .. status .. "\n"
  148. end
  149. ret = ret .. "\nIf your spec is Not Yet Implemented (NYI), please send me your customized config so I can add it as the default config for your spec!\n"
  150. return ret
  151. end
  152.  
  153.  
  154. ns.defaultDB = {
  155. point = {'CENTER', 'UIParent', 'CENTER'},
  156. isActive = true,
  157. version = 4,
  158. }
  159.  
  160. ns.defaultDBG = {
  161. profiles = {
  162. default = {},
  163. },
  164. itemInfo = {},
  165. profilePerChar = {},
  166. defaultProfile = 'default',
  167. version = 4,
  168. }
  169.  
  170. ns.db = {
  171. point = {'CENTER', 'UIParent', 'CENTER'},
  172. isActive = true,
  173. version = 4,
  174. }
  175.  
  176. ns.frames = {
  177. config = {}, -- validated barframe config entries - format = ns.frames.config[i] = {barconfig}
  178. frames = {}, -- all loaded barframes
  179. active = {}, -- refs to barframes currently collecting information (matches talent spec)
  180. shown = {}, -- refs to barframes currently visible to the player (matches stance)
  181. mouseover = {}, -- refs to barframes requiring mouseover target information
  182. }
  183. ns.defaultconfig = {
  184. showTrinketBars = {
  185. default = true,
  186. boolean = true,
  187. name = 'Show Trinket Bars',
  188. desc = 'When enabled, displays trinkets in addition to spells and abilities.',
  189. },
  190. castLine = {
  191. default = true,
  192. boolean = true,
  193. number = true,
  194. name = 'End-of-Cast Line',
  195. desc = 'When enabled, adds a vertical line which marks the end of any spellcast in progress.',
  196. },
  197. gcdStyle = {
  198. default = 'line',
  199. valid = {'line','bar',false},
  200. name = 'Global Cooldown Style',
  201. desc = 'When set to Line, a vertical line will mark the end of the current GCD. \n When set to Bar, a textured bar is used instead. \n Can also be disabled to neither track or display the GCD.',
  202. },
  203.  
  204. enableRedshift = {
  205. default = false,
  206. boolean = true,
  207. name = 'Enable Redshift',
  208. desc = 'An optional module which hides Axis untless certain conditions, such as combat or targeting, are met.',
  209. },
  210. Redshift = {
  211. name = 'Redshift States',
  212. desc = 'Conditions for the Redshift Module to show Axis.',
  213. sub = {
  214. showCombat = {
  215. default = true,
  216. boolean = true,
  217. name = 'Show in Combat',
  218. desc = 'When enabled, displays Axis when in combat.',
  219. },
  220. showHarm = {
  221. default = true,
  222. boolean = true,
  223. name = 'Show Harmful Units',
  224. desc = 'When enabled, displays Axis when an attackable unit is targeted.',
  225. },
  226. showHelp = {
  227. default = false,
  228. boolean = true,
  229. name = 'Show Helpful Units',
  230. desc = 'When enabled, displays Axis when a friendly unit is targeted.',
  231. },
  232. showBoss = {
  233. default = true,
  234. boolean = true,
  235. name = 'Show on Boss',
  236. desc = 'When enabled, displays Axis when a boss-level unit is targeted.',
  237. },
  238. showFocus = {
  239. default = false,
  240. boolean = true,
  241. name = 'Show on Focus',
  242. desc = 'When enabled, displays Axis when you have a focus target.'
  243. },
  244. hideVehicle = {
  245. default = true,
  246. boolean = true,
  247. name = 'Hide in Vehicle',
  248. desc = 'When enabled, HIDES Axis when using a vehicle with its own actionbar.',
  249. },
  250. hideVitals = {
  251. default = true,
  252. boolean = true,
  253. name = 'Hide Vitals',
  254. desc = 'When enabled, the Vitals display is hidden whenever Axis is hidden.',
  255. },
  256. },
  257. },
  258.  
  259. -- Pulse = 0.5,
  260. -- PulseIntensity = 0.5,
  261. -- PulseFPS = 30,
  262.  
  263. Lines = {
  264. default = false,
  265. boolean = true,
  266. table = true,
  267. name = 'Static Lines',
  268. desc = 'When enabled, enables the Lines Module.',
  269. },
  270. LinesColor = {
  271. default = {1,1,1,0.5},
  272. table = true,
  273. name = 'Static Line Colors',
  274. desc = 'The color of any static lines being displayed by the Lines Axis Module.'
  275. },
  276.  
  277. anchor = {
  278. default = {"TOPRIGHT", "EventHorizonHandle", "BOTTOMRIGHT"},
  279. table = true,
  280. name = 'Anchor Position',
  281. desc = "Axis' Handle Information",
  282. },
  283. width = {
  284. default = 150,
  285. number = true,
  286. name = 'Bar Width',
  287. desc = 'Set the width of shown bars. Icons add to the actual width of the window.'
  288. },
  289. height = {
  290. default = 18,
  291. number = true,
  292. name = 'Bar Height',
  293. desc = 'Set the height of each individual bar. Also sets the width of icons.',
  294. },
  295. spacing = {
  296. default = 0,
  297. number = true,
  298. name = 'Bar Spacing',
  299. desc = 'Set the spacing between each shown bar.',
  300. },
  301. staticheight = {
  302. default = false,
  303. number = true,
  304. boolean = true,
  305. name = 'Static Height',
  306. desc = 'When set, Axis will resize its bars to fit this height. \n When disabled, Axis will grow or shrink depending on the number of shown bars.'
  307. },
  308. hideIcons = {
  309. default = false,
  310. boolean = true,
  311. name = 'Hide Bar Icons',
  312. desc = 'When enabled, Icons are not shown, however stack-text is still shown.',
  313. },
  314.  
  315. past = {
  316. default = -3,
  317. number = true,
  318. name = 'Past Time',
  319. desc = 'How many seconds in the past for Axis to display.',
  320. },
  321. future = {
  322. default = 12,
  323. number = true,
  324. name = 'Future Time',
  325. desc = 'How many seconds in the future for Axis to display.'
  326. },
  327.  
  328. texturedbars = {
  329. default = true,
  330. boolean = true,
  331. name = 'Textured Bars',
  332. desc = 'When enabled, Axis displays textured bars according to the Bar Texture option. \n When disabled, Axis displays the bars as a solid color.',
  333. },
  334.  
  335. bartexture = {
  336. default = "Interface\\Addons\\EventHorizon\\Smooth",
  337. string = true,
  338. name = 'Bar Texture',
  339. desc = 'Set the texture to use for each bar.',
  340. },
  341. texturealphamultiplier = {
  342. default = 2,
  343. number = true,
  344. name = 'Texture Alpha-Multiplier',
  345. desc = 'This option directly influences the opacity of textured bars to account for varying degrees of visibility.'
  346. },
  347.  
  348. backdrop = {
  349. default = true,
  350. boolean = true,
  351. name = 'Show Backdrop',
  352. desc = 'When enabled, Axis displays the backdrop.',
  353. },
  354. padding = {
  355. default = 3,
  356. number = true,
  357. name = 'Backdrop Padding',
  358. desc = 'Set the padding between the backdrop and bar edges.'
  359. },
  360. bg = {
  361. default = "Interface\\ChatFrame\\ChatFrameBackground",
  362. string = true,
  363. name = 'Backdrop Texture',
  364. desc = 'Set the texture to use for the backdrop.',
  365. },
  366. border = {
  367. default = "Interface\\Tooltips\\UI-Tooltip-Border",
  368. string = true,
  369. name = 'Backdrop Border Texture',
  370. desc = 'Set the texture to use for the backdrop border.',
  371. },
  372. edgesize = {
  373. default = 8,
  374. number = true,
  375. name = 'Backdrop Edge Size',
  376. desc = 'Set the thickness of the backdrop border.',
  377. },
  378. inset = {
  379. default = {top = 2, bottom = 2, left = 2, right = 2},
  380. table = true,
  381. name = 'Backdrop Insets',
  382. desc = 'Trim the backdrop texture to account for its border.',
  383. },
  384.  
  385. stackFont = {
  386. default = false,
  387. boolean = true,
  388. string = true,
  389. name = 'Stack Text Font',
  390. desc = 'Sets the font of the stack text shown on bar icons.',
  391. },
  392. stackFontSize = {
  393. default = false,
  394. boolean = true,
  395. number = true,
  396. name = 'Stack Text Size',
  397. desc = 'Set the size of the stack text shown on bar icons.',
  398. },
  399. stackFontOutline = {
  400. default = false,
  401. valid = {'OUTLINE','THICKOUTLINE','MONOCHROME',false},
  402. name = 'Stack Text Outline',
  403. desc = 'Set the outline of the stack text shown on bar icons.',
  404. },
  405. stackFontColor = {
  406. default = false,
  407. table = true,
  408. name = 'Stack Text Color',
  409. desc = 'Sets the color of the stack text shown on bar icons.',
  410. },
  411. stackFontShadow = {
  412. default = false,
  413. table = true,
  414. boolean = true,
  415. name = 'Stack Text Shadow',
  416. desc = 'Apply a shadow effect to the stack text shown on bar icons. \n This option adjusts the shadow color and can be left at default for black.',
  417. },
  418. stackFontShadowOffset = {
  419. default = false,
  420. table = true,
  421. boolean = true,
  422. name = 'Stack Text Shadow Offset',
  423. desc = 'Set the offset of the stack text shadow.',
  424. },
  425. stackOnRight = {
  426. default = false,
  427. boolean = true,
  428. name = 'Stack Text on Right',
  429. desc = 'When enabled the stack text is displayed on the right-hand side of the bars. \n When disabled, stack text is shown on the left side, as default.',
  430. },
  431. }
  432.  
  433. ns.defaultcolors = {
  434. sent = {true,class == 'PRIEST' and 0.7 or 1,1},
  435. tick = {true,class == 'PRIEST' and 0.7 or 1,1},
  436. casting = {0,1,0.2,0.25},
  437. castLine = {0,1,0.2,0.3},
  438. cooldown = {0.6,0.8,1,0.3},
  439. debuffmine = {true,class == 'PRIEST' and 0.7 or 1,0.3},
  440. debuff = {true,0.5,0.3},
  441. playerbuff = {true,class == 'PRIEST' and 0.7 or 1,0.3},
  442. nowLine = {1,1,1,0.3},
  443. bgcolor = {0,0,0,0.6},
  444. bordercolor = {1,1,1,1},
  445. gcdColor = {1,1,1,0.5},
  446. }
  447.  
  448. ns.defaultlayouts = {
  449. tick = {
  450. top = 0,
  451. bottom = 0.2,
  452. },
  453. smalldebuff = {
  454. top = 0.2,
  455. bottom = 0.35,
  456. },
  457. cantcast = {
  458. top = 0.35,
  459. bottom = 1,
  460. },
  461. default = {
  462. top = 0.2,
  463. bottom = 1,
  464. },
  465. }
  466.  
  467. ns.config = {Redshift = {}, blendModes = {}}
  468. ns.layouts = {}
  469. ns.colors = {}
  470.  
  471. ns.glyphs = {} -- currently active glyph storage. format = ns.glyphs[i] = glyphID
  472. ns.otherIDs = {} -- combatlog events either not directly tied to bars, or using spells other than bar.spellID
  473. ns.modules = {} -- storage for loaded modules - format = module = ns.modules[string.lower(moduleName)] = {namespace}
  474.  
  475.  
  476. ns.vars = { -- storage for widely used vars/math/etc - format = ns.vars[var] = val
  477. config = {},
  478. onepixelwide = 1,
  479. visibleFrame = true,
  480. numframes = 0,
  481. buff = {},
  482. debuff = {},
  483. }
  484. local vars = ns.vars
  485.  
  486. local UnitDebuff = UnitDebuff
  487. local UnitBuff = UnitBuff
  488.  
  489. local SpellFrame = {}
  490.  
  491. local EventHorizonFrame = CreateFrame('Frame','EventHorizonFrame',UIParent)
  492. local mainframe = CreateFrame('Frame',nil,EventHorizonFrame)
  493. local frame = CreateFrame('Frame')
  494. local frame2 = CreateFrame('Frame')
  495. local frame3 = CreateFrame('Frame')
  496. ns.mainframe = mainframe
  497.  
  498. -- Frames to be created on demand
  499. local handle
  500.  
  501.  
  502. local function printhelp(...) if select('#',...)>0 then return tostring((select(1,...))), printhelp(select(2,...)) end end
  503. local function debug(...)
  504. if DEBUG then
  505. print(...)
  506. end
  507. end
  508. local function print(...)
  509. ChatFrame1:AddMessage('EventHorizon: '..strjoin(',',printhelp(...)))
  510. end
  511.  
  512. local draworder = {
  513. default = -8,
  514. cooldown = -7,
  515. debuff = -6,
  516. playerbuff = -5,
  517. debuffmine = -4,
  518. casting = -3,
  519. sent = -2,
  520. tick = -1,
  521. channeltick = 0,
  522. now = 1,
  523. gcd = 2,
  524. nowI = 7,
  525. }
  526.  
  527. local auraids = {
  528. tick = true,
  529. cantcast = true,
  530. debuff = true,
  531. playerbuff = true,
  532. debuffmine = true,
  533. }
  534.  
  535. local customColors = {
  536. debuff = true,
  537. debuffmine = true,
  538. playerbuff = true,
  539. }
  540.  
  541. local exemptColors = {
  542. default = true,
  543. sent = true,
  544. tick = true,
  545. channeltick = true,
  546. castLine = true,
  547. nowLine = true,
  548. bgcolor = true,
  549. bordercolor = true,
  550. gcdColor = true,
  551. }
  552.  
  553. local equipSlots = {
  554. ["ChestSlot"] = 5,
  555. ["FeetSlot"] = 8,
  556. ["Finger0Slot"] = 11,
  557. ["Finger1Slot"] = 12,
  558. ["HandsSlot"] = 10,
  559. ["HeadSlot"] = 1,
  560. ["LegsSlot"] = 7,
  561. ["MainHandSlot"] = 16,
  562. ["NeckSlot"] = 2,
  563. ["SecondaryHandSlot"] = 17,
  564. ["ShirtSlot"] = 4,
  565. ["ShoulderSlot"] = 3,
  566. ["TabardSlot"] = 19,
  567. ["Trinket0Slot"] = 13,
  568. ["Trinket1Slot"] = 14,
  569. ["WaistSlot"] = 6,
  570. ["WristSlot"] = 9,
  571. }
  572.  
  573. local mainframeEvents = {
  574. ['COMBAT_LOG_EVENT_UNFILTERED'] = true,
  575. ['PLAYER_SPECIALIZATION_UPDATE'] = true,
  576. ['UPDATE_SHAPESHIFT_FORM'] = true,
  577. ['UPDATE_SHAPESHIFT_FORMS'] = true,
  578. ['SPELL_UPDATE_COOLDOWN'] = true,
  579. ['PLAYER_LEVEL_UP'] = true,
  580. ['PLAYER_TARGET_CHANGED'] = true,
  581. ['UNIT_AURA'] = true,
  582. ['PLAYER_TOTEM_UPDATE'] = true
  583. }
  584.  
  585. local reloadEvents = {
  586. ['GLYPH_ADDED'] = true,
  587. ['GLYPH_ENABLED'] = true,
  588. ['GLYPH_REMOVED'] = true,
  589. ['GLYPH_UPDATED'] = true,
  590. ['GLYPH_DISABLED'] = true,
  591. ['PLAYER_REGEN_DISABLED'] = true,
  592. ['PLAYER_REGEN_ENABLED'] = true,
  593. ['ZONE_CHANGED_NEW_AREA'] = true,
  594. ['ZONE_CHANGED_INDOORS'] = true,
  595. ['LFG_LOCK_INFO_RECEIVED'] = true,
  596. ['PLAYER_SPECIALIZATION_CHANGED'] = true,
  597. ['ACTIVE_TALENT_GROUP_CHANGED'] = true,
  598.  
  599. }
  600.  
  601. local tickevents = {
  602. ['SPELL_PERIODIC_DAMAGE'] = true,
  603. ['SPELL_PERIODIC_HEAL'] = true,
  604. ['SPELL_PERIODIC_ENERGIZE'] = true,
  605. ['SPELL_PERIODIC_DRAIN'] = true,
  606. ['SPELL_PERIODIC_LEACH'] = true,
  607. ['SPELL_DAMAGE'] = true,
  608. ['SPELL_HEAL'] = true,
  609. --['SPELL_AURA_APPLIED'] = true,
  610. }
  611.  
  612. -- Dispatch event to method of the event's name.
  613. local EventHandler = function (self, event, ...)
  614. local f = self[event]
  615. if f then
  616. --if event ~= 'COMBAT_LOG_EVENT_UNFILTERED' then print(event) end
  617. f(self,...)
  618. if event ~= 'COMBAT_LOG_EVENT_UNFILTERED' then
  619. ns:ModuleEvent(event,...)
  620. end
  621. end
  622. end
  623.  
  624. local Clone = function (t)
  625. local new = {}
  626. local i, v = next(t, nil) -- i is an index of t, v = t[i]
  627. while i do
  628. new[i] = v
  629. i, v = next(t, i)
  630. end
  631. return new
  632. end
  633.  
  634. -- pairs(t) for metatable usage. Doesn't return numeric index unless value is keyless.
  635. function mpairs(t)
  636. local visited = {}
  637. local f
  638. f = function(_, k)
  639. if not t then
  640. return
  641. end
  642. while true do
  643. local k2, v2 = next(t, k)
  644. if k2 == nil then
  645. break
  646. end
  647. if not visited[k2] then
  648. visited[k2] = true
  649. return k2, v2
  650. end
  651. k = k2
  652. end
  653. local mt = getmetatable(t)
  654. if mt then
  655. local indextable = mt.__index
  656. if type(indextable) == "table" then
  657. t = indextable
  658. return f()
  659. end
  660. end
  661. t = nil
  662. end
  663. return f
  664. end
  665.  
  666. -- Since Blizzard doesn't provide the ability to look up a slot name from a slotID...
  667. local GetSlotName = function (slot)
  668. for k,v in pairs(equipSlots) do
  669. if v == slot then return k end
  670. end
  671. end
  672.  
  673. local mainframe_PLAYER_TOTEM_UPDATE = function( self, slot )
  674. for i,spellframe in pairs(ns.frames.frames) do
  675. if (spellframe.totem) then
  676. spellframe:PLAYER_TOTEM_UPDATE( slot )
  677. -- Totem Mastery doesn't populate immediately, so we delay.
  678. C_Timer.After( 0.1, function () spellframe:PLAYER_TOTEM_UPDATE( slot ) end )
  679. end
  680. end
  681. end
  682.  
  683. local mainframe_UNIT_AURA = function (self,unit)
  684. if vars.buff[unit] then
  685. table.wipe(vars.buff[unit])
  686. for i = 1,50 do
  687. local name, _, icon, count, _, duration, expirationTime, source, _, _, spellID = UnitBuff(unit,i)
  688. --print(name,icon,count,duration,expirationTime,source,spellID)
  689. if not (name and spellID) then break end
  690. table.insert(vars.buff[unit],{
  691. name = name,
  692. icon = icon,
  693. count = count,
  694. duration = duration,
  695. expirationTime = expirationTime,
  696. source = source,
  697. spellID = spellID,
  698. })
  699. end
  700. end
  701. if vars.debuff[unit] then
  702. table.wipe(vars.debuff[unit])
  703. for i = 1,100 do
  704. local name, _, icon, count, _, duration, expirationTime, source, _, _, spellID = UnitDebuff(unit,i)
  705. if not (name and spellID) then break end
  706. table.insert(vars.debuff[unit], {
  707. name = name,
  708. icon = icon,
  709. count = count,
  710. duration = duration,
  711. expirationTime = expirationTime,
  712. source = source,
  713. spellID = spellID,
  714. })
  715. end
  716. end
  717. for i,spellframe in pairs(ns.frames.frames) do
  718. if (spellframe.auraunit and spellframe.auraunit == unit) then
  719. spellframe:UNIT_AURA(unit)
  720. end
  721. end
  722. end
  723.  
  724. local GetAura = function (self)
  725. local s = self.isType == 'playerbuff' and 'buff' or 'debuff'
  726. local a = vars[s][self.auraunit]
  727. if not a then return end
  728. if type(self.auraname) == 'table' then
  729. for k,aura in pairs(a) do
  730. for i = 1,#self.auraname do
  731. if (aura.name == self.auraname[i]) and (aura.source == 'player' or self.unique) and (not(self.uniqueID) or self.uniqueID == aura.spellID) then
  732. return aura.name, aura.icon, aura.count, aura.duration, aura.expirationTime, aura.source, aura.spellID
  733. end
  734. end
  735. end
  736. else
  737. for k,aura in pairs(a) do
  738. if (aura.name == self.auraname) and (aura.source == 'player' or self.unique) and (not(self.uniqueID) or self.uniqueID == aura.spellID) then
  739. return aura.name, aura.icon, aura.count, aura.duration, aura.expirationTime, aura.source, aura.spellID
  740. end
  741. end
  742. end
  743. end
  744.  
  745. ns.GetAura = function (self,auralist,auratype,unit)
  746. if not auratype and unit then return error('Invalid arg in EventHorizon:GetAura(self,auralist,auratype,unit)') end
  747. local a = vars[auratype][unit]
  748. if not a then return end
  749. if type(auralist) == 'table' then
  750. for k,aura in pairs(a) do
  751. for i = 1,#auralist do
  752. local t = type(auralist[i])
  753. if (t == 'string' and aura.name or t == 'number' and aura.spellID) == auralist[i] then
  754. return aura.name, aura.icon, aura.count, aura.duration, aura.expirationTime, aura.source, aura.spellID
  755. end
  756. end
  757. end
  758. else
  759. for k,aura in pairs(a) do
  760. local t = type(auralist)
  761. if (t == 'string' and aura.name or t == 'number' and aura.spellID) == auralist then
  762. return aura.name, aura.icon, aura.count, aura.duration, aura.expirationTime, aura.source, aura.spellID
  763. end
  764. end
  765. end
  766. end
  767.  
  768. -- SpellFrame - All spell bar frames inherit from this class.
  769.  
  770. --Indicators represent a point or range of time. There are different types. The type determines the color and position.
  771. local typeparent = {}
  772.  
  773. local SetStacks = function (self,count)
  774. if count>1 then
  775. self.stacks:SetFormattedText('%d',count)
  776. elseif self.glyphstacks then
  777. if self.glyphstacks[guid] and (self.glyphstacks[guid] > 0) then
  778. self.stacks:SetText(self.glyphstacks[guid])
  779. else
  780. self.stacks:SetText()
  781. end
  782. else
  783. self.stacks:SetText()
  784. end
  785. end
  786.  
  787. local SpellFrame_NotInteresting = function (self, unitid, spellname)
  788. return unitid ~= 'player' or spellname ~= self.spellname
  789. end
  790.  
  791. -- FindItemInfo:
  792. local SpellFrame_FindItemInfo = function (self,slotID)
  793. local itemID = self.itemID or GetInventoryItemID('player',slotID or self.slotID)
  794. if itemID then
  795. local dbI = EventHorizonDBG.itemInfo[itemID]
  796. if dbI and (dbI.name and dbI.tex) then
  797. return itemID,dbI.name,dbI.tex
  798. else
  799. local name,_,_,_,_,_,_,_,_,tex = GetItemInfo(itemID)
  800. if (name and tex) then
  801. EventHorizonDBG.itemInfo[itemID] = {name = name, tex = tex}
  802. return itemID,name,tex
  803. end
  804. end
  805. end
  806. end
  807.  
  808. local SpellFrame_AddIndicator = function (self, typeid, layoutid, time, usetexture)
  809. local indicator
  810. local parent = typeparent[typeid]
  811. local ndtex, ndcol
  812. if usetexture and self.bartexture then ndtex = self.bartexture end
  813. if typeid and customColors[typeid] then
  814. if self.barcolorunique and typeid == 'debuff' then
  815. ndcol = self.barcolorunique
  816. elseif self.barcolor then
  817. ndcol = self.barcolor
  818. end
  819. end
  820.  
  821. if not parent then
  822. --print'creating indicator parent'
  823. parent = {}
  824. parent.unused = {}
  825. typeparent[typeid] = parent
  826. --if DEBUG and typeid=='tick' then parent.numchildren=0 end--]]
  827. end
  828. if #parent.unused>0 then
  829. indicator = tremove(parent.unused)
  830. indicator:ClearAllPoints()
  831. indicator.time = nil
  832. indicator.start = nil
  833. indicator.stop = nil
  834. indicator.happened = nil
  835. --if DEBUG and typeid=='tick' then debug('reusing indicator',indicator.frameindex) end--]]
  836. else
  837. indicator = mainframe:CreateTexture(nil, 'ARTWORK', nil, draworder[typeid])
  838. indicator.parent = parent
  839. --if DEBUG and typeid=='tick' then parent.numchildren=parent.numchildren+1 indicator.frameindex=parent.numchildren debug('adding indicator',indicator.frameindex) end--]]
  840. end
  841. -- Layout
  842. local layouts = ns.layouts
  843. local layout = layouts[layoutid] or layouts.default
  844. local color = ndcol or ns.colors[typeid] or ns.colors.default
  845. if layoutid == 'frameline' then
  846. color = typeid == 'sent' and ns.colors.castLine or ns.colors[typeid]
  847. indicator:SetPoint('TOP',ns.mainframe)
  848. indicator:SetPoint('BOTTOM',ns.mainframe)
  849. else
  850. indicator:SetPoint('TOP',self, 'TOP', 0, -layout.top*vars.barheight)
  851. indicator:SetPoint('BOTTOM',self, 'TOP', 0, -layout.bottom*vars.barheight)
  852. end
  853.  
  854. if usetexture then
  855. indicator:SetTexture(ndtex or vars.bartexture)
  856. indicator:SetTexCoord(unpack(layout.texcoords))
  857. else
  858. indicator:SetTexture(1,1,1,1)
  859. end
  860. indicator:SetVertexColor(unpack(ndcol or color))
  861. if ns.config.blendModes[typeid] and type(ns.config.blendModes[typeid]) == 'string' then
  862. indicator:SetBlendMode(ns.config.blendModes[typeid])
  863. end
  864.  
  865. indicator:Hide()
  866. indicator:SetWidth(vars.onepixelwide)
  867. indicator.time = time
  868. indicator.typeid = typeid
  869. indicator.layoutid = layoutid
  870. if indicator then
  871. tinsert(self.indicators, indicator)
  872. end
  873. return indicator
  874. end
  875.  
  876. local SpellFrame_AddSegment = function (self, typeid, layoutid, start, stop, start2)
  877. if stop<start then return end
  878. local indicator = self:AddIndicator(typeid, layoutid, start, vars.texturedbars)
  879. indicator.time = nil
  880. indicator.start = start
  881. indicator.stop = stop
  882. --debug(start,stop)
  883. return indicator
  884. end
  885.  
  886. local SpellFrame_Remove = function (self,indicator)
  887. if type(indicator)=='number' then
  888. local index, indicator = indicator, self.indicators[indicator]
  889. indicator:Hide()
  890. --if DEBUG and indicator.typeid=='tick' then debug('deleting',indicator.frameindex) end--]]
  891. tinsert(indicator.parent.unused, tremove(self.indicators, index))
  892. else
  893. for index=1,#self.indicators do
  894. if self.indicators[index]==indicator then
  895. indicator:Hide()
  896. --if DEBUG and indicator.typeid=='tick' then debug('deleting',indicator.frameindex) end--]]
  897. tinsert(indicator.parent.unused, tremove(self.indicators,index))
  898. break
  899. end
  900. end
  901. end
  902. end
  903.  
  904. local SpellFrame_OnUpdate = function (self,elapsed)
  905. local now = GetTime()
  906. local diff = now+vars.past
  907.  
  908. -- spellframe.nexttick is used to schedule the creation of predicted ticks as soon as they scroll past now+future.
  909. local nexttick = self.nexttick
  910. if nexttick and nexttick <= now+vars.future then
  911. if nexttick<=self.lasttick then
  912. self:AddIndicator('tick', 'tick', nexttick)
  913. self.latesttick = nexttick
  914. self.nexttick = nexttick + (self.dotMod or self.dot)
  915. else
  916. self.nexttick = nil
  917. end
  918. end
  919. for k=#self.indicators,1,-1 do
  920. local indicator = self.indicators[k]
  921. local time = indicator.time
  922. if time then
  923. -- Example:
  924. -- [-------|------->--------]
  925. -- past now time future
  926. -- now=795, time=800, past=-3, then time is time-now-past after past.
  927. local p = (time-diff)*vars.scale
  928. local remove = p<0 or (time<=now and indicator.typeid=='tick' and not indicator.happened)
  929. if remove then
  930. indicator:Hide()
  931. --if DEBUG and indicator.typeid=='tick' then debug('deleting',indicator.frameindex) end--]]
  932. tinsert(indicator.parent.unused, tremove(self.indicators,k))
  933. elseif p<=1 then
  934. indicator:SetPoint('LEFT', self, 'LEFT', p*vars.barwidth, 0)
  935. indicator:Show()
  936. end
  937. else
  938. local start, stop = indicator.start, indicator.stop
  939. local p1 = (start-diff)*vars.scale
  940. local p2 = (stop-diff)*vars.scale
  941. if p2<0 then
  942. indicator:Hide()
  943. --if DEBUG and indicator.typeid=='tick' then debug('deleting',indicator.frameindex) end--]]
  944. tinsert(indicator.parent.unused, tremove(self.indicators,k))
  945. elseif 1<p1 then
  946. indicator:Hide()
  947. else
  948. indicator:Show()
  949. indicator:SetPoint('LEFT', self, 'LEFT', 0<=p1 and p1*vars.barwidth or 0, 0)
  950. indicator:SetPoint('RIGHT', self, 'LEFT', p2<=1 and p2*vars.barwidth+1 or vars.barwidth, 0)
  951. end
  952. end
  953. end
  954. end
  955.  
  956. local SpellFrame_UNIT_SPELLCAST_SENT = function (self, unitid, spellname, spellrank, spelltarget)
  957. if ((self.cast and not(self.cast[spellname])) or (spellname ~= self.spellname)) or unitid ~= 'player' then return end
  958. local now = GetTime()
  959. self:AddIndicator('sent', 'default', now)
  960. end
  961.  
  962. local Cast_Start = function (self, unitid, spellname, spellrank)
  963. if not(self.cast[spellname]) or unitid ~= 'player' then return end
  964. local _,_,_,_,startTime,endTime,_ = self.cast[spellname].func(unitid)
  965. if not(startTime and endTime) then return end
  966.  
  967. startTime, endTime = startTime/1000, endTime/1000
  968. self.casting = self:AddSegment('casting', 'default', startTime, endTime)
  969.  
  970. local name,_,icon = GetSpellInfo(self.cast[spellname].id)
  971. self.lastcast = name
  972. if not(self.keepIcon) then
  973. self.icon:SetTexture(icon)
  974. end
  975.  
  976. if vars.castLine and (endTime - startTime > vars.castLine) then
  977. self.castLine = self:AddIndicator('sent', 'frameline', endTime)
  978. end
  979.  
  980. local numhits = self.cast[spellname].numhits and self.cast[spellname].numhits ~= true and self.cast[spellname].numhits
  981. if numhits then
  982. local casttime = endTime - startTime
  983. local tick = casttime/numhits
  984. if numhits and numhits ~= true then
  985. for i=1,numhits do
  986. self:AddIndicator('channeltick', 'channeltick', startTime + i*tick)
  987. end
  988. end
  989. end
  990. end
  991.  
  992. local Cast_Update = function (self, unitid, spellname, spellrank)
  993. --debug('UNIT_SPELLCAST_CHANNEL_UPDATE',unitid, spellname, spellrank)
  994. if not(self.cast[spellname]) or unitid ~= 'player' then return end
  995. local _,_,_,_,startTime,endTime,_ = self.cast[spellname].func(unitid)
  996. if not (startTime and endTime) then return end
  997. startTime, endTime = startTime/1000, endTime/1000
  998. if self.casting then
  999. self.casting.stop = endTime
  1000. if vars.castLine and self.castLine then
  1001. self.castLine.time = endTime
  1002. end
  1003. end
  1004. self:RemoveChannelTicksAfter(endTime)
  1005. end
  1006.  
  1007. local Cast_Stop = function (self, unitid, spellname, spellrank)
  1008. if not(self.cast[spellname]) or unitid ~= 'player' then return end
  1009. local now = GetTime()
  1010. if self.casting then
  1011. self.casting.stop = now
  1012. if vars.castLine and self.castLine then
  1013. self.castLine.time = now
  1014. end
  1015. self.casting = nil
  1016. end
  1017. self:RemoveChannelTicksAfter(now)
  1018. end
  1019.  
  1020. local SpellFrame_PLAYER_TOTEM_UPDATE = function ( self, slot )
  1021.  
  1022. if not ( self.totem ) then return end
  1023. local tUp, tName, tStart, tDuration, tIcon = GetTotemInfo( slot )
  1024. if not tUp or tName ~= GetSpellInfo( self.totem ) then return end
  1025.  
  1026. local now = GetTime()
  1027.  
  1028. local name, icon, count, duration, expirationTime, source, spellID = tName, tIcon, 1, tDuration, tStart + tDuration, "player", tID
  1029. local addnew
  1030.  
  1031. if name then
  1032. start = now
  1033.  
  1034. if icon and not (self.cast or self.slotID or self.keepIcon) then self.icon:SetTexture(icon) end
  1035.  
  1036. if self.aurasegment and (self.aurasegment.lastunit == "player") then
  1037. -- The aura is currently displayed
  1038. if expirationTime~=self.aurasegment.stop then
  1039. if self.alwaysrefresh and not self.cast then -- alwaysrefresh = buff. Cast + buff - HoT = BAD. Buffs with cast time and no HoT component are treated much differently.
  1040. if self.dot then -- ...check to see if it's a HoT. If so, it's treated as a DoT.
  1041. self.aurasegment.stop = start-0.2
  1042. if self.cast and self.useSmalldebuff then
  1043. self.cantcast.stop = start-0.2
  1044. end
  1045. self:RemoveTicksAfter(start)
  1046. addnew = true
  1047. else
  1048. -- If it's a buff with no cast time or HoT component, no special handling needed, move along.
  1049. self.aurasegment.stop = expirationTime
  1050. end
  1051. else
  1052. -- The aura was replaced.
  1053. self.aurasegment.stop = start-0.2
  1054. if self.cast and self.useSmalldebuff then
  1055. self.cantcast.stop = start-0.2
  1056. end
  1057. self:RemoveTicksAfter(start)
  1058. addnew = true
  1059. end
  1060. if self.internalcooldown and type(self.internalcooldown) == 'number' then
  1061. local stop = now + self.internalcooldown
  1062. if start > stop then
  1063. start = now
  1064. end
  1065. self:AddSegment('cooldown', 'cooldown', start, stop)
  1066. end
  1067. end
  1068.  
  1069. else
  1070. addnew = true
  1071. if self.internalcooldown and type(self.internalcooldown) == 'number' then
  1072. local stop = now + self.internalcooldown
  1073. if start > stop then
  1074. start = now
  1075. end
  1076. self:AddSegment('cooldown', 'cooldown', start, stop)
  1077. end
  1078. end
  1079. SetStacks(self,1)
  1080. else
  1081. if self.aurasegment then
  1082. if math.abs(self.aurasegment.stop - now)>0.3 then
  1083. self.aurasegment.stop = now
  1084. if self.cast and self.useSmalldebuff then
  1085. self.cantcast.stop = now-0.2
  1086. end
  1087. end
  1088. self:RemoveTicksAfter(now)
  1089. self.aurasegment = nil
  1090. self.stacks:SetText()
  1091. end
  1092. end
  1093. self:UpdateTotem(addnew, source, now, start, expirationTime, duration, name)
  1094. end
  1095.  
  1096. local SpellFrame_UNIT_AURA = function (self, unitid)
  1097. if unitid~=self.auraunit then return end
  1098. if not (self.spellname and self.auraname) then return end
  1099.  
  1100. local name, icon, count, duration, expirationTime, source, spellID = GetAura(self,unitid)
  1101. --print(name, icon, count, duration, expirationTime, source, spellID)
  1102. local afflicted = name and (self.unique or source=='player') and (not self.minstacks or count>=self.minstacks)
  1103. local addnew
  1104. local now = GetTime()
  1105. local start
  1106. local targ = UnitName(self.auraunit)
  1107.  
  1108. if self.uniqueID and self.uniqueID ~= spellID then
  1109. return
  1110. end
  1111.  
  1112. if self.aurasegment and expirationTime == 0 and duration == 0 then -- Timeless aura, bar exists (Overkill)
  1113. for i = #self.indicators,1,-1 do
  1114. self:Remove(i)
  1115. end
  1116. self.aurasegment = nil
  1117. self.nexttick = nil
  1118. self.stacks:SetText()
  1119. return
  1120. end
  1121.  
  1122. if expirationTime == 0 then
  1123. return
  1124. end
  1125.  
  1126. if afflicted then
  1127. start = expirationTime-duration
  1128. if icon and not(self.cast or self.slotID or self.keepIcon) then self.icon:SetTexture(icon) end
  1129. if self.aurasegment and (self.aurasegment.lastunit == targ) then
  1130. -- The aura is currently displayed
  1131. if expirationTime~=self.aurasegment.stop then
  1132. if self.alwaysrefresh and not self.cast then -- alwaysrefresh = buff. Cast + buff - HoT = BAD. Buffs with cast time and no HoT component are treated much differently.
  1133. if self.dot then -- ...check to see if it's a HoT. If so, it's treated as a DoT.
  1134. self.aurasegment.stop = start-0.2
  1135. if self.cast and self.useSmalldebuff then
  1136. self.cantcast.stop = start-0.2
  1137. end
  1138. self:RemoveTicksAfter(start)
  1139. addnew = true
  1140. else
  1141. -- If it's a buff with no cast time or HoT component, no special handling needed, move along.
  1142. self.aurasegment.stop = expirationTime
  1143. end
  1144. else
  1145. -- The aura was replaced.
  1146. self.aurasegment.stop = start-0.2
  1147. if self.cast and self.useSmalldebuff then
  1148. self.cantcast.stop = start-0.2
  1149. end
  1150. self:RemoveTicksAfter(start)
  1151. addnew = true
  1152. end
  1153. if self.internalcooldown and type(self.internalcooldown) == 'number' then
  1154. local stop = now + self.internalcooldown
  1155. if start > stop then
  1156. start = now
  1157. end
  1158. self:AddSegment('cooldown', 'cooldown', start, stop)
  1159. end
  1160. end
  1161. else
  1162. addnew = true
  1163. if self.internalcooldown and type(self.internalcooldown) == 'number' then
  1164. local stop = now + self.internalcooldown
  1165. if start > stop then
  1166. start = now
  1167. end
  1168. self:AddSegment('cooldown', 'cooldown', start, stop)
  1169. end
  1170. end
  1171. SetStacks(self,count)
  1172. else
  1173. if self.aurasegment then
  1174. if math.abs(self.aurasegment.stop - now)>0.3 then
  1175. self.aurasegment.stop = now
  1176. if self.cast and self.useSmalldebuff then
  1177. self.cantcast.stop = now-0.2
  1178. end
  1179. end
  1180. self:RemoveTicksAfter(now)
  1181. self.aurasegment = nil
  1182. self.stacks:SetText()
  1183. end
  1184. end
  1185. self:UpdateDoT(addnew, source, now, start, expirationTime, duration, name)
  1186. end
  1187.  
  1188. local mainframe_PLAYER_TARGET_CHANGED = function (self)
  1189. local exists = UnitExists('target')
  1190. local dead
  1191. if exists then
  1192. dead = UnitIsDead('target')
  1193. end
  1194. for i,spellframe in pairs(ns.frames.frames) do
  1195. if spellframe.auraunit == 'target' then
  1196. if spellframe.aurasegment then
  1197. for i = #spellframe.indicators,1,-1 do
  1198. local ind = spellframe.indicators[i]
  1199. if auraids[ind.typeid] then
  1200. spellframe:Remove(i)
  1201. end
  1202. end
  1203. spellframe.aurasegment = nil
  1204. spellframe.targetdebuff = nil
  1205. spellframe.nexttick = nil
  1206. spellframe.recenttick = nil
  1207. spellframe.stacks:SetText()
  1208. end
  1209.  
  1210. if spellframe.refreshable then
  1211. if exists then
  1212. if dead then
  1213. spellframe.debuffs[UnitGUID('target')] = nil
  1214. else
  1215. spellframe.targetdebuff = spellframe.debuffs[UnitGUID('target')]
  1216. end
  1217. end
  1218. end
  1219. end
  1220. end
  1221.  
  1222. if UnitExists('target') then
  1223. self:UNIT_AURA('target')
  1224. end
  1225. end
  1226.  
  1227. local SpellFrame_RemoveTicksAfter = function (self, min)
  1228. local indicators = self.indicators
  1229. for i = #indicators,1,-1 do
  1230. local ind = indicators[i]
  1231. if (ind.typeid == 'tick') and ind.time>min then
  1232. self:Remove(i)
  1233. end
  1234. end
  1235. --print('removing ticks after',min)
  1236. self.nexttick = nil
  1237. end
  1238.  
  1239. local SpellFrame_RemoveChannelTicksAfter = function (self, min)
  1240. local indicators = self.indicators
  1241. for i = #indicators,1,-1 do
  1242. local ind = indicators[i]
  1243. if ind.typeid == 'channeltick' and ind.time>min then
  1244. self:Remove(i)
  1245. end
  1246. end
  1247. self.nextchanneltick = nil
  1248. end
  1249.  
  1250. local mainframe_CLEU_OtherInterestingSpell = function (self, time, event, hideCaster, srcguid, srcname, srcflags, destguid, destname, destflags, spellid, spellname)
  1251. local now = GetTime()
  1252.  
  1253. if ns.otherIDs[spellname] then
  1254. local id = ns.otherIDs[spellname]
  1255. local bf = ns.frames.frames
  1256. if event == 'SPELL_DAMAGE' and id.isChannel then
  1257. for i in pairs(bf) do
  1258. if bf[i].cast and bf[i].cast[spellname] then
  1259. local tick = bf[i]:AddIndicator('tick', 'tick', now)
  1260. tick.happened = true
  1261. break
  1262. end
  1263. end
  1264. elseif event == 'SPELL_CAST_SUCCESS' or event == 'SPELL_DAMAGE' and id.isGlyph then -- Glyph refresh
  1265. if id.last and (now < (id.last + 0.9)) then -- Throttle heavily, don't want stacks getting blown
  1266. --debug("Ignoring "..event.." from "..spellname.." at "..now)
  1267. return
  1268. else
  1269. --debug("Interesting spell potentially not in frame detected! Spell: "..spellname,event)
  1270. id.last = now
  1271. for i in pairs(bf) do
  1272. local gr = bf[i].glyphrefresh or nil
  1273. local gs = bf[i].glyphstacks or nil
  1274. if gr and (gr[3] == spellname) then
  1275. if gs[destguid] then
  1276. gs[destguid] = gs[destguid] - 1
  1277. bf[i].stacks:SetText(gs[destguid] > 0 and gs[destguid] or nil)
  1278. end
  1279. --debug("SUCCESS! "..gr[3].." has triggered "..frame[i].auraname)
  1280. end
  1281. end
  1282. end
  1283. end
  1284. end
  1285. end
  1286.  
  1287. local AddTicks = {}
  1288. AddTicks.stop = function (self,now,fresh)
  1289. local nexttick = self.start
  1290. while nexttick<=self.stop+0.1 do
  1291. if now+vars.future<nexttick then
  1292. self.nexttick = nexttick
  1293. self.lasttick = self.stop
  1294. break
  1295. end
  1296. if now+vars.past<=nexttick then
  1297. self:AddIndicator('tick', 'tick', nexttick)
  1298. self.latesttick = nexttick
  1299. end
  1300. nexttick = nexttick+(self.dotMod or self.dot)
  1301. end
  1302. end
  1303.  
  1304. AddTicks.start = function (self,now)
  1305.  
  1306. local nexttick = now+(self.dotMod or self.dot)
  1307. while nexttick<=(self.stop+0.2) do
  1308. if now+vars.future<nexttick then
  1309. -- The next tick is not visible yet.
  1310. self.nexttick = nexttick
  1311. self.lasttick = self.stop
  1312. break
  1313. end
  1314. if now+vars.past<=nexttick then
  1315. local tick = self:AddIndicator('tick', 'tick', nexttick)
  1316. self.latesttick = nexttick
  1317. end
  1318. nexttick = nexttick+(self.dotMod or self.dot)
  1319. end
  1320. end
  1321.  
  1322. local SpellFrame_COMBAT_LOG_EVENT_UNFILTERED = function (self, timestamp, event, hideCaster, srcguid,srcname,srcflags, destguid,destname,destflags, spellid,spellname)
  1323. local now = GetTime()
  1324. if event == 'SPELL_AURA_REMOVED' then
  1325. if self.glyphrefresh then
  1326. self.glyphstacks[destguid] = 0
  1327. end
  1328. end
  1329.  
  1330. if event == 'SPELL_CAST_SUCCESS' then
  1331. --debug('SPELL_CAST_SUCCESS',destguid)
  1332. self.castsuccess[destguid] = now
  1333.  
  1334. if self.glyphrefresh then
  1335. for i = 1,6 do
  1336. if ns.glyphs[i] == self.glyphrefresh[2] then
  1337. self.glyphstacks[destguid] = self.glyphrefresh[1]
  1338. SetStacks()
  1339. end
  1340. end
  1341. end
  1342. elseif tickevents[event] then
  1343. local isInvalid = not(self.dot) and (self.cast and self.cast[spellname] and not(self.cast[spellname].numhits)) -- filter out cast+channel bars
  1344. if isInvalid then return end
  1345. if UnitGUID(self.auraunit or 'target')==destguid then
  1346. local tick = self:AddIndicator('tick', 'tick', now)
  1347. tick.happened = true
  1348. if (self.dot and (self.stop and self.stop ~= nil)) then
  1349. if self.isHasted and self.ticks then
  1350. self.dotMod = self.ticks.last and (now - self.ticks.last) or self.dot
  1351. self.dotMod = self.dotMod > self.dot and self.dot or self.dotMod
  1352. self.ticks.last = now
  1353. end
  1354. self:RemoveTicksAfter(now) -- Reconstruct ticks from spellframe info
  1355. local nexttick = now+(self.dotMod or self.dot)
  1356. self.nexttick = nil
  1357. self.recenttick = now
  1358. while nexttick<=(self.stop+0.2) do -- Account for lag
  1359. if now+vars.future<nexttick then
  1360. -- The next tick is not visible yet.
  1361. self.nexttick = nexttick
  1362. self.lasttick = self.stop
  1363. break
  1364. end
  1365. if now+vars.past<=nexttick then
  1366. -- The next tick is visible.
  1367. local tick = self:AddIndicator('tick', 'tick', nexttick)
  1368. if nexttick<=now then
  1369. tick.happened = true
  1370. end
  1371. self.latesttick = nexttick
  1372. end
  1373. nexttick=nexttick+(self.dotMod or self.dot)
  1374. end
  1375. end
  1376. end
  1377. end
  1378. end
  1379.  
  1380. local SpellFrame_UNIT_AURA_refreshable = function (self, unitid)
  1381. if unitid~=self.auraunit then return end
  1382. if not(self.auraname and self.spellname) then return end
  1383. local name, icon, count, duration, expirationTime, source, spellID = GetAura(self,unitid)
  1384. local afflicted = name and (self.unique or source=='player') and (not self.minstacks or count>=self.minstacks)
  1385. local addnew, refresh
  1386. local now = GetTime()
  1387. local guid = UnitGUID(self.auraunit or 'target')
  1388. --print(name,source,self.spellname,self.auraname)
  1389. -- First find out if the debuff was refreshed.
  1390.  
  1391. --[[ if self.aurasegment and expirationTime == 0 and duration == 0 then -- Timeless aura, bar exists (Overkill)
  1392. for i = #self.indicators,1,-1 do
  1393. self:Remove(i)
  1394. end
  1395. self.aurasegment = nil
  1396. self.nexttick = nil
  1397. self.stacks:SetText()
  1398. return
  1399. end ]]
  1400.  
  1401. --[[ if expirationTime == 0 then
  1402. return
  1403. end ]]
  1404.  
  1405. if afflicted then
  1406. start = expirationTime-duration
  1407. if icon and not(self.cast or self.slotID or self.keepIcon) then self.icon:SetTexture(icon) end
  1408. if self.targetdebuff then
  1409. if self.targetdebuff.stop == expirationTime then
  1410. start = self.targetdebuff.start
  1411. else
  1412. -- Check for refresh.
  1413. if start < self.targetdebuff.stop then
  1414. local totalduration = self.targetdebuff.stop - self.targetdebuff.start
  1415. local lasttick = self.targetdebuff.stop - math.fmod(totalduration, self.dotMod or self.dot)
  1416. local success = self.castsuccess[guid]
  1417. local not_recast = true -- Poisons are never actually recast, so we default to true here, because success will always be nil.
  1418. if success then
  1419. not_recast = math.abs(success-start)>0.5
  1420. end
  1421. if not_recast and start < lasttick then
  1422. -- The current debuff was refreshed.
  1423. start = self.targetdebuff.start
  1424. refresh = true
  1425. end
  1426. end
  1427. end
  1428. end
  1429. if self.aurasegment then
  1430. if expirationTime~=self.aurasegment.stop and not refresh then
  1431. -- The current debuff was replaced.
  1432. self.aurasegment.stop = start-0.2
  1433. self:RemoveTicksAfter(start)
  1434.  
  1435. --debug('replaced')
  1436. addnew = true
  1437. end
  1438. else
  1439. addnew = true
  1440. end
  1441. SetStacks(self,count)
  1442. else
  1443. if self.aurasegment then
  1444. if math.abs(self.aurasegment.stop - now)>0.3 then
  1445. -- The current debuff ended.
  1446. self.aurasegment.stop = now
  1447. if self.cantcast then
  1448. self.cantcast.stop = now
  1449. end
  1450. end
  1451. self:RemoveTicksAfter(now)
  1452. self.aurasegment = nil
  1453. self.cantcast = nil
  1454. self.targetdebuff = nil
  1455. self.recenttick = nil
  1456. self.stacks:SetText()
  1457. end
  1458. end
  1459. self:UpdateDoT(addnew, source, now, start, expirationTime, duration, name, refresh, guid)
  1460. end
  1461.  
  1462. local SpellFrame_UpdateDoT = function (self, addnew, source, now, start, expirationTime, duration, name, refresh, guid)
  1463. local addticks
  1464. local isHasted
  1465. local checkDoT = self.auranamePrimary or name
  1466. local isPrimary = checkDoT == name or nil
  1467. self.start, self.stop, self.duration = start, expirationTime, duration
  1468.  
  1469. local targ = UnitName(self.auraunit)
  1470. if addnew then
  1471. --debug('addnew', start, expirationTime)
  1472. local typeid = (source=='player' and self.isType) or (source~='player' and 'debuff')
  1473. if self.cast and self.useSmalldebuff then
  1474. self.aurasegment = self:AddSegment(typeid, 'smalldebuff', start, expirationTime)
  1475.  
  1476. local hastedcasttime = select(7, GetSpellInfo(self.lastcast or self.spellname))/1000
  1477. self.cantcast = self:AddSegment(typeid, 'cantcast', start, expirationTime-hastedcasttime)
  1478.  
  1479. --local pandemic_duration = duration*0.3
  1480. --self.cantcast = self:AddSegment(typeid, 'cantcast', start, expirationTime - pandemic_duration
  1481. --self.cantcast.pandemic_duration = pandemic_duration
  1482.  
  1483. self.aurasegment.lastunit = targ
  1484. else
  1485. self.aurasegment = self:AddSegment(typeid, 'default', start, expirationTime)
  1486. self.aurasegment.lastunit = targ
  1487. end
  1488. -- Add visible ticks.
  1489. if self.dot and isPrimary then
  1490. addticks = start
  1491. end
  1492. if self.debuffs then
  1493. -- Refreshable only.
  1494. self.targetdebuff = {start=start, stop=expirationTime}
  1495. self.debuffs[guid] = self.targetdebuff
  1496. end
  1497. self.recenttick = now
  1498. elseif refresh then
  1499. -- debug('refresh', start, expirationTime)
  1500. -- Note: refresh requires afflicted and self.targetdebuff. Also, afflicted and not self.debuff implies addnew.
  1501. -- So we can get here only if afflicted and self.debuff and self.targetdebuff.
  1502. self.aurasegment.stop = expirationTime
  1503. self.targetdebuff.stop = expirationTime
  1504. if self.cantcast then
  1505. self.cantcast.start = start
  1506. self.cantcast.stop = expirationTime - select(7, GetSpellInfo(self.lastcast or self.spellname))/1000
  1507. end
  1508. if self.latesttick then
  1509. addticks = self.latesttick
  1510. end
  1511. end
  1512. if addticks then
  1513. addticks = self.recenttick or addticks
  1514. local nexttick = addticks+(self.dotMod or self.dot)
  1515. self.nexttick = nil
  1516.  
  1517. if self.hasted then
  1518. if type(self.hasted) == 'number' then
  1519. for i = 1,6 do
  1520. if ns.glyphs[i] == self.hasted then isHasted = true end
  1521. end
  1522. elseif self.hasted == true then
  1523. isHasted = true
  1524. end
  1525. end
  1526.  
  1527. if isHasted and self.expectedTicks then -- Using expectedTicks
  1528. self.dotMod = (expirationTime - start)/self.expectedTicks
  1529. local magearmor = UnitBuff(self.auraunit, "Mage Armor")
  1530. if self.dot and magearmor and self.dotMod <= 1.5 then self.dotMod = self.dotMod * 2 end -- Adjust for mage armor rank 5+ 50% duration reduction, use number comparison to make sure we're not changing something that shouldn't be.
  1531. elseif isHasted then
  1532. local bct = ns.config.hastedSpellID[2]
  1533. local hct = select(7, GetSpellInfo(ns.config.hastedSpellID[1]))/1000
  1534. self.dotMod = self.dot*(hct/bct)
  1535. --[[ if ns.config.nonAffectingHaste then
  1536. for i,nah in ipairs(ns.config.nonAffectingHaste) do
  1537. local name,_,_,_,_,source = ns:GetAura(nah[1],'buff','player')
  1538. if name and (source == 'player') then
  1539. self.dotMod = self.dotMod * nah[2]
  1540. end
  1541. end
  1542. end--]]
  1543. end
  1544. self:RemoveTicksAfter(now)
  1545. --self:AddTicks(now)
  1546.  
  1547. if self.hasted then
  1548. if type(self.hasted) == 'number' then
  1549. for i = 1,6 do
  1550. if ns.glyphs[i] == self.hasted then isHasted = true end
  1551. end
  1552. elseif self.hasted == true then
  1553. isHasted = true
  1554. end
  1555. end
  1556.  
  1557. if isHasted and self.ticks then -- Tick-process haste handling
  1558. self.ticks.last = self.ticks.last or now
  1559. self.dotMod = self.dotMod and self.dotMod or self.dot
  1560. if (self.dotMod > self.dot) or (self.dotMod < 1.5) then self.dotMod = self.dot end
  1561. self.isHasted = true
  1562. elseif isHasted and self.expectedTicks then -- Using expectedTicks
  1563. self.dotMod = (expirationTime - start)/self.expectedTicks
  1564. end
  1565.  
  1566. while nexttick<=(self.stop+0.2) do -- Account for lag
  1567. if now+vars.future<nexttick then
  1568. -- The next tick is not visible yet.
  1569. self.nexttick = nexttick
  1570. self.lasttick = self.stop
  1571. break
  1572. end
  1573. if now+vars.past<=nexttick then
  1574. -- The next tick is visible.
  1575. local tick = self:AddIndicator('tick', 'tick', nexttick)
  1576. if nexttick<=now then
  1577. tick.happened = true
  1578. end
  1579. self.latesttick = nexttick
  1580. end
  1581. nexttick=nexttick+(self.dotMod or self.dot)
  1582. end
  1583.  
  1584. end
  1585. end
  1586.  
  1587. local SpellFrame_UpdateTotem = function (self, addnew, source, now, start, expirationTime, duration, name, refresh, guid)
  1588. local addticks
  1589. local isHasted
  1590. local checkDoT = self.auranamePrimary or name
  1591. local isPrimary = checkDoT == name or nil
  1592. self.start, self.stop, self.duration = start, expirationTime, duration
  1593.  
  1594. local targ = "player"
  1595. if addnew then
  1596. --debug('addnew', start, expirationTime)
  1597. local typeid = (source=='player' and self.isType) or (source~='player' and 'debuff')
  1598. if self.cast and self.useSmalldebuff then
  1599. self.aurasegment = self:AddSegment(typeid, 'smalldebuff', start, expirationTime)
  1600.  
  1601. local hastedcasttime = select(7, GetSpellInfo(self.lastcast or self.spellname))/1000
  1602. self.cantcast = self:AddSegment(typeid, 'cantcast', start, expirationTime-hastedcasttime)
  1603.  
  1604. --local pandemic_duration = duration*0.3
  1605. --self.cantcast = self:AddSegment(typeid, 'cantcast', start, expirationTime - pandemic_duration
  1606. --self.cantcast.pandemic_duration = pandemic_duration
  1607.  
  1608. self.aurasegment.lastunit = targ
  1609. else
  1610. self.aurasegment = self:AddSegment(typeid, 'default', start, expirationTime)
  1611. self.aurasegment.lastunit = targ
  1612. end
  1613. -- Add visible ticks.
  1614. if self.dot and isPrimary then
  1615. addticks = start
  1616. end
  1617. if self.debuffs then
  1618. -- Refreshable only.
  1619. self.targetdebuff = {start=start, stop=expirationTime}
  1620. self.debuffs[guid] = self.targetdebuff
  1621. end
  1622. self.recenttick = now
  1623. elseif refresh then
  1624. -- debug('refresh', start, expirationTime)
  1625. -- Note: refresh requires afflicted and self.targetdebuff. Also, afflicted and not self.debuff implies addnew.
  1626. -- So we can get here only if afflicted and self.debuff and self.targetdebuff.
  1627. self.aurasegment.stop = expirationTime
  1628. self.targetdebuff.stop = expirationTime
  1629. if self.cantcast then
  1630. self.cantcast.start = start
  1631. self.cantcast.stop = expirationTime - select(7, GetSpellInfo(self.lastcast or self.spellname))/1000
  1632. end
  1633. if self.latesttick then
  1634. addticks = self.latesttick
  1635. end
  1636. end
  1637. if addticks then
  1638. addticks = self.recenttick or addticks
  1639. local nexttick = addticks+(self.dotMod or self.dot)
  1640. self.nexttick = nil
  1641.  
  1642. if self.hasted then
  1643. if type(self.hasted) == 'number' then
  1644. for i = 1,6 do
  1645. if ns.glyphs[i] == self.hasted then isHasted = true end
  1646. end
  1647. elseif self.hasted == true then
  1648. isHasted = true
  1649. end
  1650. end
  1651.  
  1652. if isHasted and self.expectedTicks then -- Using expectedTicks
  1653. self.dotMod = (expirationTime - start)/self.expectedTicks
  1654. local magearmor = UnitBuff(self.auraunit, "Mage Armor")
  1655. if self.dot and magearmor and self.dotMod <= 1.5 then self.dotMod = self.dotMod * 2 end -- Adjust for mage armor rank 5+ 50% duration reduction, use number comparison to make sure we're not changing something that shouldn't be.
  1656. elseif isHasted then
  1657. local bct = ns.config.hastedSpellID[2]
  1658. local hct = select(7, GetSpellInfo(ns.config.hastedSpellID[1]))/1000
  1659. self.dotMod = self.dot*(hct/bct)
  1660. --[[ if ns.config.nonAffectingHaste then
  1661. for i,nah in ipairs(ns.config.nonAffectingHaste) do
  1662. local name,_,_,_,_,source = ns:GetAura(nah[1],'buff','player')
  1663. if name and (source == 'player') then
  1664. self.dotMod = self.dotMod * nah[2]
  1665. end
  1666. end
  1667. end--]]
  1668. end
  1669. self:RemoveTicksAfter(now)
  1670. --self:AddTicks(now)
  1671.  
  1672. if self.hasted then
  1673. if type(self.hasted) == 'number' then
  1674. for i = 1,6 do
  1675. if ns.glyphs[i] == self.hasted then isHasted = true end
  1676. end
  1677. elseif self.hasted == true then
  1678. isHasted = true
  1679. end
  1680. end
  1681.  
  1682. if isHasted and self.ticks then -- Tick-process haste handling
  1683. self.ticks.last = self.ticks.last or now
  1684. self.dotMod = self.dotMod and self.dotMod or self.dot
  1685. if (self.dotMod > self.dot) or (self.dotMod < 1.5) then self.dotMod = self.dot end
  1686. self.isHasted = true
  1687. elseif isHasted and self.expectedTicks then -- Using expectedTicks
  1688. self.dotMod = (expirationTime - start)/self.expectedTicks
  1689. end
  1690.  
  1691. while nexttick<=(self.stop+0.2) do -- Account for lag
  1692. if now+vars.future<nexttick then
  1693. -- The next tick is not visible yet.
  1694. self.nexttick = nexttick
  1695. self.lasttick = self.stop
  1696. break
  1697. end
  1698. if now+vars.past<=nexttick then
  1699. -- The next tick is visible.
  1700. local tick = self:AddIndicator('tick', 'tick', nexttick)
  1701. if nexttick<=now then
  1702. tick.happened = true
  1703. end
  1704. self.latesttick = nexttick
  1705. end
  1706. nexttick=nexttick+(self.dotMod or self.dot)
  1707. end
  1708.  
  1709. end
  1710. end
  1711.  
  1712. local SpellFrame_PLAYER_REGEN_ENABLED = function (self)
  1713. local thresh = GetTime() - 10
  1714. local remove = {}
  1715. for guid,data in pairs(self.debuffs) do
  1716. if data.stop < thresh then
  1717. tinsert(remove, guid)
  1718. end
  1719. end
  1720. for _,guid in ipairs(remove) do
  1721. --debug('removing',guid,self.spellname)
  1722. self.debuffs[guid]=nil
  1723. end
  1724. end
  1725.  
  1726. local SpellFrame_SPELL_UPDATE_COOLDOWN = function (self)
  1727. -- print(self.spellname,self.cooldownID,'SPELL_UPDATE_COOLDOWN')
  1728. if self.slotID and self.cooldownID and not(self.spellname) then -- item is equipped but has none of the needed info, so rescan it.
  1729. return self:PLAYER_EQUIPMENT_CHANGED(self.slotID)
  1730. end
  1731. if not(self.cooldownID or self.spellname) then return end
  1732.  
  1733. local start = 0
  1734. local duration = 0
  1735. local enabled
  1736. local ready
  1737.  
  1738. if self.cooldownTable then -- we choose the one with the longer CD (This is mostly for sfiend/mindbender bar)
  1739. for i,cooldown in pairs(self.cooldownTable) do
  1740. start2, duration2, enabled2 = self.CooldownFunction(cooldown)
  1741. if start2+duration2 > start+duration then
  1742. --print(cooldown, "better", start2+duration2, start+duration)
  1743. start = start2
  1744. duration = duration2
  1745. enabled = enabled2
  1746. ready = (enabled==1 and start~=0 and duration) and start+duration
  1747. end
  1748. end
  1749. else
  1750. start, duration, enabled = self.CooldownFunction(self.cooldownID or self.spellname)
  1751. ready = enabled==1 and start~=0 and duration and start+duration
  1752. end
  1753. --print(start, duration, enabled, ready)
  1754. if ready and duration>1.5 then
  1755. -- The spell is on cooldown, but not just because of the GCD.
  1756. if self.cooldownID then
  1757.  
  1758. end
  1759. if self.cooldown ~= ready then -- The CD has changed since last check
  1760. if not(self.coolingdown) then -- No CD bar exists.
  1761. self.coolingdown = self:AddSegment('cooldown', self.smallCooldown and 'smallCooldown' or 'cooldown', start, ready)
  1762. elseif self.coolingdown.stop and self.coolingdown.stop ~= ready then -- cd exists but has changed
  1763. self.coolingdown.start = start
  1764. self.coolingdown.stop = ready
  1765. end
  1766. self.cooldown = ready
  1767. end
  1768. else
  1769. if self.coolingdown then
  1770. -- Spell is off cooldown or cd expires during GCD window
  1771. local now = GetTime()
  1772. -- See when the cooldown is ready. If the spell is currently on GCD, check the GCD end; otherwise check now.
  1773. if self.cooldown > (ready or now) then
  1774. -- The cooldown ended earlier.
  1775. self.coolingdown.stop = now
  1776. end
  1777. self.coolingdown = nil
  1778. end
  1779. self.cooldown = nil
  1780. end
  1781. end
  1782.  
  1783. local SpellFrame_PLAYER_EQUIPMENT_CHANGED = function (self,slot,equipped)
  1784. if not (slot or self.slotID) or (self.slotID ~= slot) then return end
  1785.  
  1786. for i = #self.indicators,1,-1 do
  1787. self:Remove(i)
  1788. end
  1789.  
  1790. self.aurasegment = nil
  1791. self.nexttick = nil
  1792. self.stacks:SetText()
  1793.  
  1794. self.cooldown = nil
  1795. self.coolingdown = nil
  1796. self.playerbuff = nil
  1797. self.spellname = nil
  1798. self.auraname = nil
  1799. self.internalcooldown = true
  1800.  
  1801. local itemID,name,tex = self:FindItemInfo()
  1802. self.cooldownID = itemID
  1803.  
  1804. if (itemID and name and tex) and not(ns.trinkets.blacklist[name]) then
  1805. self.spellname = name
  1806. self.icon:SetTexture(tex)
  1807.  
  1808. self.stance = true -- Always show
  1809.  
  1810. if ns.trinkets[name] then
  1811. if type(ns.trinkets[name]) == 'number' then
  1812. self.playerbuff = ns.trinkets[name]
  1813. elseif type(ns.trinkets[name]) == 'table' then
  1814. self.playerbuff = ns.trinkets[name][1]
  1815. self.internalcooldown = ns.trinkets[name][2]
  1816. end
  1817. elseif self.slotID == 10 then
  1818. self.playerbuff = 54758 -- Engy gloves
  1819. elseif self.slotID == 8 then
  1820. self.playerbuff = 54861 -- Nitro Boosts
  1821. end
  1822.  
  1823. if type(self.playerbuff)=='number' then
  1824. self.auraname = (GetSpellInfo(self.playerbuff))
  1825. elseif type(self.playerbuff)=='table' then
  1826. self.auraname = {}
  1827. self.auranamePrimary = (GetSpellInfo(self.playerbuff[1]))
  1828. for i,id in ipairs(self.playerbuff) do
  1829. tinsert(self.auraname, (GetSpellInfo(id)))
  1830. end
  1831. self.AuraFunction = UnitBuffUnique
  1832. else
  1833. self.auraname = self.spellname
  1834. end
  1835. self:SPELL_UPDATE_COOLDOWN()
  1836. else
  1837. self.stance = 50 -- More efficient than other methods of hiding the bar.
  1838. self.icon:SetTexture(0,0,0,0)
  1839. end
  1840.  
  1841. -- Throttle equipment checks to every 2 seconds. This should decrease overall cpu load while making equipment checks more reliable on beta/ptr.
  1842. -- vars.EQCframes = vars.EQCframes or {}
  1843. -- table.insert(vars.EQCframes,self)
  1844. --print('equipment update - slot '..self.slotID)
  1845. if not(vars.currentEQcheck) then
  1846. frame2.elapsed = 0
  1847. vars.currentEQcheck = true
  1848. frame2:SetScript('OnUpdate', function (self,elapsed)
  1849. self.elapsed = self.elapsed + elapsed
  1850. if self.elapsed >= 2 then
  1851. mainframe:UPDATE_SHAPESHIFT_FORM()
  1852. --[[ for i,v in ipairs(vars.EQCframes) do
  1853. v:SPELL_UPDATE_COOLDOWN()
  1854. end ]]--
  1855. vars.currentEQcheck = nil
  1856. -- vars.EQCframes = nil
  1857. --print('equipment check onupdate complete and cleared')
  1858. self:SetScript('OnUpdate',nil)
  1859. end
  1860. end)
  1861. end
  1862. end
  1863.  
  1864. --[[
  1865. Things get ugly again here.
  1866. UNIT_AURA does not fire for mouseover units, so we need to emulate it.
  1867. UPDATE_MOUSEOVER_UNIT does not fire when a mouseover unit is cleared, so we need to emulate that as well.
  1868. The UMU check is unthrottled by necessity. UNIT_AURA doesn't really need to be run more than 10 times per second, so it gets throttled to save cycles.
  1869. ]]--
  1870. local TTL,TSLU = 0.15,0
  1871. local UpdateMouseover = function (self,elapsed)
  1872. TSLU = TSLU+elapsed
  1873. if not(UnitExists('mouseover')) then
  1874. ns:CheckMouseover()
  1875. frame:SetScript('OnUpdate',nil)
  1876. else
  1877. if (TSLU >= TTL) then
  1878. mainframe:UNIT_AURA('mouseover')
  1879. TSLU = 0
  1880. end
  1881. end
  1882. end
  1883.  
  1884. local SpellFrame_UPDATE_MOUSEOVER_UNIT = function (self)
  1885. --print("UPDATE_MOUSEOVER_UNIT")
  1886. if UnitExists('mouseover') then
  1887. vars.isMouseover = true
  1888. self.auraunit = 'mouseover'
  1889. else
  1890. vars.isMouseover = nil
  1891. self.auraunit = self.baseunit
  1892. end
  1893. frame:SetScript('OnUpdate',UpdateMouseover)
  1894.  
  1895. if self.aurasegment then
  1896. for i = #self.indicators,1,-1 do
  1897. local ind = self.indicators[i]
  1898. if auraids[ind.typeid] then
  1899. self:Remove(i)
  1900. end
  1901. end
  1902. self.aurasegment = nil
  1903. self.nexttick = nil
  1904. self.stacks:SetText()
  1905. end
  1906.  
  1907. if self.refreshable then
  1908. if UnitExists(self.auraunit) then
  1909. local guid = UnitGUID(self.auraunit)
  1910. if UnitIsDead(self.auraunit) then
  1911. self.debuffs[guid] = nil
  1912. else
  1913. self.targetdebuff = self.debuffs[guid]
  1914. --if self.targetdebuff then debug(self.spellname, 'have old') end
  1915. mainframe:UNIT_AURA(self.auraunit)
  1916. --if self.aurasegment then debug(self.spellname, 'added new') end
  1917. if self.glyphstacks and self.aurasegment then
  1918.  
  1919. local count = self.glyphstacks[guid]
  1920. if self.glyphstacks[guid] and ( count > 0) then
  1921. self.stacks:SetText(self.glyphstacks[UnitGUID(self.auraunit)])
  1922. end
  1923. end
  1924. end
  1925. end
  1926. elseif UnitExists(self.auraunit) then
  1927. mainframe:UNIT_AURA(self.auraunit)
  1928. end
  1929. end
  1930.  
  1931. -- A SpellFrame is active (i.e. listening to events) iff the talent requirements are met.
  1932. -- The table EventHorizon.frames.active contains all the active frames.
  1933. -- If the stance requirement is not met, the frame is hidden, but still active.
  1934. local SpellFrame_Deactivate = function (self)
  1935. if not self.isActive then return end
  1936. --debug('unregistering events for', self.spellname)
  1937. self:UnregisterAllEvents()
  1938. if self.interestingCLEU then
  1939. mainframe.framebyspell[self.spellname] = nil
  1940. end
  1941. self:Hide()
  1942. for index=#self.indicators,1,-1 do
  1943. self:Remove(index)
  1944. end
  1945. self.isActive = nil
  1946. end
  1947.  
  1948. local SpellFrame_Activate = function (self)
  1949. if self.isActive then return end
  1950. --debug('registering events for', self.spellname)
  1951. for event in pairs(self.interestingEvent) do
  1952. self:RegisterEvent(event)
  1953. end
  1954. if self.interestingCLEU and self.spellname then
  1955. debug(self.spellname)
  1956. mainframe.framebyspell[self.spellname] = self
  1957. end
  1958.  
  1959. self:Show()
  1960. self.isActive = true
  1961. end
  1962.  
  1963. local timer = 0
  1964. local checkInProgress
  1965. function ns:CheckTalents()
  1966. if checkInProgress then return end
  1967. checkInProgress = true
  1968. frame3:SetScript('OnUpdate', function (f,elapsed)
  1969. timer = timer + elapsed
  1970. if timer >= 2 then
  1971. timer = 0
  1972. checkInProgress = nil
  1973. ns:CheckRequirements()
  1974. f:SetScript('OnUpdate',nil)
  1975. end
  1976. end)
  1977. end
  1978.  
  1979. function ns:SetFrameDimensions()
  1980. local left,right,top,bottom = 0.07, 0.93, 0.07, 0.93
  1981. local barheight2 = self.config.height
  1982. local modHeight = self.config.height
  1983.  
  1984. local sfn = type(self.config.staticframes) == 'number' and self.config.staticframes or 0
  1985. local sfi = self.config.hideIcons == true
  1986. if (#ns.frames.shown >= sfn) and type(self.config.staticheight) == 'number' then
  1987. mainframe:SetHeight(self.config.staticheight)
  1988. vars.barheight = (self.config.staticheight - (vars.barspacing*(vars.numframes - 1)))/vars.numframes
  1989. modHeight = vars.barheight
  1990. local ratio = vars.barheight/barheight2
  1991. ratio = math.abs( (1-(1/ratio))/2 ) -- Yes, this was a bitch to figure out.
  1992. if vars.barheight > barheight2 then -- icon is taller than it is wide
  1993. left = left + ratio
  1994. right = right - ratio
  1995. else
  1996. top = top + ratio
  1997. bottom = bottom - ratio
  1998. end
  1999. else
  2000. vars.barheight = barheight2
  2001. mainframe:SetHeight(vars.numframes * (vars.barheight+vars.barspacing) - vars.barspacing)
  2002. end
  2003.  
  2004. vars.nowleft = -vars.past/(vars.future-vars.past)*vars.barwidth-0.5 + (ns.config.hideIcons and 0 or ns.config.height)
  2005. if ns.frames.nowIndicator then
  2006. ns.frames.nowIndicator:SetPoint('BOTTOM',mainframe,'BOTTOM')
  2007. ns.frames.nowIndicator:SetPoint('TOPLEFT',mainframe,'TOPLEFT', vars.nowleft, 0)
  2008. ns.frames.nowIndicator:SetWidth(vars.onepixelwide)
  2009. ns.frames.nowIndicator:SetTexture(unpack(self.colors.nowLine))
  2010. end
  2011.  
  2012. local shownframes = #ns.frames.shown > 0
  2013. if shownframes then
  2014. for i = 1,#ns.frames.shown do
  2015. local spellframe = ns.frames.shown[i]
  2016. if spellframe then
  2017. --spellframe:ClearAllPoints()
  2018. spellframe:SetHeight(vars.barheight)
  2019. spellframe:SetWidth(vars.barwidth)
  2020.  
  2021. spellframe.icon:ClearAllPoints()
  2022. spellframe:SetPoint('RIGHT', mainframe, 'RIGHT')
  2023. if i == 1 then
  2024. spellframe:SetPoint('TOPLEFT', mainframe, 'TOPLEFT', sfi and 0 or barheight2, 0)
  2025. else
  2026. spellframe:SetPoint('TOPLEFT', ns.frames.shown[i-1], 'BOTTOMLEFT', 0, -vars.barspacing)
  2027. end
  2028. if not(sfi) then
  2029. spellframe.icon:SetPoint('TOPRIGHT',spellframe,'TOPLEFT')
  2030. spellframe.icon:SetWidth(barheight2)
  2031. spellframe.icon:SetHeight(modHeight)
  2032. spellframe.icon:SetTexCoord(left,right,top,bottom)
  2033. end
  2034.  
  2035. local indicators = #spellframe.indicators > 0
  2036. if indicators then
  2037. for i=1,#spellframe.indicators do
  2038. local indicator = spellframe.indicators[i]
  2039. if indicator then
  2040. local ndtex, ndcol
  2041. local typeid = indicator.typeid
  2042. local layoutid = indicator.layoutid
  2043. local usetexture
  2044.  
  2045. if not(exemptColors[typeid]) and self.bartexture then ndtex = self.bartexture end
  2046. if typeid and customColors[typeid] then
  2047. usetexture = true
  2048. if spellframe.barcolorunique and typeid == 'debuff' then
  2049. ndcol = spellframe.barcolorunique
  2050. elseif spellframe.barcolor then
  2051. ndcol = spellframe.barcolor
  2052. end
  2053. elseif typeid == 'cooldown' then
  2054. usetexture = true
  2055. end
  2056. usetexture = usetexture and vars.texturedbars
  2057.  
  2058. -- Layout
  2059. local layouts = ns.layouts
  2060. local layout = layouts[layoutid] or layouts.default
  2061. local color = ndcol or ns.colors[typeid] or ns.colors.default
  2062.  
  2063. if layoutid == 'frameline' then
  2064. color = typeid == 'sent' and ns.colors.castLine or ns.colors[typeid]
  2065. indicator:SetPoint('TOP',ns.mainframe)
  2066. indicator:SetPoint('BOTTOM',ns.mainframe)
  2067. else
  2068. indicator:SetPoint('TOP',spellframe, 'TOP', 0, -layout.top*vars.barheight)
  2069. indicator:SetPoint('BOTTOM',spellframe, 'TOP', 0, -layout.bottom*vars.barheight)
  2070. end
  2071.  
  2072. if usetexture then
  2073. indicator:SetTexture(ndtex or vars.bartexture)
  2074. indicator:SetTexCoord(unpack(layout.texcoords))
  2075. else
  2076. indicator:SetTexture(1,1,1,1)
  2077. end
  2078. indicator:SetVertexColor(unpack(ndcol or color))
  2079. --if typeid == 'casting' then print(unpack(ndcol or color)) end
  2080. end
  2081. end
  2082. end
  2083.  
  2084. end
  2085. end
  2086. end
  2087. end
  2088. --[[
  2089. function ns:AddCheckedTalent(tab,index)
  2090. local required = true
  2091. for k,v in ipairs(self.talents) do
  2092. if (v[1] == tab) and (v[2] == index) then
  2093. required = nil
  2094. end
  2095. end
  2096. if required then
  2097. table.insert(self.talents,{tab,index})
  2098. end
  2099. end
  2100. ]]
  2101. function ns:CheckRequirements()
  2102. --print('CheckTalents')
  2103. table.wipe(self.frames.active)
  2104. table.wipe(self.frames.mouseover)
  2105. --print('checkrequirements')
  2106. --print(GetTime())
  2107.  
  2108. EventHorizonDB.charInfo = EventHorizonDB.charInfo or {}
  2109. local cc = EventHorizonDB.charInfo
  2110.  
  2111. vars.activeTree = GetSpecialization() or 0
  2112. vars.activeTalentGroup = GetActiveSpecGroup('player')
  2113. vars.currentLevel = UnitLevel('player')
  2114.  
  2115. vars.currentTalents = {};
  2116.  
  2117. for i=1, GetNumTalents() do
  2118. local nameTalent, icon, tier, column, active = GetTalentInfo(i);
  2119. vars.currentTalents[i] = active;
  2120. end
  2121.  
  2122. self.glyphs = {}
  2123. for i = 1,6 do
  2124. local enabled,_,_,gsID,_ = GetGlyphSocketInfo(i)
  2125. if enabled and gsID then
  2126. self.glyphs[i] = gsID
  2127. else
  2128. self.glyphs[i] = nil
  2129. end
  2130. end
  2131.  
  2132.  
  2133. for i,config in ipairs(self.frames.config) do
  2134. local rG = false -- Glyphs don't exist anymore for combat affecting behavior (maybe?)
  2135. local rS = config.requiredTree
  2136. local rL = config.requiredLevel or 1
  2137. local rT = config.requiredTalent
  2138. local nRT = config.requiredTalentUnselected
  2139.  
  2140. local haveGlyphReq = true
  2141. local haveSpecReq = true
  2142. local haveTalentReq = true
  2143. local haveTalentRequiredUnselected = true
  2144. local haveLevelReq = rL <= vars.currentLevel
  2145.  
  2146.  
  2147. if rG then
  2148. haveGlyphReq = nil
  2149. for i,glyph in ipairs(self.glyphs) do
  2150. if rG == glyph then haveGlyphReq = true end
  2151. end
  2152. end
  2153.  
  2154. if rS then
  2155. haveSpecReq = nil
  2156. if type(rS) == 'number' then
  2157. rS = {rS}
  2158. end
  2159. --print(config.spellID,rS)
  2160. for i,spec in ipairs(rS) do
  2161. --print(" ", spec, vars.activeTree)
  2162. if spec == vars.activeTree then
  2163. haveSpecReq = true
  2164. end
  2165. end
  2166. end
  2167.  
  2168. -- All talents must be active for it to work
  2169. if rT then
  2170. haveTalentReq = true
  2171. if type(rT) == 'number' then
  2172. rT = {rT}
  2173. end
  2174. --nameTalent, icon, tier, column, active = GetTalentInfo(rT);
  2175. for i, talent in ipairs(rT) do
  2176. haveTalentReq = haveTalentReq and vars.currentTalents[talent]
  2177. end
  2178. end
  2179.  
  2180. --print("nRT Check:", nRT, vars.currentTalents[nRT])
  2181. if nRT then
  2182. if vars.currentTalents[nRT] then
  2183. haveTalentRequiredUnselected = nil
  2184. end
  2185. end
  2186.  
  2187. -- Check if there already is a frame
  2188. local spellframe = self.frames.frames[i]
  2189. local frameExists = spellframe~=nil
  2190.  
  2191. if haveGlyphReq and haveSpecReq and haveLevelReq and haveTalentReq and haveTalentRequiredUnselected then
  2192. if frameExists then
  2193. spellframe:Activate()
  2194. else
  2195. spellframe = self:CreateSpellBar(config)
  2196. self.frames.frames[i] = spellframe
  2197. end
  2198. table.insert(self.frames.active, spellframe)
  2199. if spellframe.usemouseover then
  2200. table.insert(self.frames.mouseover, spellframe)
  2201. end
  2202.  
  2203. if type(config.cooldown)=="table" then -- We need to update the spellID again
  2204. spellframe.cooldownTable = config.cooldown
  2205. end
  2206. else
  2207. if frameExists then
  2208. spellframe:Deactivate()
  2209. end
  2210. end
  2211. end
  2212.  
  2213. local activate = #self.frames.active > 0
  2214. self:Activate(activate)
  2215. if activate then
  2216. mainframe:UPDATE_SHAPESHIFT_FORM()
  2217. end
  2218.  
  2219. ns:ModuleEvent('CheckTalents')
  2220. end
  2221.  
  2222. local mainframe_UPDATE_SHAPESHIFT_FORM = function (self)
  2223. local stance = GetShapeshiftForm()
  2224. -- On PLAYER_LOGIN, GetShapeshiftForm() sometimes returns a bogus value (2 on a priest with 1 form). Ignored for Warlocks and cached information.
  2225. if not(stance) or (GetNumShapeshiftForms() and class ~= 'WARLOCK' and stance>GetNumShapeshiftForms()) then return end
  2226. mainframe:SetHeight(1)
  2227. table.wipe(ns.frames.shown)
  2228.  
  2229. EventHorizonDB.charInfo.stance = stance
  2230. vars.numframes = 0
  2231.  
  2232. for i,spellframe in ipairs(ns.frames.active) do
  2233. local shown = spellframe:IsShown()
  2234.  
  2235. if spellframe.stance then
  2236. shown = false
  2237. if type(spellframe.stance) == 'table' then
  2238. shown = false
  2239. for i in ipairs(spellframe.stance) do
  2240. if spellframe.stance[i] == stance then
  2241. shown = true
  2242. end
  2243. end
  2244. elseif spellframe.stance == true then
  2245. shown = true
  2246. elseif spellframe.stance == stance and not shown then
  2247. shown = true
  2248. elseif spellframe.stance and spellframe.stance ~= stance and shown then
  2249. shown = false
  2250. end
  2251. end
  2252.  
  2253. if spellframe.notstance then
  2254. shown = true
  2255. if spellframe.notstance and type(spellframe.notstance) == 'table' then
  2256. for i in ipairs(spellframe.notstance) do
  2257. if spellframe.notstance[i] == stance then
  2258. shown = false
  2259. end
  2260. end
  2261. elseif spellframe.notstance == stance then
  2262. shown = false
  2263. end
  2264. end
  2265.  
  2266. if shown then
  2267. spellframe:Show()
  2268. vars.numframes = vars.numframes+1
  2269. table.insert(ns.frames.shown,spellframe)
  2270. else
  2271. spellframe:Hide()
  2272. for i,indicator in ipairs(spellframe.indicators) do
  2273. indicator:Hide()
  2274. end
  2275. end
  2276. end
  2277.  
  2278. if vars.numframes>0 then
  2279. ns:SetFrameDimensions()
  2280. if (EventHorizonDB.redshift) and (ns.modules.redshift.isReady and EventHorizonDB.redshift.isActive == true) then
  2281. ns.modules.redshift:Check()
  2282. elseif ns.isActive and vars.visibleFrame then
  2283. mainframe:Show()
  2284. end
  2285. else
  2286. mainframe:Hide()
  2287. end
  2288.  
  2289. return true
  2290. end
  2291.  
  2292. function ns:CheckMouseover()
  2293. for i,spellframe in ipairs(self.frames.mouseover) do
  2294. spellframe:UPDATE_MOUSEOVER_UNIT()
  2295. end
  2296. end
  2297.  
  2298. -- GCD indicator
  2299. local mainframe_SPELL_UPDATE_COOLDOWN = function (self)
  2300. if ns.frames.gcd then
  2301. local start, duration = GetSpellCooldown(vars.gcdSpellName)
  2302. local sfi = ns.config.hideIcons
  2303. --print(start,duration)
  2304. if start and duration and duration>0 then
  2305. vars.gcdend = start+duration
  2306. mainframe:SetScript('OnUpdate', function (self, elapsed)
  2307. if vars.gcdend then
  2308. local now = GetTime()
  2309. if vars.gcdend<=now then
  2310. vars.gcdend = nil
  2311. ns.frames.gcd:Hide()
  2312. else
  2313. local diff = now+vars.past
  2314. local p = (vars.gcdend-diff)*vars.scale
  2315. if p<=1 then
  2316. ns.frames.gcd:SetPoint('RIGHT', self, 'RIGHT',(p-1)*vars.barwidth+vars.onepixelwide, 0)
  2317. ns.frames.gcd:Show()
  2318. end
  2319. end
  2320. end
  2321. end)
  2322. else
  2323. vars.gcdend = nil
  2324. ns.frames.gcd:Hide()
  2325. mainframe:SetScript('OnUpdate', nil)
  2326. end
  2327. end
  2328. end
  2329.  
  2330. -- Dispatch the CLEU.
  2331. local mainframe_COMBAT_LOG_EVENT_UNFILTERED = function (self,time, event, hideCaster, srcguid,srcname,srcflags, destguid,destname,destflags, spellid,spellname)
  2332. if srcguid~=vars.playerguid or event:sub(1,5)~='SPELL' then return end
  2333. local spellframe = self.framebyspell[spellname]
  2334. if ns.otherIDs[spellname] then
  2335. mainframe:CLEU_OtherInterestingSpell(time, event, hideCaster, srcguid,srcname,srcflags, destguid,destname,destflags, spellid,spellname)
  2336. end
  2337. if spellframe then
  2338. if spellframe.interestingCLEU[event] then
  2339. spellframe:COMBAT_LOG_EVENT_UNFILTERED(time, event, hideCaster, srcguid,srcname,srcflags, destguid,destname,destflags, spellid,spellname)
  2340. end
  2341. end
  2342. end
  2343.  
  2344. function ns:LoadClassModule()
  2345. local class = select(2,UnitClass('player'))
  2346.  
  2347. class = class:sub(1,1)..class:sub(2):lower() -- 'WARLOCK' -> 'Warlock'
  2348.  
  2349. local name, _, _, enabled, loadable = GetAddOnInfo('EventHorizon_'..class)
  2350.  
  2351. DisableAddOn('EventHorizon_Redshift')
  2352. DisableAddOn('EventHorizon_Lines')
  2353.  
  2354. if not loadable then return end
  2355.  
  2356. local loaded, reason = LoadAddOn(name)
  2357. if loaded and self.InitializeClass then
  2358. return true
  2359. end
  2360. end
  2361.  
  2362. --[[
  2363. spellid: number, rank doesn't matter
  2364. abbrev: string
  2365. config: table
  2366. {
  2367. cast = <boolean>,
  2368. channeled = <boolean>,
  2369. numhits = <number of hits per channel>,
  2370. cooldown = <boolean>,
  2371. debuff = <boolean>,
  2372. dot = <tick interval in s, requires debuff>,
  2373. refreshable = <boolean>,
  2374. }
  2375. --]]
  2376. function ns:NewSpell(config)
  2377. local spellid = config.spellID or config.itemID or config.slotID
  2378. if type(spellid)~='number' then
  2379. return
  2380. end
  2381.  
  2382. table.insert(self.frames.config, config)
  2383. end
  2384.  
  2385.  
  2386.  
  2387.  
  2388. function ns:newSpell(config) -- New class config to old class config
  2389. local n = {}
  2390. local c = config
  2391.  
  2392. n.spellID = (type(config.debuff)=="table" and (type(config.debuff[1])=="table" and config.debuff[1][1] or config.debuff[1]) or config.debuff) or (type(config.cast)=="table" and config.cast[1] or config.cast) or (type(config.cooldown)=="table" and config.cooldown[1] or config.cooldown) or (type(config.playerbuff)=="table" and (type(config.playerbuff[1])=="table" and config.playerbuff[1][1] or config.playerbuff[1]) or config.playerbuff) or EventHorizon.config.gcdSpellID
  2393.  
  2394. n.itemID = c.itemID
  2395. n.slotID = c.slotID
  2396. n.cast = c.cast
  2397. n.channeled = c.channel or c.channeled
  2398. n.cooldown = c.cooldown
  2399. n.refreshable = c.refreshable == false and false or true
  2400.  
  2401. if type(c.debuff) == "table" then
  2402. if type(c.debuff[1]) == "table" then
  2403. n.dot = c.debuff[1][2]
  2404. n.debuff = {}
  2405. for i,debuff in ipairs(c.debuff) do
  2406. table.insert(n.debuff, debuff[1])
  2407. end
  2408. else
  2409. n.debuff = c.debuff[1]
  2410. n.dot = c.debuff[2]
  2411. end
  2412. elseif c.debuff then
  2413. n.debuff = c.debuff
  2414. end
  2415.  
  2416.  
  2417.  
  2418. if type(c.playerbuff) == "table" and not n.debuff then
  2419. if type(c.playerbuff[1]) == "table" then
  2420. n.dot = c.playerbuff[1][2]
  2421. n.playerbuff = {}
  2422. for i,spell in ipairs(c.playerbuff) do
  2423. table.insert(n.playerbuff, spell[1])
  2424. end
  2425. else
  2426. n.playerbuff = c.playerbuff[1]
  2427. n.dot = c.playerbuff[2]
  2428. end
  2429. else
  2430. n.playerbuff = c.playerbuff
  2431. end
  2432.  
  2433. if n.dot == 0 then
  2434. n.dot = nil
  2435. end
  2436.  
  2437. if class == "HUNTER" or class == "ROGUE" or class == "WARRIOR" or class == "DEATHKNIGHT" or class == "DEATH_KNIGHT" or class == "MONK" then
  2438. n.hasted = false
  2439. elseif class == "PRIEST" or class == "MAGE" or class == "WARLOCK" or class == "PALADIN" or class == "SHAMAN" then
  2440. n.hasted = true
  2441. else
  2442. if GetSpecialization() == 1 then --Balance Druid
  2443. n.hasted = true
  2444. else
  2445. n.hasted = false
  2446. end
  2447. end
  2448.  
  2449. if c.hasted == true or c.hasted == false then -- overwrite default. Ex: Doom for warlocks doesn't benefit from haste
  2450. n.hasted = c.hasted
  2451. end
  2452.  
  2453. n.recast = c.recast
  2454. n.minstacks = c.minstacks
  2455. n.internalcooldown = c.internalcooldown
  2456. n.unique = c.unique
  2457. n.keepIcon = c.keepIcon
  2458. n.icon = c.icon
  2459. n.smallCooldown = c.smallCooldown
  2460.  
  2461. n.requiredGlyph = c.requiredGlyph
  2462. n.requiredTree = c.requiredTree
  2463. n.requiredLevel = c.requiredLevel
  2464. n.requiredTalent = c.requiredTalent
  2465. n.requiredTalentUnselected = c.requiredTalentUnselected
  2466.  
  2467. n.stance = c.stance
  2468. n.auraunit = c.unitaura
  2469. n.auraunit = c.auraunit
  2470.  
  2471. n.totem = c.totem
  2472.  
  2473. -- print("Debuff is type", type(config.debuff), "and has 1st value of", select(1,config.debuff))
  2474. debug("Adding", n.spellID, n.debuff, n.cast, n.dot, n.cooldown)
  2475. ns:NewSpell(n)
  2476.  
  2477. end
  2478.  
  2479.  
  2480.  
  2481.  
  2482.  
  2483.  
  2484.  
  2485.  
  2486.  
  2487.  
  2488. --Set spellframe attributes separately from bar creation. Helps keep things tidy and all, y'know?
  2489. local function SetSpellAttributes(spellframe,config)
  2490. local interestingEvent = {}
  2491. local interestingCLEU = {}
  2492. local config = config
  2493. local otherids = ns.otherIDs
  2494. local spellname = spellframe.spellname
  2495. interestingEvent['UNIT_SPELLCAST_SENT'] = true
  2496.  
  2497. if config.itemID or config.slotID then
  2498. spellframe.cooldown = true -- Not getting out of this one. It's an item, what else do you watch?
  2499. spellframe.cooldownTable = type(config.cooldown)=="table" and config.cooldown or nil
  2500. spellframe.cooldownID = config.itemID
  2501. spellframe.CooldownFunction = GetItemCooldown
  2502. interestingEvent['SPELL_UPDATE_COOLDOWN'] = true
  2503. interestingEvent['BAG_UPDATE_COOLDOWN'] = true
  2504. if config.slotID then
  2505. config.playerbuff = true
  2506. config.internalcooldown = true -- Failsafe
  2507. interestingEvent['PLAYER_EQUIPMENT_CHANGED'] = true
  2508. end
  2509. end
  2510.  
  2511. if config.cast or config.channeled then
  2512. spellframe.cast = {}
  2513. if config.channeled then
  2514. -- Register for the CLEU tick event.
  2515. interestingCLEU.SPELL_DAMAGE = true
  2516. -- Register event functions
  2517. interestingEvent['UNIT_SPELLCAST_CHANNEL_START'] = true
  2518. interestingEvent['UNIT_SPELLCAST_CHANNEL_STOP'] = true
  2519. interestingEvent['UNIT_SPELLCAST_CHANNEL_UPDATE'] = true
  2520. local tcc = type(config.channeled)
  2521. if config.channeled == true then -- defaults
  2522. spellframe.cast[spellname] = {
  2523. numhits = config.numhits or true, -- use numhits as an indicator that the cast is channeled and not to change smalldebuff, or something.
  2524. func = UnitChannelInfo,
  2525. id = config.spellID,
  2526. }
  2527. elseif tcc == 'number' then
  2528. local sn = GetSpellInfo(config.channeled)
  2529. spellframe.cast[sn] = {
  2530. numhits = config.numhits or true, -- use numhits as an indicator that the cast is channeled and not to change smalldebuff, or something.
  2531. func = UnitChannelInfo,
  2532. id = config.channeled,
  2533. }
  2534. otherids[sn] = {isChannel = true}
  2535. elseif tcc == 'table' then
  2536. local channel = type(config.channeled[1]) == 'table' and config.channeled or {config.channeled}
  2537. for _,v in ipairs(channel) do
  2538. local sn = GetSpellInfo(v[1])
  2539. spellframe.cast[sn] = {
  2540. numhits = v[2] or config.numhits or true,
  2541. func = UnitChannelInfo,
  2542. id = v[1],
  2543. }
  2544. otherids[sn] = {isChannel = true}
  2545. end
  2546. end
  2547. end
  2548. if config.cast then
  2549. interestingEvent['UNIT_SPELLCAST_START'] = true
  2550. interestingEvent['UNIT_SPELLCAST_STOP'] = true
  2551. interestingEvent['UNIT_SPELLCAST_DELAYED'] = true
  2552. spellframe.useSmalldebuff = config.recast
  2553. if ((config.debuff or config.playerbuff) and type(config.debuff or config.playerbuff) == 'boolean') then
  2554. spellframe.useSmalldebuff = true
  2555. end
  2556. if config.cast == true then
  2557. spellframe.cast[spellname] = {
  2558. func = UnitCastingInfo,
  2559. id = config.spellID
  2560. }
  2561. else
  2562. config.cast = type(config.cast) == 'table' and config.cast or {config.cast}
  2563. for _,id in ipairs(config.cast) do
  2564. spellframe.cast[GetSpellInfo(id)] = {
  2565. func = UnitCastingInfo,
  2566. id = id
  2567. }
  2568. end
  2569. end
  2570. end
  2571. end
  2572.  
  2573. if config.cooldown then
  2574. if type(config.cooldown) == 'number' then
  2575. spellframe.cooldownID = config.cooldown
  2576. spellframe.cooldown = true
  2577. spellframe.cooldownTable = nil
  2578. elseif type(config.cooldown) == 'table' then -- If the second spellID entered is actually usable, then use that otherwise use the other
  2579. spellframe.cooldownID = true
  2580. spellframe.cooldownTable = config.cooldown
  2581. spellframe.cooldown = true
  2582. else -- /shrug
  2583. spellframe.cooldown = config.cooldown
  2584. spellframe.cooldownTable = nil
  2585. end
  2586. spellframe.CooldownFunction = GetSpellCooldown
  2587. interestingEvent['SPELL_UPDATE_COOLDOWN'] = true
  2588. end
  2589.  
  2590. if config.debuff then
  2591. spellframe.isType = 'debuffmine'
  2592. spellframe.AuraFunction = UnitDebuff
  2593. spellframe.auraunit = config.auraunit or 'target'
  2594. vars.debuff[spellframe.auraunit] = {}
  2595. if spellframe.auraunit == 'mouseover' then
  2596. interestingEvent['UPDATE_MOUSEOVER_UNIT'] = true
  2597. spellframe.usemouseover = true
  2598. spellframe.baseunit = config.baseunit or 'target'
  2599. end
  2600. local tcd = type(config.debuff)
  2601. if tcd == 'number' then
  2602. spellframe.auraname = (GetSpellInfo(config.debuff))
  2603. elseif tcd == 'table' then
  2604. spellframe.auranamePrimary = (GetSpellInfo(config.spellID))
  2605. spellframe.auraname = {}
  2606. for i,id in ipairs(config.debuff) do
  2607. tinsert(spellframe.auraname, (GetSpellInfo(id)))
  2608. end
  2609. spellframe.AuraFunction = UnitDebuffUnique
  2610. else
  2611. spellframe.auraname = spellname
  2612. end
  2613. -- interestingEvent['UNIT_AURA'] = true
  2614. -- interestingEvent['PLAYER_TARGET_CHANGED'] = true
  2615. if config.dot then
  2616. spellframe.dot = config.dot
  2617. interestingCLEU.SPELL_PERIODIC_DAMAGE = true
  2618. spellframe.AddTicks = AddTicks.stop
  2619. if config.refreshable then
  2620. spellframe.refreshable = true
  2621. spellframe.UNIT_AURA = SpellFrame.UNIT_AURA_refreshable
  2622. -- spellframe.PLAYER_TARGET_CHANGED = SpellFrame.PLAYER_TARGET_CHANGED_refreshable
  2623. interestingEvent['PLAYER_REGEN_ENABLED'] = true
  2624. interestingCLEU.SPELL_CAST_SUCCESS=true
  2625. spellframe.debuffs = {}
  2626. spellframe.castsuccess = {}
  2627. end
  2628. end
  2629. elseif config.playerbuff then
  2630. spellframe.isType = 'playerbuff'
  2631. spellframe.AuraFunction = UnitBuff
  2632. spellframe.auraunit = config.auraunit or 'player'
  2633. vars.buff[spellframe.auraunit] = {}
  2634. if config.auraunit then
  2635. -- interestingEvent['PLAYER_TARGET_CHANGED'] = true
  2636. end
  2637. if spellframe.auraunit == 'mouseover' then
  2638. interestingEvent['UPDATE_MOUSEOVER_UNIT'] = true
  2639. spellframe.usemouseover = true
  2640. spellframe.baseunit = config.baseunit or 'target'
  2641. end
  2642. spellframe.alwaysrefresh = true
  2643. -- interestingEvent['UNIT_AURA'] = true
  2644. local tcp = type(config.playerbuff)
  2645. if tcp == 'number' then
  2646. spellframe.auraname = (GetSpellInfo(config.playerbuff))
  2647. elseif tcp == 'table' then
  2648. spellframe.auraname = {}
  2649. spellframe.auranamePrimary = (GetSpellInfo(config.spellID))
  2650. for i,id in ipairs(config.playerbuff) do
  2651. tinsert(spellframe.auraname, (GetSpellInfo(id)))
  2652. end
  2653. spellframe.AuraFunction = UnitBuffUnique
  2654. else
  2655. spellframe.auraname = spellname
  2656. end
  2657.  
  2658. if config.dot then -- Register for periodic effect intervals.
  2659. spellframe.dot = config.dot
  2660. spellframe.AddTicks = AddTicks.stop
  2661. interestingCLEU.SPELL_PERIODIC_HEAL = true
  2662. if config.refreshable then
  2663. spellframe.refreshable = true
  2664. spellframe.UNIT_AURA = SpellFrame.UNIT_AURA_refreshable
  2665. -- spellframe.PLAYER_TARGET_CHANGED = SpellFrame.PLAYER_TARGET_CHANGED_refreshable
  2666. interestingEvent['PLAYER_REGEN_ENABLED'] = true
  2667. interestingCLEU.SPELL_CAST_SUCCESS=true
  2668. spellframe.debuffs = {}
  2669. spellframe.castsuccess = {}
  2670. end
  2671. end
  2672.  
  2673. elseif config.totem then
  2674. spellframe.totem = config.totem
  2675. spellframe.isType = 'playerbuff'
  2676. spellframe.auraunit = config.auraunit or 'player'
  2677. spellframe.AuraFunction = 'GetTotemInfo'
  2678. vars.buff[spellframe.auraunit] = {}
  2679. spellframe.alwaysrefresh = true
  2680. spellframe.auraname = GetSpellInfo(config.totem)
  2681. end
  2682.  
  2683.  
  2684. if config.cleu or config.event then -- Register custom CLEU events.
  2685. if config.event then -- Optional alias for the forgetful.
  2686. config.cleu = config.event
  2687. end
  2688. local cleu = type(config.cleu)
  2689. if cleu == 'string' then -- Single event
  2690. interestingCLEU[config.cleu] = true
  2691. elseif cleu == 'table' then
  2692. for i in pairs(config.cleu) do -- Multiple events
  2693. interestingCLEU[ config.cleu[i] ] = true
  2694. end
  2695. end
  2696. end
  2697.  
  2698. if type(config.glyphrefresh) == 'table' then
  2699. spellframe.glyphrefresh = config.glyphrefresh
  2700. spellframe.glyphstacks = {}
  2701. if not otherids[config.glyphrefresh[3]] then
  2702. otherids[config.glyphrefresh[3]] = {}
  2703. end
  2704. otherids[config.glyphrefresh[3]][spellname] = true
  2705. otherids[config.glyphrefresh[3]].isGlyph = true
  2706. interestingCLEU.SPELL_AURA_REMOVED = true
  2707. end
  2708.  
  2709. spellframe.hasted = config.hasted
  2710. spellframe.minstacks = config.minstacks
  2711. spellframe.stance = config.stance
  2712. spellframe.notstance = config.notstance
  2713. spellframe.internalcooldown = config.internalcooldown
  2714. spellframe.bartexture = config.bartexture
  2715. spellframe.barcolor = config.barcolor
  2716. spellframe.barcolorunique = config.barcolorunique
  2717. spellframe.unique = config.unique
  2718. spellframe.uniqueID = config.uniqueID
  2719. spellframe.keepIcon = config.keepIcon
  2720. spellframe.smallCooldown = config.smallCooldown
  2721.  
  2722. spellframe.interestingCLEU = interestingCLEU
  2723. return interestingEvent
  2724. end
  2725.  
  2726.  
  2727. function ns:CreateSpellBar(config)
  2728. local spellid = config.spellID
  2729. local invid = config.itemID
  2730. local csid = config.slotID
  2731. local slotname, spellname, tex, _
  2732.  
  2733. local spellframe = CreateFrame('Frame', nil, mainframe)
  2734. mainframe.numframes = mainframe.numframes+1
  2735.  
  2736. if spellid then
  2737. spellname, _, tex = GetSpellInfo(spellid)
  2738. elseif invid then
  2739. spellname,_,_,_,_,_,_,_,_,tex,_ = GetItemInfo(invid)
  2740. elseif csid then
  2741. slotname = GetSlotName(csid)
  2742. spellframe.slotID = csid
  2743. spellframe.slotName = slotname
  2744. local itemID = GetInventoryItemID('player',csid)
  2745. if itemID then
  2746. spellname,_,_,_,_,_,_,_,_,tex,_ = GetItemInfo(itemID)
  2747. else
  2748. spellname = slotName
  2749. tex = nil
  2750. end
  2751. end
  2752. local basename = slotname or spellname
  2753. --debug('creating frame for ',spellname)
  2754. spellframe.spellname = spellname
  2755.  
  2756. -- Create the bar.
  2757. spellframe.indicators = {}
  2758. if ns.config.barbg then
  2759. spellframe:SetBackdrop{bgFile = vars.bartexture}
  2760. spellframe:SetBackdropColor(unpack(ns.colors.barbgcolor))
  2761. end
  2762.  
  2763. -- Create and set the spell icon.
  2764. if config.icon then
  2765. local t = type(config.icon)
  2766. if t == 'number' then
  2767. if spellid then
  2768. _,_,tex = GetSpellInfo(config.icon)
  2769. elseif invid then
  2770. tex = select(10,GetItemInfo(config.icon))
  2771. end
  2772. elseif t == 'string' then
  2773. tex = config.icon
  2774. end
  2775. config.keepIcon = true
  2776. end
  2777. spellframe.icon = spellframe:CreateTexture(nil, 'BORDER')
  2778. spellframe.icon:SetTexture(tex)
  2779.  
  2780. spellframe.stacks = spellframe:CreateFontString(nil, 'OVERLAY')
  2781. if vars.stackFont then
  2782. spellframe.stacks:SetFont(vars.stackFont,vars.stackFontSize)
  2783. if vars.stackFontShadow then
  2784. spellframe.stacks:SetShadowColor(unpack(vars.stackFontShadow))
  2785. spellframe.stacks:SetShadowOffset(unpack(vars.stackFontShadowOffset))
  2786. end
  2787. else
  2788. spellframe.stacks:SetFontObject('NumberFontNormalSmall')
  2789. end
  2790. spellframe.stacks:SetVertexColor(unpack(vars.stackFontColor))
  2791.  
  2792. for k,v in pairs(SpellFrame) do
  2793. if not spellframe[k] then spellframe[k] = v end
  2794. end
  2795.  
  2796. spellframe.interestingEvent = SetSpellAttributes(spellframe,config)
  2797.  
  2798. spellframe:SetScript('OnEvent', EventHandler)
  2799. spellframe:SetScript('OnUpdate', spellframe.OnUpdate)
  2800.  
  2801. local sfi = self.config.hideIcons
  2802. local sor = self.config.stackOnRight
  2803. -- Layout
  2804. spellframe.stacks:SetPoint((sfi and not(sor)) and 'BOTTOMLEFT' or 'BOTTOMRIGHT', (sfi or sor) and spellframe or spellframe.icon, (sfi and not(sor)) and 'BOTTOMLEFT' or 'BOTTOMRIGHT')
  2805. spellframe.stacks:SetJustifyH(sor and 'LEFT' or 'RIGHT')
  2806.  
  2807. spellframe:Activate()
  2808.  
  2809. if config.slotID then
  2810. spellframe:PLAYER_EQUIPMENT_CHANGED(config.slotID) -- Initialize trinkets and such if needed.
  2811. end
  2812.  
  2813. return spellframe
  2814. end
  2815.  
  2816. function ns:LoadModules()
  2817. for i,module in pairs(self.modules) do
  2818. if not EventHorizonDB[i] then EventHorizonDB[i] = { isActive = true } end
  2819. if (EventHorizonDB[i] and EventHorizonDB[i].isActive == true) or module.alwaysLoad then
  2820. if not(self.modules[i].Enable) then self.modules[i].Enable = function () end end
  2821. if not(self.modules[i].Disable) then self.modules[i].Disable = function () end end
  2822. self.modules[i]:Init()
  2823. self.modules[i]:Enable()
  2824. vars.modulesLoaded = true
  2825. end
  2826. end
  2827. end
  2828.  
  2829. function ns:ActivateModule(module,slash)
  2830. if EventHorizonDB[module].isActive ~= true then
  2831. self.modules[module].isActive = true
  2832. EventHorizonDB[module].isActive = true
  2833. if not(self.modules[module].isReady == true) then self.modules[module].Init() end
  2834. self.modules[module].Enable(slash)
  2835. end
  2836. end
  2837.  
  2838. function ns:DeactivateModule(module,slash)
  2839. if EventHorizonDB[module].isActive == true then
  2840. self.modules[module].isActive = false
  2841. EventHorizonDB[module].isActive = false
  2842. if not(self.modules[module].isReady == true) then self.modules[module].Init() end
  2843. self.modules[module].Disable(slash)
  2844. end
  2845. end
  2846.  
  2847. function ns:ToggleModule(module,slash)
  2848. if EventHorizonDB[module].isActive ~= true then
  2849. self:ActivateModule(module,slash)
  2850. else
  2851. self:DeactivateModule(module,slash)
  2852. end
  2853. end
  2854.  
  2855. -- External event handler for modules, same rules as EH's event handler (passes self, extra args, event is presumed known)
  2856. function ns:ModuleEvent(event, ...)
  2857. for i,module in pairs(self.modules) do
  2858. local f = module[event]
  2859. if f then
  2860. f(module,...)
  2861. end
  2862. end
  2863. end
  2864.  
  2865. function ns:Activate(...)
  2866. local activate = select('#',...) == 0 or ...
  2867. --debug('Activate',activate, ...)
  2868. if not activate then
  2869. return self:Deactivate()
  2870. end
  2871. for k,v in pairs(mainframeEvents) do
  2872. mainframe:RegisterEvent(k)
  2873. end
  2874.  
  2875. self.isActive = true
  2876. vars.visibleFrame = true
  2877.  
  2878. if (self.modules.redshift.isReady and EventHorizonDB.redshift.isActive == true) then
  2879. self.modules.redshift:Check()
  2880. else
  2881. mainframe:Show()
  2882. end
  2883. end
  2884.  
  2885. function ns:Deactivate()
  2886. if self.isActive==false then
  2887. return
  2888. end
  2889. mainframe:UnregisterAllEvents()
  2890. mainframe:RegisterEvent('PLAYER_SPECIALIZATION_UPDATE')
  2891.  
  2892. mainframe:Hide()
  2893.  
  2894. self.isActive = false
  2895. end
  2896.  
  2897. function ns:InitDB()
  2898. if not EventHorizonDB then EventHorizonDB = self.defaultDB end
  2899. -- print('initdb')
  2900. local reset
  2901. -- Upgrade DB.
  2902. if EventHorizonDB and not EventHorizonDB.version then
  2903. EventHorizonDB.version = 0
  2904. end
  2905. if EventHorizonDB.version ~= EventHorizon.defaultDB.version then
  2906. reset = true
  2907. table.wipe(EventHorizonDB)
  2908. EventHorizonDB = Clone(ns.defaultDB)
  2909. end
  2910.  
  2911. if not EventHorizonDBG then EventHorizonDBG = self.defaultDBG end
  2912. -- Upgrade DB.
  2913. if EventHorizonDBG and not EventHorizonDBG.version then
  2914. EventHorizonDBG.version = 0
  2915. end
  2916. if EventHorizonDBG.version ~= EventHorizon.defaultDBG.version then
  2917. reset = true
  2918. table.wipe(EventHorizonDBG)
  2919. EventHorizonDBG = Clone(ns.defaultDBG)
  2920. end
  2921.  
  2922. -- If profile doesn't exist, set it to the default.
  2923. EventHorizonDBG.profilePerChar[playername] = EventHorizonDBG.profilePerChar[playername] or EventHorizonDBG.defaultProfile
  2924. local ppc = EventHorizonDBG.profilePerChar[playername]
  2925.  
  2926. if not EventHorizonDBG.profiles[ppc] then
  2927. EventHorizonDBG.profiles[ppc] = {}
  2928. end
  2929. self.vars.currentProfile = EventHorizonDBG.profiles[ppc]
  2930.  
  2931. if reset then
  2932. print('Your savedvariables have been reset due to potential conflicts with older versions.')
  2933. end
  2934.  
  2935. self.db = EventHorizonDB
  2936. self.dbg = EventHorizonDBG
  2937. end
  2938.  
  2939. --[[
  2940. Should only be called after the DB is loaded and spell and talent information is available.
  2941. --]]
  2942. function ns:Initialize()
  2943. -- Make sure this function is called only once.
  2944. --self.Initialize = nil
  2945. --print('initialize')
  2946. self:InitDB()
  2947.  
  2948. local popupIn = function(popup_to_show, delay)
  2949. local popup_f = CreateFrame("frame")
  2950. local elapsedTime = 0
  2951. popup_f:SetScript("OnUpdate", function(self, elapsed)
  2952. elapsedTime = elapsedTime + elapsed
  2953. if elapsedTime > delay then
  2954. StaticPopup_Show(popup_to_show)
  2955. popup_f:SetScript("OnUpdate", nil)
  2956. popup_f = nil
  2957. end
  2958. end)
  2959. end
  2960.  
  2961. StaticPopupDialogs["EH_WodDialog"] = {
  2962. text = WodStatusText,
  2963. showAlert = true,
  2964. button1 = "Hide Forever",
  2965. button2 = "Hide",
  2966. hideOnEscape = 1,
  2967. OnAccept = function()
  2968. StaticPopup_Hide("EH_WodDialog")
  2969. EventHorizonDB.__WodClassStatusNotification2 = true
  2970. end
  2971. }
  2972.  
  2973.  
  2974. StaticPopupDialogs["EH_LegionDialog2"] = {
  2975. text = BuildLegionClassConfigStatusText(),
  2976. showAlert = true,
  2977. button1 = "Hide Forever",
  2978. button2 = "Hide",
  2979. hideOnEscape = 1,
  2980. OnAccept = function()
  2981. StaticPopup_Hide("EH_LegionDialog2")
  2982. EventHorizonDB.__LegionClassConfigStatusNotification2 = true
  2983. end
  2984. }
  2985.  
  2986.  
  2987. StaticPopupDialogs["EH_LegionDialog1"] = {
  2988. text = LegionStatusText,
  2989. showAlert = true,
  2990. button1 = "Hide Forever",
  2991. button2 = "Hide",
  2992. hideOnEscape = 1,
  2993. OnAccept = function()
  2994. EventHorizonDB.__LegionClassStatusNotification2 = true
  2995. end,
  2996. OnHide = function()
  2997. StaticPopup_Hide("EH_LegionDialog1")
  2998. popupIn("EH_LegionDialog2", 0.5)
  2999. end,
  3000. }
  3001.  
  3002. if Wod and not Legion then
  3003. if not EventHorizonDB.__WodClassStatusNotification2 then
  3004. popupIn("EH_WodDialog", 2)
  3005. end
  3006. elseif Legion then
  3007. if EventHorizonDB.__LegionClassStatusNotification2 then -- Dialog1 hidden
  3008. if not EventHorizonDB.__LegionClassConfigStatusNotification2 then -- We've not hidden dialog 2
  3009. popupIn("EH_LegionDialog2", 2)
  3010. end
  3011. else
  3012. popupIn("EH_LegionDialog1", 2) -- Need to show Dialog1
  3013. end
  3014. end
  3015.  
  3016. --debug('GetTalentInfo(1,1)',GetTalentInfo(1,1))
  3017. vars.playerguid = UnitGUID('player')
  3018.  
  3019. -- Create the main and spell frames.
  3020. mainframe:SetHeight(1)
  3021. mainframe.numframes = 0
  3022. mainframe.framebyspell= {}
  3023. mainframe:SetScript('OnEvent', EventHandler)
  3024. mainframe:SetScale(self.config.scale or 1)
  3025.  
  3026. vars.buff.player = {}
  3027.  
  3028. if not self:LoadClassModule() then
  3029. return
  3030. end
  3031.  
  3032. self:ApplyConfig()
  3033.  
  3034. self:InitializeClass()
  3035. if self.config.showTrinketBars and self.config.showTrinketBars == true then
  3036. self:NewSpell({slotID = 13})
  3037. self:NewSpell({slotID = 14})
  3038. end
  3039.  
  3040. local sfi = self.config.hideIcons
  3041. mainframe:SetWidth(vars.barwidth + (sfi and 0 or self.config.height))
  3042.  
  3043. self:SetupStyleFrame() -- Spawn backdrop frame.
  3044.  
  3045. -- Create the indicator for the current time.
  3046. -- Bugfix: When the UI scale is at a very low setting, textures with a width of 1
  3047. -- were not visible in some resolutions.
  3048. local effectiveScale = mainframe:GetEffectiveScale()
  3049. if effectiveScale then
  3050. vars.onepixelwide = 1/effectiveScale
  3051. end
  3052. --nowI = CreateFrame('Frame',nil,mainframe)
  3053. --nowI:SetFrameLevel(20)
  3054. ns.frames.nowIndicator = mainframe:CreateTexture(nil, 'ARTWORK', nil, draworder.nowI)
  3055.  
  3056. ns.frames.nowIndicator:SetPoint('BOTTOM',mainframe,'BOTTOM')
  3057. ns.frames.nowIndicator:SetPoint('TOPLEFT',mainframe,'TOPLEFT', vars.nowleft, 0)
  3058. ns.frames.nowIndicator:SetWidth(vars.onepixelwide)
  3059. ns.frames.nowIndicator:SetTexture(unpack(self.colors.nowLine))
  3060. if self.config.blendModes.nowLine and type(self.config.blendModes.nowLine) == 'string' then
  3061. ns.frames.nowIndicator:SetBlendMode(self.config.blendModes.nowLine)
  3062. end
  3063.  
  3064. local anchor = self.config.anchor or {'TOPRIGHT', 'EventHorizonHandle', 'BOTTOMRIGHT'}
  3065. if anchor[2]=='EventHorizonHandle' then
  3066. -- Create the handle to reposition the frame.
  3067. handle = CreateFrame('Frame', 'EventHorizonHandle', mainframe)
  3068. handle:SetFrameStrata('HIGH')
  3069. handle:SetWidth(10)
  3070. handle:SetHeight(5)
  3071. handle:EnableMouse(true)
  3072. handle:SetClampedToScreen(1)
  3073. handle:RegisterForDrag('LeftButton')
  3074. handle:SetScript('OnDragStart', function(handle, button) handle:StartMoving() end)
  3075. handle:SetScript('OnDragStop', function(handle)
  3076. handle:StopMovingOrSizing()
  3077. local a,b,c,d,e = handle:GetPoint(1)
  3078. if type(b)=='frame' then
  3079. b=b:GetName()
  3080. end
  3081. EventHorizonDB.point = {a,b,c,d,e}
  3082. end)
  3083. handle:SetMovable(true)
  3084.  
  3085. mainframe:SetPoint(unpack(anchor))
  3086. handle:SetPoint(unpack(EventHorizonDB.point))
  3087.  
  3088. handle.tex = handle:CreateTexture(nil, 'ARTWORK', nil, 7)
  3089. handle.tex:SetAllPoints()
  3090. handle:SetScript('OnEnter',function(frame) frame.tex:SetTexture(1,1,1,1) end)
  3091. handle:SetScript('OnLeave',function(frame) frame.tex:SetTexture(1,1,1,0.1) end)
  3092. handle.tex:SetTexture(1,1,1,0.1)
  3093.  
  3094. if EventHorizonDB.isLocked then
  3095. handle:Hide()
  3096. end
  3097. end
  3098.  
  3099. vars.gcdSpellName = self.config.gcdSpellID and (GetSpellInfo(self.config.gcdSpellID))
  3100. if vars.gcdSpellName and self.config.gcdStyle then
  3101. -- Create the GCD indicator, register cooldown event.
  3102. ns.frames.gcd = mainframe:CreateTexture(nil, 'ARTWORK',nil,draworder.gcd)
  3103. ns.frames.gcd:SetPoint('BOTTOM',mainframe,'BOTTOM')
  3104. ns.frames.gcd:SetPoint('TOP',mainframe,'TOP')
  3105. ns.frames.gcd:Hide()
  3106.  
  3107. if self.config.gcdStyle == 'line' then
  3108. ns.frames.gcd:SetWidth(vars.onepixelwide)
  3109. else
  3110. ns.frames.gcd:SetPoint('LEFT',mainframe,'LEFT', vars.nowleft, 0)
  3111. end
  3112.  
  3113. local gcdColor = self.colors.gcdColor or {.5,.5,.5,.3}
  3114. ns.frames.gcd:SetTexture(unpack(gcdColor))
  3115. if self.config.blendModes.gcdColor and type(self.config.blendModes.gcdColor) == 'string' then
  3116. ns.frames.gcd:SetBlendMode(self.config.blendModes.gcdColor)
  3117. end
  3118. end
  3119.  
  3120. mainframe:SetPoint(unpack(anchor))
  3121.  
  3122. SLASH_EVENTHORIZON1 = '/eventhorizon'
  3123. SLASH_EVENTHORIZON2 = '/ehz'
  3124. SlashCmdList['EVENTHORIZON'] = function(msg)
  3125. local cmd = string.lower(msg)
  3126. local toggle = not(msg) or cmd == ''
  3127.  
  3128. if cmd == 'help' then
  3129. debug'Use "/eventhorizon" or "/ehz" to show or hide EventHorizon.'
  3130. debug'To enable or disable a module, use "/ehz ModuleName". For example, "/ehz redshift".'
  3131. debug'To see a list of currently installed modules and visible bars, use "/ehz status".'
  3132. if anchor[2]=='EventHorizonHandle' then
  3133. debug(' EventHorizon is currently '..(EventHorizonDB.isLocked and 'locked.' or 'movable.'))
  3134. debug(' To '..(EventHorizonDB.isLocked and 'unlock ' or 'lock ')..'EventHorizon, use "/ehz lock".')
  3135. debug' If you are unable see or move EventHorizon, use "/ehz reset".'
  3136. end
  3137. elseif cmd == 'status' then
  3138. print('Installed plugins:')
  3139. for i in pairs(self.modules) do debug(' '..i) end
  3140. print('Visible bars:')
  3141. for i,v in pairs(self.frames.shown) do debug(' '..v.spellname) end
  3142. elseif cmd == 'reset' then
  3143. if anchor[2]=='EventHorizonHandle' then
  3144. debug'Resetting EventHorizon\'s position.'
  3145. EventHorizonHandle:SetPoint(unpack(self.defaultDB.point))
  3146. self:CheckTalents()
  3147. else
  3148. print"The frame is otherwise anchored. Adjust config.anchor in [my]config.lua to move EventHorizon."
  3149. end
  3150. elseif cmd == 'lock' then
  3151. if anchor[2]=='EventHorizonHandle' then
  3152. if EventHorizonHandle:IsShown() then
  3153. EventHorizonHandle:Hide()
  3154. EventHorizonDB.isLocked = true
  3155. else
  3156. EventHorizonHandle:Show()
  3157. EventHorizonDB.isLocked = nil
  3158. end
  3159. print("The frame is now "..(EventHorizonDB.isLocked and 'locked.' or 'unlocked.'))
  3160. else
  3161. print"The frame is otherwise anchored. Adjust config.anchor in [my]config.lua to move EventHorizon."
  3162. end
  3163. elseif self.modules[cmd] then
  3164. self:ToggleModule(string.lower(msg))
  3165. print(string.lower(msg)..' has been turned '..((self.modules[string.lower(msg)].isActive == true) and 'ON' or 'OFF')..'.')
  3166. elseif toggle then
  3167. if self.isActive then
  3168. print('Deactivating EventHorizon. Use "/ehz help" to see what else you can do.')
  3169. self:Deactivate()
  3170. mainframe:UnregisterEvent('PLAYER_SPECIALIZATION_UPDATE')
  3171. else
  3172. print('Activating EventHorizon. Use "/ehz help" to see what else you can do.')
  3173. self:Activate()
  3174. self:CheckTalents()
  3175. end
  3176. else
  3177. print('Invalid command. Use "/ehz" alone to show or hide EventHorizon, or "/ehz help" to see a list of commands.')
  3178. end
  3179. EventHorizonDB.isActive = self.isActive
  3180. end
  3181.  
  3182. ns.isActive = EventHorizonDB.isActive
  3183. if not EventHorizonDB.isActive then
  3184. self:Deactivate()
  3185. mainframe:UnregisterEvent('PLAYER_SPECIALIZATION_UPDATE')
  3186. end
  3187.  
  3188. self:CheckRequirements()
  3189. self:LoadModules()
  3190.  
  3191. if not(ns.config.hastedSpellID and type(ns.config.hastedSpellID) == 'table') then
  3192. vars.useOldHaste = true
  3193. end
  3194. if ns.config.nonAffectingHaste then
  3195. if type(ns.config.nonAffectingHaste[1]) == 'number' then
  3196. ns.config.nonAffectingHaste = {ns.config.nonAffectingHaste}
  3197. end
  3198. end
  3199.  
  3200. self.isReady = true
  3201. end
  3202.  
  3203. function ns:ApplyConfig()
  3204. table.wipe(ns.vars.config)
  3205. local config = {}
  3206. local ppc = EventHorizonDBG.profilePerChar[playername] -- EventHorizonDBG.profilePerChar[playername] = 'ProfileName'
  3207. if ppc then
  3208. config = EventHorizonDBG.profiles[ppc] -- EventHorizon.profiles[ProfileName] = {overriddenConfig}
  3209. end
  3210. setmetatable(config,{__index = ns.config}) -- Set non-overridden values to what is in [my]config.lua
  3211.  
  3212. vars.past = -math.abs(config.past) or -3 -- We really don't want config.past to be positive, so negative absolute values work great here.
  3213. vars.future = math.abs(config.future) or 9
  3214. vars.barheight = config.height or 18
  3215. vars.barwidth = config.width or 150
  3216. vars.barspacing = config.spacing or 0
  3217. vars.scale = 1/(vars.future-vars.past)
  3218. vars.bartexture = config.bartexture or 'Interface\\Addons\\EventHorizon\\Smooth'
  3219. vars.texturedbars = config.texturedbars
  3220. vars.texturealpha = config.texturealphamultiplier or 1
  3221. vars.classburn = config.classburn or 0.7
  3222. vars.classalpha = config.classalpha or 0.3
  3223. vars.castLine = config.castLine and ((type(config.castLine) == 'number' and config.castLine) or config.castLine == true and 0) or nil
  3224. vars.nowleft = -vars.past/(vars.future-vars.past)*vars.barwidth-0.5 + (config.hideIcons and 0 or config.height)
  3225.  
  3226. local classcolors = CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS
  3227. self.classcolor = Clone(classcolors[select(2,UnitClass('player'))])
  3228.  
  3229. vars.stackFont = config.stackFont
  3230. vars.stackFontSize = config.stackFontSize
  3231. vars.stackFontColor = config.stackFontColor == true and {1,1,1,1} or config.stackFontColor or {1,1,1,1}
  3232. vars.stackFontShadow = config.stackFontShadow == true and {0,0,0,0.5} or config.stackFontShadow or {0,0,0,0.5}
  3233. vars.stackFontShadowOffset = config.stackFontShadowOffset == true and {1,-1} or config.stackFontShadowOffset or {1,-1}
  3234.  
  3235. for colorid,color in pairs(self.colors) do
  3236. if color[1] == true then
  3237. if color[2] then
  3238. self.colors[colorid] = {self.classcolor.r * color[2], self.classcolor.g * color[2], self.classcolor.b * color[2], color[3] or vars.classalpha} -- For really bad reasons, this took a very long time to get right...
  3239. else
  3240. self.colors[colorid] = {self.classcolor.r, self.classcolor.g, self.classcolor.b, vars.classalpha}
  3241. end
  3242. end
  3243. end
  3244.  
  3245. if vars.texturedbars then
  3246. for colorid,color in pairs(self.colors) do
  3247. if color[4] and not(exemptColors[colorid]) then
  3248. color[4] = vars.texturealpha*color[4]
  3249. end
  3250. end
  3251. end
  3252.  
  3253. local layouts = self.layouts
  3254. layouts.frameline = {
  3255. top = 0,
  3256. bottom = 1,
  3257. }
  3258. local default = layouts.default
  3259. for typeid,layout in pairs(layouts) do
  3260. if typeid~='default' then
  3261. for k,v in pairs(default) do
  3262. if layout[k]==nil then
  3263. layout[k] = v
  3264. end
  3265. end
  3266. end
  3267. layout.texcoords = {0, 1, layout.top, layout.bottom}
  3268. end
  3269.  
  3270. ns:ModuleEvent('ApplyConfig')
  3271. end
  3272.  
  3273. function ns:UpdateConfig()
  3274. if not(self.isReady) then return end
  3275. self:ApplyConfig()
  3276.  
  3277. mainframe:SetScale(self.config.scale or 1)
  3278. local effectiveScale = mainframe:GetEffectiveScale()
  3279. if effectiveScale then
  3280. vars.onepixelwide = 1/effectiveScale
  3281. end
  3282.  
  3283. self:SetupStyleFrame() -- Spawn backdrop frame.
  3284.  
  3285. vars.nowleft = -vars.past/(vars.future-vars.past)*vars.barwidth-0.5 + (ns.config.hideIcons and 0 or ns.config.height)
  3286. --nowI:SetFrameLevel(20)
  3287. ns.frames.nowIndicator:SetPoint('BOTTOM',mainframe,'BOTTOM')
  3288. ns.frames.nowIndicator:SetPoint('TOPLEFT',mainframe,'TOPLEFT', vars.nowleft, 0)
  3289. ns.frames.nowIndicator:SetTexture(unpack(self.colors.nowLine))
  3290.  
  3291. local anchor = self.config.anchor or {'TOPRIGHT', 'EventHorizonHandle', 'BOTTOMRIGHT'}
  3292. if anchor[2]=='EventHorizonHandle' then
  3293. -- Create the handle to reposition the frame.
  3294. handle = handle or CreateFrame('Frame', 'EventHorizonHandle', mainframe)
  3295. handle:SetFrameStrata('HIGH')
  3296. handle:SetWidth(10)
  3297. handle:SetHeight(5)
  3298. handle:EnableMouse(true)
  3299. handle:SetClampedToScreen(1)
  3300. handle:RegisterForDrag('LeftButton')
  3301. handle:SetScript('OnDragStart', function(handle, button) handle:StartMoving() end)
  3302. handle:SetScript('OnDragStop', function(handle)
  3303. handle:StopMovingOrSizing()
  3304. local a,b,c,d,e = handle:GetPoint(1)
  3305. if type(b)=='frame' then
  3306. b=b:GetName()
  3307. end
  3308. EventHorizonDB.point = {a,b,c,d,e}
  3309. end)
  3310. handle:SetMovable(true)
  3311.  
  3312. mainframe:SetPoint(unpack(anchor))
  3313. handle:SetPoint(unpack(EventHorizonDB.point))
  3314.  
  3315. handle.tex = handle:CreateTexture(nil, 'BORDER')
  3316. handle.tex:SetAllPoints()
  3317. handle:SetScript('OnEnter',function(frame) frame.tex:SetTexture(1,1,1,1) end)
  3318. handle:SetScript('OnLeave',function(frame) frame.tex:SetTexture(1,1,1,0.1) end)
  3319. handle.tex:SetTexture(1,1,1,0.1)
  3320. end
  3321.  
  3322. vars.gcdSpellName = self.config.gcdSpellID and (GetSpellInfo(self.config.gcdSpellID))
  3323. if vars.gcdSpellName and self.config.gcdStyle then
  3324. -- Create the GCD indicator, register cooldown event.
  3325. ns.frames.gcd = mainframe:CreateTexture('EventHorizonns.frames.gcd', 'BORDER')
  3326. ns.frames.gcd:SetPoint('BOTTOM',mainframe,'BOTTOM')
  3327. ns.frames.gcd:SetPoint('TOP',mainframe,'TOP')
  3328. ns.frames.gcd:Hide()
  3329.  
  3330. if self.config.gcdStyle == 'line' then
  3331. ns.frames.gcd:SetWidth(vars.onepixelwide)
  3332. else
  3333. ns.frames.gcd:SetPoint('LEFT',mainframe,'LEFT', vars.nowleft, 0)
  3334. end
  3335.  
  3336. local gcdColor = self.colors.gcdColor or {.5,.5,.5,.3}
  3337. ns.frames.gcd:SetTexture(unpack(gcdColor))
  3338. end
  3339.  
  3340. mainframe:SetPoint(unpack(anchor))
  3341. self:SetFrameDimensions()
  3342. end
  3343.  
  3344. local glyphCheck = function ()
  3345. if ns.isActive == true then
  3346. ns:CheckTalents()
  3347.  
  3348. for i,spellframe in pairs(ns.frames.shown) do
  3349. if spellframe.slotID then spellframe:PLAYER_EQUIPMENT_CHANGED(spellframe.slotID) end
  3350. end
  3351. end
  3352. end
  3353.  
  3354. function ns:SetupStyleFrame()
  3355. local c = self.config.backdrop
  3356. if c then
  3357. if not(self.styleframe) then self.styleframe = CreateFrame('Frame',nil,mainframe) end
  3358. else
  3359. if self.styleframe then self.styleframe:Hide() end
  3360. return
  3361. end
  3362.  
  3363. local styleframe = self.styleframe
  3364. local stylebg = self.config.bg or 'Interface\\ChatFrame\\ChatFrameBackground'
  3365. local styleborder = self.config.border or 'Interface\\Tooltips\\UI-Tooltip-Border'
  3366. local stylebgcolor = self.colors.bgcolor or {0,0,0,0.6}
  3367. local stylebordercolor = self.colors.bordercolor or {1,1,1,1}
  3368. local styleinset = self.config.inset or {top = 2, bottom = 2, left = 2, right = 2}
  3369. local stylepadding = self.config.padding or 3
  3370. local styleedge = self.config.edgesize or 8
  3371.  
  3372. styleframe:SetFrameStrata('BACKGROUND')
  3373. styleframe:SetPoint('TOPRIGHT', mainframe, 'TOPRIGHT', stylepadding, stylepadding)
  3374. styleframe:SetPoint('BOTTOMLEFT', mainframe, 'BOTTOMLEFT', -stylepadding, -stylepadding)
  3375. styleframe:SetBackdrop({
  3376. bgFile = stylebg,
  3377. edgeFile = styleborder, tileSize = 0, edgeSize = styleedge,
  3378. insets = styleinset,
  3379. })
  3380. styleframe:SetBackdropColor(unpack(stylebgcolor))
  3381. styleframe:SetBackdropBorderColor(unpack(stylebordercolor))
  3382. end
  3383.  
  3384. function ns:RegisterModule(module,namespace)
  3385. if not(module and namespace) then
  3386. print("Module registration failed. Usage: EventHorizon:RegisterModule(module,namespace)")
  3387. end
  3388. local module = string.lower(module)
  3389. self.modules[module] = namespace
  3390. end
  3391.  
  3392.  
  3393. frame:SetScript('OnEvent', EventHandler)
  3394. frame:RegisterEvent('PLAYER_LOGIN')
  3395.  
  3396. frame.PLAYER_ALIVE = function (self)
  3397. self:SetScript('OnUpdate', UpdateMouseover)
  3398. frame2:SetScript('OnEvent', EventHandler)
  3399. for k,v in pairs(reloadEvents) do
  3400. frame2:RegisterEvent(k)
  3401. frame2[k] = glyphCheck
  3402. end
  3403. if not(ns.isReady) then
  3404. ns:Initialize()
  3405. else
  3406. ns:LoadModules()
  3407. end
  3408. self:UnregisterEvent('PLAYER_ALIVE')
  3409. end
  3410.  
  3411. frame.PLAYER_LOGIN = function (self)
  3412. local talents = GetTalentInfo(1)
  3413. if talents then
  3414. self:UnregisterEvent('PLAYER_LOGIN')
  3415. self:SetScript('OnUpdate', UpdateMouseover)
  3416. frame2:SetScript('OnEvent', EventHandler)
  3417. for k,v in pairs(reloadEvents) do
  3418. frame2:RegisterEvent(k)
  3419. frame2[k] = glyphCheck
  3420. end
  3421. if not(ns.isReady) then
  3422. ns:Initialize()
  3423. else
  3424. ns:LoadModules()
  3425. end
  3426. else
  3427. self:UnregisterEvent('PLAYER_LOGIN')
  3428. self:RegisterEvent('PLAYER_ALIVE')
  3429. end
  3430. end
  3431.  
  3432. mainframe.CLEU_OtherInterestingSpell = mainframe_CLEU_OtherInterestingSpell
  3433. mainframe.UPDATE_SHAPESHIFT_FORM = mainframe_UPDATE_SHAPESHIFT_FORM
  3434. mainframe.SPELL_UPDATE_COOLDOWN = mainframe_SPELL_UPDATE_COOLDOWN
  3435. mainframe.COMBAT_LOG_EVENT_UNFILTERED = mainframe_COMBAT_LOG_EVENT_UNFILTERED
  3436. mainframe.UPDATE_SHAPESHIFT_FORMS = mainframe_UPDATE_SHAPESHIFT_FORM
  3437. mainframe.PLAYER_SPECIALIZATION_UPDATE = ns.CheckTalents
  3438. mainframe.PLAYER_LEVEL_UP = ns.CheckTalents
  3439. mainframe.PLAYER_TARGET_CHANGED = mainframe_PLAYER_TARGET_CHANGED
  3440. mainframe.UNIT_AURA = mainframe_UNIT_AURA
  3441. mainframe.PLAYER_TOTEM_UPDATE = mainframe_PLAYER_TOTEM_UPDATE
  3442.  
  3443. SpellFrame.NotInteresting = SpellFrame_NotInteresting
  3444. SpellFrame.AddSegment = SpellFrame_AddSegment
  3445. SpellFrame.AddIndicator = SpellFrame_AddIndicator
  3446. SpellFrame.Remove = SpellFrame_Remove
  3447. SpellFrame.RemoveTicksAfter = SpellFrame_RemoveTicksAfter
  3448. SpellFrame.RemoveChannelTicksAfter = SpellFrame_RemoveChannelTicksAfter
  3449. SpellFrame.OnUpdate = SpellFrame_OnUpdate
  3450. SpellFrame.UpdateDoT = SpellFrame_UpdateDoT
  3451. SpellFrame.UpdateTotem = SpellFrame_UpdateTotem
  3452. SpellFrame.Activate = SpellFrame_Activate
  3453. SpellFrame.Deactivate = SpellFrame_Deactivate
  3454. SpellFrame.FindItemInfo = SpellFrame_FindItemInfo
  3455.  
  3456. SpellFrame.UNIT_AURA = SpellFrame_UNIT_AURA
  3457. SpellFrame.UNIT_AURA_refreshable = SpellFrame_UNIT_AURA_refreshable
  3458. SpellFrame.COMBAT_LOG_EVENT_UNFILTERED = SpellFrame_COMBAT_LOG_EVENT_UNFILTERED
  3459. SpellFrame.UNIT_SPELLCAST_SENT = SpellFrame_UNIT_SPELLCAST_SENT
  3460. SpellFrame.UNIT_SPELLCAST_CHANNEL_START = Cast_Start
  3461. SpellFrame.UNIT_SPELLCAST_CHANNEL_UPDATE = Cast_Update
  3462. SpellFrame.UNIT_SPELLCAST_CHANNEL_STOP = Cast_Stop
  3463. SpellFrame.UNIT_SPELLCAST_START = Cast_Start
  3464. SpellFrame.UNIT_SPELLCAST_STOP = Cast_Stop
  3465. SpellFrame.UNIT_SPELLCAST_DELAYED = Cast_Update
  3466. --SpellFrame.PLAYER_TARGET_CHANGED = SpellFrame_PLAYER_TARGET_CHANGED
  3467. --SpellFrame.PLAYER_TARGET_CHANGED_refreshable = SpellFrame_PLAYER_TARGET_CHANGED_refreshable
  3468. SpellFrame.PLAYER_REGEN_ENABLED = SpellFrame_PLAYER_REGEN_ENABLED
  3469. SpellFrame.SPELL_UPDATE_COOLDOWN = SpellFrame_SPELL_UPDATE_COOLDOWN
  3470. SpellFrame.BAG_UPDATE_COOLDOWN = SpellFrame_SPELL_UPDATE_COOLDOWN
  3471. SpellFrame.PLAYER_EQUIPMENT_CHANGED = SpellFrame_PLAYER_EQUIPMENT_CHANGED
  3472. SpellFrame.UPDATE_MOUSEOVER_UNIT = SpellFrame_UPDATE_MOUSEOVER_UNIT
  3473. SpellFrame.PLAYER_TOTEM_UPDATE = SpellFrame_PLAYER_TOTEM_UPDATE
  3474.  
  3475. local Redshift = {}
  3476. Redshift.Check = function (self)
  3477. if EventHorizonDB.redshift.isActive ~= true then
  3478. return Redshift:Disable()
  3479. end
  3480. if not(Redshift.frame) then
  3481. Redshift.frame = CreateFrame("FRAME",nil,UIParent)
  3482. Redshift.frame:SetScript('OnEvent',EventHandler)
  3483. for k,v in pairs(Redshift.Events) do
  3484. if v then
  3485. Redshift.frame:RegisterEvent(k)
  3486. Redshift.frame[k] = Redshift.Check
  3487. end
  3488. end
  3489. end
  3490.  
  3491. local s = Redshift.states
  3492.  
  3493. showState = nil
  3494.  
  3495. local attackable = UnitCanAttack("player","target")
  3496. local targeting = UnitExists("target")
  3497. local focusing = UnitExists("focus")
  3498. local classify = UnitClassification("target")
  3499. local dead = UnitIsDeadOrGhost("target")
  3500. local vehicle = UnitHasVehicleUI("player")
  3501.  
  3502. if(s.showCombat and UnitAffectingCombat("player")) then
  3503. showState = true
  3504. end
  3505.  
  3506. if (s.showFocus and UnitExists("focus")) then
  3507. showState = true
  3508. end
  3509.  
  3510. if targeting then
  3511. if(s.showHelp and not attackable) and not dead then
  3512. showState = true
  3513. end
  3514. if(s.showHarm and attackable) and not dead then
  3515. showState = true
  3516. end
  3517. if(s.showBoss and classify == "worldboss") and not dead then
  3518. showState = true
  3519. end
  3520. end
  3521.  
  3522. if (s.hideVehicle and UnitHasVehicleUI("player")) then
  3523. showState = nil
  3524. end
  3525.  
  3526. if showState then
  3527. vars.visibleFrame = true
  3528. mainframe:Show()
  3529. if EventHorizon_VitalsFrame and s.hideVitals then
  3530. EventHorizon_VitalsFrame:Show()
  3531. end
  3532. else
  3533. vars.visibleFrame = false
  3534. mainframe:Hide()
  3535. if EventHorizon_VitalsFrame and s.hideVitals then
  3536. EventHorizon_VitalsFrame:Hide()
  3537. end
  3538. end
  3539. end
  3540.  
  3541. Redshift.Init = function ()
  3542. local settingsChanged = EventHorizonDB.redshift.lastConfig ~= ns.config.enableRedshift
  3543. EventHorizonDB.redshift.lastConfig = ns.config.enableRedshift
  3544.  
  3545. if settingsChanged then
  3546. local ends = ns.config.enableRedshift and 'enabled' or 'disabled'
  3547. local settingsString = "Redshift has been "..ends.." via config.lua. Ingame settings have been adjusted to match. Use /ehz redshift to enable or disable Redshift as needed."
  3548. EventHorizonDB.redshift.isActive = ns.config.enableRedshift
  3549. end
  3550.  
  3551. local db = EventHorizonDB.redshift.isActive
  3552. if not (db) then return end
  3553.  
  3554. Redshift.states = {}
  3555. Redshift.Events = {
  3556. ["PLAYER_REGEN_DISABLED"] = true,
  3557. ["PLAYER_REGEN_ENABLED"] = true,
  3558. ["PLAYER_TARGET_CHANGED"] = true,
  3559. ["PLAYER_GAINS_VEHICLE_DATA"] = true,
  3560. ["PLAYER_LOSES_VEHICLE_DATA"] = true,
  3561. ["UNIT_ENTERED_VEHICLE"] = true,
  3562. ["UNIT_EXITED_VEHICLE"] = true,
  3563. ["UNIT_ENTERING_VEHICLE"] = true,
  3564. ["UNIT_EXITING_VEHICLE"] = true,
  3565. ["VEHICLE_PASSENGERS_CHANGED"] = true,
  3566. }
  3567.  
  3568. for k,v in pairs(ns.config.Redshift) do
  3569. if v then
  3570. Redshift.states[k] = true
  3571. end
  3572. end
  3573.  
  3574. if (EventHorizonDB.redshift.isActive == true) then Redshift:Check() end
  3575. Redshift.isReady = true
  3576. end
  3577.  
  3578. Redshift.Enable = function (slash)
  3579. if EventHorizonDB.redshift.isActive and not(Redshift.isReady) then
  3580. Redshift:Init()
  3581. Redshift:Check()
  3582. elseif EventHorizonDB.redshift.isActive then
  3583. Redshift:Check()
  3584. end
  3585. if Redshift.frame then Redshift.frame:SetScript('OnEvent',EventHandler) end
  3586. end
  3587.  
  3588. Redshift.Disable = function (slash)
  3589. vars.visibleFrame = true
  3590. if Redshift.frame then Redshift.frame:SetScript('OnEvent',nil) end
  3591. if ns.isActive == true then mainframe:Show() end
  3592. end
  3593.  
  3594. local Lines = {}
  3595. Lines.CreateLines = function ()
  3596. if Lines.frame then return end
  3597. local c = ns.config.Lines
  3598. local db = EventHorizonDB.lines.isActive == true
  3599. if not(c and db) then return
  3600. elseif type(c) == 'number' then c = {c} -- Turn numbers into delicious tables.
  3601. elseif type(c) ~= 'table' then return -- Turn away everything else.
  3602. end
  3603.  
  3604. Lines.frame = CreateFrame('Frame',nil,UIParent)
  3605. Lines.line = {}
  3606.  
  3607. local multicolor
  3608. local color = ns.config.LinesColor
  3609. if color and type(color) == 'table' then
  3610. if type(color[1]) == 'table' then
  3611. multicolor = true -- trying not to further complicate things
  3612. for i,v in ipairs(c) do
  3613. if not(color[i]) then
  3614. color[i] = color[i-1] -- if we have more lines than colors, we need moar colors
  3615. end
  3616. end
  3617. end
  3618. else
  3619. color = {1,1,1,0.5}
  3620. end
  3621.  
  3622. local now = -vars.past/(vars.future-vars.past)*vars.barwidth-0.5 + vars.barheight
  3623. local pps = (vars.barwidth+vars.barheight-now)/vars.future
  3624.  
  3625. for i = 1, #c do
  3626. local seconds = c[i]
  3627. local position = now+(pps*seconds)
  3628. Lines.line[i] = mainframe:CreateTexture(nil,"OVERLAY")
  3629. Lines.line[i]:SetPoint('TOPLEFT', mainframe, 'TOPLEFT', position, 0)
  3630. if multicolor then
  3631. Lines.line[i]:SetTexture(unpack(color[i]))
  3632. else
  3633. Lines.line[i]:SetTexture(unpack(color))
  3634. end
  3635. Lines.line[i]:SetWidth(vars.onepixelwide)
  3636. Lines.line[i]:SetPoint('BOTTOM', mainframe, 'BOTTOM')
  3637. end
  3638.  
  3639. Lines.Enable = function ()
  3640. for i = 1,#Lines.line do
  3641. Lines.line[i]:Show()
  3642. end
  3643. end
  3644.  
  3645. Lines.Disable = function ()
  3646. for i = 1,#Lines.line do
  3647. Lines.line[i]:Hide()
  3648. end
  3649. end
  3650. end
  3651.  
  3652. Lines.Init = function ()
  3653. Lines.CreateLines()
  3654. Lines.isReady = true
  3655. end
  3656. --[[
  3657. local Pulse = {
  3658. cache = {}, -- subframe storage
  3659. frames = {}, -- framerefs
  3660. alwaysLoad = true, -- Flag EH to load the module even when db.isActive ~= true
  3661. }
  3662.  
  3663. Pulse.Enable = function ()
  3664. local cv = ns.config.Pulse
  3665. local sv = ns.db.pulse
  3666. local int = ns.config.PulseIntensity
  3667. local fps = ns.config.PulseFPS
  3668. if not(fps) or (type(fps) ~= 'number' or fps == 0) then
  3669. fps = ns.defaultconfig.PulseFPS
  3670. end
  3671.  
  3672. if not Pulse.frame then
  3673. Pulse.frame = CreateFrame('Frame',nil,mainframe)
  3674. end
  3675.  
  3676. Pulse.duration = (cv and sv.isActive) and ((cv == true or type(cv) ~= 'number') and ns.defaultconfig.Pulse or type(cv) == 'number' and cv)
  3677. Pulse.intensity = int and (type(int) == 'number' and int > 0 and int) or ns.defaultconfig.PulseIntensity
  3678. Pulse.TTL = (1000/fps)/1000
  3679.  
  3680. if Pulse.duration and not(Pulse.framesCreated) then
  3681. local ehf = EventHorizon.frames.frames
  3682. -- for k,v in pairs(ns.frames.frames) do print(k,v) end
  3683. for k,bar in pairs(ns.frames.frames) do -- Use frametable since we want actual bar refs. Don't use ipairs here (will need to fix in other places, likely), as it won't fully iterate.
  3684. -- local bar = ehf[i]
  3685. if bar and bar.cooldown then
  3686. local temp = {
  3687. spellframe = bar,
  3688. ['SPELL_UPDATE_COOLDOWN'] = true,
  3689. }
  3690. table.insert(Pulse.frames,temp)
  3691. elseif bar and (bar.slotID or bar.internalcooldown) then -- Check for slotID first, since it doesn't always use internalcooldown
  3692. local temp = {
  3693. spellframe = bar,
  3694. ['UNIT_AURA'] = true,
  3695. }
  3696. table.insert(Pulse.frames,temp)
  3697. end
  3698. end
  3699. Pulse.framesCreated = true
  3700. end
  3701.  
  3702. Pulse.frame:SetScript('OnEvent',Pulse.OnEvent)
  3703. Pulse.frame:RegisterEvent('UNIT_AURA')
  3704. Pulse.frame:RegisterEvent('SPELL_UPDATE_COOLDOWN')
  3705. end
  3706.  
  3707. Pulse.Disable = function ()
  3708. Pulse.duration = nil
  3709. end
  3710.  
  3711. Pulse.OnFlash = function (self,elapsed)
  3712. self.TSLU = self.TSLU + elapsed
  3713. while self.TSLU >= Pulse.TTL do
  3714. self.current = self.current and (self.current - self.TSLU) or Pulse.duration
  3715. self.alpha = self.current >= 0 and self.current/(Pulse.duration*Pulse.intensity) or 0
  3716. self.tex:SetAlpha(self.alpha)
  3717. self.TSLU = self.TSLU - Pulse.TTL
  3718. if self.current <= 0 then
  3719. self:SetScript('OnUpdate',nil)
  3720. self.current = nil
  3721. self.alpha = nil
  3722. print('clearing onupdates')
  3723. end
  3724. end
  3725. end
  3726.  
  3727. Pulse.OnAura = function (self,elapsed)
  3728. self.remaining = self.remaining - elapsed
  3729.  
  3730. if self.current and self.alpha then -- finish off any remaining pulse
  3731. self.TSLU = self.TSLU + elapsed
  3732. while self.TSLU >= Pulse.TTL do
  3733. self.current = self.current and (self.current - self.TSLU) or Pulse.duration
  3734. self.alpha = self.current >= 0 and self.current/(Pulse.duration*Pulse.intensity) or 0
  3735. self.tex:SetAlpha(self.alpha)
  3736. self.TSLU = self.TSLU - Pulse.TTL
  3737. if self.current <= 0 then
  3738. self.current = nil
  3739. self.alpha = nil
  3740. end
  3741. end
  3742. end
  3743.  
  3744. if self.remaining <= 0 and Pulse.duration then
  3745. self.activeCD = nil
  3746. self.remaining = nil
  3747. self.TSLU = 0
  3748. self:SetAllPoints(self.parent)
  3749. self.tex:SetAllPoints(self)
  3750. print('setting Pulse.OnFlash')
  3751. self:SetScript('OnUpdate',Pulse.OnFlash)
  3752. elseif self.remaining <= 0 then
  3753. self.activeCD = nil
  3754. self.remaining = nil
  3755. self:SetScript('OnUpdate',nil)
  3756. end
  3757. end
  3758.  
  3759. Pulse.OnEvent = function (self,event,unit)
  3760. if event == 'UNIT_AURA' and unit ~= 'player' then return end
  3761. for i = 1,#Pulse.frames do
  3762. local frame = Pulse.frames[i]
  3763. local f = Pulse.frames[i].spellframe
  3764. if f then
  3765. local icd = f.internalcooldown
  3766. local cdTime
  3767. local apply
  3768. local now = GetTime()
  3769.  
  3770. if icd and icd == true then
  3771. icd = nil
  3772. end
  3773.  
  3774. if icd and event == 'UNIT_AURA' then
  3775. local _,_,_,_,_,_,expirationTime,active = f.AuraFunction('player',f.auraname)
  3776. local activeCD = frame.activeCD
  3777. if active and expirationTime and (frame.flash and not(frame.flash.activeCD) or not(frame.flash)) then
  3778. apply = true
  3779. frame.activeCD = true
  3780. cdTime = f.internalcooldown
  3781. local start = expirationTime
  3782. local stop = now + f.internalcooldown
  3783. if start > stop then
  3784. start = now
  3785. end
  3786. f:AddSegment('cooldown', 'cooldown', start, stop)
  3787. end
  3788. elseif event == 'SPELL_UPDATE_COOLDOWN' and not(icd) then
  3789. local start, duration, enabled = f.CooldownFunction(f.cooldownID or f.spellname)
  3790. local onCooldown = enabled == 1 and start ~= duration and start > 0 and duration > 1.5 -- Check duration against GCD to ensure we're not pulsing every time the char does something
  3791. cdTime = duration
  3792.  
  3793. if onCooldown and not(frame.activeCD) then
  3794. apply = true
  3795. print(onCooldown,frame.activeCD,start,duration,enabled)
  3796. elseif (frame.flash and frame.flash.remaining) and not(onCooldown) then -- cooldown expired before pulse went off
  3797. frame.flash.remaining = 0 -- flash at next opportunity
  3798. --print'flash'
  3799. elseif (frame.flash and frame.flash.remaining) and (frame.flash.remaining > (start+duration-now+1)) then -- HOPEFULLY this will catch elemental shaman 2t10.
  3800. frame.flash.remaining = start+duration-now
  3801. end
  3802. frame.activeCD = onCooldown
  3803. end
  3804.  
  3805. if apply and cdTime and Pulse.duration then -- check for fresh application, make sure a cooldown time exists, and don't do anything if pulses are disabled
  3806. if not(frame.flash) then
  3807. frame.flash = CreateFrame('Frame',nil,f)
  3808. frame.flash.tex = frame.flash:CreateTexture(nil,'BACKGROUND')
  3809. frame.flash.tex:SetTexture(vars.texturedbars and vars.bartexture or unpack{1,1,1,1})
  3810. frame.flash.tex:SetAlpha(0)
  3811. frame.flash.TSLU = 0
  3812. end
  3813. frame.flash.parent = f
  3814. frame.flash.TSLU = (frame.flash.current and frame.flash.alpha) and frame.flash.TSLU or 0
  3815. frame.flash.remaining = cdTime
  3816. frame.flash.activeCD = true
  3817. frame.flash:SetScript('OnUpdate',Pulse.OnAura)
  3818. end
  3819. end
  3820. end
  3821. end
  3822.  
  3823. Pulse.Init = function ()
  3824. if Pulse.frame then return end
  3825. Pulse:Enable()
  3826. Pulse.isReady = true
  3827. end
  3828. ]]--
  3829. ns:RegisterModule('lines',Lines)
  3830. ns:RegisterModule('redshift',Redshift)
  3831. --ns:RegisterModule('pulse',Pulse)
  3832.  
  3833.  
  3834.  
  3835.  
  3836.  
  3837. -- SpellID Debug (Shows up in the tooltip) --
  3838. if spellIDsEnabled then
  3839. local select, UnitBuff, UnitDebuff, UnitAura, tonumber, strfind, hooksecurefunc =
  3840. select, UnitBuff, UnitDebuff, UnitAura, tonumber, strfind, hooksecurefunc
  3841.  
  3842. local function addLine(self,id,isItem)
  3843. if isItem then
  3844. self:AddDoubleLine("ItemID:","|cffffffff"..id)
  3845. else
  3846. self:AddDoubleLine("SpellID:","|cffffffff"..id)
  3847. end
  3848. self:Show()
  3849. end
  3850.  
  3851. -- Spell Hooks ----------------------------------------------------------------
  3852. hooksecurefunc(GameTooltip, "SetUnitBuff", function(self,...)
  3853. local id = select(11,UnitBuff(...))
  3854. if id then addLine(self,id) end
  3855. end)
  3856.  
  3857. hooksecurefunc(GameTooltip, "SetUnitDebuff", function(self,...)
  3858. local id = select(11,UnitDebuff(...))
  3859. if id then addLine(self,id) end
  3860. end)
  3861.  
  3862. hooksecurefunc(GameTooltip, "SetUnitAura", function(self,...)
  3863. local id = select(11,UnitAura(...))
  3864. if id then addLine(self,id) end
  3865. end)
  3866.  
  3867. GameTooltip:HookScript("OnTooltipSetSpell", function(self)
  3868. local id = select(3,self:GetSpell())
  3869. if id then addLine(self,id) end
  3870. end)
  3871.  
  3872. hooksecurefunc("SetItemRef", function(link, ...)
  3873. local id = tonumber(link:match("spell:(%d+)"))
  3874. if id then addLine(ItemRefTooltip,id) end
  3875. end)
  3876.  
  3877. -- Item Hooks -----------------------------------------------------------------
  3878.  
  3879. local function attachItemTooltip(self)
  3880. local link = select(2,self:GetItem())
  3881. if not link then return end
  3882. local id = select(3,strfind(link, "^|%x+|Hitem:(%-?%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%-?%d+):(%-?%d+)"))
  3883. if id then addLine(self,id,true) end
  3884. end
  3885.  
  3886. GameTooltip:HookScript("OnTooltipSetItem", attachItemTooltip)
  3887. ItemRefTooltip:HookScript("OnTooltipSetItem", attachItemTooltip)
  3888. ItemRefShoppingTooltip1:HookScript("OnTooltipSetItem", attachItemTooltip)
  3889. ItemRefShoppingTooltip2:HookScript("OnTooltipSetItem", attachItemTooltip)
  3890. ShoppingTooltip1:HookScript("OnTooltipSetItem", attachItemTooltip)
  3891. ShoppingTooltip2:HookScript("OnTooltipSetItem", attachItemTooltip)
  3892.  
  3893. if not Wod then
  3894. ItemRefShoppingTooltip3:HookScript("OnTooltipSetItem", attachItemTooltip)
  3895. ShoppingTooltip3:HookScript("OnTooltipSetItem", attachItemTooltip)
  3896. end
  3897. end
  3898. --END TEMP--
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement