Advertisement
Guest User

Event Horizon Continued (MoP Beta)

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