You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

698 lines
20 KiB

------------------------------------------------------------------------
-- Collection of functions that can be used in multiple places
------------------------------------------------------------------------
local E, L, V, P, G = unpack(ElvUI)
local LCS = E.Libs.LCS
local _G = _G
local wipe, max, next = wipe, max, next
local type, ipairs, pairs, unpack = type, ipairs, pairs, unpack
local strfind, strlen, tonumber, tostring = strfind, strlen, tonumber, tostring
local CreateFrame = CreateFrame
local GetAddOnEnableState = GetAddOnEnableState
local GetBattlefieldArenaFaction = GetBattlefieldArenaFaction
local GetInstanceInfo = GetInstanceInfo
local GetNumGroupMembers = GetNumGroupMembers
local GetSpecialization = not E.Retail and LCS.GetSpecialization or GetSpecialization
local GetSpecializationRole = not E.Retail and LCS.GetSpecializationRole or GetSpecializationRole
local hooksecurefunc = hooksecurefunc
local InCombatLockdown = InCombatLockdown
local IsAddOnLoaded = IsAddOnLoaded
local IsInRaid = IsInRaid
local IsLevelAtEffectiveMaxLevel = IsLevelAtEffectiveMaxLevel
local IsSpellKnown = IsSpellKnown
local IsTrialAccount = IsTrialAccount
local IsVeteranTrialAccount = IsVeteranTrialAccount
local IsWargame = IsWargame
local IsXPUserDisabled = IsXPUserDisabled
local RequestBattlefieldScoreData = RequestBattlefieldScoreData
local SetCVar = SetCVar
local UIParentLoadAddOn = UIParentLoadAddOn
local UnitAura = UnitAura
local UnitFactionGroup = UnitFactionGroup
local UnitGroupRolesAssigned = UnitGroupRolesAssigned
local UnitHasVehicleUI = UnitHasVehicleUI
local UnitInParty = UnitInParty
local UnitInRaid = UnitInRaid
local UnitIsMercenary = UnitIsMercenary
local UnitIsUnit = UnitIsUnit
local HideUIPanel = HideUIPanel
local GameMenuButtonAddons = GameMenuButtonAddons
local GameMenuButtonLogout = GameMenuButtonLogout
local GameMenuFrame = GameMenuFrame
local C_PetBattles_IsInBattle = C_PetBattles and C_PetBattles.IsInBattle
local C_PvP_IsRatedBattleground = C_PvP and C_PvP.IsRatedBattleground
local ERR_NOT_IN_COMBAT = ERR_NOT_IN_COMBAT
local FACTION_ALLIANCE = FACTION_ALLIANCE
local FACTION_HORDE = FACTION_HORDE
local PLAYER_FACTION_GROUP = PLAYER_FACTION_GROUP
-- GLOBALS: ElvDB, ElvUF
function E:ClassColor(class, usePriestColor)
if not class then return end
local color = (_G.CUSTOM_CLASS_COLORS and _G.CUSTOM_CLASS_COLORS[class]) or _G.RAID_CLASS_COLORS[class]
if type(color) ~= 'table' then return end
if not color.colorStr then
color.colorStr = E:RGBToHex(color.r, color.g, color.b, 'ff')
elseif strlen(color.colorStr) == 6 then
color.colorStr = 'ff'..color.colorStr
end
if usePriestColor and class == 'PRIEST' and tonumber(color.colorStr, 16) > tonumber(E.PriestColors.colorStr, 16) then
return E.PriestColors
else
return color
end
end
function E:InverseClassColor(class, usePriestColor, forceCap)
local color = E:CopyTable({}, E:ClassColor(class, usePriestColor))
local capColor = class == "PRIEST" or forceCap
color.r = capColor and max(1-color.r,0.35) or (1-color.r)
color.g = capColor and max(1-color.g,0.35) or (1-color.g)
color.b = capColor and max(1-color.b,0.35) or (1-color.b)
color.colorStr = E:RGBToHex(color.r, color.g, color.b, 'ff')
return color
end
do -- other non-english locales require this
E.UnlocalizedClasses = {}
for k, v in pairs(_G.LOCALIZED_CLASS_NAMES_MALE) do E.UnlocalizedClasses[v] = k end
for k, v in pairs(_G.LOCALIZED_CLASS_NAMES_FEMALE) do E.UnlocalizedClasses[v] = k end
function E:UnlocalizedClassName(className)
return (className and className ~= '') and E.UnlocalizedClasses[className]
end
end
do
local essenceTextureID = 2975691
function E:ScanTooltipTextures()
local tt = E.ScanTooltip
if not tt.gems then
tt.gems = {}
else
wipe(tt.gems)
end
if not tt.essences then
tt.essences = {}
else
for _, essences in pairs(tt.essences) do
wipe(essences)
end
end
local step = 1
for i = 1, 10 do
local tex = _G['ElvUI_ScanTooltipTexture'..i]
local texture = tex and tex:IsShown() and tex:GetTexture()
if texture then
if texture == essenceTextureID then
local selected = (tt.gems[i-1] ~= essenceTextureID and tt.gems[i-1]) or nil
if not tt.essences[step] then tt.essences[step] = {} end
tt.essences[step][1] = selected --essence texture if selected or nil
tt.essences[step][2] = tex:GetAtlas() --atlas place 'tooltip-heartofazerothessence-major' or 'tooltip-heartofazerothessence-minor'
tt.essences[step][3] = texture --border texture placed by the atlas
--`CollectEssenceInfo` will add 4 (hex quality color) and 5 (essence name)
step = step + 1
if selected then
tt.gems[i-1] = nil
end
else
tt.gems[i] = texture
end
end
end
return tt.gems, tt.essences
end
end
do
local function FindAura(key, value, unit, index, filter, ...)
local name, _, _, _, _, _, _, _, _, spellID = ...
if not name then
return
elseif key == 'name' and value == name then
return ...
elseif key == 'spellID' and value == spellID then
return ...
else
index = index + 1
FindAura(value, key, unit, index, filter, UnitAura(unit, index, filter))
end
end
function E:GetAuraByID(unit, spellID, filter)
return FindAura('spellID', spellID, unit, 1, filter, UnitAura(unit, 1, filter))
end
function E:GetAuraByName(unit, name, filter)
return FindAura('name', name, unit, 1, filter, UnitAura(unit, 1, filter))
end
end
function E:GetThreatStatusColor(status, nothreat)
local color = ElvUF.colors.threat[status]
if color then
return color[1], color[2], color[3], color[4] or 1
elseif nothreat then
if status == -1 then -- how or why?
return 1, 1, 1, 1
else
return .7, .7, .7, 1
end
end
end
function E:GetPlayerRole()
local role = E.Retail and UnitGroupRolesAssigned('player') or 'NONE'
return (role == 'NONE' and E.myspec and GetSpecializationRole(E.myspec)) or role
end
function E:CheckRole()
E.myspec = GetSpecialization()
E.myrole = E:GetPlayerRole()
if E.Retail then
E:UpdateDispelClasses()
end
end
do -- keep this synced with oUF_AuraHighlight and oUF_RaidDebuffs
local SingeMagic = 89808
local DevourMagic = {
[19505] = 'Rank 1',
[19731] = 'Rank 2',
[19734] = 'Rank 3',
[19736] = 'Rank 4',
[27276] = 'Rank 5',
[27277] = 'Rank 6'
}
local ExcludeClass = {
PRIEST = true, -- has Mass Dispel on Shadow
WARLOCK = true, -- uses PET check only
}
local function CheckPetSpells()
if E.Retail then
return IsSpellKnown(SingeMagic, true)
else
for spellID in next, DevourMagic do
if IsSpellKnown(spellID, true) then
return true
end
end
end
end
function E:UpdateDispelClasses(event, arg1)
local dispel = E.DispelClasses[E.myclass]
if dispel == nil then return end
if event == 'UNIT_PET' then
if arg1 == 'player' and E.myclass == 'WARLOCK' then
dispel.Magic = CheckPetSpells()
end
elseif event == 'CHARACTER_POINTS_CHANGED' and arg1 > 0 then
return -- Not interested in gained points from leveling
elseif E.myrole and not ExcludeClass[E.myclass] then
dispel.Magic = (E.myrole == 'HEALER')
end
end
end
function E:IsDispellableByMe(debuffType)
local dispel = E.DispelClasses[E.myclass]
return dispel and dispel[debuffType]
end
do
local function SetOriginalHeight(f)
if InCombatLockdown() then
E:RegisterEventForObject('PLAYER_REGEN_ENABLED', SetOriginalHeight, SetOriginalHeight)
return
end
E.UIParent:SetHeight(E.UIParent.origHeight)
if f == SetOriginalHeight then
E:UnregisterEventForObject('PLAYER_REGEN_ENABLED', SetOriginalHeight, SetOriginalHeight)
end
end
local function SetModifiedHeight(f)
if InCombatLockdown() then
E:RegisterEventForObject('PLAYER_REGEN_ENABLED', SetModifiedHeight, SetModifiedHeight)
return
end
E.UIParent:SetHeight(E.UIParent.origHeight - (_G.OrderHallCommandBar:GetHeight() + E.Border))
if f == SetModifiedHeight then
E:UnregisterEventForObject('PLAYER_REGEN_ENABLED', SetModifiedHeight, SetModifiedHeight)
end
end
--This function handles disabling of OrderHall Bar or resizing of ElvUIParent if needed
function E:HandleCommandBar()
if E.global.general.commandBarSetting == 'DISABLED' then
_G.OrderHallCommandBar:UnregisterAllEvents()
_G.OrderHallCommandBar:SetScript('OnShow', _G.OrderHallCommandBar.Hide)
_G.OrderHallCommandBar:Hide()
_G.UIParent:UnregisterEvent('UNIT_AURA') --Only used for OrderHall Bar
elseif E.global.general.commandBarSetting == 'ENABLED_RESIZEPARENT' then
_G.OrderHallCommandBar:HookScript('OnShow', SetModifiedHeight)
_G.OrderHallCommandBar:HookScript('OnHide', SetOriginalHeight)
end
end
end
do
local Masque = E.Libs.Masque
local MasqueGroupState = {}
local MasqueGroupToTableElement = {
['ActionBars'] = {'actionbar', 'actionbars'},
['Pet Bar'] = {'actionbar', 'petBar'},
['Stance Bar'] = {'actionbar', 'stanceBar'},
['Buffs'] = {'auras', 'buffs'},
['Debuffs'] = {'auras', 'debuffs'},
}
function E:MasqueCallback(Group, _, _, _, _, Disabled)
if not E.private then return end
local element = MasqueGroupToTableElement[Group]
if element then
if Disabled then
if E.private[element[1]].masque[element[2]] and MasqueGroupState[Group] == 'enabled' then
E.private[element[1]].masque[element[2]] = false
E:StaticPopup_Show('CONFIG_RL')
end
MasqueGroupState[Group] = 'disabled'
else
MasqueGroupState[Group] = 'enabled'
end
end
end
if Masque then
Masque:Register('ElvUI', E.MasqueCallback)
end
end
function E:Dump(object, inspect)
if GetAddOnEnableState(E.myname, 'Blizzard_DebugTools') == 0 then
E:Print('Blizzard_DebugTools is disabled.')
return
end
local debugTools = IsAddOnLoaded('Blizzard_DebugTools')
if not debugTools then UIParentLoadAddOn('Blizzard_DebugTools') end
if inspect then
local tableType = type(object)
if tableType == 'table' then
_G.DisplayTableInspectorWindow(object)
else
E:Print('Failed: ', tostring(object), ' is type: ', tableType,'. Requires table object.')
end
else
_G.DevTools_Dump(object)
end
end
function E:AddNonPetBattleFrames()
if InCombatLockdown() then
E:UnregisterEventForObject('PLAYER_REGEN_DISABLED', E.AddNonPetBattleFrames, E.AddNonPetBattleFrames)
return
elseif E:IsEventRegisteredForObject('PLAYER_REGEN_DISABLED', E.AddNonPetBattleFrames) then
E:UnregisterEventForObject('PLAYER_REGEN_DISABLED', E.AddNonPetBattleFrames, E.AddNonPetBattleFrames)
end
for object, data in pairs(E.FrameLocks) do
local parent, strata
if type(data) == 'table' then
parent, strata = data.parent, data.strata
elseif data == true then
parent = _G.UIParent
end
local obj = _G[object] or object
obj:SetParent(parent)
if strata then
obj:SetFrameStrata(strata)
end
end
end
function E:RemoveNonPetBattleFrames()
if InCombatLockdown() then
E:RegisterEventForObject('PLAYER_REGEN_DISABLED', E.RemoveNonPetBattleFrames, E.RemoveNonPetBattleFrames)
return
elseif E:IsEventRegisteredForObject('PLAYER_REGEN_DISABLED', E.RemoveNonPetBattleFrames) then
E:UnregisterEventForObject('PLAYER_REGEN_DISABLED', E.RemoveNonPetBattleFrames, E.RemoveNonPetBattleFrames)
end
for object in pairs(E.FrameLocks) do
local obj = _G[object] or object
obj:SetParent(E.HiddenFrame)
end
end
function E:RegisterPetBattleHideFrames(object, originalParent, originalStrata)
if not object or not originalParent then
E:Print('Error. Usage: RegisterPetBattleHideFrames(object, originalParent, originalStrata)')
return
end
object = _G[object] or object
--If already doing pokemon
if E.Retail and C_PetBattles_IsInBattle() then
object:SetParent(E.HiddenFrame)
end
E.FrameLocks[object] = {
parent = originalParent,
strata = originalStrata or nil,
}
end
function E:UnregisterPetBattleHideFrames(object)
if not object then
E:Print('Error. Usage: UnregisterPetBattleHideFrames(object)')
return
end
object = _G[object] or object
--Check if object was registered to begin with
if not E.FrameLocks[object] then return end
--Change parent of object back to original parent
local originalParent = E.FrameLocks[object].parent
if originalParent then
object:SetParent(originalParent)
end
--Change strata of object back to original
local originalStrata = E.FrameLocks[object].strata
if originalStrata then
object:SetFrameStrata(originalStrata)
end
--Remove object from table
E.FrameLocks[object] = nil
end
function E:RegisterObjectForVehicleLock(object, originalParent)
if not object or not originalParent then
E:Print('Error. Usage: RegisterObjectForVehicleLock(object, originalParent)')
return
end
object = _G[object] or object
--Entering/Exiting vehicles will often happen in combat.
--For this reason we cannot allow protected objects.
if object.IsProtected and object:IsProtected() then
E:Print('Error. Object is protected and cannot be changed in combat.')
return
end
--Check if we are already in a vehicles
if E.Retail and UnitHasVehicleUI('player') then
object:SetParent(E.HiddenFrame)
end
--Add object to table
E.VehicleLocks[object] = originalParent
end
function E:UnregisterObjectForVehicleLock(object)
if not object then
E:Print('Error. Usage: UnregisterObjectForVehicleLock(object)')
return
end
object = _G[object] or object
--Check if object was registered to begin with
if not E.VehicleLocks[object] then
return
end
--Change parent of object back to original parent
local originalParent = E.VehicleLocks[object]
if originalParent then
object:SetParent(originalParent)
end
--Remove object from table
E.VehicleLocks[object] = nil
end
function E:EnterVehicleHideFrames(_, unit)
if unit ~= 'player' then return end
for object in pairs(E.VehicleLocks) do
object:SetParent(E.HiddenFrame)
end
end
function E:ExitVehicleShowFrames(_, unit)
if unit ~= 'player' then return end
for object, originalParent in pairs(E.VehicleLocks) do
object:SetParent(originalParent)
end
end
function E:RequestBGInfo()
RequestBattlefieldScoreData()
end
function E:PLAYER_ENTERING_WORLD(_, initLogin, isReload)
E:CheckRole()
if initLogin or not ElvDB.DisabledAddOns then
ElvDB.DisabledAddOns = {}
end
if initLogin or isReload then
E:CheckIncompatible()
end
if not E.MediaUpdated then
E:UpdateMedia()
E.MediaUpdated = true
end
-- Blizzard will set this value to int(60/CVar cameraDistanceMax)+1 at logout if it is manually set higher than that
if not E.Retail and E.db.general.lockCameraDistanceMax then
SetCVar('cameraDistanceMaxZoomFactor', E.db.general.cameraDistanceMax)
end
local _, instanceType = GetInstanceInfo()
if instanceType == 'pvp' then
E.BGTimer = E:ScheduleRepeatingTimer('RequestBGInfo', 5)
E:RequestBGInfo()
elseif E.BGTimer then
E:CancelTimer(E.BGTimer)
E.BGTimer = nil
end
end
function E:PLAYER_REGEN_ENABLED()
if E.ShowOptionsUI then
E:ToggleOptionsUI()
E.ShowOptionsUI = nil
end
end
function E:PLAYER_REGEN_DISABLED()
local err
if IsAddOnLoaded('ElvUI_OptionsUI') then
local ACD = E.Libs.AceConfigDialog
if ACD and ACD.OpenFrames and ACD.OpenFrames.ElvUI then
ACD:Close('ElvUI')
err = true
end
end
if E.CreatedMovers then
for name in pairs(E.CreatedMovers) do
local mover = _G[name]
if mover and mover:IsShown() then
mover:Hide()
err = true
end
end
end
if err then
E:Print(ERR_NOT_IN_COMBAT)
end
end
function E:XPIsUserDisabled()
return E.Retail and IsXPUserDisabled()
end
function E:XPIsTrialMax()
return E.Retail and (IsTrialAccount() or IsVeteranTrialAccount()) and (E.myLevel == 20)
end
function E:XPIsLevelMax()
return IsLevelAtEffectiveMaxLevel(E.mylevel) or E:XPIsUserDisabled() or E:XPIsTrialMax()
end
function E:GetGroupUnit(unit)
if UnitIsUnit(unit, 'player') then return end
if strfind(unit, 'party') or strfind(unit, 'raid') then
return unit
end
-- returns the unit as raid# or party# when grouped
if UnitInParty(unit) or UnitInRaid(unit) then
local isInRaid = IsInRaid()
for i = 1, GetNumGroupMembers() do
local groupUnit = (isInRaid and 'raid' or 'party')..i
if UnitIsUnit(unit, groupUnit) then
return groupUnit
end
end
end
end
function E:GetUnitBattlefieldFaction(unit)
local englishFaction, localizedFaction = UnitFactionGroup(unit)
-- this might be a rated BG or wargame and if so the player's faction might be altered
-- should also apply if `player` is a mercenary.
if unit == 'player' and E.Retail then
if C_PvP_IsRatedBattleground() or IsWargame() then
englishFaction = PLAYER_FACTION_GROUP[GetBattlefieldArenaFaction()]
localizedFaction = (englishFaction == 'Alliance' and FACTION_ALLIANCE) or FACTION_HORDE
elseif UnitIsMercenary(unit) then
if englishFaction == 'Alliance' then
englishFaction, localizedFaction = 'Horde', FACTION_HORDE
else
englishFaction, localizedFaction = 'Alliance', FACTION_ALLIANCE
end
end
end
return englishFaction, localizedFaction
end
function E:PositionGameMenuButton()
if E.Retail then
GameMenuFrame.Header.Text:SetTextColor(unpack(E.media.rgbvaluecolor))
end
GameMenuFrame:Height(GameMenuFrame:GetHeight() + GameMenuButtonLogout:GetHeight() - 4)
local button = GameMenuFrame[E.name]
button:SetFormattedText('%s%s|r', E.media.hexvaluecolor, E.name)
local _, relTo, _, _, offY = GameMenuButtonLogout:GetPoint()
if relTo ~= button then
button:ClearAllPoints()
button:Point('TOPLEFT', relTo, 'BOTTOMLEFT', 0, -1)
GameMenuButtonLogout:ClearAllPoints()
GameMenuButtonLogout:Point('TOPLEFT', button, 'BOTTOMLEFT', 0, offY)
end
end
function E:NEUTRAL_FACTION_SELECT_RESULT()
E.myfaction, E.myLocalizedFaction = UnitFactionGroup('player')
end
function E:PLAYER_LEVEL_UP(_, level)
E.mylevel = level
end
function E:LoadAPI()
E:RegisterEvent('PLAYER_LEVEL_UP')
E:RegisterEvent('PLAYER_ENTERING_WORLD')
E:RegisterEvent('PLAYER_REGEN_ENABLED')
E:RegisterEvent('PLAYER_REGEN_DISABLED')
E:RegisterEvent('UI_SCALE_CHANGED', 'PixelScaleChanged')
E:RegisterEvent('UNIT_PET', 'UpdateDispelClasses')
if E.Retail then
E:RegisterEvent('NEUTRAL_FACTION_SELECT_RESULT')
E:RegisterEvent('PET_BATTLE_CLOSE', 'AddNonPetBattleFrames')
E:RegisterEvent('PET_BATTLE_OPENING_START', 'RemoveNonPetBattleFrames')
E:RegisterEvent('PLAYER_SPECIALIZATION_CHANGED', 'CheckRole')
E:RegisterEvent('CHARACTER_POINTS_CHANGED', 'UpdateDispelClasses')
E:RegisterEvent('PLAYER_TALENT_UPDATE', 'UpdateDispelClasses')
else
E:RegisterEvent('CHARACTER_POINTS_CHANGED', 'CheckRole')
end
if E.Retail or E.Wrath then
E:RegisterEvent('UNIT_ENTERED_VEHICLE', 'EnterVehicleHideFrames')
E:RegisterEvent('UNIT_EXITED_VEHICLE', 'ExitVehicleShowFrames')
end
do -- setup cropIcon texCoords
local opt = E.db.general.cropIcon
local modifier = 0.04 * opt
for i, v in ipairs(E.TexCoords) do
if i % 2 == 0 then
E.TexCoords[i] = v - modifier
else
E.TexCoords[i] = v + modifier
end
end
end
if _G.OrderHallCommandBar then
E:HandleCommandBar()
elseif E.Retail then
local frame = CreateFrame('Frame')
frame:RegisterEvent('ADDON_LOADED')
frame:SetScript('OnEvent', function(Frame, event, addon)
if event == 'ADDON_LOADED' and addon == 'Blizzard_OrderHallUI' then
if InCombatLockdown() then
Frame:RegisterEvent('PLAYER_REGEN_ENABLED')
else
E:HandleCommandBar()
end
Frame:UnregisterEvent(event)
elseif event == 'PLAYER_REGEN_ENABLED' then
E:HandleCommandBar()
Frame:UnregisterEvent(event)
end
end)
end
local GameMenuButton = CreateFrame('Button', nil, GameMenuFrame, 'GameMenuButtonTemplate')
GameMenuButton:SetScript('OnClick', function()
E:ToggleOptionsUI() --We already prevent it from opening in combat
if not InCombatLockdown() then
HideUIPanel(GameMenuFrame)
end
end)
GameMenuFrame[E.name] = GameMenuButton
if not E:IsAddOnEnabled('ConsolePortUI_Menu') then
GameMenuButton:Size(GameMenuButtonLogout:GetWidth(), GameMenuButtonLogout:GetHeight())
GameMenuButton:Point('TOPLEFT', GameMenuButtonAddons, 'BOTTOMLEFT', 0, -1)
hooksecurefunc('GameMenuFrame_UpdateVisibleButtons', E.PositionGameMenuButton)
end
end