Initial commit
This commit is contained in:
396
src/types/unitframe.lua
Normal file
396
src/types/unitframe.lua
Normal file
@@ -0,0 +1,396 @@
|
||||
-- Copyright 2023 <omicron.me@protonmail.com>
|
||||
--
|
||||
-- This file is part of Omicron Frames
|
||||
--
|
||||
-- Omicron Frames is free software: you can redistribute it and/or modify it
|
||||
-- under the terms of the GNU General Public License as published by the Free
|
||||
-- Software Foundation, either version 3 of the License, or (at your option)
|
||||
-- any later version.
|
||||
--
|
||||
-- Omicron Frames is distributed in the hope that it will be useful, but
|
||||
-- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
-- more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License along with
|
||||
-- Omicron Frames. If not, see <https://www.gnu.org/licenses/>.
|
||||
local omif = select(2, ...)
|
||||
local types = omif.GetModule("types")
|
||||
local StatusBar = types.StatusBar
|
||||
local AuraList = types.AuraList
|
||||
local Indicator = types.Indicator
|
||||
|
||||
types.UnitFrame = types.CreateClass("UnitFrame")
|
||||
local UnitFrame = types.UnitFrame
|
||||
|
||||
|
||||
local colors = {
|
||||
hostile = {0.5, 0.0, 0.0},
|
||||
neutral = {0.7, 0.7, 0.0},
|
||||
healthy = {0, 0.1, 0},
|
||||
high = {0, 1.0, 0},
|
||||
mid = {1.0, 1.0, 0},
|
||||
low = {1.0, 0.0, 0},
|
||||
offline = {0.7, 0.7, 0.7},
|
||||
dead = {0.7, 0.7, 0.7},
|
||||
magic = {0.4, 0.4, 1.0},
|
||||
disease = {0.4, 0.2, 0.0},
|
||||
poison = {0.0, 0.7, 0.7},
|
||||
curse = {0.7, 0.0, 0.7},
|
||||
immune = {0.0, 0.2, 0.4},
|
||||
bomb = {1.0, 0.7, 0.7},
|
||||
cyan = {0.0, 0.8, 0.8},
|
||||
white = {1.0, 1.0, 1.0}
|
||||
}
|
||||
|
||||
function UnitFrame:Init(unit, width, height)
|
||||
self.unit = unit
|
||||
local secure = self:CreateSecureFrame(width, height)
|
||||
secure:Hide()
|
||||
self.hp = StatusBar:new(self, width, height)
|
||||
self.auras = AuraList:new(self)
|
||||
local overlays = CreateFrame("Frame", nil, secure)
|
||||
overlays:SetFrameStrata("MEDIUM")
|
||||
overlays:SetFrameLevel(100)
|
||||
overlays:SetAllPoints(true)
|
||||
overlays:Show()
|
||||
self.overlays = overlays
|
||||
self.indicators = {
|
||||
Indicator:new(self, self.auras, 14, "TOPLEFT", 2, -2, colors.white, 383648),
|
||||
Indicator:new(self, self.auras, 14, "TOPLEFT", 2, -2, colors.white, 974),
|
||||
Indicator:new(self, self.auras, 14, "BOTTOMLEFT", 2, 2, colors.cyan, 61295)
|
||||
}
|
||||
self:RegisterEvents()
|
||||
self:Enable()
|
||||
end
|
||||
|
||||
function UnitFrame:StartRangeTicker()
|
||||
if self.rangeTicker then
|
||||
return
|
||||
end
|
||||
local delta = 0.45 + (fastrandom(0, 100)/1000)
|
||||
self.rangeTicker = C_Timer.NewTicker(delta, function()
|
||||
self:UpdateRange()
|
||||
end)
|
||||
end
|
||||
|
||||
function UnitFrame:StopRangeTicker()
|
||||
if self.rangeTicker then
|
||||
self.rangeTicker:Cancel()
|
||||
self.rangeTicker = nil
|
||||
end
|
||||
end
|
||||
|
||||
function UnitFrame:UpdateRange()
|
||||
local unit = self.unit
|
||||
if not UnitIsFriend("player", unit) and IsSpellInRange("Lightning Bolt", unit) ~= 1 then
|
||||
self.secureFrame:SetAlpha(0.2)
|
||||
elseif UnitIsFriend("player", unit) and IsSpellInRange("Healing Surge", unit) ~= 1 then
|
||||
self.secureFrame:SetAlpha(0.2)
|
||||
else
|
||||
self.secureFrame:SetAlpha(1.0)
|
||||
end
|
||||
end
|
||||
|
||||
function UnitFrame:SetSpellAction(button, spell)
|
||||
local secure = self.secureFrame
|
||||
local attributeName = button:gsub("type", "spell")
|
||||
|
||||
secure:SetAttribute(button, "spell")
|
||||
secure:SetAttribute(attributeName, spell)
|
||||
end
|
||||
|
||||
function UnitFrame:SetMacroAction(button, macro)
|
||||
local secure = self.secureFrame
|
||||
local attributeName = button:gsub("type", "macrotext")
|
||||
|
||||
secure:SetAttribute(button, "macro")
|
||||
secure:SetAttribute(attributeName, macro:gsub("@UNIT", "@" .. self.unit))
|
||||
end
|
||||
|
||||
function UnitFrame:PrepareWheelBinds()
|
||||
-- By default you can't use the SecureUnitButtonTemplate to handle scroll
|
||||
-- wheel "clicks". We solve this by using SecureHandler*Template to bind
|
||||
-- scroll wheel to the current unit frame button when the mouse enters and
|
||||
-- to unbind it when the mouse leaves. When the keybind is triggered the
|
||||
-- button will receive a click with a custom button name
|
||||
|
||||
local secure = self.secureFrame
|
||||
local bindScript = ([[
|
||||
self:ClearBindings()
|
||||
self:SetBindingClick(false, "MOUSEWHEELUP", "BUTTONNAME", "wheel-up")
|
||||
self:SetBindingClick(false, "MOUSEWHEELDOWN", "BUTTONNAME", "wheel-down")
|
||||
self:SetBindingClick(false, "ALT-MOUSEWHEELUP", "BUTTONNAME", "alt-wheel-up")
|
||||
self:SetBindingClick(false, "ALT-MOUSEWHEELDOWN", "BUTTONNAME", "alt-wheel-down")
|
||||
]]):gsub("BUTTONNAME", "OmicronSecureFrame" .. self.unit)
|
||||
|
||||
local removeBindScript = [[
|
||||
self:ClearBindings()
|
||||
]]
|
||||
secure:SetAttribute("_onenter", bindScript)
|
||||
secure:SetAttribute("_onleave", removeBindScript)
|
||||
secure:SetAttribute("_onshow", removeBindScript)
|
||||
secure:SetAttribute("_onhide", removeBindScript)
|
||||
end
|
||||
|
||||
function UnitFrame:CreateSecureFrame(width, height)
|
||||
local name = "OmicronSecureFrame" .. self.unit
|
||||
local templates = table.concat({
|
||||
"SecureUnitButtonTemplate",
|
||||
"SecureHandlerEnterLeaveTemplate",
|
||||
"SecureHandlerShowHideTemplate",
|
||||
}, ",")
|
||||
local secure = CreateFrame("Button", name, UIParent, templates)
|
||||
self.secureFrame = secure
|
||||
secure:SetFrameStrata("MEDIUM")
|
||||
secure:SetFrameLevel(0)
|
||||
secure:SetAttribute("unit", self.unit)
|
||||
|
||||
secure:SetSize(width, height)
|
||||
|
||||
secure:RegisterForClicks("AnyDown")
|
||||
self:PrepareWheelBinds()
|
||||
|
||||
-- No modifiers
|
||||
secure:SetAttribute("type1", "target")
|
||||
self:SetMacroAction("type2", "/use [@UNIT,dead,help]Ancestral Vision; [@UNIT]Chain Heal")
|
||||
self:SetMacroAction("type3", "/use [@UNIT,dead,help]Ancestral Spirit; [@UNIT]Purify Spirit")
|
||||
self:SetMacroAction("type-wheel-up", "/use [@UNIT]Healing Surge")
|
||||
self:SetMacroAction("type-wheel-down", "/use [@UNIT,help]Riptide")
|
||||
|
||||
-- alt
|
||||
self:SetSpellAction("type-alt-wheel-up", "Healing Wave")
|
||||
self:SetSpellAction("type-alt-wheel-down", "Earth Shield")
|
||||
|
||||
-- Shift
|
||||
secure:SetAttribute("SHIFT-type2", "menu")
|
||||
|
||||
|
||||
if self.unit == "player" then
|
||||
secure.menu = function(self)
|
||||
ToggleDropDownMenu(1, nil, PlayerFrameDropDown, "cursor", 0 ,0)
|
||||
end
|
||||
elseif self.unit == "target" then
|
||||
secure.menu = function(self)
|
||||
ToggleDropDownMenu(1, nil, TargetFrameDropDown, "cursor", 0 ,0)
|
||||
end
|
||||
elseif self.unit == "focus" then
|
||||
secure.menu = function(self)
|
||||
ToggleDropDownMenu(1, nil, FocusFrameDropDown, "cursor", 0 ,0)
|
||||
end
|
||||
elseif self.unit:match("party%d") == self.unit then
|
||||
--secure.menu = function(self)
|
||||
-- ToggleDropDownMenu(1, nil, FriendsFrameDropDown, "cursor", 0 ,0)
|
||||
--end
|
||||
end
|
||||
return secure
|
||||
end
|
||||
|
||||
function UnitFrame:Enable()
|
||||
if self.unit ~= "player" then
|
||||
RegisterUnitWatch(self.secureFrame)
|
||||
else
|
||||
self.secureFrame:Show()
|
||||
end
|
||||
if UnitExists(self.unit) then
|
||||
self:UpdateAll(self:HasUnitChanged())
|
||||
end
|
||||
end
|
||||
|
||||
function UnitFrame:Disable()
|
||||
if self.unit ~= "player" then
|
||||
UnregisterUnitWatch(self.secure_frame)
|
||||
else
|
||||
self.secureFrame:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
-- returns true if this unit is a target unit
|
||||
function UnitFrame:IsTargetFrame()
|
||||
return self.unit:match(".*target") == self.unit
|
||||
end
|
||||
|
||||
-- returns the target owner
|
||||
function UnitFrame:GetTargetOwner()
|
||||
if self.unit == "target" then
|
||||
return "player"
|
||||
else
|
||||
return self.unit:sub(1, -7)
|
||||
end
|
||||
end
|
||||
|
||||
-- Set position from the center of the UIParent
|
||||
function UnitFrame:SetPosition(x, y)
|
||||
self.secureFrame:SetPoint("CENTER", UIParent, x, y)
|
||||
end
|
||||
|
||||
function UnitFrame:RegisterEvents()
|
||||
local unit = self.unit
|
||||
local secure = self.secureFrame
|
||||
|
||||
secure:SetScript("OnShow", function(frame, event, ...)
|
||||
self:OnShow()
|
||||
end)
|
||||
|
||||
secure:SetScript("OnHide", function(frame, event, ...)
|
||||
self:OnHide()
|
||||
end)
|
||||
|
||||
secure:SetScript("OnEvent", function(frame, event, ...)
|
||||
self[event](self, ...)
|
||||
end)
|
||||
|
||||
if self:IsTargetFrame() then
|
||||
local owner = self:GetTargetOwner()
|
||||
if owner == "player" then
|
||||
secure:RegisterEvent("PLAYER_TARGET_CHANGED")
|
||||
else
|
||||
secure:RegisterUnitEvent("UNIT_TARGET", self.parent:GetTargetOwner())
|
||||
end
|
||||
end
|
||||
|
||||
secure:RegisterUnitEvent("UNIT_AURA", unit)
|
||||
secure:RegisterUnitEvent("UNIT_HEALTH", unit)
|
||||
secure:RegisterUnitEvent("UNIT_MAXHEALTH", unit)
|
||||
end
|
||||
|
||||
-- returns whether or not the unit guid has changed since the last call to this
|
||||
-- function. Always returns false if the current unit does not exist.
|
||||
function UnitFrame:HasUnitChanged()
|
||||
local unit = self.unit
|
||||
if not UnitExists(unit) then
|
||||
self.guid = nil
|
||||
return false
|
||||
end
|
||||
|
||||
local guid = UnitGUID(unit)
|
||||
if self.guid == guid then
|
||||
return false
|
||||
else
|
||||
self.guid = guid
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function UnitFrame:UNIT_AURA(unit, info)
|
||||
if self:HasUnitChanged() then
|
||||
self:UpdateAll(true)
|
||||
return
|
||||
end
|
||||
if self.auras:Update(info) then
|
||||
self:UpdateHealthColor()
|
||||
end
|
||||
end
|
||||
|
||||
function UnitFrame:UNIT_HEALTH()
|
||||
if self:HasUnitChanged() then
|
||||
self:UpdateAll(true)
|
||||
return
|
||||
end
|
||||
self:UpdateHealth()
|
||||
self:UpdateHealthColor()
|
||||
end
|
||||
|
||||
function UnitFrame:UNIT_MAXHEALTH()
|
||||
if self:HasUnitChanged() then
|
||||
self:UpdateAll(true)
|
||||
return
|
||||
end
|
||||
self:UpdateHealth()
|
||||
self:UpdateHealthColor()
|
||||
end
|
||||
|
||||
function UnitFrame:UNIT_TARGET()
|
||||
self:UpdateAll(self:HasUnitChanged())
|
||||
end
|
||||
|
||||
function UnitFrame:PLAYER_TARGET_CHANGED()
|
||||
self:UpdateAll(self:HasUnitChanged())
|
||||
end
|
||||
|
||||
function UnitFrame:OnShow()
|
||||
self:StartRangeTicker()
|
||||
self:UpdateAll(self:HasUnitChanged())
|
||||
end
|
||||
|
||||
function UnitFrame:OnHide()
|
||||
self.guid = nil
|
||||
self:StopRangeTicker()
|
||||
end
|
||||
|
||||
function UnitFrame:UpdateAll(unitChanged)
|
||||
if not UnitExists(self.unit) then
|
||||
return
|
||||
end
|
||||
if unitChanged then
|
||||
self.auras:Reset()
|
||||
end
|
||||
self:UpdateHealth()
|
||||
self:UpdateHealthColor()
|
||||
self:UpdateRange()
|
||||
end
|
||||
|
||||
function UnitFrame:UpdateHealth()
|
||||
local unit = self.unit
|
||||
local val = UnitHealth(unit)
|
||||
local max = UnitHealthMax(unit)
|
||||
|
||||
self.hp:SetRange(0, max)
|
||||
self.hp:SetValue(val)
|
||||
end
|
||||
|
||||
function UnitFrame:UpdateHealthColor()
|
||||
local unit = self.unit
|
||||
local val = UnitHealth(unit)
|
||||
local max = UnitHealthMax(unit)
|
||||
|
||||
if max == 0 then
|
||||
return
|
||||
end
|
||||
local pct = val / max
|
||||
local isFriend = UnitIsFriend("player", unit)
|
||||
|
||||
if not isFriend then
|
||||
self.hp:SetColor(unpack(colors.hostile))
|
||||
elseif self.auras.statusCount["Immune"] then
|
||||
self.hp:SetColor(unpack(colors.immune))
|
||||
elseif self.auras.statusCount["Bomb"] then
|
||||
self.hp:SetColor(unpack(colors.bomb))
|
||||
elseif self.auras.statusCount["Magic"] then
|
||||
self.hp:SetColor(unpack(colors.magic))
|
||||
elseif self.auras.statusCount["Curse"] then
|
||||
self.hp:SetColor(unpack(colors.curse))
|
||||
elseif self.auras.statusCount["Poison"] then
|
||||
self.hp:SetColor(unpack(colors.poison))
|
||||
elseif self.auras.statusCount["Disease"] then
|
||||
self.hp:SetColor(unpack(colors.disease))
|
||||
elseif isFriend and UnitIsDead(unit) then
|
||||
self.hp:SetColor(unpack(colors.dead))
|
||||
elseif isFriend and pct >= 0.90 then
|
||||
self.hp:SetColor(unpack(colors.healthy))
|
||||
elseif isFriend and pct >= 0.75 then
|
||||
local progress = (pct - 0.75) / (0.90-0.75)
|
||||
self.hp:SetInterpolatedColor(colors.mid, colors.high, progress)
|
||||
else
|
||||
local progress = pct / 0.75
|
||||
-- Quadratic progress so we get to the dangerous colors faster as hp
|
||||
-- goes lower
|
||||
progress = progress * progress
|
||||
self.hp:SetInterpolatedColor(colors.low, colors.mid, progress)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
-- UNIT_AURA
|
||||
-- UNIT_CLASSIFICATION_CHANGED
|
||||
-- UNIT_COMBAT
|
||||
-- UNIT_CONNECTION
|
||||
-- UNIT_DISPLAYPOWER
|
||||
-- UNIT_FACTION
|
||||
-- UNIT_FLAGS
|
||||
-- UNIT_LEVEL
|
||||
-- UNIT_MANA
|
||||
-- UNIT_HEALTH_PREDICTION
|
||||
-- UNIT_PHASE
|
||||
]]--
|
||||
|
Reference in New Issue
Block a user