Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- UI.lua
- -- Dynamic UI Elements
- local addon, ns = ...
- local Hekili = _G[addon]
- local class = Hekili.Class
- local state = Hekili.State
- local FindUnitBuffByID, FindUnitDebuffByID = ns.FindUnitBuffByID, ns.FindUnitDebuffByID
- -- Atlas/Textures
- local AddTexString, GetTexString, AtlasToString, GetAtlasFile, GetAtlasCoords = ns.AddTexString, ns.GetTexString, ns.AtlasToString, ns.GetAtlasFile, ns.GetAtlasCoords
- local frameStratas = ns.FrameStratas
- local getInverseDirection = ns.getInverseDirection
- local multiUnpack = ns.multiUnpack
- local orderedPairs = ns.orderedPairs
- local round = ns.round
- local IsCurrentItem = C_Item.IsCurrentItem
- local IsUsableItem = C_Item.IsUsableItem
- local IsCurrentSpell = C_Spell.IsCurrentSpell
- local GetItemCooldown = C_Item.GetItemCooldown
- local GetSpellTexture = C_Spell.GetSpellTexture
- local IsUsableSpell = C_Spell.IsSpellUsable
- local GetSpellCooldown = function(spellID)
- local spellCooldownInfo = C_Spell.GetSpellCooldown(spellID)
- if spellCooldownInfo then
- return spellCooldownInfo.startTime, spellCooldownInfo.duration, spellCooldownInfo.isEnabled, spellCooldownInfo.modRate
- end
- return 0, 0, false, 0
- end
- local format, insert = string.format, table.insert
- local HasVehicleActionBar, HasOverrideActionBar, IsInPetBattle, UnitHasVehicleUI, UnitOnTaxi = HasVehicleActionBar, HasOverrideActionBar, C_PetBattles.IsInBattle, UnitHasVehicleUI, UnitOnTaxi
- local Tooltip = ns.Tooltip
- local Masque, MasqueGroup
- local _
- function Hekili:GetScale()
- return PixelUtil.GetNearestPixelSize( 1, PixelUtil.GetPixelToUIUnitFactor(), 1 )
- --[[ local monitorIndex = (tonumber(GetCVar("gxMonitor")) or 0) + 1
- local resolutions = {GetScreenResolutions()}
- local resolution = resolutions[GetCurrentResolution()] or GetCVar("gxWindowedResolution")
- return (GetCVar("UseUIScale") == "1" and (GetScreenHeight() / resolution:match("%d+x(%d+)")) or 1) ]]
- end
- local movementData = {}
- local function startScreenMovement(frame)
- movementData.origX, movementData.origY = select( 4, frame:GetPoint() )
- frame:StartMoving()
- movementData.fromX, movementData.fromY = select( 4, frame:GetPoint() )
- frame.Moving = true
- end
- local function stopScreenMovement(frame)
- local resolution = C_VideoOptions.GetCurrentGameWindowSize()
- local scrW, scrH = resolution.x, resolution.y
- local scale, pScale = Hekili:GetScale(), UIParent:GetScale()
- scrW = scrW / ( scale * pScale )
- scrH = scrH / ( scale * pScale )
- local limitX = (scrW - frame:GetWidth() ) / 2
- local limitY = (scrH - frame:GetHeight()) / 2
- movementData.toX, movementData.toY = select( 4, frame:GetPoint() )
- frame:StopMovingOrSizing()
- frame.Moving = false
- frame:ClearAllPoints()
- frame:SetPoint( "CENTER", nil, "CENTER",
- max(-limitX, min(limitX, movementData.origX + (movementData.toX - movementData.fromX))),
- max(-limitY, min(limitY, movementData.origY + (movementData.toY - movementData.fromY))) )
- Hekili:SaveCoordinates()
- end
- local function Mover_OnMouseUp(self, btn)
- local obj = self.moveObj or self
- if (btn == "LeftButton" and obj.Moving) then
- stopScreenMovement(obj)
- Hekili:SaveCoordinates()
- elseif btn == "RightButton" then
- if obj:GetName() == "HekiliNotification" then
- LibStub( "AceConfigDialog-3.0" ):SelectGroup( "Hekili", "displays", "nPanel" )
- return
- elseif obj and obj.id then
- LibStub( "AceConfigDialog-3.0" ):SelectGroup( "Hekili", "displays", obj.id )
- return
- end
- end
- end
- local function Mover_OnMouseDown( self, btn )
- local obj = self.moveObj or self
- if Hekili.Config and btn == "LeftButton" and not obj.Moving then
- startScreenMovement(obj)
- end
- end
- local function Button_OnMouseUp( self, btn )
- local display = self.display
- local mover = _G[ "HekiliDisplay" .. display ]
- if (btn == "LeftButton" and mover.Moving) then
- stopScreenMovement(mover)
- elseif (btn == "RightButton") then
- if mover.Moving then
- stopScreenMovement(mover)
- end
- local mouseInteract = Hekili.Pause or Hekili.Config
- for i = 1, #ns.UI.Buttons do
- for j = 1, #ns.UI.Buttons[i] do
- ns.UI.Buttons[i][j]:EnableMouse(mouseInteract)
- end
- end
- ns.UI.Notification:EnableMouse( Hekili.Config )
- -- Hekili:SetOption( { "locked" }, true )
- GameTooltip:Hide()
- end
- Hekili:SaveCoordinates()
- end
- local function Button_OnMouseDown(self, btn)
- local display = self.display
- local mover = _G[ "HekiliDisplay" .. display ]
- if Hekili.Config and btn == "LeftButton" and not mover.Moving then
- startScreenMovement(mover)
- end
- end
- function ns.StartConfiguration( external )
- Hekili.Config = true
- local scaleFactor = Hekili:GetScale()
- local ccolor = RAID_CLASS_COLORS[select(2, UnitClass("player"))]
- -- Notification Panel
- ns.UI.Notification.Mover = ns.UI.Notification.Mover or CreateFrame( "Frame", "HekiliNotificationMover", ns.UI.Notification, "BackdropTemplate" )
- ns.UI.Notification.Mover:SetAllPoints(HekiliNotification)
- ns.UI.Notification.Mover:SetBackdrop( {
- bgFile = "Interface/Buttons/WHITE8X8",
- edgeFile = "Interface/Buttons/WHITE8X8",
- tile = false,
- tileSize = 0,
- edgeSize = 1,
- insets = { left = 0, right = 0, top = 0, bottom = 0 }
- } )
- ns.UI.Notification.Mover:SetBackdropColor( 0, 0, 0, .8 )
- ns.UI.Notification.Mover:SetBackdropBorderColor( ccolor.r, ccolor.g, ccolor.b, 1 )
- ns.UI.Notification.Mover:Show()
- local f = ns.UI.Notification.Mover
- if not f.Header then
- f.Header = f:CreateFontString( "HekiliNotificationHeader", "OVERLAY", "GameFontNormal" )
- local path = f.Header:GetFont()
- f.Header:SetFont( path, 18, "OUTLINE" )
- end
- f.Header:SetAllPoints( HekiliNotificationMover )
- f.Header:SetText( "Notifications" )
- f.Header:SetJustifyH( "CENTER" )
- f.Header:Show()
- if HekiliNotificationMover:GetFrameLevel() > HekiliNotification:GetFrameLevel() then
- local orig = HekiliNotificationMover:GetFrameLevel()
- HekiliNotification:SetFrameLevel(orig)
- HekiliNotificationMover:SetFrameLevel(orig-1)
- end
- ns.UI.Notification:EnableMouse( true )
- ns.UI.Notification:SetMovable( true )
- HekiliNotification:SetScript( "OnMouseDown", Mover_OnMouseDown )
- HekiliNotification:SetScript( "OnMouseUp", Mover_OnMouseUp )
- HekiliNotification:SetScript( "OnEnter", function( self )
- local H = Hekili
- if H.Config then
- Tooltip:SetOwner( self, "ANCHOR_TOPRIGHT" )
- Tooltip:SetText( "Hekili: Notifications" )
- Tooltip:AddLine( "Left-click and hold to move.", 1, 1, 1 )
- Tooltip:AddLine( "Right-click to open Notification panel settings.", 1, 1, 1 )
- Tooltip:Show()
- end
- end )
- HekiliNotification:SetScript( "OnLeave", function(self)
- Tooltip:Hide()
- end )
- Hekili:ProfileFrame( "NotificationFrame", HekiliNotification )
- for i, v in pairs( ns.UI.Displays ) do
- if v.Backdrop then
- v.Backdrop:Hide()
- end
- if v.Header then
- v.Header:Hide()
- end
- if ns.UI.Buttons[ i ][ 1 ] and Hekili.DB.profile.displays[ i ] then
- -- if not Hekili:IsDisplayActive( i ) then v:Show() end
- v.Backdrop = v.Backdrop or CreateFrame( "Frame", v:GetName().. "_Backdrop", UIParent, "BackdropTemplate" )
- v.Backdrop:ClearAllPoints()
- if not v:IsAnchoringRestricted() then
- v:EnableMouse( true )
- v:SetMovable( true )
- for id, btn in ipairs( ns.UI.Buttons[ i ] ) do
- btn:EnableMouse( false )
- end
- local left, right, top, bottom = v:GetPerimeterButtons()
- if left and right and top and bottom then
- v.Backdrop:SetPoint( "LEFT", left, "LEFT", -2, 0 )
- v.Backdrop:SetPoint( "RIGHT", right, "RIGHT", 2, 0 )
- v.Backdrop:SetPoint( "TOP", top, "TOP", 0, 2 )
- v.Backdrop:SetPoint( "BOTTOM", bottom, "BOTTOM", 0, -2 )
- else
- v.Backdrop:SetWidth( v:GetWidth() + 2 )
- v.Backdrop:SetHeight( v:GetHeight() + 2 )
- v.Backdrop:SetPoint( "CENTER", v, "CENTER" )
- end
- end
- v.Backdrop:SetFrameStrata( v:GetFrameStrata() )
- v.Backdrop:SetFrameLevel( v:GetFrameLevel() + 1 )
- v.Backdrop.moveObj = v
- v.Backdrop:SetBackdrop( {
- bgFile = "Interface/Buttons/WHITE8X8",
- edgeFile = "Interface/Buttons/WHITE8X8",
- tile = false,
- tileSize = 0,
- edgeSize = 1,
- insets = { left = 0, right = 0, top = 0, bottom = 0 }
- } )
- local ccolor = RAID_CLASS_COLORS[ select(2, UnitClass("player")) ]
- if Hekili:IsDisplayActive( v.id, true ) then
- v.Backdrop:SetBackdropBorderColor( ccolor.r, ccolor.g, ccolor.b, 1 )
- else
- v.Backdrop:SetBackdropBorderColor( 0.5, 0.5, 0.5, 0.5 )
- end
- v.Backdrop:SetBackdropColor( 0, 0, 0, 0.8 )
- v.Backdrop:Show()
- v.Backdrop:SetScript( "OnMouseDown", Mover_OnMouseDown )
- v.Backdrop:SetScript( "OnMouseUp", Mover_OnMouseUp )
- v.Backdrop:SetScript( "OnEnter", function( self )
- local H = Hekili
- if H.Config then
- Tooltip:SetOwner( self, "ANCHOR_TOPRIGHT" )
- Tooltip:SetText( "Hekili: " .. i )
- Tooltip:AddLine( "Left-click and hold to move.", 1, 1, 1 )
- Tooltip:AddLine( "Right-click to open " .. i .. " display settings.", 1, 1, 1 )
- if not H:IsDisplayActive( i, true ) then Tooltip:AddLine( "This display is not currently active.", 0.5, 0.5, 0.5 ) end
- Tooltip:Show()
- end
- end )
- v.Backdrop:SetScript( "OnLeave", function( self )
- Tooltip:Hide()
- end )
- v:Show()
- if not v.Header then
- v.Header = v.Backdrop:CreateFontString( "HekiliDisplay" .. i .. "Header", "OVERLAY", "GameFontNormal" )
- local path = v.Header:GetFont()
- v.Header:SetFont( path, 18, "OUTLINE" )
- end
- v.Header:ClearAllPoints()
- v.Header:SetAllPoints( v.Backdrop )
- if i == "Defensives" then v.Header:SetText( AtlasToString( "nameplates-InterruptShield" ) )
- elseif i == "Interrupts" then v.Header:SetText( AtlasToString( "voicechat-icon-speaker-mute" ) )
- elseif i == "Cooldowns" then v.Header:SetText( AtlasToString( "chromietime-32x32" ) )
- else v.Header:SetText( i ) end
- v.Header:SetJustifyH("CENTER")
- v.Header:Show()
- else
- v:Hide()
- end
- end
- if not external then
- if not Hekili.OptionsReady then Hekili:RefreshOptions() end
- local ACD = LibStub( "AceConfigDialog-3.0" )
- ACD:SetDefaultSize( "Hekili", 800, 608 )
- ACD:Open( "Hekili" )
- local oFrame = ACD.OpenFrames["Hekili"].frame
- oFrame:SetResizeBounds( 800, 120 )
- ns.OnHideFrame = ns.OnHideFrame or CreateFrame( "Frame" )
- ns.OnHideFrame:SetParent( oFrame )
- ns.OnHideFrame:SetScript( "OnHide", function(self)
- ns.StopConfiguration()
- self:SetScript( "OnHide", nil )
- self:SetParent( nil )
- if not InCombatLockdown() then
- collectgarbage()
- Hekili:UpdateDisplayVisibility()
- else
- C_Timer.After( 0, function() Hekili:UpdateDisplayVisibility() end )
- end
- end )
- if not ns.OnHideFrame.firstTime then
- ACD:SelectGroup( "Hekili", "packs" )
- ACD:SelectGroup( "Hekili", "displays" )
- ACD:SelectGroup( "Hekili", "displays", "Multi" )
- ACD:SelectGroup( "Hekili", "general" )
- ns.OnHideFrame.firstTime = true
- end
- Hekili:ProfileFrame( "CloseOptionsFrame", ns.OnHideFrame )
- end
- Hekili:UpdateDisplayVisibility()
- end
- function Hekili:OpenConfiguration()
- ns.StartConfiguration()
- end
- function ns.StopConfiguration()
- Hekili.Config = false
- local scaleFactor = Hekili:GetScale()
- local mouseInteract = Hekili.Pause
- for id, display in pairs( Hekili.DisplayPool ) do
- display:EnableMouse( false )
- if not display:IsAnchoringRestricted() then display:SetMovable( true ) end
- -- v:SetBackdrop( nil )
- if display.Header then
- display.Header:Hide()
- end
- if display.Backdrop then
- display.Backdrop:Hide()
- end
- for i, btn in ipairs( display.Buttons ) do
- btn:EnableMouse( mouseInteract )
- btn:SetMovable( false )
- end
- end
- HekiliNotification:EnableMouse( false )
- HekiliNotification:SetMovable( false )
- HekiliNotification.Mover:Hide()
- -- HekiliNotification.Mover.Header:Hide()
- end
- local function MasqueUpdate( Addon, Group, SkinID, Gloss, Backdrop, Colors, Disabled )
- if Disabled then
- for dispID, display in ipairs( ns.UI.Buttons ) do
- for btnID, button in ipairs( display ) do
- button.__MSQ_NormalTexture:Hide()
- button.Texture:SetAllPoints( button )
- end
- end
- end
- end
- do
- ns.UI.Menu = ns.UI.Menu or CreateFrame( "Frame", "HekiliMenu", UIParent, "UIDropDownMenuTemplate" )
- local menu = ns.UI.Menu
- Hekili:ProfileFrame( "HekiliMenu", menu )
- menu.info = {}
- menu.AddButton = UIDropDownMenu_AddButton
- menu.AddSeparator = UIDropDownMenu_AddSeparator
- local function SetDisplayMode( mode )
- Hekili.DB.profile.toggles.mode.value = mode
- if WeakAuras and WeakAuras.ScanEvents then WeakAuras.ScanEvents( "HEKILI_TOGGLE", "mode", mode ) end
- if ns.UI.Minimap then ns.UI.Minimap:RefreshDataText() end
- Hekili:UpdateDisplayVisibility()
- Hekili:ForceUpdate( "HEKILI_TOGGLE", true )
- end
- local function IsDisplayMode( p, mode )
- return Hekili.DB.profile.toggles.mode.value == mode
- end
- local menuData = {
- {
- isTitle = 1,
- text = "Hekili",
- notCheckable = 1,
- },
- {
- text = "Enable",
- func = function () Hekili:Toggle() end,
- checked = function () return Hekili.DB.profile.enabled end,
- },
- {
- text = "Pause",
- func = function () return Hekili:TogglePause() end,
- checked = function () return Hekili.Pause end,
- },
- {
- isSeparator = 1,
- },
- {
- isTitle = 1,
- text = "Display Mode",
- notCheckable = 1,
- },
- {
- text = "Auto",
- func = function () SetDisplayMode( "automatic" ) end,
- checked = function () return IsDisplayMode( p, "automatic" ) end,
- },
- {
- text = "Single",
- func = function () SetDisplayMode( "single" ) end,
- checked = function () return IsDisplayMode( p, "single" ) end,
- },
- {
- text = "AOE",
- func = function () SetDisplayMode( "aoe" ) end,
- checked = function () return IsDisplayMode( p, "aoe" ) end,
- },
- {
- text = "Dual",
- func = function () SetDisplayMode( "dual" ) end,
- checked = function () return IsDisplayMode( p, "dual" ) end,
- },
- {
- text = "Reactive",
- func = function () SetDisplayMode( "reactive" ) end,
- checked = function () return IsDisplayMode( p, "reactive" ) end,
- },
- {
- isSeparator = 1,
- },
- {
- isTitle = 1,
- text = "Toggles",
- notCheckable = 1,
- },
- {
- text = "Cooldowns",
- func = function() Hekili:FireToggle( "cooldowns" ); ns.UI.Minimap:RefreshDataText() end,
- checked = function () return Hekili.DB.profile.toggles.cooldowns.value end,
- },
- {
- text = "Minor CDs",
- func = function() Hekili:FireToggle( "essences" ); ns.UI.Minimap:RefreshDataText() end,
- checked = function () return Hekili.DB.profile.toggles.essences.value end,
- },
- {
- text = "Interrupts",
- func = function() Hekili:FireToggle( "interrupts" ); ns.UI.Minimap:RefreshDataText() end,
- checked = function () return Hekili.DB.profile.toggles.interrupts.value end,
- },
- {
- text = "Defensives",
- func = function() Hekili:FireToggle( "defensives" ); ns.UI.Minimap:RefreshDataText() end,
- checked = function () return Hekili.DB.profile.toggles.defensives.value end,
- },
- {
- text = "Potions",
- func = function() Hekili:FireToggle( "potions" ); ns.UI.Minimap:RefreshDataText() end,
- checked = function () return Hekili.DB.profile.toggles.potions.value end,
- },
- }
- local specsParsed = false
- menu.args = {}
- UIDropDownMenu_SetDisplayMode( menu, "MENU" )
- function menu:initialize( level, list )
- if not level and not list then
- return
- end
- if level == 1 then
- if not specsParsed then
- -- Add specialization toggles where applicable.
- for i, spec in pairs( Hekili.Class.specs ) do
- if i > 0 then
- insert( menuData, {
- isSeparator = 1,
- hidden = function () return Hekili.State.spec.id ~= i end,
- } )
- insert( menuData, {
- isTitle = 1,
- text = spec.name,
- notCheckable = 1,
- hidden = function () return Hekili.State.spec.id ~= i end,
- } )
- insert( menuData, {
- text = "|TInterface\\Addons\\Hekili\\Textures\\Cycle:0|t Recommend Target Swaps",
- tooltipTitle = "|TInterface\\Addons\\Hekili\\Textures\\Cycle:0|t Recommend Target Swaps",
- tooltipText = "If checked, the |TInterface\\Addons\\Hekili\\Textures\\Cycle:0|t indicator may be displayed which means you should use the ability on a different target.",
- tooltipOnButton = true,
- func = function ()
- local spec = rawget( Hekili.DB.profile.specs, i )
- if spec then
- spec.cycle = not spec.cycle
- if Hekili.DB.profile.notifications.enabled then
- Hekili:Notify( "Recommend Target Swaps: " .. ( spec.cycle and "ON" or "OFF" ) )
- else
- Hekili:Print( "Recommend Target Swaps: " .. ( spec.cycle and " |cFF00FF00ENABLED|r." or " |cFFFF0000DISABLED|r." ) )
- end
- end
- end,
- checked = function ()
- local spec = rawget( Hekili.DB.profile.specs, i )
- return spec.cycle
- end,
- hidden = function () return Hekili.State.spec.id ~= i end,
- } )
- -- Check for Toggles.
- for n, setting in pairs( spec.settings ) do
- if setting.info and ( not setting.info.arg or setting.info.arg() ) then
- if setting.info.type == "toggle" then
- local name = type( setting.info.name ) == "function" and setting.info.name() or setting.info.name
- local submenu
- submenu = {
- text = name,
- tooltipTitle = name,
- tooltipText = type( setting.info.desc ) == "function" and setting.info.desc() or setting.info.desc,
- tooltipOnButton = true,
- func = function ()
- menu.args[1] = setting.name
- setting.info.set( menu.args, not setting.info.get( menu.args ) )
- local nm = type( setting.info.name ) == "function" and setting.info.name() or setting.info.name
- if Hekili.DB.profile.notifications.enabled then
- Hekili:Notify( nm .. ": " .. ( setting.info.get( menu.args ) and "ON" or "OFF" ) )
- else
- Hekili:Print( nm .. ": " .. ( setting.info.get( menu.args ) and " |cFF00FF00ENABLED|r." or " |cFFFF0000DISABLED|r." ) )
- end
- submenu.text = nm
- submenu.tooltipTitle = nm
- submenu.tooltipText = type( setting.info.desc ) == "function" and setting.info.desc() or setting.info.desc
- end,
- checked = function ()
- menu.args[1] = setting.name
- return setting.info.get( menu.args )
- end,
- hidden = function () return Hekili.State.spec.id ~= i end,
- }
- insert( menuData, submenu )
- elseif setting.info.type == "select" then
- local name = type( setting.info.name ) == "function" and setting.info.name() or setting.info.name
- local submenu
- submenu = {
- text = name,
- tooltipTitle = name,
- tooltipText = type( setting.info.desc ) == "function" and setting.info.desc() or setting.info.desc,
- tooltipOnButton = true,
- hasArrow = true,
- menuList = {},
- notCheckable = true,
- hidden = function () return Hekili.State.spec.id ~= i end,
- }
- local values = setting.info.values
- if type( values ) == "function" then values = values() end
- if values then
- if setting.info.sorting then
- for _, k in orderedPairs( setting.info.sorting ) do
- local v = values[ k ]
- insert( submenu.menuList, {
- text = v,
- func = function ()
- menu.args[1] = setting.name
- setting.info.set( menu.args, k )
- local nm = type( setting.info.name ) == "function" and setting.info.name() or setting.info.name
- submenu.text = nm
- submenu.tooltipTitle = nm
- submenu.tooltipText = type( setting.info.desc ) == "function" and setting.info.desc() or setting.info.desc
- for k, v in pairs( Hekili.DisplayPool ) do
- v:OnEvent( "HEKILI_MENU" )
- end
- end,
- checked = function ()
- menu.args[1] = setting.name
- return setting.info.get( menu.args ) == k
- end,
- hidden = function () return Hekili.State.spec.id ~= i end,
- } )
- end
- else
- for k, v in orderedPairs( values ) do
- insert( submenu.menuList, {
- text = v,
- func = function ()
- menu.args[1] = setting.name
- setting.info.set( menu.args, k )
- local nm = type( setting.info.name ) == "function" and setting.info.name() or setting.info.name
- submenu.text = nm
- submenu.tooltipTitle = nm
- submenu.tooltipText = type( setting.info.desc ) == "function" and setting.info.desc() or setting.info.desc
- for k, v in pairs( Hekili.DisplayPool ) do
- v:OnEvent( "HEKILI_MENU" )
- end
- end,
- checked = function ()
- menu.args[1] = setting.name
- return setting.info.get( menu.args ) == k
- end,
- hidden = function () return Hekili.State.spec.id ~= i end,
- } )
- end
- end
- end
- insert( menuData, submenu )
- elseif setting.info.type == "range" then
- local submenu = {
- text = type( setting.info.name ) == "function" and setting.info.name() or setting.info.name,
- tooltipTitle = type( setting.info.name ) == "function" and setting.info.name() or setting.info.name,
- tooltipText = type( setting.info.desc ) == "function" and setting.info.desc() or setting.info.desc,
- tooltipOnButton = true,
- notCheckable = true,
- hidden = function () return Hekili.State.spec.id ~= i end,
- hasArrow = true,
- menuList = {}
- }
- local slider = {
- text = type( setting.info.name ) == "function" and setting.info.name() or setting.info.name,
- tooltipTitle = type( setting.info.name ) == "function" and setting.info.name() or setting.info.name,
- tooltipText = type( setting.info.desc ) == "function" and setting.info.desc() or setting.info.desc,
- tooltipOnButton = true,
- notCheckable = true,
- hidden = function () return Hekili.State.spec.id ~= i end,
- }
- local cn = "HekiliSpec" .. i .. "Option" .. n
- local cf = CreateFrame( "Frame", cn, UIParent, "HekiliPopupDropdownRangeTemplate" )
- cf.Slider:SetAccessorFunction( function()
- menu.args[1] = setting.name
- return setting.info.get( menu.args )
- end )
- cf.Slider:SetMutatorFunction( function( val )
- menu.args[1] = setting.name
- return setting.info.set( menu.args, val )
- end )
- cf.Slider:SetMinMaxValues( setting.info.min, setting.info.max )
- cf.Slider:SetValueStep( setting.info.step or 1 )
- cf.Slider:SetObeyStepOnDrag( true )
- cf.Slider:SetScript( "OnEnter", function( self )
- local tooltip = GetAppropriateTooltip()
- tooltip:SetOwner( cf.Slider, "ANCHOR_RIGHT", 0, 2 )
- GameTooltip_SetTitle( tooltip, slider.tooltipTitle )
- GameTooltip_AddNormalLine( tooltip, slider.tooltipText, true )
- tooltip:Show()
- end )
- cf.Slider:SetScript( "OnLeave", function( self )
- GameTooltip:Hide()
- end )
- slider.customFrame = cf
- insert( submenu.menuList, slider )
- --[[ local low, high, step = setting.info.min, setting.info.max, setting.info.step
- local fractional, factor = step < 1, 1 / step
- if fractional then
- low = low * factor
- high = high * factor
- step = step * factor
- end
- if ceil( ( high - low ) / step ) > 20 then
- step = ceil( ( high - low ) / 20 )
- if step % ( setting.info.step or 1 ) ~= 0 then
- step = step - ( step % ( setting.info.step or 1 ) )
- end
- end
- for j = low, high, step do
- local actual = j / factor
- insert( submenu.menuList, {
- text = tostring( actual ),
- func = function ()
- menu.args[1] = setting.name
- setting.info.set( menu.args, actual )
- local name = type( setting.info.name ) == "function" and setting.info.name() or setting.info.name
- if Hekili.DB.profile.notifications.enabled then
- Hekili:Notify( name .. " set to |cFF00FF00" .. actual .. "|r." )
- else
- Hekili:Print( name .. " set to |cFF00FF00" .. actual .. "|r." )
- end
- end,
- checked = function ()
- menu.args[1] = setting.name
- return setting.info.get( menu.args ) == actual
- end,
- hidden = function () return Hekili.State.spec.id ~= i end,
- } )
- end ]]
- insert( menuData, submenu )
- end
- end
- end
- end
- end
- specsParsed = true
- end
- end
- local use = list or menuData
- local classic = Hekili.IsClassic()
- for i, data in ipairs( use ) do
- data.classicChecks = classic
- if not data.hidden or ( type( data.hidden ) == 'function' and not data.hidden() ) then
- if data.isSeparator then
- menu.AddSeparator( level )
- else
- menu.AddButton( data, level )
- end
- end
- end
- end
- end
- do
- ns.UI.Displays = ns.UI.Displays or {}
- local dPool = ns.UI.Displays
- Hekili.DisplayPool = dPool
- local alphaUpdateEvents = {
- PET_BATTLE_OPENING_START = 1,
- PET_BATTLE_CLOSE = 1,
- BARBER_SHOP_OPEN = 1,
- BARBER_SHOP_CLOSE = 1,
- PLAYER_GAINS_VEHICLE_DATA = 1,
- PLAYER_LOSES_VEHICLE_DATA = 1,
- UNIT_ENTERING_VEHICLE = 1,
- UNIT_ENTERED_VEHICLE = 1,
- UNIT_EXITED_VEHICLE = 1,
- UNIT_EXITING_VEHICLE = 1,
- VEHICLE_ANGLE_SHOW = 1,
- VEHICLE_UPDATE = 1,
- UPDATE_VEHICLE_ACTIONBAR = 1,
- UPDATE_OVERRIDE_ACTIONBAR = 1,
- CLIENT_SCENE_OPENED = 1,
- CLIENT_SCENE_CLOSED = 1,
- -- UNIT_FLAGS = 1,
- PLAYER_TARGET_CHANGED = 1,
- PLAYER_ENTERING_WORLD = 1,
- PLAYER_REGEN_ENABLED = 1,
- PLAYER_REGEN_DISABLED = 1,
- ACTIVE_TALENT_GROUP_CHANGED = 1,
- ZONE_CHANGED = 1,
- ZONE_CHANGED_INDOORS = 1,
- ZONE_CHANGED_NEW_AREA = 1,
- PLAYER_CONTROL_LOST = 1,
- PLAYER_CONTROL_GAINED = 1,
- PLAYER_MOUNT_DISPLAY_CHANGED = 1,
- UPDATE_ALL_UI_WIDGETS = 1,
- }
- local kbEvents = {
- -- ACTIONBAR_SLOT_CHANGED = 1,
- ACTIONBAR_PAGE_CHANGED = 1,
- ACTIONBAR_UPDATE_STATE = 1,
- SPELLS_CHANGED = 1,
- UPDATE_SHAPESHIFT_FORM = 1,
- }
- local flashEvents = {
- -- This unregisters flash frames in SpellFlash.
- ACTIONBAR_SHOWGRID = 1,
- -- These re-register flash frames in SpellFlash (after 0.5 - 1.0s).
- ACTIONBAR_HIDEGRID = 1,
- LEARNED_SPELL_IN_TAB = 1,
- CHARACTER_POINTS_CHANGED = 1,
- ACTIVE_TALENT_GROUP_CHANGED = 1,
- UPDATE_MACROS = 1,
- VEHICLE_UPDATE = 1,
- }
- local pulseAuras = 0.1
- local pulseDelay = 0.05
- local pulseGlow = 0.25
- local pulseTargets = 0.1
- local pulseRange = TOOLTIP_UPDATE_TIME
- local pulseFlash = 0.5
- local flashOffset = {
- Primary = 0,
- AOE = 0.25,
- Interrupts = 0.125,
- Defensives = 0.333,
- Cooldowns = 0.416
- }
- local oocRefresh = 1
- local icRefresh = {
- Primary = 0.25,
- AOE = 0.25,
- Interrupts = 0.25,
- Defensives = 0.5,
- Cooldowns = 0.25
- }
- local LRC = LibStub( "LibRangeCheck-3.0" )
- local LSF = SpellFlashCore
- local catchFlash, lastFramesFlashed = nil, {}
- if LSF then
- hooksecurefunc( LSF, "FlashFrame", function( frame )
- local flash = frame and frame.SpellFlashCoreAddonFlashFrame
- -- We need to know what flashed so we can force it to stop flashing when the recommendation changes.
- if catchFlash and flash then
- lastFramesFlashed[ flash ] = 1
- end
- end )
- end
- local LSR = LibStub("SpellRange-1.0")
- local Glower = LibStub("LibCustomGlow-1.0")
- local function CalculateAlpha( id )
- if IsInPetBattle() or Hekili.Barber or Hekili.ClientScene or UnitHasVehicleUI( "player" ) or HasVehicleActionBar() or HasOverrideActionBar() or UnitOnTaxi( "player" ) or not Hekili:IsDisplayActive( id ) then
- return 0
- end
- local prof = Hekili.DB.profile
- local conf = prof.displays[ id ]
- local spec = state.spec.id and prof.specs[ state.spec.id ]
- local aoe = spec and spec.aoe or 3
- local _, zoneType = IsInInstance()
- if not conf.enabled then
- return 0
- elseif id == "AOE" and Hekili:GetToggleState( "mode" ) == "reactive" and Hekili:GetNumTargets() < aoe then
- return 0
- elseif zoneType == "pvp" or zoneType == "arena" then
- if not conf.visibility.advanced then return conf.visibility.pvp.alpha end
- if conf.visibility.pvp.hideMounted and IsMounted() then return 0 end
- if conf.visibility.pvp.combatTarget > 0 and state.combat > 0 and UnitExists( "target" ) and not UnitIsDead( "target" ) and UnitCanAttack( "player", "target" ) then
- return conf.visibility.pvp.combatTarget
- elseif conf.visibility.pvp.combat > 0 and state.combat > 0 then
- return conf.visibility.pvp.combat
- elseif conf.visibility.pvp.target > 0 and UnitExists( "target" ) and not UnitIsDead( "target" ) and UnitCanAttack( "player", "target" ) then
- return conf.visibility.pvp.target
- elseif conf.visibility.pvp.always > 0 then
- return conf.visibility.pvp.always
- end
- return 0
- end
- if not conf.visibility.advanced then return conf.visibility.pve.alpha end
- if conf.visibility.pve.hideMounted and IsMounted() then return 0 end
- if conf.visibility.pve.combatTarget > 0 and state.combat > 0 and UnitExists( "target" ) and not UnitIsDead( "target" ) and UnitCanAttack( "player", "target" ) then
- return conf.visibility.pve.combatTarget
- elseif conf.visibility.pve.combat > 0 and state.combat > 0 then
- return conf.visibility.pve.combat
- elseif conf.visibility.pve.target > 0 and UnitExists( "target" ) and not UnitIsDead( "target" ) and UnitCanAttack( "player", "target" ) then
- return conf.visibility.pve.target
- elseif conf.visibility.pve.always > 0 then
- return conf.visibility.pve.always
- end
- return 0
- end
- local numDisplays = 0
- function Hekili:CreateDisplay( id )
- local conf = rawget( self.DB.profile.displays, id )
- if not conf then return end
- if not dPool[ id ] then
- numDisplays = numDisplays + 1
- dPool[ id ] = CreateFrame( "Frame", "HekiliDisplay" .. id, UIParent )
- dPool[ id ].index = numDisplays
- Hekili:ProfileFrame( "HekiliDisplay" .. id, dPool[ id ] )
- end
- local d = dPool[ id ]
- d.id = id
- d.alpha = 0
- d.numIcons = conf.numIcons
- d.firstForce = 0
- d.threadLocked = false
- local scale = self:GetScale()
- local border = 2
- d:SetSize( scale * ( border + ( conf.primaryWidth or 50 ) ), scale * ( border + ( conf.primaryHeight or 50 ) ) )
- --[[ d:SetIgnoreParentScale( true )
- d:SetScale( UIParent:GetScale() ) ]]
- d:ClearAllPoints()
- d:SetPoint( "CENTER", UIParent, "CENTER", conf.x or 0, conf.y or -225 )
- d:SetParent( UIParent )
- d:SetFrameStrata( conf.frameStrata or "MEDIUM" )
- d:SetFrameLevel( conf.frameLevel or ( 10 * d.index ) )
- if not d:IsAnchoringRestricted() then
- d:SetClampedToScreen( true )
- d:EnableMouse( false )
- d:SetMovable( true )
- end
- function d:UpdateKeybindings()
- local conf = Hekili.DB.profile.displays[ self.id ]
- if conf.keybindings and conf.keybindings.enabled then
- for i, b in ipairs( self.Buttons ) do
- local a = b.Action
- if a then
- b.Keybind, b.KeybindFrom = Hekili:GetBindingForAction( a, conf, i )
- if i == 1 or conf.keybindings.queued then
- b.Keybinding:SetText( b.Keybind )
- else
- b.Keybinding:SetText( nil )
- end
- else
- b.Keybinding:SetText( nil )
- end
- end
- end
- end
- function d:IsThreadLocked()
- return self.threadLocked
- end
- function d:SetThreadLocked( locked )
- self.threadLocked = locked
- end
- local RomanNumerals = {
- "I",
- "II",
- "III",
- "IV"
- }
- function d:OnUpdate( elapsed )
- if not self.Recommendations or not Hekili.PLAYER_ENTERING_WORLD then
- return
- end
- local init = debugprofilestop()
- local profile = Hekili.DB.profile
- local conf = profile.displays[ self.id ]
- self.alphaCheck = self.alphaCheck - elapsed
- if self.alphaCheck <= 0 then
- self.alphaCheck = 0.5
- self:UpdateAlpha()
- end
- if not self.id == "Primary" and not ( self.Buttons[ 1 ] and self.Buttons[ 1 ].Action ) and not ( self.HasRecommendations or not self.NewRecommendations ) then
- return
- end
- local postAlpha = debugprofilestop()
- if Hekili.Pause and not self.paused then
- self.Buttons[ 1 ].Overlay:Show()
- self.paused = true
- elseif not Hekili.Pause and self.paused then
- self.Buttons[ 1 ].Overlay:Hide()
- self.paused = false
- end
- local now = GetTime()
- self.recTimer = self.recTimer - elapsed
- if not self:IsThreadLocked() and ( self.NewRecommendations or self.recTimer < 0 ) then
- local alpha = self.alpha
- local options = Hekili:GetActiveSpecOption( "abilities" )
- if self.HasRecommendations and self.RecommendationsStr and self.RecommendationsStr:len() == 0 then
- for i, b in ipairs( self.Buttons ) do b:Hide() end
- self.HasRecommendations = false
- else
- self.HasRecommendations = true
- for i, b in ipairs( self.Buttons ) do
- b.Recommendation = self.Recommendations[ i ]
- local action = b.Recommendation.actionName
- local caption = b.Recommendation.caption
- local indicator = b.Recommendation.indicator
- local keybind = b.Recommendation.keybind
- local exact_time = b.Recommendation.exact_time
- local ability = class.abilities[ action ]
- if ability then
- if ( conf.flash.enabled and conf.flash.suppress ) then b:Hide()
- else b:Show() end
- if i == 1 then
- -- print( "Changing", GetTime() )
- end
- if action ~= b.lastAction or self.NewRecommendations or not b.Image then
- if ability.item then
- b.Image = b.Recommendation.texture or ability.texture or select( 10, GetItemInfo( ability.item ) )
- else
- local override = options and rawget( options, action )
- b.Image = override and override.icon or b.Recommendation.texture or ability.texture or GetSpellTexture( ability.id )
- end
- b.Texture:SetTexture( b.Image )
- b.Texture:SetTexCoord( unpack( b.texCoords ) )
- b.lastAction = action
- end
- b.Texture:Show()
- if i == 1 then
- if conf.glow.highlight then
- local id = ability.item or ability.id
- local isItem = ability.item ~= nil
- if id and ( isItem and IsCurrentItem( id ) or IsCurrentSpell( id ) ) and exact_time > GetTime() then
- b.Highlight:Show()
- else
- b.Highlight:Hide()
- end
- elseif b.Highlight:IsShown() then
- b.Highlight:Hide()
- end
- end
- if ability.empowered then
- b.EmpowerLevel:SetText( RomanNumerals[ b.Recommendation.empower_to or state.max_empower ] )
- else
- b.EmpowerLevel:SetText( nil )
- end
- if conf.indicators.enabled and indicator then
- if indicator == "cycle" then
- b.Icon:SetTexture("Interface\\Addons\\Hekili\\Textures\\Cycle")
- end
- if indicator == "cancel" then
- b.Icon:SetTexture("Interface\\Addons\\Hekili\\Textures\\Cancel")
- end
- b.Icon:Show()
- else
- b.Icon:Hide()
- end
- if ( caption and conf.captions.enabled or ability.caption and not ability.empowered ) and ( i == 1 or conf.captions.queued ) then
- b.Caption:SetText( caption )
- else
- b.Caption:SetText(nil)
- end
- if conf.keybindings.enabled and ( i == 1 or conf.keybindings.queued ) then
- b.Keybinding:SetText( keybind )
- else
- b.Keybinding:SetText(nil)
- end
- if conf.glow.enabled and ( i == 1 or conf.glow.queued ) and IsSpellOverlayed( ability.id ) then
- b.glowColor = b.glowColor or {}
- if conf.glow.coloring == "class" then
- b.glowColor[1], b.glowColor[2], b.glowColor[3], b.glowColor[4] = RAID_CLASS_COLORS[ class.file ]:GetRGBA()
- elseif conf.glow.coloring == "custom" then
- b.glowColor[1], b.glowColor[2], b.glowColor[3], b.glowColor[4] = unpack(conf.glow.color)
- else
- b.glowColor[1], b.glowColor[2], b.glowColor[3], b.glowColor[4] = 0.95, 0.95, 0.32, 1
- end
- if conf.glow.mode == "default" then
- Glower.ButtonGlow_Start( b, b.glowColor )
- b.glowStop = Glower.ButtonGlow_Stop
- elseif conf.glow.mode == "autocast" then
- Glower.AutoCastGlow_Start( b, b.glowColor )
- b.glowStop = Glower.AutoCastGlow_Stop
- elseif conf.glow.mode == "pixel" then
- Glower.PixelGlow_Start( b, b.glowColor )
- b.glowStop = Glower.PixelGlow_Stop
- end
- b.glowing = true
- elseif b.glowing then
- if b.glowStop then b:glowStop() end
- b.glowing = false
- end
- else
- b:Hide()
- end
- b.Action = action
- b.Text = caption
- b.Indicator = indicator
- b.Keybind = keybind
- b.Ability = ability
- b.ExactTime = exact_time
- end
- self.glowTimer = -1
- self.rangeTimer = -1
- self.delayTimer = -1
- self.recTimer = 1
- self.alphaCheck = 0.5
- self:RefreshCooldowns( "RECS_UPDATED" )
- end
- end
- local postRecs = debugprofilestop()
- if self.HasRecommendations then
- self.glowTimer = self.glowTimer - elapsed
- if self.glowTimer < 0 or self.NewRecommendations then
- if conf.glow.enabled then
- for i, b in ipairs( self.Buttons ) do
- if not b.Action then break end
- local a = b.Ability
- if i == 1 or conf.glow.queued then
- local glowing = a.id > 0 and IsSpellOverlayed( a.id )
- if glowing and not b.glowing then
- b.glowColor = b.glowColor or {}
- if conf.glow.coloring == "class" then
- b.glowColor[1], b.glowColor[2], b.glowColor[3], b.glowColor[4] = RAID_CLASS_COLORS[ class.file ]:GetRGBA()
- elseif conf.glow.coloring == "custom" then
- b.glowColor[1], b.glowColor[2], b.glowColor[3], b.glowColor[4] = unpack(conf.glow.color)
- else
- b.glowColor[1], b.glowColor[2], b.glowColor[3], b.glowColor[4] = 0.95, 0.95, 0.32, 1
- end
- if conf.glow.mode == "default" then
- Glower.ButtonGlow_Start( b, b.glowColor )
- b.glowStop = Glower.ButtonGlow_Stop
- elseif conf.glow.mode == "autocast" then
- Glower.AutoCastGlow_Start( b, b.glowColor )
- b.glowStop = Glower.AutoCastGlow_Stop
- elseif conf.glow.mode == "pixel" then
- Glower.PixelGlow_Start( b, b.glowColor )
- b.glowStop = Glower.PixelGlow_Stop
- end
- b.glowing = true
- elseif not glowing and b.glowing then
- b:glowStop()
- b.glowing = false
- end
- else
- if b.glowing then
- b:glowStop()
- b.glowing = false
- end
- end
- end
- end
- end
- local postGlow = debugprofilestop()
- self.rangeTimer = self.rangeTimer - elapsed
- if self.rangeTimer < 0 or self.NewRecommendations then
- for i, b in ipairs( self.Buttons ) do
- local a = b.Ability
- if a and a.id then
- local outOfRange = false
- if conf.range.enabled and UnitCanAttack( "player", "target" ) then
- if conf.range.type == "melee" then
- outOfRange = ( LRC:GetRange( "target" ) or 10 ) > 7
- elseif conf.range.type == "ability" then
- local name = a.rangeSpell or a.itemSpellName or a.actualName or a.name
- if name then outOfRange = LSR.IsSpellInRange( name, "target" ) == 0 end
- end
- end
- if outOfRange and not b.outOfRange then
- b.Texture:SetDesaturated(true)
- b.Texture:SetVertexColor(1.0, 0.0, 0.0, 1.0)
- b.outOfRange = true
- elseif b.outOfRange and not outOfRange then
- b.Texture:SetDesaturated(false)
- b.Texture:SetVertexColor(1.0, 1.0, 1.0, 1.0)
- b.outOfRange = false
- end
- if not b.outOfRange then
- local _, unusable
- if a.itemCd or a.item then
- unusable = not IsUsableItem( a.itemCd or a.item )
- else
- _, unusable = IsUsableSpell( a.actualName or a.name )
- end
- if i == 1 and conf.delays.fade then
- local delay = b.ExactTime and ( b.ExactTime - now ) or 0
- --[[ local start, duration = 0, 0
- if a.gcd ~= "off" then
- start, duration = GetSpellCooldown( 61304 )
- if start > 0 then moment = start + duration - now end
- end
- local rStart, rDuration
- if a.item then
- rStart, rDuration = GetItemCooldown( a.item )
- else
- rStart, rDuration = GetSpellCooldown( a.id )
- end
- if rStart > 0 then moment = max( moment, rStart + rDuration - now ) end
- start, duration = select( 4, UnitCastingInfo( "player" ) )
- if start and start > 0 then moment = max( ( start / 1000 ) + ( duration / 1000 ) - now, moment ) end ]]
- if delay > 0.05 then
- unusable = true
- end
- end
- if unusable and not b.unusable then
- b.Texture:SetVertexColor(0.4, 0.4, 0.4, 1.0)
- b.unusable = true
- elseif b.unusable and not unusable then
- b.Texture:SetVertexColor(1.0, 1.0, 1.0, 1.0)
- b.unusable = false
- end
- end
- end
- end
- self.rangeTimer = pulseRange
- end
- local postRange = debugprofilestop()
- if self.flashReady and conf.flash.enabled and LSF and ( InCombatLockdown() or not conf.flash.combat ) then
- self.flashTimer = self.flashTimer - elapsed
- self.flashWarnings = self.flashWarnings or {}
- self.lastFlashFrames = self.lastFlashFrames or {}
- local a = self.Buttons[ 1 ].Action
- local changed = self.lastFlash ~= a
- if a and ( changed or self.flashTimer < 0 ) then
- if changed then
- for frame in pairs( self.lastFlashFrames ) do
- frame:Hide()
- frame.flashDuration = 0
- self.lastFlashFrames[ frame ] = nil
- end
- end
- self.flashTimer = conf.flash.speed or 0.4
- local ability = class.abilities[ a ]
- self.flashColor = self.flashColor or {}
- self.flashColor.r, self.flashColor.g, self.flashColor.b = unpack( conf.flash.color )
- catchFlash = GetTime()
- table.wipe( lastFramesFlashed )
- if ability.item then
- local iname = LSF.ItemName( ability.item )
- if LSF.Flashable( iname ) then
- LSF.FlashItem( iname, self.flashColor, conf.flash.size, conf.flash.brightness, conf.flash.blink, nil, profile.flashTexture, conf.flash.fixedSize, conf.flash.fixedBrightness )
- elseif conf.flash.suppress and not self.flashWarnings[ iname ] then
- self.flashWarnings[ iname ] = true
- -- Hekili:Error( "|cffff0000WARNING|r - Could not flash recommended item '" .. iname .. "' (" .. self.id .. ")." )
- end
- else
- local aFlash = ability.flash
- if aFlash then
- local flashable = false
- if type( aFlash ) == "table" then
- local lastSpell
- for _, spell in ipairs( aFlash ) do
- lastSpell = spell
- if LSF.Flashable( spell ) then
- flashable = true
- break
- end
- end
- aFlash = lastSpell
- else
- flashable = LSF.Flashable( aFlash )
- end
- if flashable then
- LSF.FlashAction( aFlash, self.flashColor, conf.flash.size, conf.flash.brightness, conf.flash.blink, nil, profile.flashTexture, conf.flash.fixedSize, conf.flash.fixedBrightness )
- elseif conf.flash.suppress and not self.flashWarnings[ aFlash ] then
- self.flashWarnings[ aFlash ] = true
- -- Hekili:Error( "|cffff0000WARNING|r - Could not flash recommended action '" .. aFlash .. "' (" .. self.id .. ")." )
- end
- else
- local id = ability.known
- if id == nil or type( id ) ~= "number" then
- id = ability.id
- end
- local sname = LSF.SpellName( id )
- if sname then
- if LSF.Flashable( sname ) then
- LSF.FlashAction( sname, self.flashColor, conf.flash.size, conf.flash.brightness, conf.flash.blink, nil, profile.flashTexture, conf.flash.fixedSize, conf.flash.fixedBrightness )
- elseif not self.flashWarnings[ sname ] then
- self.flashWarnings[ sname ] = true
- -- Hekili:Error( "|cffff0000WARNING|r - Could not flash recommended ability '" .. sname .. "' (" .. self.id .. ")." )
- end
- end
- end
- end
- catchFlash = nil
- for frame, status in pairs( lastFramesFlashed ) do
- if status ~= 0 then
- self.lastFlashFrames[ frame ] = 1
- if frame.texture ~= profile.flashTexture then
- frame.FlashTexture:SetTexture( profile.flashTexture )
- frame.texture = profile.flashTexture
- end
- end
- end
- self.lastFlash = a
- end
- end
- local postFlash = debugprofilestop()
- self.targetTimer = self.targetTimer - elapsed
- if self.targetTimer < 0 or self.NewRecommendations then
- local b = self.Buttons[ 1 ]
- if conf.targets.enabled then
- local tMin, tMax = 0, 0
- local mode = profile.toggles.mode.value
- local spec = state.spec.id and profile.specs[ state.spec.id ]
- if self.id == 'Primary' then
- if ( mode == 'dual' or mode == 'single' or mode == 'reactive' ) then tMax = 1
- elseif mode == 'aoe' then tMin = spec and spec.aoe or 3 end
- elseif self.id == 'AOE' then tMin = spec and spec.aoe or 3 end
- local detected = ns.getNumberTargets()
- local shown = detected
- if tMin > 0 then
- shown = max(tMin, shown)
- end
- if tMax > 0 then
- shown = min(tMax, shown)
- end
- if tMax == 1 or shown > 1 then
- local color = detected < shown and "|cFFFF0000" or ( shown < detected and "|cFF00C0FF" or "" )
- b.Targets:SetText( color .. shown .. "|r")
- b.targetShown = true
- else
- b.Targets:SetText(nil)
- b.targetShown = false
- end
- elseif b.targetShown then
- b.Targets:SetText(nil)
- end
- self.targetTimer = pulseTargets
- end
- local postTargets = debugprofilestop()
- local b = self.Buttons[ 1 ]
- self.delayTimer = self.delayTimer - elapsed
- if b.ExactTime and ( self.delayTimer < 0 or self.NewRecommendations ) then
- local a = b.Ability
- local delay = b.ExactTime - now
- local moment = 0
- if delay > 0 then
- local start, duration = 0, 0
- if a.gcd ~= "off" then
- start, duration = GetSpellCooldown( 61304 )
- if start > 0 then moment = start + duration - now end
- end
- start, duration = select( 4, UnitCastingInfo( "player" ) )
- if start and start > 0 then moment = max( ( start / 1000 ) + ( duration / 1000 ) - now, moment ) end
- local rStart, rDuration = 0, 0
- if a.item then
- rStart, rDuration = C_Item.GetItemCooldown( a.item )
- else
- if a.cooldown > 0 or a.spendType ~= "runes" then
- rStart, rDuration = GetSpellCooldown( a.id )
- end
- end
- if rStart > 0 then moment = max( moment, rStart + rDuration - now ) end
- end
- if conf.delays.type == "TEXT" then
- if self.delayIconShown then
- b.DelayIcon:Hide()
- self.delayIconShown = false
- end
- if delay > moment + 0.05 then
- b.DelayText:SetText( format( "%.1f", delay ) )
- self.delayTextShown = true
- else
- b.DelayText:SetText( nil )
- self.delayTextShown = false
- end
- elseif conf.delays.type == "ICON" then
- if self.delayTextShown then
- b.DelayText:SetText(nil)
- self.delayTextShown = false
- end
- if delay > moment + 0.05 then
- b.DelayIcon:Show()
- b.DelayIcon:SetAlpha( self.alpha )
- self.delayIconShown = true
- if delay < 0.5 then
- b.DelayIcon:SetVertexColor( 0.0, 1.0, 0.0, 1.0 )
- elseif delay < 1.5 then
- b.DelayIcon:SetVertexColor( 1.0, 1.0, 0.0, 1.0 )
- else
- b.DelayIcon:SetVertexColor( 1.0, 0.0, 0.0, 1.0 )
- end
- else
- b.DelayIcon:Hide()
- b.delayIconShown = false
- end
- else
- if self.delayTextShown then
- b.DelayText:SetText( nil )
- self.delayTextShown = false
- end
- if self.delayIconShown then
- b.DelayIcon:Hide()
- self.delayIconShown = false
- end
- end
- self.delayTimer = pulseDelay
- end
- self.NewRecommendations = false
- local finish = debugprofilestop()
- if self.updateTime then
- local newTime = self.updateTime * self.updateCount + ( finish - init )
- self.updateCount = self.updateCount + 1
- self.updateTime = newTime / self.updateCount
- self.updateMax = max( self.updateMax, finish - init )
- self.postAlpha = max( self.postAlpha, postAlpha - init )
- self.postRecs = max( self.postRecs, postRecs - postAlpha )
- self.postGlow = max( self.postGlow, postGlow - postRecs )
- self.postRange = max( self.postRange, postRange - postGlow )
- self.postFlash = max( self.postFlash, postFlash - postRange )
- self.postTargets = max( self.postTargets, postTargets - postFlash )
- self.postDelay = max( self.postDelay, finish - postTargets )
- else
- self.updateCount = 1
- self.updateTime = finish - init
- self.updateMax = finish - init
- self.postAlpha = postAlpha - init
- self.postRecs = postRecs - postAlpha
- self.postGlow = postGlow - postRecs
- self.postRange = postRange - postGlow
- self.postFlash = postFlash - postRange
- self.postTargets = postTargets - postFlash
- self.postDelay = finish - postTargets
- end
- end
- end
- Hekili:ProfileCPU( "HekiliDisplay" .. id .. ":OnUpdate", d.OnUpdate )
- function d:UpdateAlpha()
- if not self.Active then
- self:SetAlpha( 0 )
- self:Hide()
- self.alpha = 0
- return
- end
- local preAlpha = self.alpha or 0
- local newAlpha = CalculateAlpha( self.id )
- if preAlpha > 0 and newAlpha == 0 then
- -- self:Deactivate()
- self:SetAlpha( 0 )
- self.alphaCheck = 0.5
- else
- if preAlpha == 0 and newAlpha > 0 then
- Hekili:ForceUpdate( "DISPLAY_ALPHA_CHANGED:" .. d.id .. ":" .. preAlpha .. ":" .. newAlpha .. ":" .. GetTime() )
- end
- self:SetAlpha( newAlpha )
- self:Show()
- end
- self.alpha = newAlpha
- end
- function d:RefreshCooldowns( event )
- local gStart = GetSpellCooldown( 61304 )
- local cStart = ( select( 4, UnitCastingInfo( "player" ) ) or select( 4, UnitChannelInfo( "player" ) ) or 0 ) / 1000
- local now = GetTime()
- local conf = Hekili.DB.profile.displays[ self.id ]
- for i, rec in ipairs( self.Recommendations ) do
- local button = self.Buttons[ i ]
- if button.Action then
- local cd = button.Cooldown
- local ability = button.Ability
- local start, duration, enabled, modRate = 0, 0, 1, 1
- if ability.item then
- start, duration, enabled, modRate = GetItemCooldown( ability.item )
- elseif ability.key ~= state.empowerment.spell then
- start, duration, enabled, modRate = GetSpellCooldown( ability.id )
- end
- if i == 1 and conf.delays.extend and rec.exact_time > max( now, start + duration ) then
- start = ( start > 0 and start ) or ( cStart > 0 and cStart ) or ( gStart > 0 and gStart ) or max( state.gcd.lastStart, state.combat )
- duration = rec.exact_time - start
- elseif enabled and enabled == 0 then
- start = 0
- duration = 0
- modRate = 1
- end
- if cd.lastStart ~= start or cd.lastDuration ~= duration then
- cd:SetCooldown( start, duration, modRate )
- cd.lastStart = start
- cd.lastDuration = duration
- end
- if i == 1 and ability.empowered and conf.empowerment.glow then
- if state.empowerment.spell == ability.key and duration == 0 then
- button.Empowerment:Show()
- else
- button.Empowerment:Hide()
- end
- end
- end
- end
- end
- function d:OnEvent( event, ... )
- if not self.Recommendations then
- return
- end
- local conf = Hekili.DB.profile.displays[ self.id ]
- local init = debugprofilestop()
- if event == "SPELL_ACTIVATION_OVERLAY_GLOW_SHOW" then
- if conf.glow.enabled then
- for i, b in ipairs( self.Buttons ) do
- if i > 1 and not conf.glow.queued then
- break
- end
- if not b.Action then
- break
- end
- local a = b.Ability
- if not b.glowing and a and a.id == ... then
- b.glowColor = b.glowColor or {}
- if conf.glow.coloring == "class" then
- b.glowColor[1], b.glowColor[2], b.glowColor[3], b.glowColor[4] = RAID_CLASS_COLORS[ class.file ]:GetRGBA()
- elseif conf.glow.coloring == "custom" then
- b.glowColor[1], b.glowColor[2], b.glowColor[3], b.glowColor[4] = unpack(conf.glow.color)
- else
- b.glowColor[1], b.glowColor[2], b.glowColor[3], b.glowColor[4] = 0.95, 0.95, 0.32, 1
- end
- if conf.glow.mode == "default" then
- Glower.ButtonGlow_Start( b, b.glowColor )
- b.glowStop = Glower.ButtonGlow_Stop
- elseif conf.glow.mode == "autocast" then
- Glower.AutoCastGlow_Start( b, b.glowColor )
- b.glowStop = Glower.AutoCastGlow_Stop
- elseif conf.glow.mode == "pixel" then
- Glower.PixelGlow_Start( b, b.glowColor )
- b.glowStop = Glower.PixelGlow_Stop
- end
- b.glowing = true
- end
- end
- end
- elseif event == "SPELL_ACTIVATION_OVERLAY_GLOW_HIDE" then
- if conf.glow.enabled then
- for i, b in ipairs(self.Buttons) do
- if i > 1 and not conf.glow.queued then
- break
- end
- if not b.Action then
- break
- end
- local a = b.Ability
- if b.glowing and ( not a or a.id == ... ) then
- b:glowStop()
- b.glowing = false
- end
- end
- end
- elseif kbEvents[ event ] then
- self:UpdateKeybindings()
- elseif alphaUpdateEvents[ event ] then
- if event == "CLIENT_SCENE_OPENED" then
- if ... == 1 then -- Minigame.
- Hekili.ClientScene = true
- end
- elseif event == "CLIENT_SCENE_CLOSED" then
- Hekili.ClientScene = nil
- end
- self:UpdateAlpha()
- end
- if flashEvents[ event ] then
- self.flashReady = false
- C_Timer.After( 3, function()
- self.flashReady = true
- end )
- end
- if event == "CURRENT_SPELL_CAST_CHANGED" then
- local b = self.Buttons[ 1 ]
- if conf.glow.highlight then
- local ability = b.Ability
- local isItem, id = false, ability and ability.id
- if id and id < 0 then
- isItem = true
- id = ability.item
- end
- local spellID = select( 9, UnitCastingInfo( "player" ) ) or select( 9, UnitChannelInfo( "player" ) )
- if id and ( isItem and IsCurrentItem( id ) or IsCurrentSpell( id ) ) then -- and b.ExactTime > GetTime() then
- b.Highlight:Show()
- else
- b.Highlight:Hide()
- end
- elseif b.Highlight:IsShown() then
- b.Highlight:Hide()
- end
- end
- local finish = debugprofilestop()
- if self.eventTime then
- local newTime = self.eventTime * self.eventCount + finish - init
- self.eventCount = self.eventCount + 1
- self.eventTime = newTime / self.eventCount
- if finish - init > self.eventMax then
- self.eventMax = finish - init
- self.eventMaxType = event
- end
- else
- self.eventCount = 1
- self.eventTime = finish - init
- self.eventMax = finish - init
- self.eventMaxType = event
- end
- end
- Hekili:ProfileCPU( "HekiliDisplay" .. id .. ":OnEvent", d.OnEvent )
- function d:Activate()
- if not self.Active then
- self.Active = true
- self.Recommendations = self.Recommendations or ( ns.queue and ns.queue[ self.id ] )
- self.NewRecommendations = true
- self.alphaCheck = 0
- self.auraTimer = 0
- self.delayTimer = 0
- self.flashTimer = 0
- self.glowTimer = 0
- self.rangeTimer = 0
- self.recTimer = 0
- self.refreshTimer = 0
- self.targetTimer = 0
- self.lastUpdate = 0
- self:SetScript( "OnUpdate", self.OnUpdate )
- self:SetScript( "OnEvent", self.OnEvent )
- if not self.Initialized then
- -- Update Cooldown Wheels.
- -- self:RegisterEvent( "ACTIONBAR_UPDATE_USABLE" )
- -- self:RegisterEvent( "ACTIONBAR_UPDATE_COOLDOWN" )
- -- self:RegisterEvent( "SPELL_UPDATE_COOLDOWN" )
- -- self:RegisterEvent( "SPELL_UPDATE_USABLE" )
- -- Show/Hide Overlay Glows.
- self:RegisterEvent( "SPELL_ACTIVATION_OVERLAY_GLOW_SHOW" )
- self:RegisterEvent( "SPELL_ACTIVATION_OVERLAY_GLOW_HIDE" )
- -- Recalculate Alpha/Visibility.
- for e in pairs( alphaUpdateEvents ) do
- self:RegisterEvent( e )
- end
- -- Recheck spell displays if spells have changed.
- self:RegisterEvent( "SPELLS_CHANGED" )
- self:RegisterEvent( "CURRENT_SPELL_CAST_CHANGED" )
- -- Update keybindings.
- for k in pairs( kbEvents ) do
- self:RegisterEvent( k )
- end
- for k in pairs( flashEvents ) do
- self:RegisterEvent( k )
- end
- self.Initialized = true
- end
- -- Hekili:ProcessHooks( self.id )
- end
- end
- function d:Deactivate()
- self.Active = false
- self:SetScript( "OnUpdate", nil )
- self:SetScript( "OnEvent", nil )
- for i, b in ipairs( self.Buttons ) do
- b:Hide()
- end
- end
- function d:GetPerimeterButtons()
- local left, right, top, bottom
- local lPos, rPos, tPos, bPos
- for i = 1, self.numIcons do
- local button = self.Buttons[ i ]
- if i == 1 then
- lPos = button:GetLeft()
- rPos = button:GetRight()
- tPos = button:GetTop()
- bPos = button:GetBottom()
- left = button
- right = button
- top = button
- bottom = button
- else
- if button:GetLeft() < lPos then
- lPos = button:GetLeft()
- left = button
- end
- if button:GetRight() > rPos then
- rPos = button:GetRight()
- right = button
- end
- if button:GetTop() > tPos then
- tPos = button:GetTop()
- top = button
- end
- if button:GetBottom() < bPos then
- bPos = button:GetBottom()
- bottom = button
- end
- end
- end
- return left, right, top, bottom
- end
- -- function d:UpdatePerformance( now, used, newRecs )
- --[[
- if not InCombatLockdown() then
- self.combatUpdates.last = 0
- return
- elseif self.combatUpdates.last == 0 then
- self.combatUpdates.last = now - used
- end
- if used == nil then return end
- -- used = used / 1000 -- ms to sec.
- if self.combatTime.samples == 0 then
- self.combatTime.fastest = used
- self.combatTime.slowest = used
- self.combatTime.average = used
- self.combatTime.samples = 1
- else
- if used < self.combatTime.fastest then self.combatTime.fastest = used end
- if used > self.combatTime.slowest then
- self.combatTime.slowest = used
- end
- self.combatTime.average = ( ( self.combatTime.average * self.combatTime.samples ) + used ) / ( self.combatTime.samples + 1 )
- self.combatTime.samples = self.combatTime.samples + 1
- end
- if self.combatUpdates.samples == 0 or self.combatUpdates.last == 0 then
- if self.combatUpdates.last == 0 then
- self.combatUpdates.last = now
- else
- local interval = now - self.combatUpdates.last
- self.combatUpdates.last = now
- self.combatUpdates.shortest = interval
- self.combatUpdates.longest = interval
- self.combatUpdates.average = interval
- self.combatUpdates.samples = 1
- end
- else
- local interval = now - self.combatUpdates.last
- self.combatUpdates.last = now
- if interval < self.combatUpdates.shortest then
- self.combatUpdates.shortest = interval
- self.combatUpdates.shortEvents = nil
- local e = 0
- for k in pairs( self.eventsTriggered ) do
- if e == 0 then self.combatUpdates.shortEvents = k; e = 1
- else self.combatUpdates.shortEvents = self.combatUpdates.shortEvents .. "|" .. k end
- end
- end
- if interval > self.combatUpdates.longest then
- self.combatUpdates.longest = interval
- self.combatUpdates.longEvents = nil
- local e = 0
- for k in pairs( self.eventsTriggered ) do
- if e == 0 then self.combatUpdates.longEvents = k; e = 1
- else self.combatUpdates.longEvents = self.combatUpdates.longEvents .. "|" .. k end
- end
- end
- self.combatUpdates.average = ( ( self.combatUpdates.average * self.combatUpdates.samples ) + interval ) / ( self.combatUpdates.samples + 1 )
- self.combatUpdates.samples = self.combatUpdates.samples + 1
- end
- if self.id == "Primary" then
- self.successEvents = self.successEvents or {}
- self.failEvents = self.failEvents or {}
- local events = newRecs and self.successEvents or self.failEvents
- for k in pairs( self.eventsTriggered ) do
- if events[ k ] then events[ k ] = events[ k ] + 1
- else events[ k ] = 1 end
- end
- table.wipe( self.eventsTriggered )
- end ]]
- -- end
- ns.queue[id] = ns.queue[id] or {}
- d.Recommendations = ns.queue[id]
- ns.UI.Buttons[id] = ns.UI.Buttons[id] or {}
- d.Buttons = ns.UI.Buttons[id]
- for i = 1, 10 do
- d.Buttons[ i ] = self:CreateButton( id, i )
- d.Buttons[ i ]:Hide()
- if conf.enabled and self:IsDisplayActive( id ) and i <= conf.numIcons then
- if d.Recommendations[ i ] and d.Recommendations[ i ].actionName then
- d.Buttons[ i ]:Show()
- end
- end
- if MasqueGroup then
- MasqueGroup:AddButton( d.Buttons[i], { Icon = d.Buttons[ i ].Texture, Cooldown = d.Buttons[ i ].Cooldown } )
- end
- end
- if d.forceElvUpdate then
- local E = _G.ElvUI and ElvUI[1]
- E:UpdateCooldownOverride( 'global' )
- d.forceElvUpdate = nil
- end
- if d.flashReady == nil then
- C_Timer.After( 3, function()
- d.flashReady = true
- end )
- end
- end
- function Hekili:CreateCustomDisplay( id )
- local conf = rawget( self.DB.profile.displays, id )
- if not conf then return end
- dPool[ id ] = dPool[ id ] or CreateFrame( "Frame", "HekiliDisplay" .. id, UIParent )
- local d = dPool[ id ]
- self:ProfileFrame( "HekiliDisplay" .. id, d )
- d.id = id
- local scale = self:GetScale()
- local border = 2
- d:SetSize( scale * ( border + conf.primaryWidth ), scale * (border + conf.primaryHeight ) )
- d:SetPoint( "CENTER", nil, "CENTER", conf.x, conf.y )
- d:SetFrameStrata( "MEDIUM" )
- d:SetClampedToScreen( true )
- d:EnableMouse( false )
- d:SetMovable( true )
- d.Activate = HekiliDisplayPrimary.Activate
- d.Deactivate = HekiliDisplayPrimary.Deactivate
- d.RefreshCooldowns = HekiliDisplayPrimary.RefreshCooldowns
- d.UpdateAlpha = HekiliDisplayPrimary.UpdateAlpha
- d.UpdateKeybindings = HekiliDisplayPrimary.UpdateKeybindings
- ns.queue[id] = ns.queue[id] or {}
- d.Recommendations = ns.queue[id]
- ns.UI.Buttons[id] = ns.UI.Buttons[id] or {}
- d.Buttons = ns.UI.Buttons[id]
- for i = 1, 10 do
- d.Buttons[i] = self:CreateButton(id, i)
- d.Buttons[i]:Hide()
- if self.DB.profile.enabled and self:IsDisplayActive(id) and i <= conf.numIcons then
- if d.Recommendations[i] and d.Recommendations[i].actionName then
- d.Buttons[i]:Show()
- end
- end
- if MasqueGroup then
- MasqueGroup:AddButton(d.Buttons[i], {Icon = d.Buttons[i].Texture, Cooldown = d.Buttons[i].Cooldown})
- end
- end
- end
- local dispActive = {}
- local listActive = {}
- local actsActive = {}
- function Hekili:UpdateDisplayVisibility()
- local profile = self.DB.profile
- local displays = ns.UI.Displays
- for key in pairs( dispActive ) do
- dispActive[ key ] = nil
- end
- for list in pairs( listActive ) do
- listActive[ list ] = nil
- end
- for a in pairs( actsActive ) do
- actsActive[ a ] = nil
- end
- local specEnabled = GetSpecialization()
- specEnabled = specEnabled and GetSpecializationInfo( specEnabled )
- if class.specs[ specEnabled ] then
- specEnabled = specEnabled and rawget( profile.specs, specEnabled )
- specEnabled = specEnabled and rawget( specEnabled, "enabled" ) or false
- else
- specEnabled = false
- end
- if profile.enabled and specEnabled then
- for i, display in pairs( profile.displays ) do
- if display.enabled then
- if i == 'AOE' then
- dispActive[i] = ( profile.toggles.mode.value == 'dual' or profile.toggles.mode.value == "reactive" ) and 1 or nil
- elseif i == 'Interrupts' then
- dispActive[i] = ( profile.toggles.interrupts.value and profile.toggles.interrupts.separate ) and 1 or nil
- elseif i == 'Defensives' then
- dispActive[i] = ( profile.toggles.defensives.value and profile.toggles.defensives.separate ) and 1 or nil
- elseif i == 'Cooldowns' then
- dispActive[i] = ( profile.toggles.cooldowns.value and profile.toggles.cooldowns.separate ) and 1 or nil
- else
- dispActive[i] = 1
- end
- if dispActive[i] == nil and self.Config then
- dispActive[i] = 2
- end
- if dispActive[i] and displays[i] then
- if not displays[i].Active then displays[i]:Activate() end
- displays[i].NewRecommendations = true
- end
- else
- if displays[i] and displays[i].Active then
- displays[i]:Deactivate()
- end
- end
- end
- for packName, pack in pairs( profile.packs ) do
- if pack.spec == 0 or pack.spec == state.spec.id then
- for listName, list in pairs( pack.lists ) do
- listActive[ packName .. ":" .. listName ] = true
- -- NYI: We can cache if abilities are disabled here as well to reduce checking in ProcessHooks.
- for a, entry in ipairs( list ) do
- if entry.enabled and entry.action then
- actsActive[ packName .. ":" .. listName .. ":" .. a ] = true
- end
- end
- end
- end
- end
- else
- for _, display in pairs( displays ) do
- if display.Active then
- display:Deactivate()
- end
- end
- end
- for i, d in pairs( displays ) do
- d:UpdateAlpha()
- end
- end
- function Hekili:ReviewPacks()
- local profile = self.DB.profile
- for list in pairs( listActive ) do
- listActive[ list ] = nil
- end
- for a in pairs( actsActive ) do
- actsActive[ a ] = nil
- end
- for packName, pack in pairs( profile.packs ) do
- if pack.spec == 0 or pack.spec == state.spec.id then
- for listName, list in pairs( pack.lists ) do
- listActive[ packName .. ":" .. listName ] = true
- -- NYI: We can cache if abilities are disabled here as well to reduce checking in ProcessHooks.
- for a, entry in ipairs( list ) do
- if entry.enabled and entry.action and class.abilities[ entry.action ] then
- actsActive[ packName .. ":" .. listName .. ":" .. a ] = true
- end
- end
- end
- end
- end
- end
- function Hekili:IsDisplayActive( display, config )
- if config then
- return dispActive[ display ] == 1
- end
- return dispActive[display] ~= nil
- end
- function Hekili:IsListActive( pack, list )
- return pack == "UseItems" or ( listActive[ pack .. ":" .. list ] == true )
- end
- function Hekili:IsActionActive( pack, list, action )
- return pack == "UseItems" or ( actsActive[ pack .. ":" .. list .. ":" .. action ] == true )
- end
- function Hekili:DumpActionActive()
- DevTools_Dump( actsActive )
- end
- -- Separate the recommendations engine from each display.
- Hekili.Engine = CreateFrame( "Frame", "HekiliEngine" )
- Hekili.Engine.refreshTimer = 1
- Hekili.Engine.eventsTriggered = {}
- function Hekili.Engine:UpdatePerformance( wasted )
- -- Only track in combat.
- if not ( self.firstThreadCompleted and InCombatLockdown() ) then
- self.activeThreadTime = 0
- return
- end
- if self.firstThreadCompleted then
- local now = debugprofilestop()
- local timeSince = now - self.activeThreadStart
- self.lastUpdate = now
- if self.threadUpdates then
- local updates = self.threadUpdates.updates
- local total = updates + 1
- if wasted then
- -- Capture thrown away computation time due to forced resets.
- self.threadUpdates.meanWasted = ( self.threadUpdates.meanWasted * updates + self.activeThreadTime ) / total
- self.threadUpdates.totalWasted = ( self.threadUpdates.totalWasted + self.activeThreadTime )
- if self.activeThreadTime > self.threadUpdates.peakWasted then self.threadUpdates.peakWasted = self.activeThreadTime end
- else
- self.threadUpdates.meanClockTime = ( self.threadUpdates.meanClockTime * updates + timeSince ) / total
- self.threadUpdates.meanWorkTime = ( self.threadUpdates.meanWorkTime * updates + self.activeThreadTime ) / total
- self.threadUpdates.meanFrames = ( self.threadUpdates.meanFrames * updates + self.activeThreadFrames ) / total
- if timeSince > self.threadUpdates.peakClockTime then self.threadUpdates.peakClockTime = timeSince end
- if self.activeThreadTime > self.threadUpdates.peakWorkTime then self.threadUpdates.peakWorkTime = self.activeThreadTime end
- if self.activeThreadFrames > self.threadUpdates.peakFrames then self.threadUpdates.peakFrames = self.activeThreadFrames end
- self.threadUpdates.updates = total
- self.threadUpdates.updatesPerSec = 1000 * total / ( now - self.threadUpdates.firstUpdate )
- end
- else
- self.threadUpdates = {
- meanClockTime = timeSince,
- meanWorkTime = self.activeThreadTime,
- meanFrames = self.activeThreadFrames or 1,
- meanWasted = 0,
- firstUpdate = now,
- updates = 1,
- updatesPerSec = 1000 / ( self.activeThreadTime > 0 and self.activeThreadTime or 1 ),
- peakClockTime = timeSince,
- peakWorkTime = self.activeThreadTime,
- peakFrames = self.activeThreadFrames or 1,
- peakWasted = 0,
- totalWasted = 0
- }
- end
- end
- self.activeThreadTime = 0
- end
- local frameSpans = {}
- Hekili.Engine:SetScript( "OnUpdate", function( self, elapsed )
- if not self.activeThread then
- self.refreshTimer = self.refreshTimer + elapsed
- insert( frameSpans, elapsed )
- end
- if Hekili.DB.profile.enabled and not Hekili.Pause then
- self.refreshRate = self.refreshRate or 0.5
- self.combatRate = self.combatRate or 0.2
- local thread = self.activeThread
- local firstDisplay = nil
- local superUpdate = self.firstThreadCompleted and self.superUpdate
- -- If there's no thread, then see if we have a reason to update.
- if superUpdate or ( not thread and self.refreshTimer > ( self.criticalUpdate and self.combatRate or self.refreshRate ) ) then
- if superUpdate and thread and coroutine.status( thread ) == "suspended" then
- -- We're going to break the thread and start over from the current display in progress.
- firstDisplay = state.display
- self:UpdatePerformance( true )
- end
- self.criticalUpdate = false
- self.superUpdate = false
- self.refreshTimer = 0
- self.activeThread = coroutine.create( Hekili.Update )
- self.activeThreadTime = 0
- self.activeThreadStart = debugprofilestop()
- self.activeThreadFrames = 0
- if not self.firstThreadCompleted then
- Hekili.maxFrameTime = InCombatLockdown() and 10 or 25
- else
- if #frameSpans > 0 then
- local averageSpan = 0
- for _, span in ipairs( frameSpans ) do
- averageSpan = averageSpan + span
- end
- averageSpan = 1000 * averageSpan / #frameSpans
- wipe( frameSpans )
- Hekili.maxFrameTime = Clamp( 0.6 * averageSpan, 3, 20 ) -- Dynamically adjust to 60% of (seemingly) average frame rate between updates.
- else
- Hekili.maxFrameTime = Hekili.maxFrameTime or 10
- end
- end
- --[[
- elseif Hekili:GetActiveSpecOption( "throttleTime" ) then
- Hekili.maxFrameTime = Hekili:GetActiveSpecOption( "maxTime" ) or 15
- else
- Hekili.maxFrameTime = 15
- end ]]
- thread = self.activeThread
- end
- -- If there's a thread, process for up to user preferred limits.
- if thread and coroutine.status( thread ) == "suspended" then
- self.activeThreadFrames = self.activeThreadFrames + 1
- Hekili.activeFrameStart = debugprofilestop()
- local ok, err = coroutine.resume( thread, firstDisplay )
- if not ok then
- err = err .. "\n\n" .. debugstack( thread )
- Hekili:Error( "Update: " .. err )
- if Hekili.ActiveDebug then
- Hekili:Debug( format( "Recommendation thread terminated due to error: %s", err and err:gsub( "%%", "%%%%" ) or "Unknown" ) )
- Hekili:SaveDebugSnapshot( self.id )
- Hekili.ActiveDebug = nil
- end
- pcall( error, err )
- end
- self.activeThreadTime = self.activeThreadTime + debugprofilestop() - Hekili.activeFrameStart
- if coroutine.status( thread ) == "dead" or err then
- self.activeThread = nil
- if Hekili:GetActiveSpecOption( "throttleRefresh" ) then
- self.refreshRate = Hekili:GetActiveSpecOption( "regularRefresh" )
- self.combatRate = Hekili:GetActiveSpecOption( "combatRefresh" )
- else
- self.refreshRate = 0.5
- self.combatRate = 0.2
- end
- if ok then
- self.firstThreadCompleted = true
- self:UpdatePerformance()
- end
- end
- if ok and err == "AutoSnapshot" then
- Hekili:MakeSnapshot( true )
- end
- end
- end
- end )
- Hekili:ProfileFrame( "HekiliEngine", Hekili.Engine )
- function HekiliEngine:IsThreadActive()
- return self.activeThread and coroutine.status( self.activeThread ) == "suspended"
- end
- function Hekili:ForceUpdate( event, super )
- self.Engine.criticalUpdate = true
- if super then
- self.Engine.superUpdate = true
- end
- if self.Engine.firstForce == 0 then self.Engine.firstForce = GetTime() end
- if event then
- self.Engine.eventsTriggered[ event ] = true
- end
- end
- local LSM = LibStub("LibSharedMedia-3.0", true)
- function Hekili:CreateButton( dispID, id )
- local d = dPool[ dispID ]
- if not d then
- return
- end
- local conf = rawget( self.DB.profile.displays, dispID )
- if not conf then return end
- ns.queue[ dispID ][ id ] = ns.queue[ dispID ][ id ] or {}
- local bName = "Hekili_" .. dispID .. "_B" .. id
- local b = d.Buttons[ id ] or CreateFrame( "Button", bName, d )
- Hekili:ProfileFrame( bName, b )
- b.display = dispID
- b.index = id
- local scale = self:GetScale()
- local borderOffset = 0
- if conf.border.enabled and conf.border.fit then
- borderOffset = 2
- end
- if id == 1 then
- b:SetHeight( scale * ( ( conf.primaryHeight or 50 ) - borderOffset ) )
- b:SetWidth( scale * ( ( conf.primaryWidth or 50 ) - borderOffset ) )
- else
- b:SetHeight( scale * ( ( conf.queue.height or 30 ) - borderOffset ) )
- b:SetWidth( scale * ( ( conf.queue.width or 50 ) - borderOffset ) )
- end
- -- Texture
- if not b.Texture then
- b.Texture = b:CreateTexture( nil, "ARTWORK" )
- b.Texture:SetTexture( "Interface\\ICONS\\Spell_Nature_BloodLust" )
- b.Texture:SetAllPoints( b )
- end
- b.texCoords = b.texCoords or {}
- local zoom = 1 - ( ( conf.zoom or 0) / 200 )
- if conf.keepAspectRatio then
- local biggest = id == 1 and max( conf.primaryHeight, conf.primaryWidth ) or max( conf.queue.height, conf.queue.width )
- local height = 0.5 * zoom * ( id == 1 and conf.primaryHeight or conf.queue.height ) / biggest
- local width = 0.5 * zoom * ( id == 1 and conf.primaryWidth or conf.queue.width ) / biggest
- b.texCoords[1] = 0.5 - width
- b.texCoords[2] = 0.5 + width
- b.texCoords[3] = 0.5 - height
- b.texCoords[4] = 0.5 + height
- b.Texture:SetTexCoord( unpack( b.texCoords ) )
- else
- local zoom = zoom / 2
- b.texCoords[1] = 0.5 - zoom
- b.texCoords[2] = 0.5 + zoom
- b.texCoords[3] = 0.5 - zoom
- b.texCoords[4] = 0.5 + zoom
- b.Texture:SetTexCoord( unpack( b.texCoords ) )
- end
- -- Initialize glow/noop if button has not yet been glowed.
- b.glowing = b.glowing or false
- b.glowStop = b.glowStop or function () end
- -- Indicator Icons.
- b.Icon = b.Icon or b:CreateTexture( nil, "OVERLAY" )
- b.Icon: SetSize( max( 10, b:GetWidth() / 3 ), max( 10, b:GetHeight() / 3 ) )
- if conf.keepAspectRatio and b.Icon:GetHeight() ~= b.Icon:GetWidth() then
- local biggest = max( b.Icon:GetHeight(), b.Icon:GetWidth() )
- local height = 0.5 * b.Icon:GetHeight() / biggest
- local width = 0.5 * b.Icon:GetWidth() / biggest
- b.Icon:SetTexCoord( 0.5 - width, 0.5 + width, 0.5 - height, 0.5 + height )
- else
- b.Icon:SetTexCoord( 0, 1, 0, 1 )
- end
- local iconAnchor = conf.indicators.anchor or "RIGHT"
- b.Icon:ClearAllPoints()
- b.Icon:SetPoint( iconAnchor, b, iconAnchor, conf.indicators.x or 0, conf.indicators.y or 0 )
- b.Icon:Hide()
- -- Caption Text.
- b.Caption = b.Caption or b:CreateFontString( bName .. "_Caption", "OVERLAY" )
- local captionFont = conf.captions.font or conf.font
- b.Caption:SetFont( LSM:Fetch("font", captionFont), conf.captions.fontSize or 12, conf.captions.fontStyle or "OUTLINE" )
- local capAnchor = conf.captions.anchor or "BOTTOM"
- b.Caption:ClearAllPoints()
- b.Caption:SetPoint( capAnchor, b, capAnchor, conf.captions.x or 0, conf.captions.y or 0 )
- b.Caption:SetHeight( b:GetHeight() / 2 )
- b.Caption:SetJustifyV( capAnchor:match("RIGHT") and "RIGHT" or ( capAnchor:match( "LEFT" ) and "LEFT" or "MIDDLE" ) )
- b.Caption:SetJustifyH( conf.captions.align or "CENTER" )
- b.Caption:SetTextColor( unpack( conf.captions.color ) )
- b.Caption:SetWordWrap( false )
- local capText = b.Caption:GetText()
- b.Caption:SetText( nil )
- b.Caption:SetText( capText )
- -- Keybinding Text
- b.Keybinding = b.Keybinding or b:CreateFontString(bName .. "_KB", "OVERLAY")
- local queued = id > 1 and conf.keybindings.separateQueueStyle
- local kbFont = queued and conf.keybindings.queuedFont or conf.keybindings.font or conf.font
- b.Keybinding:SetFont( LSM:Fetch("font", kbFont), queued and conf.keybindings.queuedFontSize or conf.keybindings.fontSize or 12, queued and conf.keybindings.queuedFontStyle or conf.keybindings.fontStyle or "OUTLINE" )
- local kbAnchor = conf.keybindings.anchor or "TOPRIGHT"
- b.Keybinding:ClearAllPoints()
- b.Keybinding:SetPoint( kbAnchor, b, kbAnchor, conf.keybindings.x or 0, conf.keybindings.y or 0 )
- b.Keybinding:SetHeight( b:GetHeight() / 2 )
- b.Keybinding:SetJustifyH( kbAnchor:match("RIGHT") and "RIGHT" or ( kbAnchor:match( "LEFT" ) and "LEFT" or "CENTER" ) )
- b.Keybinding:SetJustifyV( kbAnchor:match("TOP") and "TOP" or ( kbAnchor:match( "BOTTOM" ) and "BOTTOM" or "MIDDLE" ) )
- b.Keybinding:SetTextColor( unpack( queued and conf.keybindings.queuedColor or conf.keybindings.color ) )
- b.Keybinding:SetWordWrap( false )
- local kbText = b.Keybinding:GetText()
- b.Keybinding:SetText( nil )
- b.Keybinding:SetText( kbText )
- -- Cooldown Wheel
- if not b.Cooldown then
- b.Cooldown = CreateFrame( "Cooldown", bName .. "_Cooldown", b, "CooldownFrameTemplate" )
- if id == 1 then b.Cooldown:HookScript( "OnCooldownDone", function( self )
- if b.Ability and b.Ability.empowered and conf.empowerment.glow and state.empowerment.spell == b.Ability.key then
- b.Empowerment:Show()
- else
- b.Empowerment:Hide()
- end
- end )
- end
- end
- b.Cooldown:ClearAllPoints()
- b.Cooldown:SetAllPoints( b )
- b.Cooldown:SetFrameStrata( b:GetFrameStrata() )
- b.Cooldown:SetFrameLevel( b:GetFrameLevel() + 1 )
- b.Cooldown:SetDrawBling( false )
- b.Cooldown:SetDrawEdge( false )
- b.Cooldown.noCooldownCount = conf.hideOmniCC
- if _G["ElvUI"] and not b.isRegisteredCooldown and ( ( id == 1 and conf.elvuiCooldown ) or ( id > 1 and conf.queue.elvuiCooldown ) ) then
- local E = unpack( ElvUI )
- local cd = b.Cooldown.CooldownSettings or {}
- cd.font = E.Libs.LSM:Fetch( "font", E.db.cooldown.fonts.font )
- cd.fontSize = E.db.cooldown.fonts.fontSize
- cd.fontOutline = E.db.cooldown.fonts.fontOutline
- b.Cooldown.CooldownSettings = cd
- E:RegisterCooldown( b.Cooldown )
- d.forceElvUpdate = true
- end
- -- Backdrop (for borders)
- b.Backdrop = b.Backdrop or Mixin( CreateFrame("Frame", bName .. "_Backdrop", b ), BackdropTemplateMixin )
- b.Backdrop:ClearAllPoints()
- b.Backdrop:SetWidth( b:GetWidth() + ( conf.border.thickness and ( 2 * conf.border.thickness ) or 2 ) )
- b.Backdrop:SetHeight( b:GetHeight() + ( conf.border.thickness and ( 2 * conf.border.thickness ) or 2 ) )
- local framelevel = b:GetFrameLevel()
- if framelevel > 0 then
- -- b.Backdrop:SetFrameStrata( "MEDIUM" )
- b.Backdrop:SetFrameLevel( framelevel - 1 )
- else
- local lowerStrata = frameStratas[ b:GetFrameStrata() ]
- lowerStrata = frameStratas[ lowerStrata - 1 ]
- b.Backdrop:SetFrameStrata( lowerStrata or "LOW" )
- end
- b.Backdrop:SetPoint( "CENTER", b, "CENTER" )
- b.Backdrop:Hide()
- if conf.border.enabled then
- b.Backdrop:SetBackdrop( {
- bgFile = nil,
- edgeFile = "Interface\\Buttons\\WHITE8X8",
- tile = false,
- tileSize = 0,
- edgeSize = conf.border.thickness or 1,
- insets = { left = -1, right = -1, top = -1, bottom = -1 }
- } )
- if conf.border.coloring == 'custom' then
- b.Backdrop:SetBackdropBorderColor( unpack( conf.border.color ) )
- else
- b.Backdrop:SetBackdropBorderColor( RAID_CLASS_COLORS[ class.file ]:GetRGBA() )
- end
- b.Backdrop:Show()
- else
- b.Backdrop:SetBackdrop( nil )
- b.Backdrop:SetBackdropColor( 0, 0, 0, 0 )
- b.Backdrop:Hide()
- end
- -- Primary Icon Stuff
- if id == 1 then
- -- Anchoring stuff for the queue.
- b:ClearAllPoints()
- b:SetPoint( "CENTER", d, "CENTER" )
- -- Highlight
- if not b.Highlight then
- b.Highlight = b:CreateTexture( nil, "OVERLAY" )
- b.Highlight:SetTexture( "Interface\\Buttons\\ButtonHilight-Square" )
- b.Highlight:SetAllPoints( b )
- b.Highlight:SetBlendMode( "ADD" )
- b.Highlight:Hide()
- end
- -- Target Counter
- b.Targets = b.Targets or b:CreateFontString( bName .. "_Targets", "OVERLAY" )
- local tarFont = conf.targets.font or conf.font
- b.Targets:SetFont( LSM:Fetch( "font", tarFont ), conf.targets.fontSize or 12, conf.targets.fontStyle or "OUTLINE" )
- local tarAnchor = conf.targets.anchor or "BOTTOM"
- b.Targets:ClearAllPoints()
- b.Targets:SetPoint( tarAnchor, b, tarAnchor, conf.targets.x or 0, conf.targets.y or 0 )
- b.Targets:SetHeight( b:GetHeight() / 2 )
- b.Targets:SetJustifyH( tarAnchor:match("RIGHT") and "RIGHT" or ( tarAnchor:match( "LEFT" ) and "LEFT" or "CENTER" ) )
- b.Targets:SetJustifyV( tarAnchor:match("TOP") and "TOP" or ( tarAnchor:match( "BOTTOM" ) and "BOTTOM" or "MIDDLE" ) )
- b.Targets:SetTextColor( unpack( conf.targets.color ) )
- b.Targets:SetWordWrap( false )
- local tText = b.Targets:GetText()
- b.Targets:SetText( nil )
- b.Targets:SetText( tText )
- -- Aura Counter
- -- Disabled for Now
- --[[ b.Auras = b.Auras or b:CreateFontString(bName .. "_Auras", "OVERLAY")
- local auraFont = conf.auraFont or (ElvUI and "PT Sans Narrow" or "Arial Narrow")
- b.Auras:SetFont(LSM:Fetch("font", auraFont), conf.auraFontSize or 12, conf.auraFontStyle or "OUTLINE")
- b.Auras:SetSize(b:GetWidth(), b:GetHeight() / 2)
- local auraAnchor = conf.auraAnchor or "BOTTOM"
- b.Auras:ClearAllPoints()
- b.Auras:SetPoint(auraAnchor, b, auraAnchor, conf.xOffsetAuras or 0, conf.yOffsetAuras or 0)
- b.Auras:SetJustifyH(
- auraAnchor:match("RIGHT") and "RIGHT" or (auraAnchor:match("LEFT") and "LEFT" or "CENTER")
- )
- b.Auras:SetJustifyV(
- auraAnchor:match("TOP") and "TOP" or (auraAnchor:match("BOTTOM") and "BOTTOM" or "MIDDLE")
- )
- b.Auras:SetTextColor(1, 1, 1, 1) ]]
- -- Delay Counter
- b.DelayText = b.DelayText or b:CreateFontString( bName .. "_DelayText", "OVERLAY" )
- local delayFont = conf.delays.font or conf.font
- b.DelayText:SetFont( LSM:Fetch("font", delayFont), conf.delays.fontSize or 12, conf.delays.fontStyle or "OUTLINE" )
- local delayAnchor = conf.delays.anchor or "TOPLEFT"
- b.DelayText:ClearAllPoints()
- b.DelayText:SetPoint( delayAnchor, b, delayAnchor, conf.delays.x, conf.delays.y or 0 )
- b.DelayText:SetHeight( b:GetHeight() / 2 )
- b.DelayText:SetJustifyH( delayAnchor:match( "RIGHT" ) and "RIGHT" or ( delayAnchor:match( "LEFT" ) and "LEFT" or "CENTER") )
- b.DelayText:SetJustifyV( delayAnchor:match( "TOP" ) and "TOP" or ( delayAnchor:match( "BOTTOM" ) and "BOTTOM" or "MIDDLE") )
- b.DelayText:SetTextColor( unpack( conf.delays.color ) )
- local dText = b.DelayText:GetText()
- b.DelayText:SetText( nil )
- b.DelayText:SetText( dText )
- -- Delay Icon
- b.DelayIcon = b.DelayIcon or b:CreateTexture( bName .. "_DelayIcon", "OVERLAY" )
- b.DelayIcon:SetSize( min( 20, max( 10, b:GetSize() / 3 ) ), min( 20, max( 10, b:GetSize() / 3 ) ) )
- b.DelayIcon:SetTexture( "Interface\\FriendsFrame\\StatusIcon-Online" )
- b.DelayIcon:SetDesaturated( true )
- b.DelayIcon:SetVertexColor( 1, 0, 0, 1 )
- b.DelayIcon:ClearAllPoints()
- b.DelayIcon:SetPoint( delayAnchor, b, delayAnchor, conf.delays.x or 0, conf.delays.y or 0 )
- b.DelayIcon:Hide()
- -- Empowerment
- b.Empowerment = b.Empowerment or b:CreateTexture( bName .. "_Empower", "OVERLAY" )
- b.Empowerment:SetAtlas( "bags-glow-artifact" )
- b.Empowerment:SetVertexColor( 1, 1, 1, 1 )
- b.Empowerment:ClearAllPoints()
- b.Empowerment:SetPoint( "TOPLEFT", b, "TOPLEFT", -1, 1 )
- b.Empowerment:SetPoint( "BOTTOMRIGHT", b, "BOTTOMRIGHT", 1, -1 )
- b.Empowerment:Hide()
- -- Overlay (for Pause)
- b.Overlay = b.Overlay or b:CreateTexture( nil, "OVERLAY" )
- b.Overlay:SetAllPoints( b )
- b.Overlay:SetAtlas( "creditsscreen-assets-buttons-pause" )
- b.Overlay:SetVertexColor( 1, 1, 1, 1 )
- -- b.Overlay:SetTexCoord( unpack( b.texCoords ) )
- b.Overlay:Hide()
- elseif id == 2 then
- -- Anchoring for the remainder.
- local queueAnchor = conf.queue.anchor or "RIGHT"
- local qOffsetX = ( conf.queue.offsetX or 5 )
- local qOffsetY = ( conf.queue.offsetY or 0 )
- b:ClearAllPoints()
- if queueAnchor:sub( 1, 5 ) == "RIGHT" then
- local dir, align = "RIGHT", queueAnchor:sub(6)
- b:SetPoint( align .. getInverseDirection(dir), "Hekili_" .. dispID .. "_B1", align .. dir, ( borderOffset + qOffsetX ) * scale, qOffsetY * scale )
- elseif queueAnchor:sub( 1, 4 ) == "LEFT" then
- local dir, align = "LEFT", queueAnchor:sub(5)
- b:SetPoint( align .. getInverseDirection(dir), "Hekili_" .. dispID .. "_B1", align .. dir, -1 * ( borderOffset + qOffsetX ) * scale, qOffsetY * scale )
- elseif queueAnchor:sub( 1, 3) == "TOP" then
- local dir, align = "TOP", queueAnchor:sub(4)
- b:SetPoint( getInverseDirection(dir) .. align, "Hekili_" .. dispID .. "_B1", dir .. align, 0, ( borderOffset + qOffsetY ) * scale )
- else -- BOTTOM
- local dir, align = "BOTTOM", queueAnchor:sub(7)
- b:SetPoint( getInverseDirection(dir) .. align, "Hekili_" .. dispID .. "_B1", dir .. align, 0, -1 * ( borderOffset + qOffsetY ) * scale )
- end
- else
- local queueDirection = conf.queue.direction or "RIGHT"
- local btnSpacing = borderOffset + ( conf.queue.spacing or 5 )
- b:ClearAllPoints()
- if queueDirection == "RIGHT" then
- b:SetPoint( getInverseDirection(queueDirection), "Hekili_" .. dispID .. "_B" .. id - 1, queueDirection, btnSpacing * scale, 0 )
- elseif queueDirection == "LEFT" then
- b:SetPoint( getInverseDirection(queueDirection), "Hekili_" .. dispID .. "_B" .. id - 1, queueDirection, -1 * btnSpacing * scale, 0 )
- elseif queueDirection == "TOP" then
- b:SetPoint( getInverseDirection(queueDirection), "Hekili_" .. dispID .. "_B" .. id - 1, queueDirection, 0, btnSpacing * scale )
- else -- BOTTOM
- b:SetPoint( getInverseDirection(queueDirection), "Hekili_" .. dispID .. "_B" .. id - 1, queueDirection, 0, -1 * btnSpacing * scale )
- end
- end
- -- Caption Text.
- b.EmpowerLevel = b.EmpowerLevel or b:CreateFontString( bName .. "_EmpowerLevel", "OVERLAY" )
- local empowerFont = conf.empowerment.font or conf.font
- b.EmpowerLevel:SetFont( LSM:Fetch("font", empowerFont), conf.empowerment.fontSize or 12, conf.empowerment.fontStyle or "OUTLINE" )
- local empAnchor = conf.empowerment.anchor or "CENTER"
- b.EmpowerLevel:ClearAllPoints()
- b.EmpowerLevel:SetPoint( empAnchor, b, empAnchor, conf.empowerment.x or 0, conf.empowerment.y or 0 )
- -- b.EmpowerLevel:SetHeight( b:GetHeight() * 0.6 )
- b.EmpowerLevel:SetJustifyV( empAnchor:match("RIGHT") and "RIGHT" or ( empAnchor:match( "LEFT" ) and "LEFT" or "MIDDLE" ) )
- b.EmpowerLevel:SetJustifyH( conf.empowerment.align or "CENTER" )
- b.EmpowerLevel:SetTextColor( unpack( conf.empowerment.color ) )
- b.EmpowerLevel:SetWordWrap( false )
- local empText = b.EmpowerLevel:GetText()
- b.EmpowerLevel:SetText( nil )
- b.EmpowerLevel:SetText( empText )
- -- Mover Stuff.
- b:SetScript( "OnMouseDown", Button_OnMouseDown )
- b:SetScript( "OnMouseUp", Button_OnMouseUp )
- b:SetScript( "OnEnter", function( self )
- local H = Hekili
- --[[ if H.Config then
- Tooltip:SetOwner( self, "ANCHOR_TOPRIGHT" )
- Tooltip:SetBackdropColor( 0, 0, 0, 0.8 )
- Tooltip:SetText( "Hekili: " .. dispID )
- Tooltip:AddLine( "Left-click and hold to move.", 1, 1, 1 )
- Tooltip:Show()
- self:SetMovable( true )
- else ]]
- if ( H.Pause and d.HasRecommendations and b.Recommendation ) then
- H:ShowDiagnosticTooltip( b.Recommendation )
- end
- end )
- b:SetScript( "OnLeave", function(self)
- HekiliTooltip:Hide()
- end )
- Hekili:ProfileFrame( bName, b )
- b:EnableMouse( false )
- b:SetMovable( false )
- return b
- end
- end
- -- Builds and maintains the visible UI elements.
- -- Buttons (as frames) are never deleted, but should get reused effectively.
- local builtIns = {
- "Primary", "AOE", "Cooldowns", "Interrupts", "Defensives"
- }
- function Hekili:BuildUI()
- if not Masque then
- Masque = LibStub( "Masque", true )
- if Masque then
- Masque:Register( addon, MasqueUpdate, self )
- MasqueGroup = Masque:Group( addon )
- end
- end
- local LSM = LibStub( "LibSharedMedia-3.0" )
- ns.UI.Keyhandler = ns.UI.Keyhandler or CreateFrame( "Button", "Hekili_Keyhandler", UIParent )
- ns.UI.Keyhandler:RegisterForClicks( "AnyDown" )
- ns.UI.Keyhandler:SetScript( "OnClick", function( self, button, down )
- Hekili:FireToggle( button )
- end )
- Hekili:ProfileFrame( "KeyhandlerFrame", ns.UI.Keyhandler )
- local scaleFactor = self:GetScale()
- local mouseInteract = self.Pause
- -- Notification Panel
- local notif = self.DB.profile.notifications
- local f = ns.UI.Notification or CreateFrame( "Frame", "HekiliNotification", UIParent )
- Hekili:ProfileFrame( "HekiliNotification", f )
- f:SetSize( notif.width * scaleFactor, notif.height * scaleFactor )
- f:SetClampedToScreen( true )
- f:ClearAllPoints()
- f:SetPoint("CENTER", nil, "CENTER", notif.x, notif.y )
- f.Text = f.Text or f:CreateFontString( "HekiliNotificationText", "OVERLAY" )
- f.Text:SetAllPoints( f )
- f.Text:SetFont( LSM:Fetch( "font", notif.font ), notif.fontSize * scaleFactor, notif.fontStyle )
- f.Text:SetJustifyV("MIDDLE")
- f.Text:SetJustifyH("CENTER")
- f.Text:SetTextColor(1, 1, 1, 1)
- if not notif.enabled then f:Hide()
- else f.Text:SetText(nil); f:Show() end
- ns.UI.Notification = f
- -- End Notification Panel
- -- Displays
- for disp in pairs( self.DB.profile.displays ) do
- self:CreateDisplay( disp )
- end
- --if Hekili.Config then ns.StartConfiguration() end
- if MasqueGroup then
- MasqueGroup:ReSkin()
- end
- -- Check for a display that has been removed.
- for display, buttons in ipairs(ns.UI.Buttons) do
- if not Hekili.DB.profile.displays[display] then
- for i, _ in ipairs(buttons) do
- buttons[i]:Hide()
- end
- end
- end
- if Hekili.Config then
- ns.StartConfiguration(true)
- end
- end
- local T = ns.lib.Format.Tokens
- local SyntaxColors = {}
- function ns.primeTooltipColors()
- T = ns.lib.Format.Tokens
- --- Assigns a color to multiple tokens at once.
- local function Color(Code, ...)
- for Index = 1, select("#", ...) do
- SyntaxColors[select(Index, ...)] = Code
- end
- end
- Color( "|cffB266FF", T.KEYWORD ) -- Reserved Words
- Color( "|cffffffff", T.LEFTCURLY, T.RIGHTCURLY, T.LEFTBRACKET, T.RIGHTBRACKET, T.LEFTPAREN, T.RIGHTPAREN )
- Color( "|cffFF66FF", T.UNKNOWN,
- T.ADD,
- T.SUBTRACT,
- T.MULTIPLY,
- T.DIVIDE,
- T.POWER,
- T.MODULUS,
- T.CONCAT,
- T.VARARG,
- T.ASSIGNMENT,
- T.PERIOD,
- T.COMMA,
- T.SEMICOLON,
- T.COLON,
- T.SIZE,
- T.EQUALITY,
- T.NOTEQUAL,
- T.LT,
- T.LTE,
- T.GT,
- T.GTE )
- Color( "|cFFB2FF66", multiUnpack(ns.keys, ns.attr) )
- Color( "|cffFFFF00", T.NUMBER )
- Color( "|cff888888", T.STRING, T.STRING_LONG )
- Color( "|cff55cc55", T.COMMENT_SHORT, T.COMMENT_LONG )
- Color( "|cff55ddcc", -- Minimal standard Lua functions
- "assert",
- "error",
- "ipairs",
- "next",
- "pairs",
- "pcall",
- "print",
- "select",
- "tonumber",
- "tostring",
- "type",
- "unpack",
- -- Libraries
- "bit",
- "coroutine",
- "math",
- "string",
- "table"
- )
- Color( "|cffddaaff", -- Some of WoW's aliases for standard Lua functions
- -- math
- "abs",
- "ceil",
- "floor",
- "max",
- "min",
- -- string
- "format",
- "gsub",
- "strbyte",
- "strchar",
- "strconcat",
- "strfind",
- "strjoin",
- "strlower",
- "strmatch",
- "strrep",
- "strrev",
- "strsplit",
- "strsub",
- "strtrim",
- "strupper",
- "tostringall",
- -- table
- "sort",
- "tinsert",
- "tremove",
- "wipe" )
- end
- local SpaceLeft = {"(%()"}
- local SpaceRight = {"(%))"}
- local DoubleSpace = {"(!=)", "(~=)", "(>=*)", "(<=*)", "(&)", "(||)", "(+)", "(*)", "(-)", "(/)"}
- local function Format(Code)
- for Index = 1, #SpaceLeft do
- Code = Code:gsub("%s-" .. SpaceLeft[Index] .. "%s-", " %1")
- end
- for Index = 1, #SpaceRight do
- Code = Code:gsub("%s-" .. SpaceRight[Index] .. "%s-", "%1 ")
- end
- for Index = 1, #DoubleSpace do
- Code = Code:gsub("%s-" .. DoubleSpace[Index] .. "%s-", " %1 ")
- end
- Code = Code:gsub("([^<>~!])(=+)", "%1 %2 ")
- Code = Code:gsub("%s+", " "):trim()
- return Code
- end
- local key_cache = setmetatable( {}, {
- __index = function( t, k )
- t[k] = k:gsub( "(%S+)%[(%d+)]", "%1.%2" )
- return t[k]
- end
- })
- function Hekili:ShowDiagnosticTooltip( q )
- if not q.actionName or not class.abilities[ q.actionName ].name then return end
- local tt = HekiliTooltip
- local fmt = ns.lib.Format
- tt:SetOwner( UIParent, "ANCHOR_CURSOR" )
- tt:SetText( class.abilities[ q.actionName ].name )
- tt:AddDoubleLine( q.listName .. " #" .. q.action, "+" .. ns.formatValue(round(q.time or 0, 2)), 1, 1, 1, 1, 1, 1 )
- if q.resources and q.resources[q.resource_type] then
- tt:AddDoubleLine(q.resource_type, ns.formatValue(q.resources[q.resource_type]), 1, 1, 1, 1, 1, 1)
- end
- if q.HookHeader or (q.HookScript and q.HookScript ~= "") then
- if q.HookHeader then
- tt:AddLine(" ")
- tt:AddLine(q.HookHeader)
- else
- tt:AddLine(" ")
- tt:AddLine("Hook Criteria")
- end
- if q.HookScript and q.HookScript ~= "" then
- local Text = Format(q.HookScript)
- tt:AddLine(fmt.FormatCode(Text, 0, SyntaxColors), 1, 1, 1, 1)
- end
- if q.HookElements then
- local applied = false
- for k, v in orderedPairs(q.HookElements) do
- if not applied then
- tt:AddLine(" ")
- tt:AddLine("Values")
- applied = true
- end
- if not key_cache[k]:find( "safebool" ) and not key_cache[k]:find( "safenum" ) and not key_cache[k]:find( "ceil" ) and not key_cache[k]:find( "floor" ) then
- tt:AddDoubleLine( key_cache[ k ], ns.formatValue(v), 1, 1, 1, 1, 1, 1)
- end
- end
- end
- end
- if q.ReadyScript and q.ReadyScript ~= "" then
- tt:AddLine(" ")
- tt:AddLine("Time Script")
- tt:AddLine(fmt.FormatCode(q.ReadyScript, 0, SyntaxColors), 1, 1, 1, 1)
- if q.ReadyElements then
- tt:AddLine("Values")
- for k, v in orderedPairs(q.ReadyElements) do
- if not key_cache[k]:find( "safebool" ) and not key_cache[k]:find( "safenum" ) and not key_cache[k]:find( "ceil" ) and not key_cache[k]:find( "floor" ) then
- tt:AddDoubleLine( key_cache[ k ], ns.formatValue(v), 1, 1, 1, 1, 1, 1)
- end
- end
- end
- end
- if q.ActScript and q.ActScript ~= "" then
- tt:AddLine(" ")
- tt:AddLine("Action Criteria")
- tt:AddLine(fmt.FormatCode(q.ActScript, 0, SyntaxColors), 1, 1, 1, 1)
- if q.ActElements then
- tt:AddLine(" ")
- tt:AddLine("Values")
- for k, v in orderedPairs(q.ActElements) do
- if not key_cache[k]:find( "safebool" ) and not key_cache[k]:find( "safenum" ) and not key_cache[k]:find( "ceil" ) and not key_cache[k]:find( "floor" ) then
- tt:AddDoubleLine( key_cache[ k ], ns.formatValue(v), 1, 1, 1, 1, 1, 1)
- end
- end
- end
- end
- if q.pack and q.listName and q.action then
- local entry = rawget( self.DB.profile.packs, q.pack )
- entry = entry and entry.lists[ q.listName ]
- entry = entry and entry[ q.action ]
- if entry and entry.description and entry.description:len() > 0 then
- tt:AddLine( " " )
- tt:AddLine( entry.description, 0, 0.7, 1, true )
- end
- end
- tt:SetMinimumWidth( 400 )
- tt:Show()
- end
- function Hekili:SaveCoordinates()
- for i in pairs(Hekili.DB.profile.displays) do
- local display = ns.UI.Displays[i]
- if display then
- local rel, x, y = select( 3, display:GetPoint() )
- self.DB.profile.displays[i].rel = "CENTER"
- self.DB.profile.displays[i].x = x
- self.DB.profile.displays[i].y = y
- end
- end
- self.DB.profile.notifications.x, self.DB.profile.notifications.y = select( 4, HekiliNotification:GetPoint() )
- end
Advertisement
Add Comment
Please, Sign In to add comment