1821 lines
54 KiB
Lua
1821 lines
54 KiB
Lua
local ElvUI = select(2, ...)
|
|
ElvUI[2] = ElvUI[1].Libs.ACL:GetLocale('ElvUI', ElvUI[1]:GetLocale()) -- Locale doesn't exist yet, make it exist.
|
|
local E, L, V, P, G = unpack(ElvUI); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
|
|
|
local _G = _G
|
|
local tonumber, pairs, ipairs, error, unpack, select, tostring = tonumber, pairs, ipairs, error, unpack, select, tostring
|
|
local strsplit, strjoin, wipe, sort, tinsert, tremove, tContains = strsplit, strjoin, wipe, sort, tinsert, tremove, tContains
|
|
local format, find, strrep, strlen, sub, gsub = format, strfind, strrep, strlen, strsub, gsub
|
|
local assert, type, pcall, xpcall, next, print = assert, type, pcall, xpcall, next, print
|
|
local rawget, rawset, setmetatable = rawget, rawset, setmetatable
|
|
|
|
local CreateFrame = CreateFrame
|
|
local GetCVar = GetCVar
|
|
local GetSpellInfo = GetSpellInfo
|
|
local GetCVarBool = GetCVarBool
|
|
local GetNumGroupMembers = GetNumGroupMembers
|
|
local GetSpecialization = GetSpecialization
|
|
local hooksecurefunc = hooksecurefunc
|
|
local InCombatLockdown = InCombatLockdown
|
|
local GetAddOnEnableState = GetAddOnEnableState
|
|
local UnitFactionGroup = UnitFactionGroup
|
|
local DisableAddOn = DisableAddOn
|
|
local IsInGroup = IsInGroup
|
|
local IsInGuild = IsInGuild
|
|
local IsInRaid = IsInRaid
|
|
local SetCVar = SetCVar
|
|
local ReloadUI = ReloadUI
|
|
local UnitGUID = UnitGUID
|
|
|
|
local ERR_NOT_IN_COMBAT = ERR_NOT_IN_COMBAT
|
|
local LE_PARTY_CATEGORY_HOME = LE_PARTY_CATEGORY_HOME
|
|
local LE_PARTY_CATEGORY_INSTANCE = LE_PARTY_CATEGORY_INSTANCE
|
|
local C_ChatInfo_SendAddonMessage = C_ChatInfo.SendAddonMessage
|
|
-- GLOBALS: ElvUIPlayerBuffs, ElvUIPlayerDebuffs
|
|
|
|
--Modules
|
|
local ActionBars = E:GetModule('ActionBars')
|
|
local AFK = E:GetModule('AFK')
|
|
local Auras = E:GetModule('Auras')
|
|
local Bags = E:GetModule('Bags')
|
|
local Blizzard = E:GetModule('Blizzard')
|
|
local Chat = E:GetModule('Chat')
|
|
local DataBars = E:GetModule('DataBars')
|
|
local DataTexts = E:GetModule('DataTexts')
|
|
local Layout = E:GetModule('Layout')
|
|
local Minimap = E:GetModule('Minimap')
|
|
local NamePlates = E:GetModule('NamePlates')
|
|
local Tooltip = E:GetModule('Tooltip')
|
|
local Totems = E:GetModule('Totems')
|
|
local UnitFrames = E:GetModule('UnitFrames')
|
|
local LSM = E.Libs.LSM
|
|
|
|
--Constants
|
|
E.noop = function() end
|
|
E.title = format('|cff1784d1%s |r', 'ElvUI')
|
|
E.version = tonumber(GetAddOnMetadata('ElvUI', 'Version'))
|
|
E.myfaction, E.myLocalizedFaction = UnitFactionGroup('player')
|
|
E.mylevel = UnitLevel('player')
|
|
E.myLocalizedClass, E.myclass, E.myClassID = UnitClass('player')
|
|
E.myLocalizedRace, E.myrace = UnitRace('player')
|
|
E.myname = UnitName('player')
|
|
E.myrealm = GetRealmName()
|
|
E.mynameRealm = format('%s - %s', E.myname, E.myrealm) -- contains spaces/dashes in realm (for profile keys)
|
|
E.myspec = GetSpecialization()
|
|
E.wowpatch, E.wowbuild = GetBuildInfo()
|
|
E.wowbuild = tonumber(E.wowbuild)
|
|
E.isMacClient = IsMacClient()
|
|
E.IsRetail = WOW_PROJECT_ID == WOW_PROJECT_MAINLINE
|
|
E.screenwidth, E.screenheight = GetPhysicalScreenSize()
|
|
E.resolution = format('%dx%d', E.screenwidth, E.screenheight)
|
|
E.NewSign = [[|TInterface\OptionsFrame\UI-OptionsFrame-NewFeatureIcon:14:14|t]] -- not used by ElvUI yet, but plugins like BenikUI and MerathilisUI use it.
|
|
E.TexturePath = [[Interface\AddOns\ElvUI\Media\Textures\]] -- for plugins?
|
|
E.InfoColor = '|cff1784d1'
|
|
E.UserList = {}
|
|
|
|
-- oUF Defines
|
|
E.oUF.Tags.Vars.E = E
|
|
E.oUF.Tags.Vars.L = L
|
|
|
|
--Tables
|
|
E.media = {}
|
|
E.frames = {}
|
|
E.unitFrameElements = {}
|
|
E.statusBars = {}
|
|
E.texts = {}
|
|
E.snapBars = {}
|
|
E.RegisteredModules = {}
|
|
E.RegisteredInitialModules = {}
|
|
E.valueColorUpdateFuncs = {}
|
|
E.TexCoords = {0, 1, 0, 1}
|
|
E.FrameLocks = {}
|
|
E.VehicleLocks = {}
|
|
E.CreditsList = {}
|
|
E.LockedCVars = {}
|
|
E.IgnoredCVars = {}
|
|
E.UpdatedCVars = {}
|
|
E.InversePoints = {
|
|
TOP = 'BOTTOM',
|
|
BOTTOM = 'TOP',
|
|
TOPLEFT = 'BOTTOMLEFT',
|
|
TOPRIGHT = 'BOTTOMRIGHT',
|
|
LEFT = 'RIGHT',
|
|
RIGHT = 'LEFT',
|
|
BOTTOMLEFT = 'TOPLEFT',
|
|
BOTTOMRIGHT = 'TOPRIGHT',
|
|
CENTER = 'CENTER'
|
|
}
|
|
|
|
E.ClassRole = {
|
|
HUNTER = 'Melee',
|
|
ROGUE = 'Melee',
|
|
MAGE = 'Caster',
|
|
PRIEST = 'Caster',
|
|
WARLOCK = 'Caster',
|
|
DEMONHUNTER = {'Melee', 'Tank'},
|
|
WARRIOR = {'Melee', 'Melee', 'Tank'},
|
|
DEATHKNIGHT = {'Tank', 'Melee', 'Melee'},
|
|
MONK = {'Tank', 'Caster', 'Melee'},
|
|
PALADIN = {'Caster', 'Tank', 'Melee'},
|
|
SHAMAN = {'Caster', 'Melee', 'Caster'},
|
|
DRUID = {'Caster', 'Melee', 'Tank', 'Caster'},
|
|
}
|
|
|
|
E.DispelClasses = {
|
|
PRIEST = { Magic = true, Disease = true },
|
|
SHAMAN = { Magic = false, Curse = true },
|
|
PALADIN = { Poison = true, Magic = false, Disease = true },
|
|
DRUID = { Magic = false, Curse = true, Poison = true, Disease = false },
|
|
MONK = { Magic = false, Disease = true, Poison = true },
|
|
MAGE = { Curse = true }
|
|
}
|
|
|
|
E.BadDispels = {
|
|
[34914] = 'Vampiric Touch', -- horrifies
|
|
[233490] = 'Unstable Affliction' -- silences
|
|
}
|
|
|
|
--Workaround for people wanting to use white and it reverting to their class color.
|
|
E.PriestColors = { r = 0.99, g = 0.99, b = 0.99, colorStr = 'fffcfcfc' }
|
|
|
|
-- Socket Type info from 8.2
|
|
E.GemTypeInfo = {
|
|
Yellow = { r = 0.97, g = 0.82, b = 0.29 },
|
|
Red = { r = 1.00, g = 0.47, b = 0.47 },
|
|
Blue = { r = 0.47, g = 0.67, b = 1.00 },
|
|
Hydraulic = { r = 1.00, g = 1.00, b = 1.00 },
|
|
Cogwheel = { r = 1.00, g = 1.00, b = 1.00 },
|
|
Meta = { r = 1.00, g = 1.00, b = 1.00 },
|
|
Prismatic = { r = 1.00, g = 1.00, b = 1.00 },
|
|
PunchcardRed = { r = 1.00, g = 0.47, b = 0.47 },
|
|
PunchcardYellow = { r = 0.97, g = 0.82, b = 0.29 },
|
|
PunchcardBlue = { r = 0.47, g = 0.67, b = 1.00 },
|
|
}
|
|
|
|
--This frame everything in ElvUI should be anchored to for Eyefinity support.
|
|
E.UIParent = CreateFrame('Frame', 'ElvUIParent', _G.UIParent)
|
|
E.UIParent:SetFrameLevel(_G.UIParent:GetFrameLevel())
|
|
E.UIParent:SetSize(_G.UIParent:GetSize())
|
|
E.UIParent:SetPoint('BOTTOM')
|
|
E.UIParent.origHeight = E.UIParent:GetHeight()
|
|
E.snapBars[#E.snapBars + 1] = E.UIParent
|
|
|
|
E.HiddenFrame = CreateFrame('Frame')
|
|
E.HiddenFrame:Hide()
|
|
|
|
do -- used in optionsUI
|
|
E.DEFAULT_FILTER = {}
|
|
for filter, tbl in pairs(G.unitframe.aurafilters) do
|
|
E.DEFAULT_FILTER[filter] = tbl.type
|
|
end
|
|
end
|
|
|
|
do
|
|
local a1,a2 = '','[%s%-]'
|
|
function E:ShortenRealm(realm)
|
|
return gsub(realm, a2, a1)
|
|
end
|
|
|
|
local a3 = format('%%-%s', E:ShortenRealm(E.myrealm))
|
|
function E:StripMyRealm(name)
|
|
return gsub(name, a3, a1)
|
|
end
|
|
end
|
|
|
|
function E:Print(...)
|
|
(E.db and _G[E.db.general.messageRedirect] or _G.DEFAULT_CHAT_FRAME):AddMessage(strjoin('', E.media.hexvaluecolor or '|cff00b3ff', 'ElvUI:|r ', ...)) -- I put DEFAULT_CHAT_FRAME as a fail safe.
|
|
end
|
|
|
|
function E:GrabColorPickerValues(r, g, b)
|
|
-- we must block the execution path to `ColorCallback` in `AceGUIWidget-ColorPicker-ElvUI`
|
|
-- in order to prevent an infinite loop from `OnValueChanged` when passing into `E.UpdateMedia` which eventually leads here again.
|
|
_G.ColorPickerFrame.noColorCallback = true
|
|
|
|
-- grab old values
|
|
local oldR, oldG, oldB = _G.ColorPickerFrame:GetColorRGB()
|
|
|
|
-- set and define the new values
|
|
_G.ColorPickerFrame:SetColorRGB(r, g, b)
|
|
r, g, b = _G.ColorPickerFrame:GetColorRGB()
|
|
|
|
-- swap back to the old values
|
|
if oldR then _G.ColorPickerFrame:SetColorRGB(oldR, oldG, oldB) end
|
|
|
|
-- free it up..
|
|
_G.ColorPickerFrame.noColorCallback = nil
|
|
|
|
return r, g, b
|
|
end
|
|
|
|
--Basically check if another class border is being used on a class that doesn't match. And then return true if a match is found.
|
|
function E:CheckClassColor(r, g, b)
|
|
r, g, b = E:GrabColorPickerValues(r, g, b)
|
|
|
|
for class in pairs(_G.RAID_CLASS_COLORS) do
|
|
if class ~= E.myclass then
|
|
local colorTable = E:ClassColor(class, true)
|
|
local red, green, blue = E:GrabColorPickerValues(colorTable.r, colorTable.g, colorTable.b)
|
|
if red == r and green == g and blue == b then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function E:SetColorTable(t, data)
|
|
if not data.r or not data.g or not data.b then
|
|
error('SetColorTable: Could not unpack color values.')
|
|
end
|
|
|
|
if t and (type(t) == 'table') then
|
|
t[1], t[2], t[3], t[4] = E:UpdateColorTable(data)
|
|
else
|
|
t = E:GetColorTable(data)
|
|
end
|
|
|
|
return t
|
|
end
|
|
|
|
function E:UpdateColorTable(data)
|
|
if not data.r or not data.g or not data.b then
|
|
error('UpdateColorTable: Could not unpack color values.')
|
|
end
|
|
|
|
if data.r > 1 or data.r < 0 then data.r = 1 end
|
|
if data.g > 1 or data.g < 0 then data.g = 1 end
|
|
if data.b > 1 or data.b < 0 then data.b = 1 end
|
|
if data.a and (data.a > 1 or data.a < 0) then data.a = 1 end
|
|
|
|
if data.a then
|
|
return data.r, data.g, data.b, data.a
|
|
else
|
|
return data.r, data.g, data.b
|
|
end
|
|
end
|
|
|
|
function E:GetColorTable(data)
|
|
if not data.r or not data.g or not data.b then
|
|
error('GetColorTable: Could not unpack color values.')
|
|
end
|
|
|
|
if data.r > 1 or data.r < 0 then data.r = 1 end
|
|
if data.g > 1 or data.g < 0 then data.g = 1 end
|
|
if data.b > 1 or data.b < 0 then data.b = 1 end
|
|
if data.a and (data.a > 1 or data.a < 0) then data.a = 1 end
|
|
|
|
if data.a then
|
|
return {data.r, data.g, data.b, data.a}
|
|
else
|
|
return {data.r, data.g, data.b}
|
|
end
|
|
end
|
|
|
|
function E:UpdateMedia()
|
|
if not E.db.general or not E.private.general then return end --Prevent rare nil value errors
|
|
|
|
--Fonts
|
|
E.media.normFont = LSM:Fetch('font', E.db.general.font)
|
|
E.media.combatFont = LSM:Fetch('font', E.private.general.dmgfont)
|
|
|
|
--Textures
|
|
E.media.blankTex = LSM:Fetch('background', 'ElvUI Blank')
|
|
E.media.normTex = LSM:Fetch('statusbar', E.private.general.normTex)
|
|
E.media.glossTex = LSM:Fetch('statusbar', E.private.general.glossTex)
|
|
|
|
--Border Color
|
|
local border = E.db.general.bordercolor
|
|
if E:CheckClassColor(border.r, border.g, border.b) then
|
|
local classColor = E:ClassColor(E.myclass, true)
|
|
E.db.general.bordercolor.r = classColor.r
|
|
E.db.general.bordercolor.g = classColor.g
|
|
E.db.general.bordercolor.b = classColor.b
|
|
end
|
|
|
|
E.media.bordercolor = {border.r, border.g, border.b}
|
|
|
|
--UnitFrame Border Color
|
|
border = E.db.unitframe.colors.borderColor
|
|
if E:CheckClassColor(border.r, border.g, border.b) then
|
|
local classColor = E:ClassColor(E.myclass, true)
|
|
E.db.unitframe.colors.borderColor.r = classColor.r
|
|
E.db.unitframe.colors.borderColor.g = classColor.g
|
|
E.db.unitframe.colors.borderColor.b = classColor.b
|
|
end
|
|
E.media.unitframeBorderColor = {border.r, border.g, border.b}
|
|
|
|
--Backdrop Color
|
|
E.media.backdropcolor = E:SetColorTable(E.media.backdropcolor, E.db.general.backdropcolor)
|
|
|
|
--Backdrop Fade Color
|
|
E.media.backdropfadecolor = E:SetColorTable(E.media.backdropfadecolor, E.db.general.backdropfadecolor)
|
|
|
|
--Value Color
|
|
local value = E.db.general.valuecolor
|
|
if E:CheckClassColor(value.r, value.g, value.b) then
|
|
value = E:ClassColor(E.myclass, true)
|
|
E.db.general.valuecolor.r = value.r
|
|
E.db.general.valuecolor.g = value.g
|
|
E.db.general.valuecolor.b = value.b
|
|
end
|
|
|
|
--Chat Tab Selector Color
|
|
local selectorColor = E.db.chat.tabSelectorColor
|
|
if E:CheckClassColor(selectorColor.r, selectorColor.g, selectorColor.b) then
|
|
selectorColor = E:ClassColor(E.myclass, true)
|
|
E.db.chat.tabSelectorColor.r = selectorColor.r
|
|
E.db.chat.tabSelectorColor.g = selectorColor.g
|
|
E.db.chat.tabSelectorColor.b = selectorColor.b
|
|
end
|
|
|
|
E.media.hexvaluecolor = E:RGBToHex(value.r, value.g, value.b)
|
|
E.media.rgbvaluecolor = {value.r, value.g, value.b}
|
|
|
|
-- Chat Panel Background Texture
|
|
local LeftChatPanel, RightChatPanel = _G.LeftChatPanel, _G.RightChatPanel
|
|
if LeftChatPanel and LeftChatPanel.tex and RightChatPanel and RightChatPanel.tex then
|
|
LeftChatPanel.tex:SetTexture(E.db.chat.panelBackdropNameLeft)
|
|
RightChatPanel.tex:SetTexture(E.db.chat.panelBackdropNameRight)
|
|
|
|
local a = E.db.general.backdropfadecolor.a or 0.5
|
|
LeftChatPanel.tex:SetAlpha(a)
|
|
RightChatPanel.tex:SetAlpha(a)
|
|
end
|
|
|
|
E:ValueFuncCall()
|
|
E:UpdateBlizzardFonts()
|
|
end
|
|
|
|
do --Update font/texture paths when they are registered by the addon providing them
|
|
--This helps fix most of the issues with fonts or textures reverting to default because the addon providing them is loading after ElvUI.
|
|
--We use a wrapper to avoid errors in :UpdateMedia because 'self' is passed to the function with a value other than ElvUI.
|
|
local function LSMCallback() E:UpdateMedia() end
|
|
LSM.RegisterCallback(E, 'LibSharedMedia_Registered', LSMCallback)
|
|
end
|
|
|
|
do
|
|
local function CVAR_UPDATE(name, value)
|
|
if not E.IgnoredCVars[name] then
|
|
local locked = E.LockedCVars[name]
|
|
if locked ~= nil and locked ~= value then
|
|
if InCombatLockdown() then
|
|
E.CVarUpdate = true
|
|
return
|
|
end
|
|
|
|
SetCVar(name, locked)
|
|
end
|
|
|
|
local func = E.UpdatedCVars[name]
|
|
if func then func(value) end
|
|
end
|
|
end
|
|
|
|
hooksecurefunc('SetCVar', CVAR_UPDATE)
|
|
function E:LockCVar(name, value)
|
|
if GetCVar(name) ~= value then
|
|
SetCVar(name, value)
|
|
end
|
|
|
|
E.LockedCVars[name] = value
|
|
end
|
|
|
|
function E:UpdatedCVar(name, func)
|
|
E.UpdatedCVars[name] = func
|
|
end
|
|
|
|
function E:IgnoreCVar(name, ignore)
|
|
E.IgnoredCVars[name] = (not not ignore) -- cast to bool, just in case
|
|
end
|
|
end
|
|
|
|
function E:ValueFuncCall()
|
|
local hex, r, g, b = E.media.hexvaluecolor, unpack(E.media.rgbvaluecolor)
|
|
for func in pairs(E.valueColorUpdateFuncs) do func(hex, r, g, b) end
|
|
end
|
|
|
|
function E:UpdateFrameTemplates()
|
|
for frame in pairs(E.frames) do
|
|
if frame and frame.template and not frame:IsForbidden() then
|
|
if not (frame.ignoreUpdates or frame.ignoreFrameTemplates) then
|
|
frame:SetTemplate(frame.template, frame.glossTex, nil, frame.forcePixelMode)
|
|
end
|
|
else
|
|
E.frames[frame] = nil
|
|
end
|
|
end
|
|
|
|
for frame in pairs(E.unitFrameElements) do
|
|
if frame and frame.template and not frame:IsForbidden() then
|
|
if not (frame.ignoreUpdates or frame.ignoreFrameTemplates) then
|
|
frame:SetTemplate(frame.template, frame.glossTex, nil, frame.forcePixelMode, frame.isUnitFrameElement)
|
|
end
|
|
else
|
|
E.unitFrameElements[frame] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
function E:UpdateBorderColors()
|
|
local r, g, b = unpack(E.media.bordercolor)
|
|
for frame in pairs(E.frames) do
|
|
if frame and frame.template and not frame:IsForbidden() then
|
|
if not (frame.ignoreUpdates or frame.forcedBorderColors) and (frame.template == 'Default' or frame.template == 'Transparent') then
|
|
frame:SetBackdropBorderColor(r, g, b)
|
|
end
|
|
else
|
|
E.frames[frame] = nil
|
|
end
|
|
end
|
|
|
|
local r2, g2, b2 = unpack(E.media.unitframeBorderColor)
|
|
for frame in pairs(E.unitFrameElements) do
|
|
if frame and frame.template and not frame:IsForbidden() then
|
|
if not (frame.ignoreUpdates or frame.forcedBorderColors) and (frame.template == 'Default' or frame.template == 'Transparent') then
|
|
frame:SetBackdropBorderColor(r2, g2, b2)
|
|
end
|
|
else
|
|
E.unitFrameElements[frame] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
function E:UpdateBackdropColors()
|
|
local r, g, b = unpack(E.media.backdropcolor)
|
|
local r2, g2, b2, a2 = unpack(E.media.backdropfadecolor)
|
|
|
|
for frame in pairs(E.frames) do
|
|
if frame and frame.template and not frame:IsForbidden() then
|
|
if not frame.ignoreUpdates then
|
|
if frame.callbackBackdropColor then
|
|
frame:callbackBackdropColor()
|
|
elseif frame.template == 'Default' then
|
|
frame:SetBackdropColor(r, g, b)
|
|
elseif frame.template == 'Transparent' then
|
|
frame:SetBackdropColor(r2, g2, b2, frame.customBackdropAlpha or a2)
|
|
end
|
|
end
|
|
else
|
|
E.frames[frame] = nil
|
|
end
|
|
end
|
|
|
|
for frame in pairs(E.unitFrameElements) do
|
|
if frame and frame.template and not frame:IsForbidden() then
|
|
if not frame.ignoreUpdates then
|
|
if frame.callbackBackdropColor then
|
|
frame:callbackBackdropColor()
|
|
elseif frame.template == 'Default' then
|
|
frame:SetBackdropColor(r, g, b)
|
|
elseif frame.template == 'Transparent' then
|
|
frame:SetBackdropColor(r2, g2, b2, frame.customBackdropAlpha or a2)
|
|
end
|
|
end
|
|
else
|
|
E.unitFrameElements[frame] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
function E:UpdateFontTemplates()
|
|
for text in pairs(E.texts) do
|
|
if text then
|
|
text:FontTemplate(text.font, text.fontSize, text.fontStyle, true)
|
|
else
|
|
E.texts[text] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
function E:RegisterStatusBar(statusBar)
|
|
E.statusBars[statusBar] = true
|
|
end
|
|
|
|
function E:UnregisterStatusBar(statusBar)
|
|
E.statusBars[statusBar] = nil
|
|
end
|
|
|
|
function E:UpdateStatusBars()
|
|
for statusBar in pairs(E.statusBars) do
|
|
if statusBar and statusBar:IsObjectType('StatusBar') then
|
|
statusBar:SetStatusBarTexture(E.media.normTex)
|
|
elseif statusBar and statusBar:IsObjectType('Texture') then
|
|
statusBar:SetTexture(E.media.normTex)
|
|
end
|
|
end
|
|
end
|
|
|
|
do
|
|
local cancel = function(popup)
|
|
DisableAddOn(popup.addon)
|
|
ReloadUI()
|
|
end
|
|
|
|
function E:IncompatibleAddOn(addon, module, info)
|
|
local popup = E.PopupDialogs.INCOMPATIBLE_ADDON
|
|
popup.button2 = info.name or module
|
|
popup.button1 = addon
|
|
popup.module = module
|
|
popup.addon = addon
|
|
popup.accept = info.accept
|
|
popup.cancel = info.cancel or cancel
|
|
|
|
E:StaticPopup_Show('INCOMPATIBLE_ADDON', popup.button1, popup.button2)
|
|
end
|
|
end
|
|
|
|
function E:IsAddOnEnabled(addon)
|
|
return GetAddOnEnableState(E.myname, addon) == 2
|
|
end
|
|
|
|
function E:IsIncompatible(module, addons)
|
|
for _, addon in ipairs(addons) do
|
|
if E:IsAddOnEnabled(addon) then
|
|
E:IncompatibleAddOn(addon, module, addons.info)
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
do
|
|
local ADDONS = {
|
|
ActionBar = {
|
|
info = {
|
|
enabled = function() return E.private.actionbar.enable end,
|
|
accept = function() E.private.actionbar.enable = false; ReloadUI() end,
|
|
name = 'ElvUI ActionBars'
|
|
},
|
|
'Bartender4',
|
|
'Dominos'
|
|
},
|
|
Chat = {
|
|
info = {
|
|
enabled = function() return E.private.chat.enable end,
|
|
accept = function() E.private.chat.enable = false; ReloadUI() end,
|
|
name = 'ElvUI Chat'
|
|
},
|
|
'Prat-3.0',
|
|
'Chatter',
|
|
'Glass'
|
|
},
|
|
NamePlates = {
|
|
info = {
|
|
enabled = function() return E.private.nameplates.enable end,
|
|
accept = function() E.private.nameplates.enable = false; ReloadUI() end,
|
|
name = 'ElvUI NamePlates'
|
|
},
|
|
'TidyPlates',
|
|
'TidyPlates_ThreatPlates',
|
|
'Healers-Have-To-Die',
|
|
'Kui_Nameplates',
|
|
'Plater',
|
|
'Aloft'
|
|
}
|
|
}
|
|
|
|
E.INCOMPATIBLE_ADDONS = ADDONS -- let addons have the ability to alter this list to trigger our popup if they want
|
|
function E:AddIncompatible(module, addonName)
|
|
if ADDONS[module] then
|
|
tinsert(ADDONS[module], addonName)
|
|
else
|
|
print(module, 'is not in the incompatibility list.')
|
|
end
|
|
end
|
|
|
|
function E:CheckIncompatible()
|
|
if E.global.ignoreIncompatible then return end
|
|
|
|
for module, addons in pairs(ADDONS) do
|
|
if addons[1] and addons.info.enabled() and E:IsIncompatible(module, addons) then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function E:CopyTable(current, default)
|
|
if type(current) ~= 'table' then
|
|
current = {}
|
|
end
|
|
|
|
if type(default) == 'table' then
|
|
for option, value in pairs(default) do
|
|
current[option] = (type(value) == 'table' and E:CopyTable(current[option], value)) or value
|
|
end
|
|
end
|
|
|
|
return current
|
|
end
|
|
|
|
function E:RemoveEmptySubTables(tbl)
|
|
if type(tbl) ~= 'table' then
|
|
E:Print('Bad argument #1 to \'RemoveEmptySubTables\' (table expected)')
|
|
return
|
|
end
|
|
|
|
for k, v in pairs(tbl) do
|
|
if type(v) == 'table' then
|
|
if next(v) == nil then
|
|
tbl[k] = nil
|
|
else
|
|
E:RemoveEmptySubTables(v)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--Compare 2 tables and remove duplicate key/value pairs
|
|
--param cleanTable : table you want cleaned
|
|
--param checkTable : table you want to check against.
|
|
--param generatedKeys : table defined in `Distributor.lua` to allow user generated tables to be exported (customTexts, customCurrencies, etc).
|
|
--return : a copy of cleanTable with duplicate key/value pairs removed
|
|
function E:RemoveTableDuplicates(cleanTable, checkTable, generatedKeys)
|
|
if type(cleanTable) ~= 'table' then
|
|
E:Print('Bad argument #1 to \'RemoveTableDuplicates\' (table expected)')
|
|
return
|
|
end
|
|
if type(checkTable) ~= 'table' then
|
|
E:Print('Bad argument #2 to \'RemoveTableDuplicates\' (table expected)')
|
|
return
|
|
end
|
|
|
|
local rtdCleaned = {}
|
|
local keyed = type(generatedKeys) == 'table'
|
|
for option, value in pairs(cleanTable) do
|
|
local default, genTable, genOption = checkTable[option]
|
|
if keyed then genTable = generatedKeys[option] else genOption = generatedKeys end
|
|
|
|
-- we only want to add settings which are existing in the default table, unless it's allowed by generatedKeys
|
|
if default ~= nil or (genTable or genOption ~= nil) then
|
|
if type(value) == 'table' and type(default) == 'table' then
|
|
if genOption ~= nil then
|
|
rtdCleaned[option] = E:RemoveTableDuplicates(value, default, genOption)
|
|
else
|
|
rtdCleaned[option] = E:RemoveTableDuplicates(value, default, genTable or nil)
|
|
end
|
|
elseif cleanTable[option] ~= default then
|
|
-- add unique data to our clean table
|
|
rtdCleaned[option] = value
|
|
end
|
|
end
|
|
end
|
|
|
|
--Clean out empty sub-tables
|
|
E:RemoveEmptySubTables(rtdCleaned)
|
|
|
|
return rtdCleaned
|
|
end
|
|
|
|
--Compare 2 tables and remove blacklisted key/value pairs
|
|
--param cleanTable : table you want cleaned
|
|
--param blacklistTable : table you want to check against.
|
|
--return : a copy of cleanTable with blacklisted key/value pairs removed
|
|
function E:FilterTableFromBlacklist(cleanTable, blacklistTable)
|
|
if type(cleanTable) ~= 'table' then
|
|
E:Print('Bad argument #1 to \'FilterTableFromBlacklist\' (table expected)')
|
|
return
|
|
end
|
|
if type(blacklistTable) ~= 'table' then
|
|
E:Print('Bad argument #2 to \'FilterTableFromBlacklist\' (table expected)')
|
|
return
|
|
end
|
|
|
|
local tfbCleaned = {}
|
|
for option, value in pairs(cleanTable) do
|
|
if type(value) == 'table' and blacklistTable[option] and type(blacklistTable[option]) == 'table' then
|
|
tfbCleaned[option] = E:FilterTableFromBlacklist(value, blacklistTable[option])
|
|
else
|
|
-- Filter out blacklisted keys
|
|
if blacklistTable[option] ~= true then
|
|
tfbCleaned[option] = value
|
|
end
|
|
end
|
|
end
|
|
|
|
--Clean out empty sub-tables
|
|
E:RemoveEmptySubTables(tfbCleaned)
|
|
|
|
return tfbCleaned
|
|
end
|
|
|
|
local function keySort(a, b)
|
|
local A, B = type(a), type(b)
|
|
|
|
if A == B then
|
|
if A == 'number' or A == 'string' then
|
|
return a < b
|
|
elseif A == 'boolean' then
|
|
return (a and 1 or 0) > (b and 1 or 0)
|
|
end
|
|
end
|
|
|
|
return A < B
|
|
end
|
|
|
|
do --The code in this function is from WeakAuras, credit goes to Mirrored and the WeakAuras Team
|
|
--Code slightly modified by Simpy, sorting from @sighol
|
|
local function recurse(tbl, level, ret)
|
|
local tkeys = {}
|
|
for i in pairs(tbl) do tinsert(tkeys, i) end
|
|
sort(tkeys, keySort)
|
|
|
|
for _, i in ipairs(tkeys) do
|
|
local v = tbl[i]
|
|
|
|
ret = ret..strrep(' ', level)..'['
|
|
if type(i) == 'string' then ret = ret..'"'..i..'"' else ret = ret..i end
|
|
ret = ret..'] = '
|
|
|
|
if type(v) == 'number' then
|
|
ret = ret..v..',\n'
|
|
elseif type(v) == 'string' then
|
|
ret = ret..'"'..v:gsub('\\', '\\\\'):gsub('\n', '\\n'):gsub('"', '\\"'):gsub('\124', '\124\124')..'",\n'
|
|
elseif type(v) == 'boolean' then
|
|
if v then ret = ret..'true,\n' else ret = ret..'false,\n' end
|
|
elseif type(v) == 'table' then
|
|
ret = ret..'{\n'
|
|
ret = recurse(v, level + 1, ret)
|
|
ret = ret..strrep(' ', level)..'},\n'
|
|
else
|
|
ret = ret..'"'..tostring(v)..'",\n'
|
|
end
|
|
end
|
|
|
|
return ret
|
|
end
|
|
|
|
function E:TableToLuaString(inTable)
|
|
if type(inTable) ~= 'table' then
|
|
E:Print('Invalid argument #1 to E:TableToLuaString (table expected)')
|
|
return
|
|
end
|
|
|
|
local ret = '{\n'
|
|
if inTable then ret = recurse(inTable, 1, ret) end
|
|
ret = ret..'}'
|
|
|
|
return ret
|
|
end
|
|
end
|
|
|
|
do --The code in this function is from WeakAuras, credit goes to Mirrored and the WeakAuras Team
|
|
--Code slightly modified by Simpy, sorting from @sighol
|
|
local lineStructureTable, profileFormat = {}, {
|
|
profile = 'E.db',
|
|
private = 'E.private',
|
|
global = 'E.global',
|
|
filters = 'E.global',
|
|
styleFilters = 'E.global'
|
|
}
|
|
|
|
local function buildLineStructure(str) -- str is profileText
|
|
for _, v in ipairs(lineStructureTable) do
|
|
if type(v) == 'string' then
|
|
str = str..'["'..v..'"]'
|
|
else
|
|
str = str..'['..v..']'
|
|
end
|
|
end
|
|
|
|
return str
|
|
end
|
|
|
|
local sameLine
|
|
local function recurse(tbl, ret, profileText)
|
|
local tkeys = {}
|
|
for i in pairs(tbl) do tinsert(tkeys, i) end
|
|
sort(tkeys, keySort)
|
|
|
|
local lineStructure = buildLineStructure(profileText)
|
|
for _, k in ipairs(tkeys) do
|
|
local v = tbl[k]
|
|
|
|
if not sameLine then
|
|
ret = ret..lineStructure
|
|
end
|
|
|
|
ret = ret..'['
|
|
|
|
if type(k) == 'string' then
|
|
ret = ret..'"'..k..'"'
|
|
else
|
|
ret = ret..k
|
|
end
|
|
|
|
if type(v) == 'table' then
|
|
tinsert(lineStructureTable, k)
|
|
sameLine = true
|
|
ret = ret..']'
|
|
ret = recurse(v, ret, profileText)
|
|
else
|
|
sameLine = false
|
|
ret = ret..'] = '
|
|
|
|
if type(v) == 'number' then
|
|
ret = ret..v..'\n'
|
|
elseif type(v) == 'string' then
|
|
ret = ret..'"'..v:gsub('\\', '\\\\'):gsub('\n', '\\n'):gsub('"', '\\"'):gsub('\124', '\124\124')..'"\n'
|
|
elseif type(v) == 'boolean' then
|
|
if v then
|
|
ret = ret..'true\n'
|
|
else
|
|
ret = ret..'false\n'
|
|
end
|
|
else
|
|
ret = ret..'"'..tostring(v)..'"\n'
|
|
end
|
|
end
|
|
end
|
|
|
|
tremove(lineStructureTable)
|
|
|
|
return ret
|
|
end
|
|
|
|
function E:ProfileTableToPluginFormat(inTable, profileType)
|
|
local profileText = profileFormat[profileType]
|
|
if not profileText then return end
|
|
|
|
wipe(lineStructureTable)
|
|
|
|
local ret = ''
|
|
if inTable and profileType then
|
|
sameLine = false
|
|
ret = recurse(inTable, ret, profileText)
|
|
end
|
|
|
|
return ret
|
|
end
|
|
end
|
|
|
|
do --Split string by multi-character delimiter (the strsplit / string.split function provided by WoW doesn't allow multi-character delimiter)
|
|
local splitTable = {}
|
|
function E:SplitString(str, delim)
|
|
assert(type (delim) == 'string' and strlen(delim) > 0, 'bad delimiter')
|
|
|
|
local start = 1
|
|
wipe(splitTable) -- results table
|
|
|
|
-- find each instance of a string followed by the delimiter
|
|
while true do
|
|
local pos = find(str, delim, start, true) -- plain find
|
|
if not pos then break end
|
|
|
|
tinsert(splitTable, sub(str, start, pos - 1))
|
|
start = pos + strlen(delim)
|
|
end -- while
|
|
|
|
-- insert final one (after last delimiter)
|
|
tinsert(splitTable, sub(str, start))
|
|
|
|
return unpack(splitTable)
|
|
end
|
|
end
|
|
|
|
do
|
|
local SendMessageWaiting -- only allow 1 delay at a time regardless of eventing
|
|
function E:SendMessage()
|
|
if IsInRaid() then
|
|
C_ChatInfo_SendAddonMessage('ELVUI_VERSIONCHK', E.version, (not IsInRaid(LE_PARTY_CATEGORY_HOME) and IsInRaid(LE_PARTY_CATEGORY_INSTANCE)) and 'INSTANCE_CHAT' or 'RAID')
|
|
elseif IsInGroup() then
|
|
C_ChatInfo_SendAddonMessage('ELVUI_VERSIONCHK', E.version, (not IsInGroup(LE_PARTY_CATEGORY_HOME) and IsInGroup(LE_PARTY_CATEGORY_INSTANCE)) and 'INSTANCE_CHAT' or 'PARTY')
|
|
elseif IsInGuild() then
|
|
C_ChatInfo_SendAddonMessage('ELVUI_VERSIONCHK', E.version, 'GUILD')
|
|
end
|
|
|
|
SendMessageWaiting = nil
|
|
end
|
|
|
|
local SendRecieveGroupSize = 0
|
|
local PLAYER_NAME = format('%s-%s', E.myname, E:ShortenRealm(E.myrealm))
|
|
local function SendRecieve(_, event, prefix, message, _, sender)
|
|
if event == 'CHAT_MSG_ADDON' then
|
|
if sender == PLAYER_NAME then return end
|
|
if prefix == 'ELVUI_VERSIONCHK' then
|
|
local msg, ver = tonumber(message), E.version
|
|
local inCombat = InCombatLockdown()
|
|
|
|
E.UserList[E:StripMyRealm(sender)] = msg
|
|
|
|
if msg and (msg > ver) and not E.recievedOutOfDateMessage then -- you're outdated D:
|
|
E:Print(L["ElvUI is out of date. You can download the newest version from www.tukui.org. Get premium membership and have ElvUI automatically updated with the Tukui Client!"])
|
|
|
|
if msg and ((msg - ver) >= 0.05) and not inCombat then
|
|
E:StaticPopup_Show('ELVUI_UPDATE_AVAILABLE')
|
|
end
|
|
|
|
E.recievedOutOfDateMessage = true
|
|
end
|
|
end
|
|
elseif event == 'GROUP_ROSTER_UPDATE' then
|
|
local num = GetNumGroupMembers()
|
|
if num ~= SendRecieveGroupSize then
|
|
if num > 1 and num > SendRecieveGroupSize then
|
|
if not SendMessageWaiting then
|
|
SendMessageWaiting = E:Delay(10, E.SendMessage)
|
|
end
|
|
end
|
|
SendRecieveGroupSize = num
|
|
end
|
|
elseif event == 'PLAYER_ENTERING_WORLD' then
|
|
if not SendMessageWaiting then
|
|
SendMessageWaiting = E:Delay(10, E.SendMessage)
|
|
end
|
|
end
|
|
end
|
|
|
|
_G.C_ChatInfo.RegisterAddonMessagePrefix('ELVUI_VERSIONCHK')
|
|
|
|
local f = CreateFrame('Frame')
|
|
f:RegisterEvent('CHAT_MSG_ADDON')
|
|
f:RegisterEvent('GROUP_ROSTER_UPDATE')
|
|
f:RegisterEvent('PLAYER_ENTERING_WORLD')
|
|
f:SetScript('OnEvent', SendRecieve)
|
|
end
|
|
|
|
function E:UpdateStart(skipCallback, skipUpdateDB)
|
|
if not skipUpdateDB then
|
|
E:UpdateDB()
|
|
end
|
|
|
|
E:UpdateMoverPositions()
|
|
E:UpdateMediaItems()
|
|
E:UpdateUnitFrames()
|
|
|
|
if not skipCallback then
|
|
E.callbacks:Fire('StaggeredUpdate')
|
|
end
|
|
end
|
|
|
|
do -- BFA Convert, deprecated..
|
|
local function buffwatchConvert(spell)
|
|
if spell.sizeOverride then spell.sizeOverride = nil end
|
|
if spell.size then spell.size = nil end
|
|
|
|
if not spell.sizeOffset then
|
|
spell.sizeOffset = 0
|
|
end
|
|
|
|
if spell.styleOverride then
|
|
spell.style = spell.styleOverride
|
|
spell.styleOverride = nil
|
|
elseif not spell.style then
|
|
spell.style = 'coloredIcon'
|
|
end
|
|
end
|
|
|
|
local ttModSwap
|
|
do -- tooltip convert
|
|
local swap = {ALL = 'HIDE',NONE = 'SHOW'}
|
|
ttModSwap = function(val) return swap[val] end
|
|
end
|
|
|
|
function E:DBConvertBFA()
|
|
--Fix issue where UIScale was incorrectly stored as string
|
|
E.global.general.UIScale = tonumber(E.global.general.UIScale)
|
|
|
|
--Not sure how this one happens, but prevent it in any case
|
|
if E.global.general.UIScale <= 0 then
|
|
E.global.general.UIScale = G.general.UIScale
|
|
end
|
|
|
|
--Combat & Resting Icon options update
|
|
if E.db.unitframe.units.player.combatIcon ~= nil then
|
|
E.db.unitframe.units.player.CombatIcon.enable = E.db.unitframe.units.player.combatIcon
|
|
E.db.unitframe.units.player.combatIcon = nil
|
|
end
|
|
if E.db.unitframe.units.player.restIcon ~= nil then
|
|
E.db.unitframe.units.player.RestIcon.enable = E.db.unitframe.units.player.restIcon
|
|
E.db.unitframe.units.player.restIcon = nil
|
|
end
|
|
|
|
-- [Fader] Combat Fade options for Player
|
|
if E.db.unitframe.units.player.combatfade ~= nil then
|
|
local enabled = E.db.unitframe.units.player.combatfade
|
|
E.db.unitframe.units.player.fader.enable = enabled
|
|
|
|
if enabled then -- use the old min alpha too
|
|
E.db.unitframe.units.player.fader.minAlpha = 0
|
|
end
|
|
|
|
E.db.unitframe.units.player.combatfade = nil
|
|
end
|
|
|
|
-- [Fader] Range check options for Units
|
|
do
|
|
local outsideAlpha
|
|
if E.db.unitframe.OORAlpha ~= nil then
|
|
outsideAlpha = E.db.unitframe.OORAlpha
|
|
E.db.unitframe.OORAlpha = nil
|
|
end
|
|
|
|
for _, unit in ipairs({'target','targettarget','targettargettarget','focus','focustarget','pet','pettarget','boss','arena','party','raid','raid40','raidpet','tank','assist'}) do
|
|
if E.db.unitframe.units[unit].rangeCheck ~= nil then
|
|
local enabled = E.db.unitframe.units[unit].rangeCheck
|
|
E.db.unitframe.units[unit].fader.enable = enabled
|
|
E.db.unitframe.units[unit].fader.range = enabled
|
|
|
|
if outsideAlpha then
|
|
E.db.unitframe.units[unit].fader.minAlpha = outsideAlpha
|
|
end
|
|
|
|
E.db.unitframe.units[unit].rangeCheck = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
--Remove stale font settings from Cooldown system for top auras
|
|
if E.db.auras.cooldown.fonts then
|
|
E.db.auras.cooldown.fonts = nil
|
|
end
|
|
|
|
--Convert Nameplate Aura Duration to new Cooldown system
|
|
if E.db.nameplates.durationFont then
|
|
E.db.nameplates.cooldown.fonts.font = E.db.nameplates.durationFont
|
|
E.db.nameplates.cooldown.fonts.fontSize = E.db.nameplates.durationFontSize
|
|
E.db.nameplates.cooldown.fonts.fontOutline = E.db.nameplates.durationFontOutline
|
|
|
|
E.db.nameplates.durationFont = nil
|
|
E.db.nameplates.durationFontSize = nil
|
|
E.db.nameplates.durationFontOutline = nil
|
|
end
|
|
|
|
if E.db.nameplates.lowHealthThreshold > 0.8 then
|
|
E.db.nameplates.lowHealthThreshold = 0.8
|
|
end
|
|
|
|
if E.db.nameplates.units.TARGET.nonTargetTransparency ~= nil then
|
|
E.global.nameplate.filters.ElvUI_NonTarget.actions.alpha = E.db.nameplates.units.TARGET.nonTargetTransparency * 100
|
|
E.db.nameplates.units.TARGET.nonTargetTransparency = nil
|
|
end
|
|
|
|
--Removed additional table in nameplate filters cause it was basically useless
|
|
for _, unit in ipairs({'PLAYER','FRIENDLY_PLAYER','ENEMY_PLAYER','FRIENDLY_NPC','ENEMY_NPC'}) do
|
|
if E.db.nameplates.units[unit].buffs and E.db.nameplates.units[unit].buffs.filters ~= nil then
|
|
E.db.nameplates.units[unit].buffs.minDuration = E.db.nameplates.units[unit].buffs.filters.minDuration or P.nameplates.units[unit].buffs.minDuration
|
|
E.db.nameplates.units[unit].buffs.maxDuration = E.db.nameplates.units[unit].buffs.filters.maxDuration or P.nameplates.units[unit].buffs.maxDuration
|
|
E.db.nameplates.units[unit].buffs.priority = E.db.nameplates.units[unit].buffs.filters.priority or P.nameplates.units[unit].buffs.priority
|
|
E.db.nameplates.units[unit].buffs.filters = nil
|
|
end
|
|
if E.db.nameplates.units[unit].debuffs and E.db.nameplates.units[unit].debuffs.filters ~= nil then
|
|
E.db.nameplates.units[unit].debuffs.minDuration = E.db.nameplates.units[unit].debuffs.filters.minDuration or P.nameplates.units[unit].debuffs.minDuration
|
|
E.db.nameplates.units[unit].debuffs.maxDuration = E.db.nameplates.units[unit].debuffs.filters.maxDuration or P.nameplates.units[unit].debuffs.maxDuration
|
|
E.db.nameplates.units[unit].debuffs.priority = E.db.nameplates.units[unit].debuffs.filters.priority or P.nameplates.units[unit].debuffs.priority
|
|
E.db.nameplates.units[unit].debuffs.filters = nil
|
|
end
|
|
end
|
|
|
|
--Moved target scale to a style filter
|
|
if E.db.nameplates.units.TARGET.scale ~= nil then
|
|
E.global.nameplate.filters.ElvUI_Target.actions.scale = E.db.nameplates.units.TARGET.scale
|
|
E.db.nameplates.units.TARGET.scale = nil
|
|
end
|
|
|
|
--Convert cropIcon to tristate
|
|
local cropIcon = E.db.general.cropIcon
|
|
if type(cropIcon) == 'boolean' then
|
|
E.db.general.cropIcon = (cropIcon and 2) or 0
|
|
end
|
|
|
|
--Vendor Greys option is now in bags table
|
|
if E.db.general.vendorGrays ~= nil then
|
|
E.db.bags.vendorGrays.enable = E.db.general.vendorGrays
|
|
E.db.general.vendorGraysDetails = nil
|
|
E.db.general.vendorGrays = nil
|
|
end
|
|
|
|
--Heal Prediction is now a table instead of a bool
|
|
for _, unit in ipairs({'player','target','focus','pet','arena','party','raid','raid40','raidpet'}) do
|
|
if type(E.db.unitframe.units[unit].healPrediction) ~= 'table' then
|
|
local enabled = E.db.unitframe.units[unit].healPrediction
|
|
E.db.unitframe.units[unit].healPrediction = {}
|
|
E.db.unitframe.units[unit].healPrediction.enable = enabled
|
|
else
|
|
local healPrediction = E.db.unitframe.units[unit].healPrediction
|
|
if healPrediction.reversedAbsorbs ~= nil then -- convert the newer setting if it existed
|
|
healPrediction.reversedAbsorbs = nil
|
|
healPrediction.absorbStyle = 'REVERSED'
|
|
|
|
-- clear extras
|
|
healPrediction.showAbsorbAmount = nil
|
|
healPrediction.showOverAbsorbs = nil
|
|
elseif healPrediction.showAbsorbAmount ~= nil then -- convert the old setting into the new wrapped setting
|
|
healPrediction.showAbsorbAmount = nil
|
|
healPrediction.absorbStyle = 'WRAPPED'
|
|
|
|
-- clear extras
|
|
healPrediction.showOverAbsorbs = nil
|
|
elseif healPrediction.showOverAbsorbs ~= nil then -- convert the over absorb toggle into the new setting
|
|
healPrediction.absorbStyle = 'NORMAL'
|
|
healPrediction.showOverAbsorbs = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
--Health Backdrop Multiplier
|
|
if E.db.unitframe.colors.healthmultiplier ~= nil then
|
|
if E.db.unitframe.colors.healthmultiplier > 0.75 then
|
|
E.db.unitframe.colors.healthMultiplier = 0.75
|
|
else
|
|
E.db.unitframe.colors.healthMultiplier = E.db.unitframe.colors.healthmultiplier
|
|
end
|
|
|
|
E.db.unitframe.colors.healthmultiplier = nil
|
|
end
|
|
|
|
--Tooltip FactionColors Setting
|
|
for i = 1, 8 do
|
|
local oldTable = E.db.tooltip.factionColors[''..i]
|
|
if oldTable then
|
|
local newTable = E:CopyTable({}, P.tooltip.factionColors[i]) -- import full table
|
|
E.db.tooltip.factionColors[i] = E:CopyTable(newTable, oldTable)
|
|
E.db.tooltip.factionColors[''..i] = nil
|
|
end
|
|
end
|
|
|
|
-- Wipe some old variables off profiles
|
|
if E.global.uiScaleInformed then E.global.uiScaleInformed = nil end
|
|
if E.global.nameplatesResetInformed then E.global.nameplatesResetInformed = nil end
|
|
if E.global.userInformedNewChanges1 then E.global.userInformedNewChanges1 = nil end
|
|
|
|
-- cvar nameplate visibility stuff
|
|
if E.db.nameplates.visibility.nameplateShowAll ~= nil then
|
|
E.db.nameplates.visibility.showAll = E.db.nameplates.visibility.nameplateShowAll
|
|
E.db.nameplates.visibility.nameplateShowAll = nil
|
|
end
|
|
if E.db.nameplates.units.FRIENDLY_NPC.showAlways ~= nil then
|
|
E.db.nameplates.visibility.friendly.npcs = E.db.nameplates.units.FRIENDLY_NPC.showAlways
|
|
E.db.nameplates.units.FRIENDLY_NPC.showAlways = nil
|
|
end
|
|
if E.db.nameplates.units.FRIENDLY_PLAYER.minions ~= nil then
|
|
E.db.nameplates.visibility.friendly.minions = E.db.nameplates.units.FRIENDLY_PLAYER.minions
|
|
E.db.nameplates.units.FRIENDLY_PLAYER.minions = nil
|
|
end
|
|
if E.db.nameplates.units.ENEMY_NPC.minors ~= nil then
|
|
E.db.nameplates.visibility.enemy.minus = E.db.nameplates.units.ENEMY_NPC.minors
|
|
E.db.nameplates.units.ENEMY_NPC.minors = nil
|
|
end
|
|
if E.db.nameplates.units.ENEMY_PLAYER.minions ~= nil or E.db.nameplates.units.ENEMY_NPC.minions ~= nil then
|
|
E.db.nameplates.visibility.enemy.minions = E.db.nameplates.units.ENEMY_PLAYER.minions or E.db.nameplates.units.ENEMY_NPC.minions
|
|
E.db.nameplates.units.ENEMY_PLAYER.minions = nil
|
|
E.db.nameplates.units.ENEMY_NPC.minions = nil
|
|
end
|
|
|
|
-- removed override stuff from aurawatch
|
|
if E.global.unitframe.buffwatch then
|
|
for _, spells in pairs(E.global.unitframe.buffwatch) do
|
|
for _, spell in pairs(spells) do
|
|
buffwatchConvert(spell)
|
|
end
|
|
end
|
|
end
|
|
|
|
if E.db.unitframe.filters.buffwatch then
|
|
for _, spell in pairs(E.db.unitframe.filters.buffwatch) do
|
|
buffwatchConvert(spell)
|
|
end
|
|
end
|
|
|
|
-- fix aurabars colors
|
|
local auraBarColors = E.global.unitframe.AuraBarColors
|
|
for spell, info in pairs(auraBarColors) do
|
|
if type(spell) == 'string' then
|
|
local spellID = select(7, GetSpellInfo(spell))
|
|
if spellID and not auraBarColors[spellID] then
|
|
auraBarColors[spellID] = info
|
|
auraBarColors[spell] = nil
|
|
spell = spellID
|
|
end
|
|
end
|
|
|
|
if type(info) == 'boolean' then
|
|
auraBarColors[spell] = { color = { r = 1, g = 1, b = 1 }, enable = info }
|
|
elseif type(info) == 'table' then
|
|
if info.r or info.g or info.b then
|
|
auraBarColors[spell] = { color = { r = info.r or 1, g = info.g or 1, b = info.b or 1 }, enable = true }
|
|
elseif info.color then -- azil created a void hole, delete it -x-
|
|
if info.color.color then info.color.color = nil end
|
|
if info.color.enable then info.color.enable = nil end
|
|
if info.color.a then info.color.a = nil end -- alpha isnt supported by this
|
|
end
|
|
end
|
|
end
|
|
|
|
if E.db.unitframe.colors.debuffHighlight.blendMode == 'MOD' then
|
|
E.db.unitframe.colors.debuffHighlight.blendMode = P.unitframe.colors.debuffHighlight.blendMode
|
|
end
|
|
|
|
do -- tooltip modifier code was dumb, change it but keep the past setting
|
|
local swap = ttModSwap(E.db.tooltip.modifierID)
|
|
if swap then E.db.tooltip.modifierID = swap end
|
|
|
|
swap = ttModSwap(E.db.tooltip.visibility.bags)
|
|
if swap then E.db.tooltip.visibility.bags = swap end
|
|
|
|
swap = ttModSwap(E.db.tooltip.visibility.unitFrames)
|
|
if swap then E.db.tooltip.visibility.unitFrames = swap end
|
|
|
|
swap = ttModSwap(E.db.tooltip.visibility.actionbars)
|
|
if swap then E.db.tooltip.visibility.actionbars = swap end
|
|
|
|
swap = ttModSwap(E.db.tooltip.visibility.combatOverride)
|
|
if swap then E.db.tooltip.visibility.combatOverride = swap end
|
|
|
|
-- remove the old combat variable and just use the mod since it supports show/hide states
|
|
local hideInCombat = E.db.tooltip.visibility.combat
|
|
if hideInCombat ~= nil then
|
|
E.db.tooltip.visibility.combat = nil
|
|
|
|
local override = E.db.tooltip.visibility.combatOverride
|
|
if hideInCombat and (override ~= 'SHIFT' and override ~= 'CTRL' and override ~= 'ALT') then -- wouldve been NONE but now it would be HIDE
|
|
E.db.tooltip.visibility.combatOverride = 'HIDE'
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function E:DBConvertSL()
|
|
if E.private.skins.cleanBossButton ~= nil then
|
|
E.db.actionbar.extraActionButton.clean = E.private.skins.cleanBossButton
|
|
E.private.skins.cleanBossButton = nil
|
|
end
|
|
|
|
if E.global.unitframe.DebuffHighlightColors then
|
|
E:CopyTable(E.global.unitframe.AuraHighlightColors, E.global.unitframe.DebuffHighlightColors)
|
|
E.global.unitframe.DebuffHighlightColors = nil
|
|
end
|
|
|
|
if E.db.unitframe.filters.buffwatch then
|
|
E.db.unitframe.filters.aurawatch = E:CopyTable({}, E.db.unitframe.filters.buffwatch)
|
|
E.db.unitframe.filters.buffwatch = nil
|
|
end
|
|
|
|
if E.global.unitframe.buffwatch then
|
|
E:CopyTable(E.global.unitframe.aurawatch, E.global.unitframe.buffwatch)
|
|
E.global.unitframe.buffwatch = nil
|
|
end
|
|
end
|
|
|
|
function E:UpdateDB()
|
|
E.private = E.charSettings.profile
|
|
E.global = E.data.global
|
|
E.db = E.data.profile
|
|
|
|
E:DBConversions()
|
|
|
|
Auras.db = E.db.auras
|
|
ActionBars.db = E.db.actionbar
|
|
Bags.db = E.db.bags
|
|
Chat.db = E.db.chat
|
|
DataBars.db = E.db.databars
|
|
DataTexts.db = E.db.datatexts
|
|
NamePlates.db = E.db.nameplates
|
|
Tooltip.db = E.db.tooltip
|
|
UnitFrames.db = E.db.unitframe
|
|
Totems.db = E.db.general.totems
|
|
|
|
--Not part of staggered update
|
|
end
|
|
|
|
function E:UpdateMoverPositions()
|
|
--The mover is positioned before it is resized, which causes issues for unitframes
|
|
--Allow movers to be 'pushed' outside the screen, when they are resized they should be back in the screen area.
|
|
--We set movers to be clamped again at the bottom of this function.
|
|
E:SetMoversClampedToScreen(false)
|
|
E:SetMoversPositions()
|
|
|
|
--Not part of staggered update
|
|
end
|
|
|
|
function E:UpdateUnitFrames()
|
|
if E.private.unitframe.enable then
|
|
UnitFrames:Update_AllFrames()
|
|
end
|
|
|
|
--Not part of staggered update
|
|
end
|
|
|
|
function E:UpdateMediaItems(skipCallback)
|
|
E:UpdateMedia()
|
|
E:UpdateFrameTemplates()
|
|
E:UpdateStatusBars()
|
|
|
|
if not skipCallback then
|
|
E.callbacks:Fire('StaggeredUpdate')
|
|
end
|
|
end
|
|
|
|
function E:UpdateLayout(skipCallback)
|
|
Layout:ToggleChatPanels()
|
|
Layout:BottomPanelVisibility()
|
|
Layout:TopPanelVisibility()
|
|
Layout:SetDataPanelStyle()
|
|
|
|
if not skipCallback then
|
|
E.callbacks:Fire('StaggeredUpdate')
|
|
end
|
|
end
|
|
|
|
function E:UpdateActionBars(skipCallback)
|
|
ActionBars:ExtraButtons_UpdateAlpha()
|
|
ActionBars:ExtraButtons_UpdateScale()
|
|
ActionBars:ExtraButtons_GlobalFade()
|
|
ActionBars:ToggleCooldownOptions()
|
|
ActionBars:UpdateButtonSettings()
|
|
ActionBars:UpdateMicroPositionDimensions()
|
|
ActionBars:UpdatePetCooldownSettings()
|
|
|
|
if not skipCallback then
|
|
E.callbacks:Fire('StaggeredUpdate')
|
|
end
|
|
end
|
|
|
|
function E:UpdateNamePlates(skipCallback)
|
|
NamePlates:ConfigureAll()
|
|
NamePlates:StyleFilterInitialize()
|
|
|
|
if not skipCallback then
|
|
E.callbacks:Fire('StaggeredUpdate')
|
|
end
|
|
end
|
|
|
|
function E:UpdateTooltip()
|
|
Tooltip:SetTooltipFonts()
|
|
end
|
|
|
|
function E:UpdateBags(skipCallback)
|
|
Bags:Layout()
|
|
Bags:Layout(true)
|
|
Bags:SizeAndPositionBagBar()
|
|
Bags:UpdateCountDisplay()
|
|
Bags:UpdateItemLevelDisplay()
|
|
|
|
if not skipCallback then
|
|
E.callbacks:Fire('StaggeredUpdate')
|
|
end
|
|
end
|
|
|
|
function E:UpdateChat(skipCallback)
|
|
Chat:SetupChat()
|
|
Chat:UpdateEditboxAnchors()
|
|
|
|
if not skipCallback then
|
|
E.callbacks:Fire('StaggeredUpdate')
|
|
end
|
|
end
|
|
|
|
function E:UpdateDataBars(skipCallback)
|
|
DataBars:AzeriteBar_Toggle()
|
|
DataBars:ExperienceBar_Toggle()
|
|
DataBars:HonorBar_Toggle()
|
|
DataBars:ReputationBar_Toggle()
|
|
DataBars:ThreatBar_Toggle()
|
|
DataBars:UpdateAll()
|
|
|
|
if not skipCallback then
|
|
E.callbacks:Fire('StaggeredUpdate')
|
|
end
|
|
end
|
|
|
|
function E:UpdateDataTexts(skipCallback)
|
|
DataTexts:LoadDataTexts()
|
|
|
|
if not skipCallback then
|
|
E.callbacks:Fire('StaggeredUpdate')
|
|
end
|
|
end
|
|
|
|
function E:UpdateMinimap(skipCallback)
|
|
Minimap:UpdateSettings()
|
|
|
|
if not skipCallback then
|
|
E.callbacks:Fire('StaggeredUpdate')
|
|
end
|
|
end
|
|
|
|
function E:UpdateAuras(skipCallback)
|
|
if ElvUIPlayerBuffs then Auras:UpdateHeader(ElvUIPlayerBuffs) end
|
|
if ElvUIPlayerDebuffs then Auras:UpdateHeader(ElvUIPlayerDebuffs) end
|
|
|
|
if not skipCallback then
|
|
E.callbacks:Fire('StaggeredUpdate')
|
|
end
|
|
end
|
|
|
|
function E:UpdateMisc(skipCallback)
|
|
AFK:Toggle()
|
|
Blizzard:SetObjectiveFrameHeight()
|
|
|
|
Totems:PositionAndSize()
|
|
|
|
if not skipCallback then
|
|
E.callbacks:Fire('StaggeredUpdate')
|
|
end
|
|
end
|
|
|
|
function E:UpdateEnd()
|
|
E:UpdateCooldownSettings('all')
|
|
|
|
if E.RefreshGUI then
|
|
E:RefreshGUI()
|
|
end
|
|
|
|
E:SetMoversClampedToScreen(true) -- Go back to using clamp after resizing has taken place.
|
|
|
|
if not E.installSetup and not E.private.install_complete then
|
|
E:Install()
|
|
end
|
|
|
|
if E.staggerUpdateRunning then
|
|
--We're doing a staggered update, but plugins expect the old UpdateAll to be called
|
|
--So call it, but skip updates inside it
|
|
E:UpdateAll(false)
|
|
end
|
|
|
|
--Done updating, let code now
|
|
E.staggerUpdateRunning = false
|
|
end
|
|
|
|
do
|
|
local staggerDelay = 0.02
|
|
local staggerTable = {}
|
|
local function CallStaggeredUpdate()
|
|
local nextUpdate, nextDelay = staggerTable[1]
|
|
if nextUpdate then
|
|
tremove(staggerTable, 1)
|
|
|
|
if nextUpdate == 'UpdateNamePlates' or nextUpdate == 'UpdateBags' then
|
|
nextDelay = 0.05
|
|
end
|
|
|
|
E:Delay(nextDelay or staggerDelay, E[nextUpdate])
|
|
end
|
|
end
|
|
E:RegisterCallback('StaggeredUpdate', CallStaggeredUpdate)
|
|
|
|
function E:StaggeredUpdateAll(event, installSetup)
|
|
if not E.initialized then
|
|
E:Delay(1, E.StaggeredUpdateAll, E, event, installSetup)
|
|
return
|
|
end
|
|
|
|
E.installSetup = installSetup
|
|
if (installSetup or event and event == 'OnProfileChanged' or event == 'OnProfileCopied') and not E.staggerUpdateRunning then
|
|
tinsert(staggerTable, 'UpdateLayout')
|
|
if E.private.actionbar.enable then
|
|
tinsert(staggerTable, 'UpdateActionBars')
|
|
end
|
|
if E.private.nameplates.enable then
|
|
tinsert(staggerTable, 'UpdateNamePlates')
|
|
end
|
|
if E.private.bags.enable then
|
|
tinsert(staggerTable, 'UpdateBags')
|
|
end
|
|
if E.private.chat.enable then
|
|
tinsert(staggerTable, 'UpdateChat')
|
|
end
|
|
if E.private.tooltip.enable then
|
|
tinsert(staggerTable, 'UpdateTooltip')
|
|
end
|
|
tinsert(staggerTable, 'UpdateDataBars')
|
|
tinsert(staggerTable, 'UpdateDataTexts')
|
|
if E.private.general.minimap.enable then
|
|
tinsert(staggerTable, 'UpdateMinimap')
|
|
end
|
|
if ElvUIPlayerBuffs or ElvUIPlayerDebuffs then
|
|
tinsert(staggerTable, 'UpdateAuras')
|
|
end
|
|
tinsert(staggerTable, 'UpdateMisc')
|
|
tinsert(staggerTable, 'UpdateEnd')
|
|
|
|
--Stagger updates
|
|
E.staggerUpdateRunning = true
|
|
E:UpdateStart()
|
|
else
|
|
--Fire away
|
|
E:UpdateAll(true)
|
|
end
|
|
end
|
|
end
|
|
|
|
function E:UpdateAll(doUpdates)
|
|
if doUpdates then
|
|
E:UpdateStart(true)
|
|
|
|
E:UpdateLayout()
|
|
E:UpdateTooltip()
|
|
E:UpdateActionBars()
|
|
E:UpdateBags()
|
|
E:UpdateChat()
|
|
E:UpdateDataBars()
|
|
E:UpdateDataTexts()
|
|
E:UpdateMinimap()
|
|
E:UpdateNamePlates()
|
|
E:UpdateAuras()
|
|
E:UpdateMisc()
|
|
E:UpdateEnd()
|
|
end
|
|
end
|
|
|
|
do
|
|
E.ObjectEventTable, E.ObjectEventFrame = {}, CreateFrame('Frame')
|
|
local eventFrame, eventTable = E.ObjectEventFrame, E.ObjectEventTable
|
|
|
|
eventFrame:SetScript('OnEvent', function(_, event, ...)
|
|
local objs = eventTable[event]
|
|
if objs then
|
|
for object, funcs in pairs(objs) do
|
|
for _, func in ipairs(funcs) do
|
|
func(object, event, ...)
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
function E:HasFunctionForObject(event, object, func)
|
|
if not (event and object and func) then
|
|
E:Print('Error. Usage: HasFunctionForObject(event, object, func)')
|
|
return
|
|
end
|
|
|
|
local objs = eventTable[event]
|
|
local funcs = objs and objs[object]
|
|
return funcs and tContains(funcs, func)
|
|
end
|
|
|
|
function E:IsEventRegisteredForObject(event, object)
|
|
if not (event and object) then
|
|
E:Print('Error. Usage: IsEventRegisteredForObject(event, object)')
|
|
return
|
|
end
|
|
|
|
local objs = eventTable[event]
|
|
local funcs = objs and objs[object]
|
|
return funcs ~= nil, funcs
|
|
end
|
|
|
|
--- Registers specified event and adds specified func to be called for the specified object.
|
|
-- Unless all parameters are supplied it will not register.
|
|
-- If the specified object has already been registered for the specified event
|
|
-- then it will just add the specified func to a table of functions that should be called.
|
|
-- When a registered event is triggered, then the registered function is called with
|
|
-- the object as first parameter, then event, and then all the parameters for the event itself.
|
|
-- @param event The event you want to register.
|
|
-- @param object The object you want to register the event for.
|
|
-- @param func The function you want executed for this object.
|
|
function E:RegisterEventForObject(event, object, func)
|
|
if not (event and object and func) then
|
|
E:Print('Error. Usage: RegisterEventForObject(event, object, func)')
|
|
return
|
|
end
|
|
|
|
local objs = eventTable[event]
|
|
if not objs then
|
|
objs = {}
|
|
eventTable[event] = objs
|
|
pcall(eventFrame.RegisterEvent, eventFrame, event)
|
|
end
|
|
|
|
local funcs = objs[object]
|
|
if not funcs then
|
|
objs[object] = {func}
|
|
elseif not tContains(funcs, func) then
|
|
tinsert(funcs, func)
|
|
end
|
|
end
|
|
|
|
--- Unregisters specified function for the specified object on the specified event.
|
|
-- Unless all parameters are supplied it will not unregister.
|
|
-- @param event The event you want to unregister an object from.
|
|
-- @param object The object you want to unregister a func from.
|
|
-- @param func The function you want unregistered for the object.
|
|
function E:UnregisterEventForObject(event, object, func)
|
|
if not (event and object and func) then
|
|
E:Print('Error. Usage: UnregisterEventForObject(event, object, func)')
|
|
return
|
|
end
|
|
|
|
local objs = eventTable[event]
|
|
local funcs = objs and objs[object]
|
|
if funcs then
|
|
for index, fnc in ipairs(funcs) do
|
|
if func == fnc then
|
|
tremove(funcs, index)
|
|
break
|
|
end
|
|
end
|
|
|
|
if #funcs == 0 then
|
|
objs[object] = nil
|
|
end
|
|
|
|
if not next(funcs) then
|
|
eventFrame:UnregisterEvent(event)
|
|
eventTable[event] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
function E:UnregisterAllEventsForObject(object, func)
|
|
if not (object and func) then
|
|
E:Print('Error. Usage: UnregisterAllEventsForObject(object, func)')
|
|
return
|
|
end
|
|
|
|
for event in pairs(eventTable) do
|
|
if E:IsEventRegisteredForObject(event, object) then
|
|
E:UnregisterEventForObject(event, object, func)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function E:ResetAllUI()
|
|
E:ResetMovers()
|
|
|
|
if E.db.lowresolutionset then
|
|
E:SetupResolution(true)
|
|
end
|
|
|
|
if E.db.layoutSet then
|
|
E:SetupLayout(E.db.layoutSet, true)
|
|
end
|
|
end
|
|
|
|
function E:ResetUI(...)
|
|
if InCombatLockdown() then E:Print(ERR_NOT_IN_COMBAT) return end
|
|
|
|
if ... == '' or ... == ' ' or ... == nil then
|
|
E:StaticPopup_Show('RESETUI_CHECK')
|
|
return
|
|
end
|
|
|
|
E:ResetMovers(...)
|
|
end
|
|
|
|
do
|
|
local function errorhandler(err)
|
|
return _G.geterrorhandler()(err)
|
|
end
|
|
|
|
function E:CallLoadFunc(func, ...)
|
|
xpcall(func, errorhandler, ...)
|
|
end
|
|
end
|
|
|
|
function E:CallLoadedModule(obj, silent, object, index)
|
|
local name, func
|
|
if type(obj) == 'table' then name, func = unpack(obj) else name = obj end
|
|
local module = name and E:GetModule(name, silent)
|
|
|
|
if not module then return end
|
|
if func and type(func) == 'string' then
|
|
E:CallLoadFunc(module[func], module)
|
|
elseif func and type(func) == 'function' then
|
|
E:CallLoadFunc(func, module)
|
|
elseif module.Initialize then
|
|
E:CallLoadFunc(module.Initialize, module)
|
|
end
|
|
|
|
if object and index then object[index] = nil end
|
|
end
|
|
|
|
function E:RegisterInitialModule(name, func)
|
|
E.RegisteredInitialModules[#E.RegisteredInitialModules + 1] = (func and {name, func}) or name
|
|
end
|
|
|
|
function E:RegisterModule(name, func)
|
|
if E.initialized then
|
|
E:CallLoadedModule((func and {name, func}) or name)
|
|
else
|
|
E.RegisteredModules[#E.RegisteredModules + 1] = (func and {name, func}) or name
|
|
end
|
|
end
|
|
|
|
function E:InitializeInitialModules()
|
|
for index, object in ipairs(E.RegisteredInitialModules) do
|
|
E:CallLoadedModule(object, true, E.RegisteredInitialModules, index)
|
|
end
|
|
end
|
|
|
|
function E:InitializeModules()
|
|
for index, object in ipairs(E.RegisteredModules) do
|
|
E:CallLoadedModule(object, true, E.RegisteredModules, index)
|
|
end
|
|
end
|
|
|
|
function E:DBConversions()
|
|
-- release converts, only one call per version
|
|
if E.db.dbConverted ~= E.version then
|
|
E.db.dbConverted = E.version
|
|
|
|
E:DBConvertBFA()
|
|
E:DBConvertSL()
|
|
end
|
|
|
|
-- development converts, always call
|
|
end
|
|
|
|
function E:RefreshModulesDB()
|
|
-- this function is specifically used to reference the new database
|
|
-- onto the unitframe module, its useful dont delete! D:
|
|
wipe(UnitFrames.db) --old ref, dont need so clear it
|
|
UnitFrames.db = E.db.unitframe --new ref
|
|
end
|
|
|
|
do
|
|
-- Shamelessly taken from AceDB-3.0 and stripped down by Simpy
|
|
function E:CopyDefaults(dest, src)
|
|
for k, v in pairs(src) do
|
|
if type(v) == 'table' then
|
|
if not rawget(dest, k) then rawset(dest, k, {}) end
|
|
if type(dest[k]) == 'table' then E:CopyDefaults(dest[k], v) end
|
|
elseif rawget(dest, k) == nil then
|
|
rawset(dest, k, v)
|
|
end
|
|
end
|
|
end
|
|
|
|
function E:RemoveDefaults(db, defaults)
|
|
setmetatable(db, nil)
|
|
|
|
for k, v in pairs(defaults) do
|
|
if type(v) == 'table' and type(db[k]) == 'table' then
|
|
E:RemoveDefaults(db[k], v)
|
|
if next(db[k]) == nil then db[k] = nil end
|
|
elseif db[k] == defaults[k] then
|
|
db[k] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function E:Initialize()
|
|
wipe(E.db)
|
|
wipe(E.global)
|
|
wipe(E.private)
|
|
|
|
local playerGUID = UnitGUID('player')
|
|
local _, serverID = strsplit('-', playerGUID)
|
|
E.serverID = tonumber(serverID)
|
|
E.myguid = playerGUID
|
|
|
|
E.data = E.Libs.AceDB:New('ElvDB', E.DF, true)
|
|
E.data.RegisterCallback(E, 'OnProfileChanged', 'StaggeredUpdateAll')
|
|
E.data.RegisterCallback(E, 'OnProfileCopied', 'StaggeredUpdateAll')
|
|
E.data.RegisterCallback(E, 'OnProfileReset', 'OnProfileReset')
|
|
E.charSettings = E.Libs.AceDB:New('ElvPrivateDB', E.privateVars)
|
|
E.charSettings.RegisterCallback(E, 'OnProfileChanged', ReloadUI)
|
|
E.charSettings.RegisterCallback(E, 'OnProfileCopied', ReloadUI)
|
|
E.charSettings.RegisterCallback(E, 'OnProfileReset', 'OnPrivateProfileReset')
|
|
E.private = E.charSettings.profile
|
|
E.global = E.data.global
|
|
E.db = E.data.profile
|
|
E.Libs.DualSpec:EnhanceDatabase(E.data, 'ElvUI')
|
|
|
|
-- default the non thing pixel border color to 191919, otherwise its 000000
|
|
if not E.PixelMode then P.general.bordercolor = { r = 0.1, g = 0.1, b = 0.1 } end
|
|
if not E.db.unitframe.thinBorders then P.unitframe.colors.borderColor = { r = 0.1, g = 0.1, b = 0.1 } end
|
|
|
|
E:DBConversions()
|
|
E:UIScale()
|
|
E:BuildPrefixValues()
|
|
E:LoadAPI()
|
|
E:LoadCommands()
|
|
E:InitializeModules()
|
|
E:RefreshModulesDB()
|
|
E:LoadMovers()
|
|
E:UpdateMedia()
|
|
E:UpdateCooldownSettings('all')
|
|
E:Tutorials()
|
|
E.initialized = true
|
|
|
|
if E.db.general.smoothingAmount and (E.db.general.smoothingAmount ~= 0.33) then
|
|
E:SetSmoothingAmount(E.db.general.smoothingAmount)
|
|
end
|
|
|
|
if not E.private.install_complete then
|
|
E:Install()
|
|
end
|
|
|
|
if E:HelloKittyFixCheck() then
|
|
E:HelloKittyFix()
|
|
end
|
|
|
|
if E.db.general.kittys then
|
|
E:CreateKittys()
|
|
E:Delay(5, E.Print, E, L["Type /hellokitty to revert to old settings."])
|
|
end
|
|
|
|
if GetCVarBool('scriptProfile') then
|
|
E:StaticPopup_Show('SCRIPT_PROFILE')
|
|
end
|
|
|
|
if E.db.general.loginmessage then
|
|
local msg = format(L["LOGIN_MSG"], E.version)
|
|
if Chat.Initialized then msg = select(2, Chat:FindURL('CHAT_MSG_DUMMY', msg)) end
|
|
print(msg)
|
|
print(L["LOGIN_MSG_HELP"])
|
|
end
|
|
end
|