Files
ice-hud/IceHUD.lua
Parnic 2a98dc961d Fixed up debugging
Apparently isFirst is no longer passed to OnEnable, so we were never setting a debug frame.
Made the additional arguments that were being passed to Debug() actually do something (currently print out a stringified version of themselves in a space-delimited string)
2016-05-17 23:19:14 -05:00

741 lines
19 KiB
Lua

local L = LibStub("AceLocale-3.0"):GetLocale("IceHUD", false)
IceHUD = LibStub("AceAddon-3.0"):NewAddon("IceHUD", "AceConsole-3.0")
local IceHUD = IceHUD
local SML = LibStub("LibSharedMedia-3.0")
local ACR = LibStub("AceConfigRegistry-3.0")
local ConfigDialog = LibStub("AceConfigDialog-3.0")
local icon = LibStub("LibDBIcon-1.0")
local AceGUI = LibStub("AceGUI-3.0")
local AceSerializer = LibStub("AceSerializer-3.0", 1)
local pendingModuleLoads = {}
local bReadyToRegisterModules = false
IceHUD.CurrTagVersion = 3
IceHUD.debugging = false
IceHUD.WowVer = select(4, GetBuildInfo())
IceHUD.validBarList = { "Bar", "HiBar", "RoundBar", "ColorBar", "RivetBar", "RivetBar2", "CleanCurves", "GlowArc",
"BloodGlaives", "ArcHUD", "FangRune", "DHUD", "CleanCurvesOut", "CleanTank", "PillTank", "GemTank" }
IceHUD.validCustomModules = {Bar="Buff/Debuff watcher", Counter="Buff/Debuff stack counter", CD="Cooldown bar", Health="Health bar", Mana="Mana bar", CounterBar="Stack count bar"}
--@debug@
IceHUD.optionsLoaded = true
--@end-debug@
local function deepcopy(object)
local lookup_table = {}
local function _copy(object)
if type(object) ~= "table" then
return object
elseif lookup_table[object] then
return lookup_table[object]
end
local new_table = {}
lookup_table[object] = new_table
for index, value in pairs(object) do
new_table[_copy(index)] = _copy(value)
end
return setmetatable(new_table, getmetatable(object))
end
return _copy(object)
end
IceHUD.deepcopy = deepcopy
IceHUD.Location = "Interface\\AddOns\\IceHUD"
StaticPopupDialogs["ICEHUD_CUSTOM_BAR_CREATED"] =
{
text = L["A custom bar has been created and can be configured through Module Settings => MyCustomBar. It is highly recommended that you change the bar name of this module so that it's easier to identify."],
button1 = OKAY,
timeout = 0,
whileDead = 1,
hideOnEscape = 0,
OnShow = function(self)
self:SetFrameStrata("TOOLTIP")
end,
OnHide = function(self)
self:SetFrameStrata("DIALOG")
end,
}
StaticPopupDialogs["ICEHUD_CUSTOM_COUNTER_CREATED"] =
{
text = L["A custom counter has been created and can be configured through Module Settings => MyCustomCounter. It is highly recommended that you change the bar name of this module so that it's easier to identify."],
button1 = OKAY,
timeout = 0,
whileDead = 1,
hideOnEscape = 0,
OnShow = function(self)
self:SetFrameStrata("TOOLTIP")
end,
OnHide = function(self)
self:SetFrameStrata("DIALOG")
end,
}
StaticPopupDialogs["ICEHUD_CUSTOM_COUNTER_BAR_CREATED"] =
{
text = L["A custom counter bar has been created and can be configured through Module Settings => MyCustomCounterBar. It is highly recommended that you change the bar name of this module so that it's easier to identify."],
button1 = OKAY,
timeout = 0,
whileDead = 1,
hideOnEscape = 0,
OnShow = function(self)
self:SetFrameStrata("TOOLTIP")
end,
OnHide = function(self)
self:SetFrameStrata("DIALOG")
end,
}
StaticPopupDialogs["ICEHUD_CUSTOM_CD_CREATED"] =
{
text = L["A custom cooldown bar has been created and can be configured through Module Settings => MyCustomCD. It is highly recommended that you change the bar name of this module so that it's easier to identify."],
button1 = OKAY,
timeout = 0,
whileDead = 1,
hideOnEscape = 0,
OnShow = function(self)
self:SetFrameStrata("TOOLTIP")
end,
OnHide = function(self)
self:SetFrameStrata("DIALOG")
end,
}
StaticPopupDialogs["ICEHUD_CUSTOM_HEALTH_CREATED"] =
{
text = L["A custom health bar has been created and can be configured through Module Settings => MyCustomHealth. It is highly recommended that you change the bar name of this module so that it's easier to identify."],
button1 = OKAY,
timeout = 0,
whileDead = 1,
hideOnEscape = 0,
OnShow = function(self)
self:SetFrameStrata("TOOLTIP")
end,
OnHide = function(self)
self:SetFrameStrata("DIALOG")
end,
}
StaticPopupDialogs["ICEHUD_CUSTOM_MANA_CREATED"] =
{
text = L["A custom mana bar has been created and can be configured through Module Settings => MyCustomMana. It is highly recommended that you change the bar name of this module so that it's easier to identify."],
button1 = OKAY,
timeout = 0,
whileDead = 1,
hideOnEscape = 0,
OnShow = function(self)
self:SetFrameStrata("TOOLTIP")
end,
OnHide = function(self)
self:SetFrameStrata("DIALOG")
end,
}
StaticPopupDialogs["ICEHUD_DELETE_CUSTOM_MODULE"] =
{
text = L["Are you sure you want to delete this module? This will remove all settings associated with it and cannot be un-done."],
button1 = YES,
button2 = NO,
timeout = 0,
whileDead = 1,
hideOnEscape = 0,
OnShow = function(self)
self:SetFrameStrata("TOOLTIP")
end,
OnHide = function(self)
self:SetFrameStrata("DIALOG")
end,
OnAccept = function(self)
IceHUD.IceCore:DeleteDynamicModule(self.data)
self.data = nil
end,
}
StaticPopupDialogs["ICEHUD_CHANGED_DOGTAG"] = {
text = L["This option requires the UI to be reloaded. Do you wish to reload it now?"],
button1 = YES,
OnShow = function(self)
self:SetFrameStrata("TOOLTIP")
end,
OnHide = function(self)
self:SetFrameStrata("DIALOG")
end,
OnAccept = function()
ReloadUI()
end,
button2 = NO,
timeout = 0,
whileDead = 1,
hideOnEscape = 0
}
StaticPopupDialogs["ICEHUD_CHANGED_PROFILE_COMBAT"] = {
text = L["You have changed IceHUD profiles while in combat. This can cause problems due to Blizzard's secure frame policy. You may need to reload your UI to repair IceHUD."],
button1 = OKAY,
OnShow = function(self)
self:SetFrameStrata("TOOLTIP")
end,
OnHide = function(self)
self:SetFrameStrata("DIALOG")
end,
timeout = 0,
whileDead = 1,
hideOnEscape = 0
}
function IceHUD:OnInitialize()
self:SetDebugging(false)
self:Debug("IceHUD:OnInitialize()")
self.IceCore = IceCore:new()
self:RegisterPendingModules()
self.IceCore:SetupDefaults()
bReadyToRegisterModules = true
self.db = LibStub("AceDB-3.0"):New("IceCoreDB", self.IceCore.defaults, true)
if not self.db or not self.db.global or not self.db.profile then
print(L["Error: IceHUD database not loaded correctly. Please exit out of WoW and delete the database file (IceHUD.lua) found in: \\World of Warcraft\\WTF\\Account\\<Account Name>>\\SavedVariables\\"])
return
end
self.db.RegisterCallback(self, "OnProfileShutdown", "PreProfileChanged")
self.db.RegisterCallback(self, "OnProfileChanged", "PostProfileChanged")
self.db.RegisterCallback(self, "OnProfileReset", "ProfileReset")
self.db.RegisterCallback(self, "OnProfileCopied", "ProfileCopied")
self:NotifyNewDb()
ConfigDialog:SetDefaultSize("IceHUD", 750, 650)
self:RegisterChatCommand("icehud", function()
if not UnitAffectingCombat("player") then
IceHUD:OpenConfig()
else
DEFAULT_CHAT_FRAME:AddMessage(L["|cff8888ffIceHUD|r: Combat lockdown restriction. Leave combat and try again."])
end
end)
self:RegisterChatCommand("rl", function() ReloadUI() end)
-- hack to allow /icehudcl to continue to function by loading the LoD options module and then re-calling the command
--[===[@non-debug@
self:RegisterChatCommand("icehudcl", function(arg)
self:UnregisterChatCommand("icehudcl")
self:LoadOptions()
LibStub("AceConfigCmd-3.0"):HandleCommand("icehudcl", "IceHUD", arg)
end)
--@end-non-debug@]===]
self:SyncSettingsVersions()
self:InitLDB()
if SML then
SML.RegisterCallback(self, "LibSharedMedia_Registered", "UpdateMedia")
end
end
function IceHUD:NotifyNewDb()
self.IceCore.accountSettings = self.db.global
self.IceCore.settings = self.db.profile
self.IceCore:SetModuleDatabases()
self.IceCore:CheckDisplayUpdateMessage()
end
function IceHUD:NotifyOptionsChange()
if ACR then
ACR:NotifyChange("IceHUD")
end
end
function IceHUD:OnEnable(isFirst)
self:Debug("IceHUD:OnEnable()")
if self.db.profile.enable then
self.IceCore:Enable()
end
-- Add dual-spec support
local LibDualSpec = LibStub('LibDualSpec-1.0', true)
if LibDualSpec then
LibDualSpec:EnhanceDatabase(self.db, "IceHUD")
end
--@debug@
IceHUD_Options:OnLoad()
--@end-debug@
-- if isFirst then
self:SetDebugging(self.IceCore:GetDebug())
self.debugFrame = ChatFrame1
-- end
end
-- add settings changes/updates here so that existing users don't lose their settings
function IceHUD:SyncSettingsVersions()
if not self.IceCore.settings.updatedOocNotFull then
self.IceCore.settings.updatedOocNotFull = true
self.IceCore.settings.alphaNotFull = self.IceCore.settings.alphaTarget
self.IceCore.settings.alphaNotFullbg = self.IceCore.settings.alphaTargetbg
end
end
function IceHUD:InitLDB()
local LDB = LibStub and LibStub("LibDataBroker-1.1", true)
if (LDB) then
local ldbButton = LDB:NewDataObject("IceHUD", {
type = "launcher",
text = L["IceHUD"],
label = L["IceHUD"],
icon = "Interface\\Icons\\Spell_Frost_Frost",
OnClick = function(button, msg)
if not UnitAffectingCombat("player") then
IceHUD:OpenConfig()
else
DEFAULT_CHAT_FRAME:AddMessage(L["|cff8888ffIceHUD|r: Combat lockdown restriction. Leave combat and try again."])
end
end,
})
if icon then
icon:Register("IceHUD", ldbButton, self.db.profile.minimap)
end
if ldbButton then
function ldbButton:OnTooltipShow()
self:AddLine(L["IceHUD"] .. " @project-version@")
self:AddLine(L["Click to open IceHUD options."], 1, 1, 1)
end
end
end
end
-- blizzard interface options
local blizOptionsPanel = CreateFrame("FRAME", "IceHUDConfigPanel", UIParent)
blizOptionsPanel.name = "IceHUD"
blizOptionsPanel.button = CreateFrame("BUTTON", "IceHUDOpenConfigButton", blizOptionsPanel, IceHUD.WowVer >= 50000 and "UIPanelButtonTemplate" or "UIPanelButtonTemplate2")
blizOptionsPanel.button:SetText("Open IceHUD configuration")
blizOptionsPanel.button:SetWidth(240)
blizOptionsPanel.button:SetHeight(30)
blizOptionsPanel.button:SetScript("OnClick", function(self) HideUIPanel(InterfaceOptionsFrame) HideUIPanel(GameMenuFrame) IceHUD:OpenConfig() end)
blizOptionsPanel.button:SetPoint('TOPLEFT', blizOptionsPanel, 'TOPLEFT', 20, -20)
InterfaceOptions_AddCategory(blizOptionsPanel)
function IceHUD:OpenConfig()
if not ConfigDialog then return end
if not self:LoadOptions() then
return
end
if ConfigDialog.OpenFrames["IceHUD"] ~= nil then
ConfigDialog:Close("IceHUD")
else
ConfigDialog:Open("IceHUD")
end
end
function IceHUD:LoadOptions()
if not self.optionsLoaded then
local loaded, reason = LoadAddOn("IceHUD_Options")
if not loaded then
print("Failed to load options module. Reason: " .. reason)
return false
else
self.optionsLoaded = true
end
end
return true
end
function IceHUD:Debug(...)
if self.debugging then
local msg = ""
for n=1,select('#', ...) do
msg = msg .. tostring(select(n, ...)) .. " "
end
self.debugFrame:AddMessage(msg)
end
end
function IceHUD:SetDebugging(bIsDebugging)
self.debugging = bIsDebugging
end
-- rounding stuff
function IceHUD:MathRound(num, idp)
if not num then
return nil
end
local mult = 10^(idp or 0)
return math.floor(num * mult + 0.5) / mult
end
function IceHUD:GetBuffCount(unit, ability, onlyMine, matchByName)
return IceHUD:GetAuraCount("HELPFUL", unit, ability, onlyMine, matchByName)
end
function IceHUD:GetDebuffCount(unit, ability, onlyMine, matchByName)
return IceHUD:GetAuraCount("HARMFUL", unit, ability, onlyMine, matchByName)
end
function IceHUD:GetAuraCount(auraType, unit, ability, onlyMine, matchByName)
if not unit or not ability then
return 0
end
if unit == "main hand weapon" or unit == "off hand weapon" then
local hasMainHandEnchant, mainHandExpiration, mainHandCharges, mainHandEnchantID, hasOffHandEnchant, offHandExpiration, offHandCharges, offHandEnchantID
= GetWeaponEnchantInfo()
if unit == "main hand weapon" and hasMainHandEnchant then
return mainHandCharges
elseif unit == "off hand weapon" and hasOffHandEnchant then
return offHandCharges
end
return 0
end
local i = 1
local name, _, texture, applications = UnitAura(unit, i, auraType..(onlyMine and "|PLAYER" or ""))
while name do
if (not matchByName and string.match(texture:upper(), ability:upper()))
or (matchByName and string.match(name:upper(), ability:upper())) then
return applications
end
i = i + 1
name, _, texture, applications = UnitAura(unit, i, auraType..(onlyMine and "|PLAYER" or ""))
end
return 0
end
do
local retval = {}
function IceHUD:HasBuffs(unit, spellIDs)
for i=1, #spellIDs do
retval[i] = false
end
local i = 1
local name, _, texture, applications, _, _, _, _, _, _, auraID = UnitAura(unit, i)
while name do
for i=1, #spellIDs do
if spellIDs[i] == auraID then
retval[i] = applications == 0 and true or applications
break
end
end
i = i + 1
name, _, texture, applications, _, _, _, _, _, _, auraID = UnitAura(unit, i)
end
return retval
end
end
function IceHUD:OnDisable()
IceHUD.IceCore:Disable()
end
function IceHUD:PreProfileChanged(db)
if UnitAffectingCombat("player") then
StaticPopup_Show("ICEHUD_CHANGED_PROFILE_COMBAT")
end
self.IceCore:Disable()
end
function IceHUD:PostProfileChanged(db, newProfile)
self:NotifyNewDb()
if self.db.profile.enable then
self.IceCore:Enable()
end
end
function IceHUD:ProfileReset()
ReloadUI()
end
function IceHUD:ProfileCopied()
ReloadUI()
end
function IceHUD:Clamp(value, min, max)
if value < min then
value = min
elseif value > max then
value = max
elseif value ~= value or not (value >= min and value <= max) then -- check for nan...
value = min
end
return value
end
function IceHUD:ShouldSwapToVehicle(...)
local arg1, arg2 = ...
if (arg1 == "player") then
if (arg2) then
return true
end
end
if (UnitHasVehicleUI("player")) then
return true
end
end
function IceHUD:xor(val1, val2)
return val1 and not val2 or val2 and not val1
end
function IceHUD:GetSelectValue(info, val)
for k,v in pairs(info.option.values) do
if v == val then
return k
end
end
return 1
end
function IceHUD:Register(element)
assert(element, "Trying to register a nil module")
if not bReadyToRegisterModules then
pendingModuleLoads[#pendingModuleLoads+1] = element
else
self.IceCore:Register(element)
end
end
function IceHUD:RegisterPendingModules()
for i=1, #pendingModuleLoads do
self.IceCore:Register(pendingModuleLoads[i])
end
pendingModuleLoads = {}
end
function IceHUD:UpdateMedia(event, mediatype, key)
if not self.db.profile or not self.IceCore.enabled then
return
end
if mediatype == "font" then
if key == self.db.profile.fontFamily then
IceHUD.IceCore:SetFontFamily(key)
end
elseif mediatype == "statusbar" then
if self.TargetOfTarget and self.TargetOfTarget.moduleSettings.enabled and key == self.TargetOfTarget.moduleSettings.texture then
self.TargetOfTarget:Redraw()
end
end
end
function IceHUD:CreateCustomModuleAndNotify(moduleKey, settings)
local newMod = nil
local popupMsg
if moduleKey == "Bar" then -- custom bar
newMod = IceCustomBar:new()
popupMsg = "ICEHUD_CUSTOM_BAR_CREATED"
elseif moduleKey == "Counter" then -- custom counter
newMod = IceCustomCount:new()
popupMsg = "ICEHUD_CUSTOM_COUNTER_CREATED"
elseif moduleKey == "CounterBar" then -- custom counter bar
newMod = IceCustomCounterBar:new()
popupMsg = "ICEHUD_CUSTOM_COUNTER_BAR_CREATED"
elseif moduleKey == "CD" then -- cooldown bar
newMod = IceCustomCDBar:new()
popupMsg = "ICEHUD_CUSTOM_CD_CREATED"
elseif moduleKey == "Health" then -- custom health bar
newMod = IceCustomHealth:new()
popupMsg = "ICEHUD_CUSTOM_HEALTH_CREATED"
elseif moduleKey == "Mana" then -- custom mana bar
newMod = IceCustomMana:new()
popupMsg = "ICEHUD_CUSTOM_MANA_CREATED"
end
if newMod ~= nil then
IceHUD.IceCore:AddNewDynamicModule(newMod, settings)
ConfigDialog:SelectGroup("IceHUD", "modules", newMod.elementName)
StaticPopup_Show(popupMsg)
end
end
local function CheckLFGMode(mode)
return (mode ~= nil and mode ~= "abandonedInDungeon" and mode ~= "queued")
end
function IceHUD:GetIsInLFGGroup()
local mode, submode
if IceHUD.WowVer >= 50000 then
mode, submode = GetLFGMode(LE_LFG_CATEGORY_LFD)
else
mode, submode = GetLFGMode()
end
local IsInLFGGroup = CheckLFGMode(mode)
if IceHUD.WowVer < 50000 then
return IsInLFGGroup
end
if not IsInLFGGroup then
mode, submode = GetLFGMode(LE_LFG_CATEGORY_RF)
IsInLFGGroup = CheckLFGMode(mode)
end
if not IsInLFGGroup then
mode, submode = GetLFGMode(LE_LFG_CATEGORY_SCENARIO)
IsInLFGGroup = CheckLFGMode(mode)
end
if not IsInLFGGroup then
mode, submode = GetLFGMode(LE_LFG_CATEGORY_LFR)
IsInLFGGroup = CheckLFGMode(mode)
end
return IsInLFGGroup
end
local BLACKLISTED_UNIT_MENU_OPTIONS = {
SET_FOCUS = "ICEHUD_SET_FOCUS",
CLEAR_FOCUS = "ICEHUD_CLEAR_FOCUS",
PET_DISMISS = "ICEHUD_PET_DISMISS",
LOCK_FOCUS_FRAME = true,
UNLOCK_FOCUS_FRAME = true,
}
UnitPopupButtons["ICEHUD_SET_FOCUS"] = {
text = L["Type %s to set focus"]:format(SLASH_FOCUS1),
tooltipText = L["Blizzard currently does not provide a proper way to right-click focus with custom unit frames."],
dist = 0,
}
UnitPopupButtons["ICEHUD_CLEAR_FOCUS"] = {
text = L["Type %s to clear focus"]:format(SLASH_CLEARFOCUS1),
tooltipText = L["Blizzard currently does not provide a proper way to right-click focus with custom unit frames."],
dist = 0,
}
UnitPopupButtons["ICEHUD_PET_DISMISS"] = {
text = L["Use your Dismiss Pet spell to dismiss a pet"],
tooltipText = L["Blizzard currently does not provide a proper way to right-click dismiss a pet with custom unit frames."],
dist = 0,
}
local munged_unit_menus = {}
local function munge_unit_menu(menu)
local result = munged_unit_menus[menu]
if result then
return result
end
if not UnitPopupMenus then
munged_unit_menus[menu] = menu
return menu
end
local data = UnitPopupMenus[menu]
if not data then
munged_unit_menus[menu] = menu
return menu
end
local found = false
local _, v
for _, v in ipairs(data) do
if BLACKLISTED_UNIT_MENU_OPTIONS[v] then
found = true
break
end
end
if not found then
-- nothing to remove or add, we're all fine here.
munged_unit_menus[menu] = menu
return menu
end
local new_data = {}
for _, v in ipairs(data) do
local blacklisted = BLACKLISTED_UNIT_MENU_OPTIONS[v]
if v == "PET_DISMISS" and select(2, UnitClass("player")) == "WARLOCK" then
blacklisted = false
end
if not blacklisted then
new_data[#new_data+1] = v
elseif blacklisted ~= true then
new_data[#new_data+1] = blacklisted
end
end
local new_menu_name = "ICEHUD_" .. menu
UnitPopupMenus[new_menu_name] = new_data
munged_unit_menus[menu] = new_menu_name
return new_menu_name
end
IceHUD.MungeUnitMenu = munge_unit_menu
local function figure_unit_menu(unit)
if unit == "focus" then
return "FOCUS"
end
if UnitIsUnit(unit, "player") then
return "SELF"
end
if UnitIsUnit(unit, "vehicle") then
-- NOTE: vehicle check must come before pet check for accuracy's sake because
-- a vehicle may also be considered your pet
return "VEHICLE"
end
if UnitIsUnit(unit, "pet") then
return "PET"
end
if not UnitIsPlayer(unit) then
return "TARGET"
end
local id = UnitInRaid(unit)
if id then
return "RAID_PLAYER", id
end
if UnitInParty(unit) then
return "PARTY"
end
return "PLAYER"
end
IceHUD_UnitFrame_DropDown = CreateFrame("Frame", "IceHUD_UnitFrame_DropDown", UIParent, "UIDropDownMenuTemplate")
UnitPopupFrames[#UnitPopupFrames+1] = "IceHUD_UnitFrame_DropDown"
IceHUD.DropdownUnit = nil
UIDropDownMenu_Initialize(IceHUD_UnitFrame_DropDown, function()
if not IceHUD.DropdownUnit then
return
end
local menu, id = figure_unit_menu(IceHUD.DropdownUnit)
if menu then
menu = IceHUD.MungeUnitMenu(menu)
UnitPopup_ShowMenu(IceHUD_UnitFrame_DropDown, menu, IceHUD.DropdownUnit, nil, id)
end
end, "MENU", nil)