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.
2568 lines
83 KiB
2568 lines
83 KiB
|
|
local detailsFramework = _G["DetailsFramework"]
|
|
|
|
if (not detailsFramework or not DetailsFrameworkCanLoad) then
|
|
return
|
|
end
|
|
|
|
local _
|
|
--lua locals
|
|
local rawset = rawset --lua local
|
|
local rawget = rawget --lua local
|
|
local setmetatable = setmetatable --lua local
|
|
local unpack = table.unpack or unpack --lua local
|
|
local type = type --lua local
|
|
local floor = math.floor --lua local
|
|
local loadstring = loadstring --lua local
|
|
local G_CreateFrame = _G.CreateFrame
|
|
local CreateFrame = function (frameType , name, parent, template, id)
|
|
local frame = G_CreateFrame(frameType , name, parent, template, id)
|
|
detailsFramework:Mixin(frame, detailsFramework.FrameFunctions)
|
|
frame:SetClipsChildren(false)
|
|
return frame
|
|
end
|
|
|
|
local UnitHealth = UnitHealth
|
|
local UnitHealthMax = UnitHealthMax
|
|
local UnitGetIncomingHeals = UnitGetIncomingHeals
|
|
local UnitGetTotalHealAbsorbs = UnitGetTotalHealAbsorbs
|
|
local UnitGetTotalAbsorbs = UnitGetTotalAbsorbs
|
|
local UnitPowerMax = UnitPowerMax
|
|
local UnitPower = UnitPower
|
|
local UnitPowerBarID = UnitPowerBarID
|
|
local GetUnitPowerBarInfoByID = GetUnitPowerBarInfoByID
|
|
local IsInGroup = IsInGroup
|
|
local UnitPowerType = UnitPowerType
|
|
local UnitIsConnected = UnitIsConnected
|
|
local UnitPlayerControlled = UnitPlayerControlled
|
|
local UnitIsTapDenied = UnitIsTapDenied
|
|
local max = math.max
|
|
local min = math.min
|
|
local abs = math.abs
|
|
local GetSpellInfo = GetSpellInfo or function(spellID) if not spellID then return nil end local si = C_Spell.GetSpellInfo(spellID) if si then return si.name, nil, si.iconID, si.castTime, si.minRange, si.maxRange, si.spellID, si.originalIconID end end
|
|
|
|
local IS_WOW_PROJECT_MAINLINE = WOW_PROJECT_ID == WOW_PROJECT_MAINLINE
|
|
local IS_WOW_PROJECT_NOT_MAINLINE = WOW_PROJECT_ID ~= WOW_PROJECT_MAINLINE
|
|
local IS_WOW_PROJECT_CLASSIC_ERA = WOW_PROJECT_ID == WOW_PROJECT_CLASSIC
|
|
|
|
local CastInfo = detailsFramework.CastInfo
|
|
|
|
local PixelUtil = PixelUtil or DFPixelUtil
|
|
|
|
local UnitGroupRolesAssigned = detailsFramework.UnitGroupRolesAssigned
|
|
local cleanfunction = function() end
|
|
|
|
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
--health bar frame
|
|
|
|
--[=[
|
|
DF:CreateHealthBar (parent, name, settingsOverride)
|
|
creates a health bar to show an unit health
|
|
@parent = frame to pass for the CreateFrame function
|
|
@name = absolute name of the frame, if omitted it uses the parent's name .. "HealthBar"
|
|
@settingsOverride = table with keys and values to replace the defaults from the framework
|
|
|
|
methods:
|
|
healthbar:SetUnit(unit)
|
|
healthBar:GetTexture()
|
|
healthBar:SetTexture(texture)
|
|
--]=]
|
|
|
|
---@class df_healthbarsettings : table
|
|
---@field CanTick boolean
|
|
---@field ShowHealingPrediction boolean
|
|
---@field ShowShields boolean
|
|
---@field BackgroundColor table
|
|
---@field Texture texturepath|textureid|atlasname
|
|
---@field ShieldIndicatorTexture texturepath|textureid|atlasname
|
|
---@field ShieldGlowTexture texturepath|textureid|atlasname
|
|
---@field ShieldGlowWidth number
|
|
---@field DontSetStatusBarTexture boolean
|
|
---@field Width number
|
|
---@field Height number
|
|
|
|
---@class df_healthbar : statusbar, df_scripthookmixin, df_statusbarmixin
|
|
---@field unit unit
|
|
---@field displayedUnit unit
|
|
---@field oldHealth number
|
|
---@field currentHealth number
|
|
---@field currentHealthMax number
|
|
---@field nextShieldHook number
|
|
---@field WidgetType string
|
|
---@field Settings df_healthbarsettings
|
|
---@field background texture
|
|
---@field incomingHealIndicator texture
|
|
---@field shieldAbsorbIndicator texture
|
|
---@field healAbsorbIndicator texture
|
|
---@field shieldAbsorbGlow texture
|
|
---@field barTexture texture
|
|
---@field SetUnit fun(self:df_healthbar, unit:unit?, displayedUnit:unit)
|
|
---@field GetTexture fun(self:df_healthbar) : texture
|
|
---@field SetTexture fun(self:df_healthbar, texture:texturepath|textureid|atlasname)
|
|
---@field SetColor fun(self:df_healthbar, red:number, green:number, blue:number, alpha:number)
|
|
---@field UpdateHealPrediction fun(self:df_healthbar)
|
|
---@field UpdateHealth fun(self:df_healthbar)
|
|
---@field UpdateMaxHealth fun(self:df_healthbar)
|
|
|
|
--healthBar meta prototype
|
|
local healthBarMetaPrototype = {
|
|
WidgetType = "healthBar",
|
|
dversion = detailsFramework.dversion,
|
|
}
|
|
|
|
--check if there's a metaPrototype already existing
|
|
if (_G[detailsFramework.GlobalWidgetControlNames["healthBar"]]) then
|
|
--get the already existing metaPrototype
|
|
local oldMetaPrototype = _G[detailsFramework.GlobalWidgetControlNames["healthBar"]]
|
|
--check if is older
|
|
if ( (not oldMetaPrototype.dversion) or (oldMetaPrototype.dversion < detailsFramework.dversion) ) then
|
|
--the version is older them the currently loading one
|
|
--copy the new values into the old metatable
|
|
for funcName, _ in pairs(healthBarMetaPrototype) do
|
|
oldMetaPrototype[funcName] = healthBarMetaPrototype[funcName]
|
|
end
|
|
end
|
|
else
|
|
--first time loading the framework
|
|
_G[detailsFramework.GlobalWidgetControlNames["healthBar"]] = healthBarMetaPrototype
|
|
end
|
|
|
|
local healthBarMetaFunctions = _G[detailsFramework.GlobalWidgetControlNames["healthBar"]]
|
|
detailsFramework:Mixin(healthBarMetaFunctions, detailsFramework.ScriptHookMixin)
|
|
|
|
--hook list
|
|
local defaultHooksForHealthBar = {
|
|
OnHide = {},
|
|
OnShow = {},
|
|
OnHealthChange = {},
|
|
OnHealthMaxChange = {},
|
|
OnAbsorbOverflow = {},
|
|
}
|
|
|
|
--use the hook already existing
|
|
healthBarMetaFunctions.HookList = healthBarMetaFunctions.HookList or defaultHooksForHealthBar
|
|
--copy the non existing values from a new version to the already existing hook table
|
|
detailsFramework.table.deploy(healthBarMetaFunctions.HookList, defaultHooksForHealthBar)
|
|
|
|
--Health Bar Meta Functions
|
|
|
|
--health bar settings
|
|
healthBarMetaFunctions.Settings = {
|
|
CanTick = false, --if true calls the method 'OnTick' every tick, the function needs to be overloaded, it receives self and deltaTime as parameters
|
|
ShowHealingPrediction = true, --when casting a healing pass, show the amount of health that spell will heal
|
|
ShowShields = true, --indicator of the amount of damage absortion the unit has
|
|
DontSetStatusBarTexture = false,
|
|
|
|
--appearance
|
|
BackgroundColor = detailsFramework:CreateColorTable (.2, .2, .2, .8),
|
|
Texture = [[Interface\RaidFrame\Raid-Bar-Hp-Fill]],
|
|
ShieldIndicatorTexture = [[Interface\RaidFrame\Shield-Fill]],
|
|
ShieldGlowTexture = [[Interface\RaidFrame\Shield-Overshield]],
|
|
ShieldGlowWidth = 16,
|
|
|
|
--default size
|
|
Width = 100,
|
|
Height = 20,
|
|
}
|
|
|
|
healthBarMetaFunctions.HealthBarEvents = {
|
|
{"PLAYER_ENTERING_WORLD"},
|
|
{"UNIT_HEALTH", true},
|
|
{"UNIT_MAXHEALTH", true},
|
|
{(IS_WOW_PROJECT_NOT_MAINLINE) and "UNIT_HEALTH_FREQUENT", true}, -- this one is classic-only...
|
|
{"UNIT_HEAL_PREDICTION", true},
|
|
{(IS_WOW_PROJECT_MAINLINE) and "UNIT_ABSORB_AMOUNT_CHANGED", true},
|
|
{(IS_WOW_PROJECT_MAINLINE) and "UNIT_HEAL_ABSORB_AMOUNT_CHANGED", true},
|
|
}
|
|
|
|
--setup the castbar to be used by another unit
|
|
healthBarMetaFunctions.SetUnit = function(self, unit, displayedUnit)
|
|
if (self.unit ~= unit or self.displayedUnit ~= displayedUnit or unit == nil) then
|
|
self.unit = unit
|
|
self.displayedUnit = displayedUnit or unit
|
|
|
|
--register events
|
|
if (unit) then
|
|
self.currentHealth = UnitHealth(unit) or 0
|
|
self.currentHealthMax = UnitHealthMax(unit) or 0
|
|
|
|
for _, eventTable in ipairs(self.HealthBarEvents) do
|
|
local event = eventTable[1]
|
|
local isUnitEvent = eventTable[2]
|
|
if event then
|
|
if (isUnitEvent) then
|
|
self:RegisterUnitEvent(event, self.displayedUnit, self.unit)
|
|
else
|
|
self:RegisterEvent(event)
|
|
end
|
|
end
|
|
end
|
|
|
|
--check for settings and update some events
|
|
if (not self.Settings.ShowHealingPrediction) then
|
|
self:UnregisterEvent("UNIT_HEAL_PREDICTION")
|
|
if IS_WOW_PROJECT_MAINLINE then
|
|
self:UnregisterEvent("UNIT_HEAL_ABSORB_AMOUNT_CHANGED")
|
|
end
|
|
self.incomingHealIndicator:Hide()
|
|
self.healAbsorbIndicator:Hide()
|
|
end
|
|
if (not self.Settings.ShowShields) then
|
|
if IS_WOW_PROJECT_MAINLINE then
|
|
self:UnregisterEvent("UNIT_ABSORB_AMOUNT_CHANGED")
|
|
end
|
|
self.shieldAbsorbIndicator:Hide()
|
|
self.shieldAbsorbGlow:Hide()
|
|
end
|
|
|
|
--set scripts
|
|
self:SetScript("OnEvent", self.OnEvent)
|
|
|
|
if (self.Settings.CanTick) then
|
|
self:SetScript("OnUpdate", self.OnTick)
|
|
end
|
|
|
|
self:PLAYER_ENTERING_WORLD(self.unit, self.displayedUnit)
|
|
else
|
|
--remove all registered events
|
|
for _, eventTable in ipairs(self.HealthBarEvents) do
|
|
local event = eventTable[1]
|
|
if event then
|
|
self:UnregisterEvent(event)
|
|
end
|
|
end
|
|
|
|
--remove scripts
|
|
self:SetScript("OnEvent", nil)
|
|
self:SetScript("OnUpdate", nil)
|
|
self:Hide()
|
|
end
|
|
end
|
|
end
|
|
|
|
healthBarMetaFunctions.Initialize = function(self)
|
|
PixelUtil.SetWidth(self, self.Settings.Width, 1)
|
|
PixelUtil.SetHeight(self, self.Settings.Height, 1)
|
|
|
|
self:SetTexture(self.Settings.Texture)
|
|
|
|
self.background:SetAllPoints()
|
|
self.background:SetColorTexture(self.Settings.BackgroundColor:GetColor())
|
|
|
|
--setpoint of these widgets are set inside the function that updates the incoming heal
|
|
self.incomingHealIndicator:SetTexture(self:GetTexture())
|
|
self.healAbsorbIndicator:SetTexture(self:GetTexture())
|
|
self.healAbsorbIndicator:SetVertexColor(.1, .8, .8)
|
|
self.shieldAbsorbIndicator:SetTexture(self.Settings.ShieldIndicatorTexture, true, true)
|
|
|
|
self.shieldAbsorbGlow:SetWidth(self.Settings.ShieldGlowWidth)
|
|
self.shieldAbsorbGlow:SetTexture(self.Settings.ShieldGlowTexture)
|
|
self.shieldAbsorbGlow:SetBlendMode("ADD")
|
|
self.shieldAbsorbGlow:SetPoint("topright", self, "topright", 8, 0)
|
|
self.shieldAbsorbGlow:SetPoint("bottomright", self, "bottomright", 8, 0)
|
|
self.shieldAbsorbGlow:Hide()
|
|
|
|
self:SetUnit(nil)
|
|
|
|
self.currentHealth = 1
|
|
self.currentHealthMax = 2
|
|
end
|
|
|
|
--call every tick
|
|
healthBarMetaFunctions.OnTick = function(self, deltaTime) end --if overrided, set 'CanTick' to true on the settings table
|
|
|
|
--when an event happen for this unit, send it to the apropriate function
|
|
healthBarMetaFunctions.OnEvent = function(self, event, ...)
|
|
local eventFunc = self[event]
|
|
if (eventFunc) then
|
|
--the function doesn't receive which event was, only 'self' and the parameters
|
|
eventFunc(self, ...)
|
|
end
|
|
end
|
|
|
|
--when the unit max health is changed
|
|
healthBarMetaFunctions.UpdateMaxHealth = function(self)
|
|
local maxHealth = UnitHealthMax(self.displayedUnit)
|
|
self:SetMinMaxValues(0, maxHealth)
|
|
self.currentHealthMax = maxHealth
|
|
|
|
if (self.OnHealthMaxChange) then --direct call
|
|
self.OnHealthMaxChange(self, self.displayedUnit)
|
|
else
|
|
self:RunHooksForWidget("OnHealthMaxChange", self, self.displayedUnit)
|
|
end
|
|
end
|
|
|
|
healthBarMetaFunctions.UpdateHealth = function(self)
|
|
-- update max health regardless to avoid weird wrong values on UpdateMaxHealth sometimes
|
|
-- local maxHealth = UnitHealthMax(self.displayedUnit)
|
|
-- self:SetMinMaxValues(0, maxHealth)
|
|
-- self.currentHealthMax = maxHealth
|
|
|
|
self.oldHealth = self.currentHealth
|
|
local health = UnitHealth(self.displayedUnit)
|
|
self.currentHealth = health
|
|
PixelUtil.SetStatusBarValue(self, health)
|
|
|
|
if (self.OnHealthChange) then --direct call
|
|
self.OnHealthChange(self, self.displayedUnit)
|
|
else
|
|
self:RunHooksForWidget("OnHealthChange", self, self.displayedUnit)
|
|
end
|
|
end
|
|
|
|
--health and absorbs prediction
|
|
healthBarMetaFunctions.UpdateHealPrediction = function(self)
|
|
local currentHealth = self.currentHealth
|
|
local currentHealthMax = self.currentHealthMax
|
|
|
|
if (not currentHealthMax or currentHealthMax <= 0) then
|
|
return
|
|
end
|
|
|
|
local healthPercent = currentHealth / currentHealthMax
|
|
|
|
--order is: the health of the unit > damage absorb > heal absorb > incoming heal
|
|
local width = self:GetWidth()
|
|
|
|
if (self.Settings.ShowHealingPrediction) then
|
|
--incoming heal on the unit from all sources
|
|
local unitHealIncoming = self.displayedUnit and UnitGetIncomingHeals(self.displayedUnit) or 0
|
|
--heal absorbs
|
|
local unitHealAbsorb = IS_WOW_PROJECT_MAINLINE and self.displayedUnit and UnitGetTotalHealAbsorbs(self.displayedUnit) or 0
|
|
|
|
if (unitHealIncoming > 0) then
|
|
--calculate what is the percent of health incoming based on the max health the player has
|
|
local incomingPercent = unitHealIncoming / currentHealthMax
|
|
self.incomingHealIndicator:Show()
|
|
self.incomingHealIndicator:SetWidth(max(1, min (width * incomingPercent, abs(healthPercent - 1) * width)))
|
|
self.incomingHealIndicator:SetPoint("topleft", self, "topleft", width * healthPercent, 0)
|
|
self.incomingHealIndicator:SetPoint("bottomleft", self, "bottomleft", width * healthPercent, 0)
|
|
else
|
|
self.incomingHealIndicator:Hide()
|
|
end
|
|
|
|
if (unitHealAbsorb > 0) then
|
|
local healAbsorbPercent = unitHealAbsorb / currentHealthMax
|
|
self.healAbsorbIndicator:Show()
|
|
self.healAbsorbIndicator:SetWidth(max(1, min (width * healAbsorbPercent, abs(healthPercent - 1) * width)))
|
|
self.healAbsorbIndicator:SetPoint("topleft", self, "topleft", width * healthPercent, 0)
|
|
self.healAbsorbIndicator:SetPoint("bottomleft", self, "bottomleft", width * healthPercent, 0)
|
|
else
|
|
self.healAbsorbIndicator:Hide()
|
|
end
|
|
end
|
|
|
|
if (self.Settings.ShowShields and IS_WOW_PROJECT_MAINLINE) then
|
|
--damage absorbs
|
|
local unitDamageAbsorb = self.displayedUnit and UnitGetTotalAbsorbs (self.displayedUnit) or 0
|
|
|
|
if (unitDamageAbsorb > 0) then
|
|
local damageAbsorbPercent = unitDamageAbsorb / currentHealthMax
|
|
self.shieldAbsorbIndicator:Show()
|
|
--set the width where the max width size is what is lower: the absorb size or the missing amount of health in the health bar
|
|
--/dump NamePlate1PlaterUnitFrameHealthBar.shieldAbsorbIndicator:GetSize()
|
|
self.shieldAbsorbIndicator:SetWidth(max(1, min (width * damageAbsorbPercent, abs(healthPercent - 1) * width)))
|
|
self.shieldAbsorbIndicator:SetPoint("topleft", self, "topleft", width * healthPercent, 0)
|
|
self.shieldAbsorbIndicator:SetPoint("bottomleft", self, "bottomleft", width * healthPercent, 0)
|
|
|
|
--if the absorb percent pass 100%, show the glow
|
|
if ((healthPercent + damageAbsorbPercent) > 1) then
|
|
self.nextShieldHook = self.nextShieldHook or 0
|
|
|
|
if (GetTime() >= self.nextShieldHook) then
|
|
self:RunHooksForWidget("OnAbsorbOverflow", self, self.displayedUnit, healthPercent + damageAbsorbPercent - 1)
|
|
self.nextShieldHook = GetTime() + 0.2
|
|
end
|
|
|
|
self.shieldAbsorbGlow:Show()
|
|
else
|
|
self.shieldAbsorbGlow:Hide()
|
|
if (self.nextShieldHook) then
|
|
self:RunHooksForWidget("OnAbsorbOverflow", self, self.displayedUnit, 0)
|
|
self.nextShieldHook = nil
|
|
end
|
|
end
|
|
else
|
|
self.shieldAbsorbIndicator:Hide()
|
|
self.shieldAbsorbGlow:Hide()
|
|
if (self.nextShieldHook) then
|
|
self:RunHooksForWidget("OnAbsorbOverflow", self, self.displayedUnit, 0)
|
|
self.nextShieldHook = nil
|
|
end
|
|
end
|
|
else
|
|
self.shieldAbsorbIndicator:Hide()
|
|
self.shieldAbsorbGlow:Hide()
|
|
if (self.nextShieldHook) then
|
|
self:RunHooksForWidget("OnAbsorbOverflow", self, self.displayedUnit, 0)
|
|
self.nextShieldHook = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
--Health Events
|
|
healthBarMetaFunctions.PLAYER_ENTERING_WORLD = function(self, ...)
|
|
self:UpdateMaxHealth()
|
|
self:UpdateHealth()
|
|
self:UpdateHealPrediction()
|
|
end
|
|
|
|
healthBarMetaFunctions.UNIT_HEALTH = function(self, unitId)
|
|
self:UpdateHealth()
|
|
self:UpdateHealPrediction()
|
|
end
|
|
|
|
healthBarMetaFunctions.UNIT_MAXHEALTH = function(self, unitId)
|
|
self:UpdateMaxHealth()
|
|
self:UpdateHealth()
|
|
self:UpdateHealPrediction()
|
|
end
|
|
|
|
healthBarMetaFunctions.UNIT_HEALTH_FREQUENT = function(self, ...)
|
|
self:UpdateHealth()
|
|
self:UpdateHealPrediction()
|
|
end
|
|
|
|
healthBarMetaFunctions.UNIT_HEAL_PREDICTION = function(self, ...)
|
|
self:UpdateMaxHealth()
|
|
self:UpdateHealth()
|
|
self:UpdateHealPrediction()
|
|
end
|
|
|
|
healthBarMetaFunctions.UNIT_ABSORB_AMOUNT_CHANGED = function(self, ...)
|
|
self:UpdateMaxHealth()
|
|
self:UpdateHealth()
|
|
self:UpdateHealPrediction()
|
|
end
|
|
|
|
healthBarMetaFunctions.UNIT_HEAL_ABSORB_AMOUNT_CHANGED = function(self, ...)
|
|
self:UpdateMaxHealth()
|
|
self:UpdateHealth()
|
|
self:UpdateHealPrediction()
|
|
end
|
|
|
|
-- ~healthbar
|
|
|
|
---comment
|
|
---@param parent frame
|
|
---@param name string?
|
|
---@param settingsOverride table? a table with key/value pairs to override the default settings
|
|
---@return df_healthbar
|
|
function detailsFramework:CreateHealthBar(parent, name, settingsOverride)
|
|
assert(name or parent:GetName(), "DetailsFramework:CreateHealthBar parameter 'name' omitted and parent has no name.")
|
|
|
|
local healthBar = CreateFrame("StatusBar", name or (parent:GetName() .. "HealthBar"), parent, "BackdropTemplate")
|
|
do --layers
|
|
--background
|
|
healthBar.background = healthBar:CreateTexture(nil, "background")
|
|
healthBar.background:SetDrawLayer("background", -6)
|
|
|
|
--artwork
|
|
--healing incoming
|
|
healthBar.incomingHealIndicator = healthBar:CreateTexture(nil, "artwork", nil, 5)
|
|
healthBar.incomingHealIndicator:SetDrawLayer("artwork", 4)
|
|
--current shields on the unit
|
|
healthBar.shieldAbsorbIndicator = healthBar:CreateTexture(nil, "artwork", nil, 3)
|
|
healthBar.shieldAbsorbIndicator:SetDrawLayer("artwork", 5)
|
|
--debuff absorbing heal
|
|
healthBar.healAbsorbIndicator = healthBar:CreateTexture(nil, "artwork", nil, 4)
|
|
healthBar.healAbsorbIndicator:SetDrawLayer("artwork", 6)
|
|
--the shield fills all the bar, show that cool glow
|
|
healthBar.shieldAbsorbGlow = healthBar:CreateTexture(nil, "artwork", nil, 6)
|
|
healthBar.shieldAbsorbGlow:SetDrawLayer("artwork", 7)
|
|
--statusbar texture
|
|
healthBar.barTexture = healthBar:CreateTexture(nil, "artwork", nil, 1)
|
|
end
|
|
|
|
--mixins
|
|
detailsFramework:Mixin(healthBar, healthBarMetaFunctions)
|
|
detailsFramework:Mixin(healthBar, detailsFramework.StatusBarFunctions)
|
|
|
|
healthBar:CreateTextureMask()
|
|
healthBar:SetTexture([[Interface\WorldStateFrame\WORLDSTATEFINALSCORE-HIGHLIGHT]])
|
|
|
|
--settings and hooks
|
|
local settings = detailsFramework.table.copy({}, healthBarMetaFunctions.Settings)
|
|
if (settingsOverride) then
|
|
detailsFramework.table.copy(settings, settingsOverride)
|
|
end
|
|
healthBar.Settings = settings
|
|
|
|
if (healthBar.Settings.DontSetStatusBarTexture) then
|
|
healthBar.barTexture:SetAllPoints()
|
|
else
|
|
healthBar:SetStatusBarTexture(healthBar.barTexture)
|
|
end
|
|
|
|
--hook list
|
|
healthBar.HookList = detailsFramework.table.copy({}, healthBarMetaFunctions.HookList)
|
|
|
|
--initialize the cast bar
|
|
healthBar:Initialize()
|
|
|
|
return healthBar
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
--power bar frame
|
|
|
|
--[=[
|
|
DF:CreatePowerBar (parent, name, settingsOverride)
|
|
creates statusbar frame to show the unit power bar
|
|
@parent = frame to pass for the CreateFrame function
|
|
@name = absolute name of the frame, if omitted it uses the parent's name .. "PPowerBar"
|
|
@settingsOverride = table with keys and values to replace the defaults from the framework
|
|
--]=]
|
|
|
|
---@class df_powerbarsettings : table
|
|
---@field ShowAlternatePower boolean
|
|
---@field ShowPercentText boolean
|
|
---@field HideIfNoPower boolean
|
|
---@field CanTick boolean
|
|
---@field BackgroundColor table
|
|
---@field Texture texturepath|textureid|atlasname
|
|
---@field Width number
|
|
---@field Height number
|
|
|
|
---@class df_powerbar : statusbar, df_scripthookmixin, df_statusbarmixin
|
|
---@field unit string
|
|
---@field displayedUnit string
|
|
---@field WidgetType string
|
|
---@field currentPower number
|
|
---@field currentPowerMax number
|
|
---@field powerType number
|
|
---@field minPower number
|
|
---@field Settings df_powerbarsettings
|
|
---@field background texture
|
|
---@field percentText fontstring
|
|
---@field SetUnit fun(self:df_healthbar, unit:unit?, displayedUnit:unit?)
|
|
|
|
detailsFramework.PowerFrameFunctions = {
|
|
WidgetType = "powerBar",
|
|
|
|
HookList = {
|
|
OnHide = {},
|
|
OnShow = {},
|
|
},
|
|
|
|
Settings = {
|
|
--misc
|
|
ShowAlternatePower = true, --if true it'll show alternate power over the regular power the unit uses
|
|
ShowPercentText = true, --if true show a text with the current energy percent
|
|
HideIfNoPower = true, --if true and the UnitMaxPower returns zero, it'll hide the power bar with self:Hide()
|
|
CanTick = false, --if it calls the OnTick function every tick
|
|
|
|
--appearance
|
|
BackgroundColor = detailsFramework:CreateColorTable (.2, .2, .2, .8),
|
|
Texture = [[Interface\RaidFrame\Raid-Bar-Resource-Fill]],
|
|
|
|
--default size
|
|
Width = 100,
|
|
Height = 20,
|
|
},
|
|
|
|
PowerBarEvents = {
|
|
{"PLAYER_ENTERING_WORLD"},
|
|
{"UNIT_DISPLAYPOWER", true},
|
|
{"UNIT_POWER_BAR_SHOW", true},
|
|
{"UNIT_POWER_BAR_HIDE", true},
|
|
{"UNIT_MAXPOWER", true},
|
|
{"UNIT_POWER_UPDATE", true},
|
|
{"UNIT_POWER_FREQUENT", true},
|
|
},
|
|
|
|
--setup the castbar to be used by another unit
|
|
SetUnit = function(self, unit, displayedUnit)
|
|
if (self.unit ~= unit or self.displayedUnit ~= displayedUnit or unit == nil) then
|
|
self.unit = unit
|
|
self.displayedUnit = displayedUnit or unit
|
|
|
|
--register events
|
|
if (unit) then
|
|
for _, eventTable in ipairs(self.PowerBarEvents) do
|
|
local event = eventTable[1]
|
|
local isUnitEvent = eventTable[2]
|
|
|
|
if (isUnitEvent) then
|
|
self:RegisterUnitEvent(event, self.displayedUnit)
|
|
else
|
|
self:RegisterEvent(event)
|
|
end
|
|
end
|
|
|
|
--set scripts
|
|
self:SetScript("OnEvent", self.OnEvent)
|
|
|
|
if (self.Settings.CanTick) then
|
|
self:SetScript("OnUpdate", self.OnTick)
|
|
end
|
|
|
|
self:Show()
|
|
self:UpdatePowerBar()
|
|
else
|
|
--remove all registered events
|
|
for _, eventTable in ipairs(self.PowerBarEvents) do
|
|
local event = eventTable[1]
|
|
self:UnregisterEvent(event)
|
|
end
|
|
|
|
--remove scripts
|
|
self:SetScript("OnEvent", nil)
|
|
self:SetScript("OnUpdate", nil)
|
|
self:Hide()
|
|
end
|
|
end
|
|
end,
|
|
|
|
Initialize = function(self)
|
|
PixelUtil.SetWidth (self, self.Settings.Width)
|
|
PixelUtil.SetHeight(self, self.Settings.Height)
|
|
|
|
self:SetTexture(self.Settings.Texture)
|
|
|
|
self.background:SetAllPoints()
|
|
self.background:SetColorTexture(self.Settings.BackgroundColor:GetColor())
|
|
|
|
if (self.Settings.ShowPercentText) then
|
|
self.percentText:Show()
|
|
PixelUtil.SetPoint(self.percentText, "center", self, "center", 0, 0)
|
|
|
|
detailsFramework:SetFontSize(self.percentText, 9)
|
|
detailsFramework:SetFontColor(self.percentText, "white")
|
|
detailsFramework:SetFontOutline(self.percentText, "OUTLINE")
|
|
else
|
|
self.percentText:Hide()
|
|
end
|
|
|
|
self:SetUnit(nil)
|
|
end,
|
|
|
|
--call every tick
|
|
OnTick = function(self, deltaTime) end, --if overrided, set 'CanTick' to true on the settings table
|
|
|
|
--when an event happen for this unit, send it to the apropriate function
|
|
OnEvent = function(self, event, ...)
|
|
local eventFunc = self[event]
|
|
if (eventFunc) then
|
|
--the function doesn't receive which event was, only 'self' and the parameters
|
|
eventFunc(self, ...)
|
|
end
|
|
end,
|
|
|
|
UpdatePowerBar = function(self)
|
|
self:UpdatePowerInfo()
|
|
self:UpdateMaxPower()
|
|
self:UpdatePower()
|
|
self:UpdatePowerColor()
|
|
end,
|
|
|
|
--power update
|
|
UpdateMaxPower = function(self)
|
|
self.currentPowerMax = UnitPowerMax(self.displayedUnit, self.powerType)
|
|
self:SetMinMaxValues(self.minPower, self.currentPowerMax)
|
|
|
|
if (self.currentPowerMax == 0 and self.Settings.HideIfNoPower) then
|
|
self:Hide()
|
|
end
|
|
end,
|
|
|
|
UpdatePower = function(self)
|
|
self.currentPower = UnitPower(self.displayedUnit, self.powerType)
|
|
PixelUtil.SetStatusBarValue(self, self.currentPower)
|
|
|
|
if (self.Settings.ShowPercentText) then
|
|
self.percentText:SetText(floor(self.currentPower / self.currentPowerMax * 100) .. "%")
|
|
end
|
|
end,
|
|
|
|
--when a event different from unit_power_update is triggered, update which type of power the unit should show
|
|
UpdatePowerInfo = function(self)
|
|
if (IS_WOW_PROJECT_MAINLINE and self.Settings.ShowAlternatePower) then -- not available in classic
|
|
local barID = UnitPowerBarID(self.displayedUnit)
|
|
local barInfo = GetUnitPowerBarInfoByID(barID)
|
|
--local name, tooltip, cost = GetUnitPowerBarStringsByID(barID);
|
|
--barInfo.barType,barInfo.minPower, barInfo.startInset, barInfo.endInset, barInfo.smooth, barInfo.hideFromOthers, barInfo.showOnRaid, barInfo.opaqueSpark, barInfo.opaqueFlash, barInfo.anchorTop, name, tooltip, cost, barInfo.ID, barInfo.forcePercentage, barInfo.sparkUnderFrame;
|
|
if (barInfo and barInfo.showOnRaid and IsInGroup()) then
|
|
self.powerType = ALTERNATE_POWER_INDEX
|
|
self.minPower = barInfo.minPower
|
|
return
|
|
end
|
|
end
|
|
|
|
self.powerType = UnitPowerType (self.displayedUnit)
|
|
self.minPower = 0
|
|
end,
|
|
|
|
--tint the bar with the color of the power, e.g. blue for a mana bar
|
|
UpdatePowerColor = function(self)
|
|
if (not UnitIsConnected (self.unit)) then
|
|
self:SetStatusBarColor(.5, .5, .5)
|
|
return
|
|
end
|
|
|
|
if (self.powerType == ALTERNATE_POWER_INDEX) then
|
|
--don't change this, keep the same color as the game tints on CompactUnitFrame.lua
|
|
self:SetStatusBarColor(0.7, 0.7, 0.6)
|
|
return
|
|
end
|
|
|
|
local powerColor = PowerBarColor[self.powerType] --don't appear to be, but PowerBarColor is a global table with all power colors /run Details:Dump (PowerBarColor)
|
|
if (powerColor) then
|
|
self:SetStatusBarColor(powerColor.r, powerColor.g, powerColor.b)
|
|
return
|
|
end
|
|
|
|
local _, _, r, g, b = UnitPowerType(self.displayedUnit)
|
|
if (r) then
|
|
self:SetStatusBarColor(r, g, b)
|
|
return
|
|
end
|
|
|
|
--if everything else fails, tint as rogue energy
|
|
powerColor = PowerBarColor["ENERGY"]
|
|
self:SetStatusBarColor(powerColor.r, powerColor.g, powerColor.b)
|
|
end,
|
|
|
|
--events
|
|
PLAYER_ENTERING_WORLD = function(self, ...)
|
|
self:UpdatePowerBar()
|
|
end,
|
|
UNIT_DISPLAYPOWER = function(self, ...)
|
|
self:UpdatePowerBar()
|
|
end,
|
|
UNIT_POWER_BAR_SHOW = function(self, ...)
|
|
self:UpdatePowerBar()
|
|
end,
|
|
UNIT_POWER_BAR_HIDE = function(self, ...)
|
|
self:UpdatePowerBar()
|
|
end,
|
|
|
|
UNIT_MAXPOWER = function(self, ...)
|
|
self:UpdateMaxPower()
|
|
self:UpdatePower()
|
|
end,
|
|
UNIT_POWER_UPDATE = function(self, ...)
|
|
self:UpdatePower()
|
|
end,
|
|
UNIT_POWER_FREQUENT = function(self, ...)
|
|
self:UpdatePower()
|
|
end,
|
|
}
|
|
|
|
detailsFramework:Mixin(detailsFramework.PowerFrameFunctions, detailsFramework.ScriptHookMixin)
|
|
|
|
-- ~powerbar
|
|
|
|
---create a power bar
|
|
---@param parent frame
|
|
---@param name string?
|
|
---@param settingsOverride table? a table with key/value pairs to override the default settings
|
|
---@return df_powerbar
|
|
function detailsFramework:CreatePowerBar(parent, name, settingsOverride)
|
|
assert(name or parent:GetName(), "DetailsFramework:CreatePowerBar parameter 'name' omitted and parent has no name.")
|
|
|
|
local powerBar = CreateFrame("StatusBar", name or (parent:GetName() .. "PowerBar"), parent, "BackdropTemplate")
|
|
do --layers
|
|
--background
|
|
powerBar.background = powerBar:CreateTexture(nil, "background")
|
|
powerBar.background:SetDrawLayer("background", -6)
|
|
|
|
--artwork
|
|
powerBar.barTexture = powerBar:CreateTexture(nil, "artwork")
|
|
powerBar:SetStatusBarTexture(powerBar.barTexture)
|
|
|
|
--overlay
|
|
powerBar.percentText = powerBar:CreateFontString(nil, "overlay", "GameFontNormal")
|
|
end
|
|
|
|
--mixins
|
|
detailsFramework:Mixin(powerBar, detailsFramework.PowerFrameFunctions)
|
|
detailsFramework:Mixin(powerBar, detailsFramework.StatusBarFunctions)
|
|
|
|
powerBar:CreateTextureMask()
|
|
powerBar:SetTexture([[Interface\WorldStateFrame\WORLDSTATEFINALSCORE-HIGHLIGHT]])
|
|
|
|
--settings and hooks
|
|
local settings = detailsFramework.table.copy({}, detailsFramework.PowerFrameFunctions.Settings)
|
|
if (settingsOverride) then
|
|
detailsFramework.table.copy(settings, settingsOverride)
|
|
end
|
|
powerBar.Settings = settings
|
|
|
|
local hookList = detailsFramework.table.copy({}, detailsFramework.PowerFrameFunctions.HookList)
|
|
powerBar.HookList = hookList
|
|
|
|
--initialize the cast bar
|
|
powerBar:Initialize()
|
|
|
|
return powerBar
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
--cast bar frame
|
|
|
|
--[=[
|
|
DF:CreateCastBar (parent, name, settingsOverride)
|
|
creates a cast bar to show an unit cast
|
|
@parent = frame to pass for the CreateFrame function
|
|
@name = absolute name of the frame, if omitted it uses the parent's name .. "CastBar"
|
|
@settingsOverride = table with keys and values to replace the defaults from the framework
|
|
--]=]
|
|
|
|
---@class df_castbarsettings : table
|
|
---@field NoFadeEffects boolean if true it won't play fade effects when a cast if finished
|
|
---@field ShowTradeSkills boolean if true, it shows cast for trade skills, e.g. creating an icon with blacksmith
|
|
---@field ShowShield boolean if true, shows the shield above the spell icon for non interruptible casts
|
|
---@field CanTick boolean if true it will run its OnTick function every tick.
|
|
---@field ShowCastTime boolean if true, show the remaining time to finish the cast, lazy tick must be enabled
|
|
---@field FadeInTime number amount of time in seconds to go from zero to 100% alpha when starting to cast
|
|
---@field FadeOutTime number amount of time in seconds to go from 100% to zero alpha when the cast finishes
|
|
---@field CanLazyTick boolean if true, it'll execute the lazy tick function, it ticks in a much slower pace comparece with the regular tick
|
|
---@field LazyUpdateCooldown number amount of time to wait for the next lazy update, this updates non critical things like the cast timer
|
|
---@field ShowEmpoweredDuration boolean full hold time for empowered spells
|
|
---@field FillOnInterrupt boolean
|
|
---@field HideSparkOnInterrupt boolean
|
|
---@field Width number
|
|
---@field Height number
|
|
---@field Colors df_castcolors
|
|
---@field BackgroundColor table
|
|
---@field Texture texturepath|textureid
|
|
---@field BorderShieldWidth number
|
|
---@field BorderShieldHeight number
|
|
---@field BorderShieldCoords table
|
|
---@field BorderShieldTexture number
|
|
---@field SpellIconWidth number
|
|
---@field SpellIconHeight number
|
|
---@field ShieldIndicatorTexture texturepath|textureid
|
|
---@field ShieldGlowTexture texturepath|textureid
|
|
---@field SparkTexture texturepath|textureid
|
|
---@field SparkWidth number
|
|
---@field SparkHeight number
|
|
---@field SparkOffset number
|
|
|
|
---@alias caststage_color
|
|
---| "Casting"
|
|
---| "Channeling"
|
|
---| "Interrupted"
|
|
---| "Failed"
|
|
---| "NotInterruptable"
|
|
---| "Finished"
|
|
|
|
---@class df_castcolors : table
|
|
---@field Casting table
|
|
---@field Channeling table
|
|
---@field Interrupted table
|
|
---@field Failed table
|
|
---@field NotInterruptable table
|
|
---@field Finished table
|
|
|
|
---@class df_castbar : statusbar, df_scripthookmixin, df_statusbarmixin
|
|
---@field unit string
|
|
---@field displayedUnit string
|
|
---@field WidgetType string
|
|
---@field value number
|
|
---@field maxValue number
|
|
---@field spellStartTime number
|
|
---@field spellEndTime number
|
|
---@field empowered boolean
|
|
---@field curStage number
|
|
---@field numStages number
|
|
---@field empStages {start:number, finish:number}[]
|
|
---@field stagePips texture[]
|
|
---@field holdAtMaxTime number
|
|
---@field casting boolean
|
|
---@field channeling boolean
|
|
---@field interrupted boolean
|
|
---@field failed boolean
|
|
---@field finished boolean
|
|
---@field canInterrupt boolean
|
|
---@field spellID spellid
|
|
---@field castID number
|
|
---@field spellName spellname
|
|
---@field spellTexture textureid
|
|
---@field Colors df_castcolors
|
|
---@field Settings df_castbarsettings
|
|
---@field background texture
|
|
---@field extraBackground texture
|
|
---@field Text fontstring
|
|
---@field BorderShield texture
|
|
---@field Icon texture
|
|
---@field Spark texture
|
|
---@field percentText fontstring
|
|
---@field barTexture texture
|
|
---@field flashTexture texture
|
|
---@field fadeOutAnimation animationgroup
|
|
---@field fadeInAnimation animationgroup
|
|
---@field flashAnimation animationgroup
|
|
---@field SetUnit fun(self:df_castbar, unit:string?)
|
|
---@field SetDefaultColor fun(self:df_castbar, colorType: caststage_color, red:any, green:number?, blue:number?, alpha:number?)
|
|
---@field UpdateCastColor fun(self:df_castbar) after setting a new color, call this function to update the bar color (while casting or channeling)
|
|
---@field GetCastColor fun(self:df_castbar) return a table with the color values for the current state of the casting process
|
|
|
|
detailsFramework.CastFrameFunctions = {
|
|
WidgetType = "castBar",
|
|
|
|
HookList = {
|
|
OnHide = {},
|
|
OnShow = {},
|
|
|
|
--can be regular cast or channel
|
|
OnCastStart = {},
|
|
},
|
|
|
|
CastBarEvents = {
|
|
{"UNIT_SPELLCAST_INTERRUPTED"},
|
|
{"UNIT_SPELLCAST_DELAYED"},
|
|
{"UNIT_SPELLCAST_CHANNEL_START"},
|
|
{"UNIT_SPELLCAST_CHANNEL_UPDATE"},
|
|
{"UNIT_SPELLCAST_CHANNEL_STOP"},
|
|
{(IS_WOW_PROJECT_MAINLINE) and "UNIT_SPELLCAST_EMPOWER_START"},
|
|
{(IS_WOW_PROJECT_MAINLINE) and "UNIT_SPELLCAST_EMPOWER_UPDATE"},
|
|
{(IS_WOW_PROJECT_MAINLINE) and "UNIT_SPELLCAST_EMPOWER_STOP"},
|
|
{(IS_WOW_PROJECT_MAINLINE) and "UNIT_SPELLCAST_INTERRUPTIBLE"},
|
|
{(IS_WOW_PROJECT_MAINLINE) and "UNIT_SPELLCAST_NOT_INTERRUPTIBLE"},
|
|
{"PLAYER_ENTERING_WORLD"},
|
|
{"UNIT_SPELLCAST_START", true},
|
|
{"UNIT_SPELLCAST_STOP", true},
|
|
{"UNIT_SPELLCAST_FAILED", true},
|
|
},
|
|
|
|
Settings = {
|
|
NoFadeEffects = false, --if true it won't play fade effects when a cast if finished
|
|
ShowTradeSkills = false, --if true, it shows cast for trade skills, e.g. creating an icon with blacksmith
|
|
ShowShield = true, --if true, shows the shield above the spell icon for non interruptible casts
|
|
CanTick = true, --if true it will run its OnTick function every tick.
|
|
ShowCastTime = true, --if true, show the remaining time to finish the cast, lazy tick must be enabled
|
|
FadeInTime = 0.1, --amount of time in seconds to go from zero to 100% alpha when starting to cast
|
|
FadeOutTime = 0.5, --amount of time in seconds to go from 100% to zero alpha when the cast finishes
|
|
CanLazyTick = true, --if true, it'll execute the lazy tick function, it ticks in a much slower pace comparece with the regular tick
|
|
LazyUpdateCooldown = 0.2, --amount of time to wait for the next lazy update, this updates non critical things like the cast timer
|
|
|
|
ShowEmpoweredDuration = true, --full hold time for empowered spells
|
|
|
|
FillOnInterrupt = true,
|
|
HideSparkOnInterrupt = true,
|
|
|
|
--default size
|
|
Width = 100,
|
|
Height = 20,
|
|
|
|
--colour the castbar statusbar by the type of the cast
|
|
Colors = {
|
|
Casting = detailsFramework:CreateColorTable (1, 0.73, .1, 1),
|
|
Channeling = detailsFramework:CreateColorTable (1, 0.73, .1, 1),
|
|
Finished = detailsFramework:CreateColorTable (0, 1, 0, 1),
|
|
NonInterruptible = detailsFramework:CreateColorTable (.7, .7, .7, 1),
|
|
Failed = detailsFramework:CreateColorTable (.4, .4, .4, 1),
|
|
Interrupted = detailsFramework:CreateColorTable (.965, .754, .154, 1),
|
|
},
|
|
|
|
--appearance
|
|
BackgroundColor = detailsFramework:CreateColorTable (.2, .2, .2, .8),
|
|
Texture = [[Interface\TargetingFrame\UI-StatusBar]],
|
|
BorderShieldWidth = 10,
|
|
BorderShieldHeight = 12,
|
|
BorderShieldCoords = {0.26171875, 0.31640625, 0.53125, 0.65625},
|
|
BorderShieldTexture = 1300837,
|
|
SpellIconWidth = 10,
|
|
SpellIconHeight = 10,
|
|
ShieldIndicatorTexture = [[Interface\RaidFrame\Shield-Fill]],
|
|
ShieldGlowTexture = [[Interface\RaidFrame\Shield-Overshield]],
|
|
SparkTexture = [[Interface\CastingBar\UI-CastingBar-Spark]],
|
|
SparkWidth = 16,
|
|
SparkHeight = 16,
|
|
SparkOffset = 0,
|
|
},
|
|
|
|
Initialize = function(self)
|
|
self.unit = "unutilized unit"
|
|
self.lazyUpdateCooldown = self.Settings.LazyUpdateCooldown
|
|
self.Colors = self.Settings.Colors
|
|
|
|
self:SetUnit(nil)
|
|
PixelUtil.SetWidth (self, self.Settings.Width)
|
|
PixelUtil.SetHeight(self, self.Settings.Height)
|
|
|
|
self.background:SetColorTexture(self.Settings.BackgroundColor:GetColor())
|
|
self.background:SetAllPoints()
|
|
self.extraBackground:SetColorTexture(0, 0, 0, 1)
|
|
self.extraBackground:SetVertexColor(self.Settings.BackgroundColor:GetColor())
|
|
self.extraBackground:SetAllPoints()
|
|
|
|
self:SetTexture(self.Settings.Texture)
|
|
|
|
self.BorderShield:SetPoint("center", self, "left", 0, 0)
|
|
self.BorderShield:SetTexture(self.Settings.BorderShieldTexture)
|
|
self.BorderShield:SetTexCoord(unpack(self.Settings.BorderShieldCoords))
|
|
self.BorderShield:SetSize(self.Settings.BorderShieldWidth, self.Settings.BorderShieldHeight)
|
|
|
|
self.Icon:SetPoint("center", self, "left", 2, 0)
|
|
self.Icon:SetSize(self.Settings.SpellIconWidth, self.Settings.SpellIconHeight)
|
|
|
|
self.Spark:SetTexture(self.Settings.SparkTexture)
|
|
self.Spark:SetSize(self.Settings.SparkWidth, self.Settings.SparkHeight)
|
|
|
|
self.percentText:SetPoint("right", self, "right", -2, 0)
|
|
self.percentText:SetJustifyH("right")
|
|
|
|
self.fadeOutAnimation.alpha1:SetDuration(self.Settings.FadeOutTime)
|
|
self.fadeInAnimation.alpha1:SetDuration(self.Settings.FadeInTime)
|
|
end,
|
|
|
|
SetDefaultColor = function(self, colorType, r, g, b, a)
|
|
assert(type(colorType) == "string", "DetailsFramework: CastBar:SetDefaultColor require a string in the first argument.")
|
|
self.Colors[colorType]:SetColor(r, g, b, a)
|
|
end,
|
|
|
|
--this get a color suggestion based on the type of cast being shown in the cast bar
|
|
GetCastColor = function(self)
|
|
if (not self.canInterrupt) then
|
|
return self.Colors.NonInterruptible
|
|
|
|
elseif (self.channeling) then
|
|
return self.Colors.Channeling
|
|
|
|
elseif (self.failed) then
|
|
return self.Colors.Failed
|
|
|
|
elseif (self.interrupted) then
|
|
return self.Colors.Interrupted
|
|
|
|
elseif (self.finished) then
|
|
return self.Colors.Finished
|
|
|
|
else
|
|
return self.Colors.Casting
|
|
end
|
|
end,
|
|
|
|
--update all colors of the cast bar
|
|
UpdateCastColor = function(self)
|
|
local castColor = self:GetCastColor()
|
|
self:SetColor(castColor) --SetColor handles with ParseColors()
|
|
end,
|
|
|
|
--initial checks to know if this is a valid cast and should show the cast bar, if this fails the cast bar won't show
|
|
IsValid = function(self, unit, castName, isTradeSkill, ignoreVisibility)
|
|
if (not ignoreVisibility and not self:IsShown()) then
|
|
return false
|
|
end
|
|
|
|
if (not self.Settings.ShowTradeSkills) then
|
|
if (isTradeSkill) then
|
|
return false
|
|
end
|
|
end
|
|
|
|
if (not castName) then
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end,
|
|
|
|
--handle the interrupt state of the cast
|
|
--this does not change the cast bar color because this function is called inside the start cast where is already handles the cast color
|
|
UpdateInterruptState = function(self)
|
|
if (self.Settings.ShowShield and not self.canInterrupt) then
|
|
self.BorderShield:Show()
|
|
else
|
|
self.BorderShield:Hide()
|
|
end
|
|
end,
|
|
|
|
--this check if the cast did reach 100% in the statusbar, mostly called from OnTick
|
|
CheckCastIsDone = function(self, event, isFinished)
|
|
--check max value
|
|
if (not isFinished and not self.finished) then
|
|
if (self.casting) then
|
|
if (self.value >= self.maxValue) then
|
|
isFinished = true
|
|
end
|
|
|
|
elseif (self.channeling) then
|
|
if (self.value > self.maxValue or self.value <= 0) then
|
|
isFinished = true
|
|
end
|
|
end
|
|
|
|
--check if passed an event (not begin used at the moment)
|
|
if (event) then
|
|
if (event == UNIT_SPELLCAST_STOP or event == UNIT_SPELLCAST_CHANNEL_STOP) then
|
|
isFinished = true
|
|
end
|
|
end
|
|
end
|
|
|
|
--the cast is finished
|
|
if (isFinished) then
|
|
if (self.casting) then
|
|
self.UNIT_SPELLCAST_STOP(self, self.unit, self.unit, self.castID, self.spellID)
|
|
|
|
elseif (self.channeling) then
|
|
self.UNIT_SPELLCAST_CHANNEL_STOP(self, self.unit, self.unit, self.castID, self.spellID)
|
|
end
|
|
|
|
return true
|
|
end
|
|
end,
|
|
|
|
--setup the castbar to be used by another unit
|
|
SetUnit = function(self, unit, displayedUnit)
|
|
if (self.unit ~= unit or self.displayedUnit ~= displayedUnit or unit == nil) then
|
|
self.unit = unit
|
|
self.displayedUnit = displayedUnit or unit
|
|
|
|
--reset the cast bar
|
|
self.casting = nil
|
|
self.channeling = nil
|
|
self.caninterrupt = nil
|
|
|
|
--register events
|
|
if (unit) then
|
|
for _, eventTable in ipairs(self.CastBarEvents) do
|
|
local event = eventTable[1]
|
|
local isUnitEvent = eventTable[2]
|
|
|
|
if event then
|
|
if (isUnitEvent) then
|
|
self:RegisterUnitEvent(event, unit)
|
|
else
|
|
self:RegisterEvent(event)
|
|
end
|
|
end
|
|
end
|
|
|
|
--set scripts
|
|
self:SetScript("OnEvent", self.OnEvent)
|
|
self:SetScript("OnShow", self.OnShow)
|
|
self:SetScript("OnHide", self.OnHide)
|
|
|
|
if (self.Settings.CanTick) then
|
|
self:SetScript("OnUpdate", self.OnTick)
|
|
end
|
|
|
|
--check is can show the cast time text
|
|
if (self.Settings.ShowCastTime and self.Settings.CanLazyTick) then
|
|
self.percentText:Show()
|
|
else
|
|
self.percentText:Hide()
|
|
end
|
|
|
|
--setup animtions
|
|
self:CancelScheduleToHide()
|
|
|
|
--self:PLAYER_ENTERING_WORLD (unit, unit)
|
|
self:OnEvent("PLAYER_ENTERING_WORLD", unit, unit)
|
|
|
|
else
|
|
for _, eventTable in ipairs(self.CastBarEvents) do
|
|
local event = eventTable[1]
|
|
if event then
|
|
self:UnregisterEvent(event)
|
|
end
|
|
end
|
|
|
|
--register main events
|
|
self:SetScript("OnUpdate", nil)
|
|
self:SetScript("OnEvent", nil)
|
|
self:SetScript("OnShow", nil)
|
|
self:SetScript("OnHide", nil)
|
|
|
|
self:Hide()
|
|
end
|
|
end
|
|
end,
|
|
|
|
--executed after a scheduled to hide timer is done
|
|
DoScheduledHide = function(timerObject)
|
|
timerObject.castBar.scheduledHideTime = nil
|
|
|
|
--just to make sure it isn't casting
|
|
if (not timerObject.castBar.casting and not timerObject.castBar.channeling) then
|
|
if (not timerObject.castBar.Settings.NoFadeEffects) then
|
|
timerObject.castBar:Animation_FadeOut()
|
|
else
|
|
timerObject.castBar:Hide()
|
|
end
|
|
end
|
|
end,
|
|
|
|
HasScheduledHide = function(self)
|
|
return self.scheduledHideTime and not self.scheduledHideTime:IsCancelled()
|
|
end,
|
|
|
|
CancelScheduleToHide = function(self)
|
|
if (self:HasScheduledHide()) then
|
|
self.scheduledHideTime:Cancel()
|
|
end
|
|
end,
|
|
|
|
--after an interrupt, do not immediately hide the cast bar, let it up for short amount of time to give feedback to the player
|
|
ScheduleToHide = function(self, delay)
|
|
if (not delay) then
|
|
if (self.scheduledHideTime and not self.scheduledHideTime:IsCancelled()) then
|
|
self.scheduledHideTime:Cancel()
|
|
end
|
|
|
|
self.scheduledHideTime = nil
|
|
return
|
|
end
|
|
|
|
--already have a scheduled timer?
|
|
if (self.scheduledHideTime and not self.scheduledHideTime:IsCancelled()) then
|
|
self.scheduledHideTime:Cancel()
|
|
end
|
|
|
|
self.scheduledHideTime = C_Timer.NewTimer(delay, self.DoScheduledHide)
|
|
self.scheduledHideTime.castBar = self
|
|
end,
|
|
|
|
OnHide = function(self)
|
|
--just in case some other effects made it have a different alpha since SetUnit won't load if the unit is the same.
|
|
self:SetAlpha(1)
|
|
--cancel any timer to hide scheduled
|
|
self:CancelScheduleToHide()
|
|
end,
|
|
|
|
--just update the current value if a spell is being cast since it wasn't running its tick function during the hide state
|
|
--everything else should be in the correct state
|
|
OnShow = function(self)
|
|
self.flashTexture:Hide()
|
|
|
|
if (self.unit) then
|
|
if (self.casting) then
|
|
local name, text, texture, startTime = CastInfo.UnitCastingInfo(self.unit)
|
|
if (name) then
|
|
--[[if not self.spellStartTime then
|
|
self:UpdateCastingInfo(self.unit)
|
|
end]]--
|
|
self.value = GetTime() - self.spellStartTime
|
|
end
|
|
|
|
self:RunHooksForWidget("OnShow", self, self.unit)
|
|
|
|
elseif (self.channeling) then
|
|
local name, text, texture, endTime = CastInfo.UnitChannelInfo(self.unit)
|
|
if (name) then
|
|
--[[if not self.spellEndTime then
|
|
self:UpdateChannelInfo(self.unit)
|
|
end]]--
|
|
self.value = self.empowered and (GetTime() - self.spellStartTime) or (self.spellEndTime - GetTime())
|
|
end
|
|
|
|
self:RunHooksForWidget("OnShow", self, self.unit)
|
|
end
|
|
end
|
|
end,
|
|
|
|
--it's triggering several events since it's not registered for the unit with RegisterUnitEvent
|
|
OnEvent = function(self, event, ...)
|
|
local arg1 = ...
|
|
local unit = self.unit
|
|
|
|
if (event == "PLAYER_ENTERING_WORLD") then
|
|
local newEvent = self.PLAYER_ENTERING_WORLD (self, unit, ...)
|
|
if (newEvent) then
|
|
self.OnEvent (self, newEvent, unit)
|
|
return
|
|
end
|
|
|
|
elseif (arg1 ~= unit) then
|
|
return
|
|
end
|
|
|
|
local eventFunc = self [event]
|
|
if (eventFunc) then
|
|
eventFunc (self, unit, ...)
|
|
end
|
|
end,
|
|
|
|
OnTick_LazyTick = function(self)
|
|
--run the lazy tick if allowed
|
|
if (self.Settings.CanLazyTick) then
|
|
--update the cast time
|
|
if (self.Settings.ShowCastTime) then
|
|
if (self.casting) then
|
|
self.percentText:SetText(format("%.1f", abs(self.value - self.maxValue)))
|
|
|
|
elseif (self.channeling) then
|
|
local remainingTime = self.empowered and abs(self.value - self.maxValue) or abs(self.value)
|
|
if (remainingTime > 999) then
|
|
self.percentText:SetText("")
|
|
else
|
|
self.percentText:SetText(format("%.1f", remainingTime))
|
|
end
|
|
else
|
|
self.percentText:SetText("")
|
|
end
|
|
end
|
|
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end,
|
|
|
|
--tick function for regular casts
|
|
OnTick_Casting = function(self, deltaTime)
|
|
self.value = self.value + deltaTime
|
|
|
|
if (self:CheckCastIsDone()) then
|
|
return
|
|
else
|
|
self:SetValue(self.value)
|
|
end
|
|
|
|
--update spark position
|
|
local sparkPosition = self.value / self.maxValue * self:GetWidth()
|
|
self.Spark:SetPoint("center", self, "left", sparkPosition + self.Settings.SparkOffset, 0)
|
|
|
|
|
|
--in order to allow the lazy tick run, it must return true, it tell that the cast didn't finished
|
|
return true
|
|
end,
|
|
|
|
--tick function for channeling casts
|
|
OnTick_Channeling = function(self, deltaTime)
|
|
self.value = self.empowered and self.value + deltaTime or self.value - deltaTime
|
|
|
|
if (self:CheckCastIsDone()) then
|
|
return
|
|
else
|
|
self:SetValue(self.value)
|
|
end
|
|
|
|
--update spark position
|
|
local sparkPosition = self.value / self.maxValue * self:GetWidth()
|
|
self.Spark:SetPoint("center", self, "left", sparkPosition + self.Settings.SparkOffset, 0)
|
|
|
|
self:CreateOrUpdateEmpoweredPips()
|
|
|
|
return true
|
|
end,
|
|
|
|
OnTick = function(self, deltaTime)
|
|
if (self.casting) then
|
|
if (not self:OnTick_Casting(deltaTime)) then
|
|
return
|
|
end
|
|
|
|
--lazy tick
|
|
self.lazyUpdateCooldown = self.lazyUpdateCooldown - deltaTime
|
|
if (self.lazyUpdateCooldown < 0) then
|
|
self:OnTick_LazyTick()
|
|
self.lazyUpdateCooldown = self.Settings.LazyUpdateCooldown
|
|
end
|
|
|
|
elseif (self.channeling) then
|
|
if (not self:OnTick_Channeling(deltaTime)) then
|
|
return
|
|
end
|
|
|
|
--lazy tick
|
|
self.lazyUpdateCooldown = self.lazyUpdateCooldown - deltaTime
|
|
if (self.lazyUpdateCooldown < 0) then
|
|
self:OnTick_LazyTick()
|
|
self.lazyUpdateCooldown = self.Settings.LazyUpdateCooldown
|
|
end
|
|
end
|
|
end,
|
|
|
|
--animation start script
|
|
Animation_FadeOutStarted = function(self)
|
|
|
|
end,
|
|
|
|
--animation finished script
|
|
Animation_FadeOutFinished = function(self)
|
|
local castBar = self:GetParent()
|
|
castBar:SetAlpha(1)
|
|
castBar:Hide()
|
|
end,
|
|
|
|
--animation start script
|
|
Animation_FadeInStarted = function(self)
|
|
|
|
end,
|
|
|
|
--animation finished script
|
|
Animation_FadeInFinished = function(self)
|
|
local castBar = self:GetParent()
|
|
castBar:Show()
|
|
castBar:SetAlpha(1)
|
|
end,
|
|
|
|
--animation calls
|
|
Animation_FadeOut = function(self)
|
|
self:ScheduleToHide(false)
|
|
|
|
if (self.fadeInAnimation:IsPlaying()) then
|
|
self.fadeInAnimation:Stop()
|
|
end
|
|
|
|
if (not self.fadeOutAnimation:IsPlaying()) then
|
|
self.fadeOutAnimation:Play()
|
|
end
|
|
end,
|
|
|
|
Animation_FadeIn = function(self)
|
|
self:ScheduleToHide (false)
|
|
|
|
if (self.fadeOutAnimation:IsPlaying()) then
|
|
self.fadeOutAnimation:Stop()
|
|
end
|
|
|
|
if (not self.fadeInAnimation:IsPlaying()) then
|
|
self.fadeInAnimation:Play()
|
|
end
|
|
end,
|
|
|
|
Animation_Flash = function(self)
|
|
if (not self.flashAnimation:IsPlaying()) then
|
|
self.flashAnimation:Play()
|
|
end
|
|
end,
|
|
|
|
Animation_StopAllAnimations = function(self)
|
|
if (self.flashAnimation:IsPlaying()) then
|
|
self.flashAnimation:Stop()
|
|
end
|
|
|
|
if (self.fadeOutAnimation:IsPlaying()) then
|
|
self.fadeOutAnimation:Stop()
|
|
end
|
|
|
|
if (self.fadeInAnimation:IsPlaying()) then
|
|
self.fadeInAnimation:Stop()
|
|
end
|
|
end,
|
|
|
|
PLAYER_ENTERING_WORLD = function(self, unit, arg1)
|
|
local isChannel = CastInfo.UnitChannelInfo(unit)
|
|
local isRegularCast = CastInfo.UnitCastingInfo(unit)
|
|
|
|
if (isChannel) then
|
|
self.channeling = true
|
|
self:UpdateChannelInfo(unit)
|
|
return self.unit == arg1 and "UNIT_SPELLCAST_CHANNEL_START"
|
|
|
|
elseif (isRegularCast) then
|
|
self.casting = true
|
|
self:UpdateCastingInfo(unit)
|
|
return self.unit == arg1 and "UNIT_SPELLCAST_START"
|
|
|
|
else
|
|
self.casting = nil
|
|
self.channeling = nil
|
|
self.failed = nil
|
|
self.finished = nil
|
|
self.interrupted = nil
|
|
self.Spark:Hide()
|
|
self:Hide()
|
|
end
|
|
end,
|
|
|
|
UpdateCastingInfo = function(self, unit, ...)
|
|
local unitID, castID, spellID = ...
|
|
local name, text, texture, startTime, endTime, isTradeSkill, uciCastID, notInterruptible, uciSpellID = CastInfo.UnitCastingInfo(unit)
|
|
spellID = uciSpellID or spellID
|
|
castID = uciCastID or castID
|
|
|
|
if spellID and (not name or not texture or not text) then
|
|
local siName, _, siIcon, siCastTime = GetSpellInfo(spellID)
|
|
texture = texture or siIcon
|
|
name = name or siName
|
|
text = text or siName
|
|
if not startTime then
|
|
startTime = GetTime()
|
|
endTime = startTime + siCastTime
|
|
end
|
|
end
|
|
|
|
--is valid?
|
|
if (not self:IsValid(unit, name, isTradeSkill, true)) then
|
|
return
|
|
end
|
|
|
|
--empowered? no!
|
|
self.holdAtMaxTime = nil
|
|
self.empowered = false
|
|
self.curStage = nil
|
|
self.numStages = nil
|
|
self.empStages = nil
|
|
self:CreateOrUpdateEmpoweredPips()
|
|
|
|
--setup cast
|
|
self.casting = true
|
|
self.channeling = nil
|
|
self.interrupted = nil
|
|
self.failed = nil
|
|
self.finished = nil
|
|
self.canInterrupt = not notInterruptible
|
|
self.spellID = spellID
|
|
self.castID = castID
|
|
self.spellName = name
|
|
self.spellTexture = texture
|
|
self.spellStartTime = startTime / 1000
|
|
self.spellEndTime = endTime / 1000
|
|
self.value = GetTime() - self.spellStartTime
|
|
self.maxValue = self.spellEndTime - self.spellStartTime
|
|
|
|
self:SetMinMaxValues(0, self.maxValue)
|
|
self:SetValue(self.value)
|
|
self:SetAlpha(1)
|
|
self.Icon:SetTexture(texture)
|
|
self.Icon:Show()
|
|
self.Text:SetText(text or name)
|
|
|
|
if (self.Settings.ShowCastTime and self.Settings.CanLazyTick) then
|
|
self.percentText:Show()
|
|
end
|
|
|
|
self.flashTexture:Hide()
|
|
self:Animation_StopAllAnimations()
|
|
|
|
self:SetAlpha(1)
|
|
|
|
--set the statusbar color
|
|
self:UpdateCastColor()
|
|
|
|
if (not self:IsShown() and not self.Settings.NoFadeEffects) then
|
|
self:Animation_FadeIn()
|
|
end
|
|
|
|
self.Spark:Show()
|
|
self:Show()
|
|
|
|
--update the interrupt cast border
|
|
self:UpdateInterruptState()
|
|
end,
|
|
|
|
UNIT_SPELLCAST_START = function(self, unit, ...)
|
|
self:UpdateCastingInfo(unit, ...)
|
|
self:RunHooksForWidget("OnCastStart", self, self.unit, "UNIT_SPELLCAST_START")
|
|
end,
|
|
|
|
CreateOrUpdateEmpoweredPips = function(self, unit, numStages, startTime, endTime)
|
|
unit = unit or self.unit
|
|
numStages = numStages or self.numStages
|
|
startTime = startTime or ((self.spellStartTime or 0) * 1000)
|
|
endTime = endTime or ((self.spellEndTime or 0) * 1000)
|
|
|
|
if not self.empStages or not numStages or numStages <= 0 then
|
|
self.stagePips = self.stagePips or {}
|
|
for i, stagePip in pairs(self.stagePips) do
|
|
stagePip:Hide()
|
|
end
|
|
return
|
|
end
|
|
|
|
local width = self:GetWidth()
|
|
local height = self:GetHeight()
|
|
for i = 1, numStages, 1 do
|
|
local curStartTime = self.empStages[i] and self.empStages[i].start
|
|
local curEndTime = self.empStages[i] and self.empStages[i].finish
|
|
local curDuration = curEndTime - curStartTime
|
|
local offset = width * curEndTime / (endTime - startTime) * 1000
|
|
if curDuration > -1 then
|
|
local stagePip = self.stagePips[i]
|
|
if not stagePip then
|
|
stagePip = self:CreateTexture(nil, "overlay", nil, 2)
|
|
stagePip:SetBlendMode("ADD")
|
|
stagePip:SetTexture([[Interface\CastingBar\UI-CastingBar-Spark]])
|
|
stagePip:SetTexCoord(11/32,18/32,9/32,23/32)
|
|
stagePip:SetSize(2, height)
|
|
--stagePip = CreateFrame("FRAME", nil, self, "CastingBarFrameStagePipTemplate")
|
|
self.stagePips[i] = stagePip
|
|
end
|
|
|
|
stagePip:ClearAllPoints()
|
|
--stagePip:SetPoint("TOP", self, "TOPLEFT", offset, -1)
|
|
--stagePip:SetPoint("BOTTOM", self, "BOTTOMLEFT", offset, 1)
|
|
--stagePip.BasePip:SetVertexColor(1, 1, 1, 1)
|
|
stagePip:SetPoint("CENTER", self, "LEFT", offset, 0)
|
|
stagePip:SetVertexColor(1, 1, 1, 1)
|
|
stagePip:Show()
|
|
end
|
|
end
|
|
end,
|
|
|
|
UpdateChannelInfo = function(self, unit, ...)
|
|
local unitID, castID, spellID = ...
|
|
local name, text, texture, startTime, endTime, isTradeSkill, notInterruptible, uciSpellID, _, numStages = CastInfo.UnitChannelInfo (unit)
|
|
spellID = uciSpellID or spellID
|
|
castID = uciCastID or castID
|
|
|
|
if spellID and (not name or not texture or not text) then
|
|
local siName, _, siIcon, siCastTime = GetSpellInfo(spellID)
|
|
texture = texture or siIcon
|
|
name = name or siName
|
|
text = text or siName
|
|
if not startTime then
|
|
startTime = GetTime()
|
|
endTime = startTime + siCastTime
|
|
end
|
|
end
|
|
|
|
--is valid?
|
|
if (not self:IsValid (unit, name, isTradeSkill, true)) then
|
|
return
|
|
end
|
|
|
|
--empowered?
|
|
self.empStages = {}
|
|
self.stagePips = self.stagePips or {}
|
|
for i, stagePip in pairs(self.stagePips) do
|
|
stagePip:Hide()
|
|
end
|
|
|
|
if numStages and numStages > 0 then
|
|
self.holdAtMaxTime = GetUnitEmpowerHoldAtMaxTime(self.unit)
|
|
self.empowered = true
|
|
self.numStages = numStages
|
|
|
|
local lastStageEndTime = 0
|
|
for i = 1, numStages do
|
|
self.empStages[i] = {
|
|
start = lastStageEndTime,
|
|
finish = lastStageEndTime + GetUnitEmpowerStageDuration(unit, i - 1) / 1000,
|
|
}
|
|
lastStageEndTime = self.empStages[i].finish
|
|
|
|
if startTime / 1000 + lastStageEndTime <= GetTime() then
|
|
self.curStage = i
|
|
end
|
|
end
|
|
|
|
if (self.Settings.ShowEmpoweredDuration) then
|
|
endTime = endTime + self.holdAtMaxTime
|
|
end
|
|
|
|
--create/update pips
|
|
self:CreateOrUpdateEmpoweredPips(unit, numStages, startTime, endTime)
|
|
else
|
|
self.holdAtMaxTime = nil
|
|
self.empowered = false
|
|
self.curStage = nil
|
|
self.numStages = nil
|
|
end
|
|
|
|
--setup cast
|
|
self.casting = nil
|
|
self.channeling = true
|
|
self.interrupted = nil
|
|
self.failed = nil
|
|
self.finished = nil
|
|
self.canInterrupt = not notInterruptible
|
|
self.spellID = spellID
|
|
self.castID = castID
|
|
self.spellName = name
|
|
self.spellTexture = texture
|
|
self.spellStartTime = startTime / 1000
|
|
self.spellEndTime = endTime / 1000
|
|
self.value = self.empowered and (GetTime() - self.spellStartTime) or (self.spellEndTime - GetTime())
|
|
self.maxValue = self.spellEndTime - self.spellStartTime
|
|
self.reverseChanneling = self.empowered
|
|
|
|
self:SetMinMaxValues(0, self.maxValue)
|
|
self:SetValue(self.value)
|
|
|
|
self:SetAlpha(1)
|
|
self.Icon:SetTexture(texture)
|
|
self.Icon:Show()
|
|
self.Text:SetText(text)
|
|
|
|
if (self.Settings.ShowCastTime and self.Settings.CanLazyTick) then
|
|
self.percentText:Show()
|
|
end
|
|
|
|
self.flashTexture:Hide()
|
|
self:Animation_StopAllAnimations()
|
|
|
|
self:SetAlpha(1)
|
|
|
|
--set the statusbar color
|
|
self:UpdateCastColor()
|
|
|
|
if (not self:IsShown() and not self.Settings.NoFadeEffects) then
|
|
self:Animation_FadeIn()
|
|
end
|
|
|
|
self.Spark:Show()
|
|
self:Show()
|
|
|
|
--update the interrupt cast border
|
|
self:UpdateInterruptState()
|
|
|
|
end,
|
|
|
|
UNIT_SPELLCAST_CHANNEL_START = function(self, unit, ...)
|
|
self:UpdateChannelInfo(unit, ...)
|
|
self:RunHooksForWidget("OnCastStart", self, self.unit, "UNIT_SPELLCAST_CHANNEL_START")
|
|
end,
|
|
|
|
UNIT_SPELLCAST_STOP = function(self, unit, ...)
|
|
local unitID, castID, spellID = ...
|
|
if (self.castID == castID) then
|
|
if (self.interrupted) then
|
|
if (self.Settings.HideSparkOnInterrupt) then
|
|
self.Spark:Hide()
|
|
end
|
|
else
|
|
self.Spark:Hide()
|
|
end
|
|
|
|
self.percentText:Hide()
|
|
|
|
local value = self:GetValue()
|
|
local _, maxValue = self:GetMinMaxValues()
|
|
|
|
if (self.interrupted) then
|
|
if (self.Settings.FillOnInterrupt) then
|
|
self:SetValue(self.maxValue or maxValue or 1)
|
|
end
|
|
else
|
|
self:SetValue(self.maxValue or maxValue or 1)
|
|
end
|
|
|
|
self.casting = nil
|
|
self.channeling = nil
|
|
self.finished = true
|
|
self.castID = nil
|
|
|
|
if (not self:HasScheduledHide()) then
|
|
--check if settings has no fade option or if its parents are not visible
|
|
if (not self:IsVisible()) then
|
|
self:Hide()
|
|
|
|
elseif (self.Settings.NoFadeEffects) then
|
|
self:ScheduleToHide (0.3)
|
|
|
|
else
|
|
self:Animation_Flash()
|
|
self:Animation_FadeOut()
|
|
end
|
|
end
|
|
|
|
self:UpdateCastColor()
|
|
end
|
|
end,
|
|
|
|
UNIT_SPELLCAST_CHANNEL_STOP = function(self, unit, ...)
|
|
local unitID, castID, spellID = ...
|
|
|
|
if (self.channeling and castID == self.castID) then
|
|
self.Spark:Hide()
|
|
self.percentText:Hide()
|
|
|
|
local value = self:GetValue()
|
|
local _, maxValue = self:GetMinMaxValues()
|
|
self:SetValue(self.maxValue or maxValue or 1)
|
|
|
|
self.casting = nil
|
|
self.channeling = nil
|
|
self.finished = true
|
|
self.castID = nil
|
|
|
|
if (not self:HasScheduledHide()) then
|
|
--check if settings has no fade option or if its parents are not visible
|
|
if (not self:IsVisible()) then
|
|
self:Hide()
|
|
|
|
elseif (self.Settings.NoFadeEffects) then
|
|
self:ScheduleToHide (0.3)
|
|
|
|
else
|
|
self:Animation_Flash()
|
|
self:Animation_FadeOut()
|
|
end
|
|
end
|
|
|
|
self:UpdateCastColor()
|
|
end
|
|
end,
|
|
|
|
UNIT_SPELLCAST_EMPOWER_START = function(self, unit, ...)
|
|
self:UNIT_SPELLCAST_CHANNEL_START(unit, ...)
|
|
end,
|
|
|
|
UNIT_SPELLCAST_EMPOWER_UPDATE = function(self, unit, ...)
|
|
self:UNIT_SPELLCAST_CHANNEL_UPDATE(unit, ...)
|
|
end,
|
|
|
|
UNIT_SPELLCAST_EMPOWER_STOP = function(self, unit, ...)
|
|
self:UNIT_SPELLCAST_CHANNEL_STOP(unit, ...)
|
|
end,
|
|
|
|
UNIT_SPELLCAST_FAILED = function(self, unit, ...)
|
|
local unitID, castID, spellID = ...
|
|
|
|
if ((self.casting or self.channeling) and castID == self.castID and not self.fadeOut) then
|
|
self.casting = nil
|
|
self.channeling = nil
|
|
self.failed = true
|
|
self.finished = true
|
|
self.castID = nil
|
|
self:SetValue(self.maxValue or select(2, self:GetMinMaxValues()) or 1)
|
|
|
|
--set the statusbar color
|
|
self:UpdateCastColor()
|
|
|
|
self.Spark:Hide()
|
|
self.percentText:Hide()
|
|
self.Text:SetText(FAILED) --auto locale within the global namespace
|
|
|
|
self:ScheduleToHide (1)
|
|
end
|
|
end,
|
|
|
|
UNIT_SPELLCAST_INTERRUPTED = function(self, unit, ...)
|
|
local unitID, castID, spellID = ...
|
|
|
|
if ((self.casting or self.channeling) and castID == self.castID and not self.fadeOut) then
|
|
self.casting = nil
|
|
self.channeling = nil
|
|
self.interrupted = true
|
|
self.finished = true
|
|
self.castID = nil
|
|
|
|
if (self.Settings.FillOnInterrupt) then
|
|
self:SetValue(self.maxValue or select(2, self:GetMinMaxValues()) or 1)
|
|
end
|
|
|
|
if (self.Settings.HideSparkOnInterrupt) then
|
|
self.Spark:Hide()
|
|
end
|
|
|
|
local castColor = self:GetCastColor()
|
|
self:SetColor(castColor) --SetColor handles with ParseColors()
|
|
|
|
self.percentText:Hide()
|
|
self.Text:SetText(INTERRUPTED) --auto locale within the global namespace
|
|
|
|
self:ScheduleToHide(1)
|
|
end
|
|
end,
|
|
|
|
UNIT_SPELLCAST_DELAYED = function(self, unit, ...)
|
|
local name, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible = CastInfo.UnitCastingInfo (unit)
|
|
|
|
if (not self:IsValid (unit, name, isTradeSkill)) then
|
|
return
|
|
end
|
|
|
|
--update the cast time
|
|
self.spellStartTime = startTime / 1000
|
|
self.spellEndTime = endTime / 1000
|
|
self.value = GetTime() - self.spellStartTime
|
|
self.maxValue = self.spellEndTime - self.spellStartTime
|
|
self:SetMinMaxValues(0, self.maxValue)
|
|
end,
|
|
|
|
UNIT_SPELLCAST_CHANNEL_UPDATE = function(self, unit, ...)
|
|
local name, text, texture, startTime, endTime, isTradeSkill, notInterruptible, spellID, _, numStages = CastInfo.UnitChannelInfo (unit)
|
|
|
|
if (not self:IsValid(unit, name, isTradeSkill)) then
|
|
return
|
|
end
|
|
|
|
--update the cast time
|
|
self.spellStartTime = startTime / 1000
|
|
self.spellEndTime = endTime / 1000
|
|
self.value = self.empowered and (GetTime() - self.spellStartTime) or (self.spellEndTime - GetTime())
|
|
self.maxValue = self.spellEndTime - self.spellStartTime
|
|
|
|
if (self.value < 0 or self.value > self.maxValue) then
|
|
self.value = 0
|
|
end
|
|
|
|
self:SetMinMaxValues(0, self.maxValue)
|
|
self:SetValue(self.value)
|
|
end,
|
|
|
|
--cast changed its state to interruptable
|
|
UNIT_SPELLCAST_INTERRUPTIBLE = function(self, unit, ...)
|
|
self.canInterrupt = true
|
|
self:UpdateCastColor()
|
|
self:UpdateInterruptState()
|
|
end,
|
|
|
|
--cast changed its state to non interruptable
|
|
UNIT_SPELLCAST_NOT_INTERRUPTIBLE = function(self, unit, ...)
|
|
self.canInterrupt = false
|
|
self:UpdateCastColor()
|
|
self:UpdateInterruptState()
|
|
end,
|
|
}
|
|
|
|
detailsFramework:Mixin(detailsFramework.CastFrameFunctions, detailsFramework.ScriptHookMixin)
|
|
|
|
-- ~castbar
|
|
|
|
---create a castbar widget
|
|
---@param parent frame
|
|
---@param name string?
|
|
---@param settingsOverride table? a table with key/value pairs to override the default settings
|
|
---@return df_castbar
|
|
function detailsFramework:CreateCastBar(parent, name, settingsOverride)
|
|
assert(name or parent:GetName(), "DetailsFramework:CreateCastBar parameter 'name' omitted and parent has no name.")
|
|
|
|
local castBar = CreateFrame("StatusBar", name or (parent:GetName() .. "CastBar"), parent, "BackdropTemplate")
|
|
|
|
do --layers
|
|
|
|
--these widgets was been made with back compatibility in mind
|
|
--they are using the same names as the retail game uses on the nameplate castbar
|
|
--this should make Plater core and Plater scripts made by users compatible with the new unit frame made on the framework
|
|
|
|
--background
|
|
castBar.background = castBar:CreateTexture(nil, "background", nil, -6)
|
|
castBar.extraBackground = castBar:CreateTexture(nil, "background", nil, -5)
|
|
|
|
--overlay
|
|
castBar.Text = castBar:CreateFontString(nil, "overlay", "SystemFont_Shadow_Small")
|
|
castBar.Text:SetDrawLayer("overlay", 1)
|
|
castBar.Text:SetPoint("center", 0, 0)
|
|
|
|
castBar.BorderShield = castBar:CreateTexture(nil, "overlay", nil, 5)
|
|
castBar.BorderShield:Hide()
|
|
|
|
castBar.Icon = castBar:CreateTexture(nil, "overlay", nil, 4)
|
|
castBar.Icon:Hide()
|
|
|
|
castBar.Spark = castBar:CreateTexture(nil, "overlay", nil, 3)
|
|
castBar.Spark:SetBlendMode("ADD")
|
|
|
|
--time left on the cast
|
|
castBar.percentText = castBar:CreateFontString(nil, "overlay", "SystemFont_Shadow_Small")
|
|
castBar.percentText:SetDrawLayer("overlay", 7)
|
|
|
|
--statusbar texture
|
|
castBar.barTexture = castBar:CreateTexture(nil, "artwork", nil, -6)
|
|
castBar:SetStatusBarTexture(castBar.barTexture)
|
|
|
|
--animations fade in and out
|
|
local fadeOutAnimationHub = detailsFramework:CreateAnimationHub(castBar, detailsFramework.CastFrameFunctions.Animation_FadeOutStarted, detailsFramework.CastFrameFunctions.Animation_FadeOutFinished)
|
|
fadeOutAnimationHub.alpha1 = detailsFramework:CreateAnimation(fadeOutAnimationHub, "ALPHA", 1, 1, 1, 0)
|
|
castBar.fadeOutAnimation = fadeOutAnimationHub
|
|
|
|
local fadeInAnimationHub = detailsFramework:CreateAnimationHub(castBar, detailsFramework.CastFrameFunctions.Animation_FadeInStarted, detailsFramework.CastFrameFunctions.Animation_FadeInFinished)
|
|
fadeInAnimationHub.alpha1 = detailsFramework:CreateAnimation(fadeInAnimationHub, "ALPHA", 1, 0.150, 0, 1)
|
|
castBar.fadeInAnimation = fadeInAnimationHub
|
|
|
|
--animatios flash
|
|
local flashTexture = castBar:CreateTexture(nil, "overlay", nil, 7)
|
|
flashTexture:SetColorTexture(1, 1, 1, 1)
|
|
flashTexture:SetAllPoints()
|
|
flashTexture:SetAlpha(0)
|
|
flashTexture:Hide()
|
|
flashTexture:SetBlendMode("ADD")
|
|
castBar.flashTexture = flashTexture
|
|
|
|
local flashAnimationHub = detailsFramework:CreateAnimationHub(flashTexture, function() flashTexture:Show() end, function() flashTexture:Hide() end)
|
|
detailsFramework:CreateAnimation(flashAnimationHub, "ALPHA", 1, 0.2, 0, 0.8)
|
|
detailsFramework:CreateAnimation(flashAnimationHub, "ALPHA", 2, 0.2, 1, 0)
|
|
castBar.flashAnimation = flashAnimationHub
|
|
end
|
|
|
|
--mixins
|
|
detailsFramework:Mixin(castBar, detailsFramework.CastFrameFunctions)
|
|
detailsFramework:Mixin(castBar, detailsFramework.StatusBarFunctions)
|
|
|
|
|
|
castBar:CreateTextureMask()
|
|
castBar:AddMaskTexture(castBar.flashTexture)
|
|
castBar:AddMaskTexture(castBar.background)
|
|
castBar:AddMaskTexture(castBar.extraBackground)
|
|
|
|
castBar:SetTexture([[Interface\WorldStateFrame\WORLDSTATEFINALSCORE-HIGHLIGHT]])
|
|
|
|
--settings and hooks
|
|
local settings = detailsFramework.table.copy({}, detailsFramework.CastFrameFunctions.Settings)
|
|
if (settingsOverride) then
|
|
detailsFramework.table.copy(settings, settingsOverride)
|
|
end
|
|
castBar.Settings = settings
|
|
|
|
local hookList = detailsFramework.table.copy({}, detailsFramework.CastFrameFunctions.HookList)
|
|
castBar.HookList = hookList
|
|
|
|
--initialize the cast bar
|
|
castBar:Initialize()
|
|
|
|
return castBar
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
--unit frame
|
|
|
|
--[=[
|
|
DF:CreateUnitFrame(parent, name, settingsOverride)
|
|
creates a very basic unit frame with a healthbar, castbar and power bar
|
|
each unit frame has a .Settings table which isn't shared among other unit frames created with this method
|
|
all members names are the same as the unit frame from the retail game
|
|
|
|
@parent = frame to pass for the CreateFrame function
|
|
@name = absolute name of the frame, if omitted a random name is created
|
|
@settingsOverride = table with keys and values to replace the defaults from the framework
|
|
|
|
--]=]
|
|
|
|
|
|
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
--unit frame
|
|
|
|
--return true if the unit has been claimed by another player (health bar is gray)
|
|
local isUnitTapDenied = function(unit)
|
|
return unit and not UnitPlayerControlled(unit) and UnitIsTapDenied(unit)
|
|
end
|
|
|
|
---@class df_unitframesettings : table
|
|
---@field ClearUnitOnHide boolean true
|
|
---@field ShowCastBar boolean true
|
|
---@field ShowPowerBar boolean true
|
|
---@field ShowUnitName boolean true
|
|
---@field ShowBorder boolean true
|
|
---@field CanModifyHealhBarColor boolean true
|
|
---@field ColorByAggro boolean false
|
|
---@field FixedHealthColor boolean false
|
|
---@field UseFriendlyClassColor boolean true
|
|
---@field UseEnemyClassColor boolean true
|
|
---@field ShowTargetOverlay boolean true
|
|
---@field BorderColor table
|
|
---@field CanTick boolean
|
|
---@field Width number
|
|
---@field Height number
|
|
---@field PowerBarHeight number
|
|
---@field CastBarHeight number
|
|
|
|
---@class df_unitframemixin
|
|
---@field WidgetType string
|
|
---@field Settings df_unitframesettings
|
|
---@field SetHealthBarColor fun(self:df_unitframe, r:number, g:number?, b:number?, a:number?)
|
|
---@field SetUnit fun(self:df_unitframe, unit:string?) sets the unit to be shown in the unit frame
|
|
---@field OnTick fun(self:df_unitframe, deltaTime:number?) if CanTick is true, this function will be called every frame
|
|
|
|
detailsFramework.UnitFrameFunctions = {
|
|
WidgetType = "unitFrame",
|
|
|
|
Settings = {
|
|
--unit frames
|
|
ClearUnitOnHide = true, --if tue it'll set the unit to nil when the unit frame is set to hide
|
|
ShowCastBar = true, --if this is false, the cast bar for the unit won't be shown
|
|
ShowPowerBar = true, --if true it'll show the power bar for the unit, e.g. the mana bar
|
|
ShowUnitName = true, --if false, the unit name won't show
|
|
ShowBorder = true, --if false won't show the border frame
|
|
|
|
--health bar color
|
|
CanModifyHealhBarColor = true, --if false it won't change the color of the health bar
|
|
ColorByAggro = false, --if true it'll color the healthbar with red color when the unit has aggro on player
|
|
FixedHealthColor = false, --color override with a table {r=1, g=1, b=1}
|
|
UseFriendlyClassColor = true, --make the healthbar class color for friendly players
|
|
UseEnemyClassColor = true, --make the healthbar class color for enemy players
|
|
|
|
--misc
|
|
ShowTargetOverlay = true, --shows a highlighht for the player current target
|
|
BorderColor = detailsFramework:CreateColorTable(0, 0, 0, 1), --border color, set to alpha zero for no border
|
|
CanTick = false, --if true it'll run the OnTick event
|
|
|
|
--size
|
|
Width = 100,
|
|
Height = 20,
|
|
PowerBarHeight = 4,
|
|
CastBarHeight = 8,
|
|
},
|
|
|
|
UnitFrameEvents = {
|
|
--run for all units
|
|
{"PLAYER_ENTERING_WORLD"},
|
|
{"PARTY_MEMBER_DISABLE"},
|
|
{"PARTY_MEMBER_ENABLE"},
|
|
{"PLAYER_TARGET_CHANGED"},
|
|
|
|
--run for one unit
|
|
{"UNIT_NAME_UPDATE", true},
|
|
{"UNIT_CONNECTION", true},
|
|
{"UNIT_ENTERED_VEHICLE", true},
|
|
{"UNIT_EXITED_VEHICLE", true},
|
|
{"UNIT_PET", true},
|
|
{"UNIT_THREAT_LIST_UPDATE", true},
|
|
},
|
|
|
|
--used when a event is triggered to quickly check if is a unit event
|
|
IsUnitEvent = {
|
|
["UNIT_NAME_UPDATE"] = true,
|
|
["UNIT_CONNECTION"] = true,
|
|
["UNIT_ENTERED_VEHICLE"] = true,
|
|
["UNIT_EXITED_VEHICLE"] = true,
|
|
["UNIT_PET"] = true,
|
|
["UNIT_THREAT_LIST_UPDATE"] = true,
|
|
},
|
|
|
|
Initialize = function(self)
|
|
self.border:SetBorderColor(self.Settings.BorderColor)
|
|
|
|
PixelUtil.SetWidth(self, self.Settings.Width, 1)
|
|
PixelUtil.SetHeight(self, self.Settings.Height, 1)
|
|
|
|
PixelUtil.SetPoint(self.powerBar, "bottomleft", self, "bottomleft", 0, 0, 1, 1)
|
|
PixelUtil.SetPoint(self.powerBar, "bottomright", self, "bottomright", 0, 0, 1, 1)
|
|
PixelUtil.SetHeight(self.powerBar, self.Settings.PowerBarHeight, 1)
|
|
|
|
--make the castbar overlap the powerbar
|
|
PixelUtil.SetPoint(self.castBar, "bottomleft", self, "bottomleft", 0, 0, 1, 1)
|
|
PixelUtil.SetPoint(self.castBar, "bottomright", self, "bottomright", 0, 0, 1, 1)
|
|
PixelUtil.SetHeight(self.castBar, self.Settings.CastBarHeight, 1)
|
|
end,
|
|
|
|
SetHealthBarColor = function(self, r, g, b, a)
|
|
self.healthBar:SetColor(r, g, b, a)
|
|
end,
|
|
|
|
--register all events which will be used by the unit frame
|
|
RegisterEvents = function(self)
|
|
--register events
|
|
for index, eventTable in ipairs(self.UnitFrameEvents) do
|
|
local event, isUnitEvent = unpack(eventTable)
|
|
if (not isUnitEvent) then
|
|
self:RegisterEvent(event)
|
|
else
|
|
self:RegisterUnitEvent (event, self.unit, self.displayedUnit ~= unit and self.displayedUnit or nil)
|
|
end
|
|
end
|
|
|
|
--check settings and unregister events for disabled features
|
|
if (not self.Settings.ColorByAggro) then
|
|
self:UnregisterEvent ("UNIT_THREAT_LIST_UPDATE")
|
|
end
|
|
|
|
--set scripts
|
|
self:SetScript("OnEvent", self.OnEvent)
|
|
self:SetScript("OnHide", self.OnHide)
|
|
|
|
if (self.Settings.CanTick) then
|
|
self:SetScript("OnUpdate", self.OnTick)
|
|
end
|
|
end,
|
|
|
|
--unregister events, called when this unit frame losses its unit
|
|
UnregisterEvents = function(self)
|
|
for index, eventTable in ipairs(self.UnitFrameEvents) do
|
|
local event, firstUnit, secondUnit = unpack(eventTable)
|
|
self:UnregisterEvent(event)
|
|
end
|
|
|
|
self:SetScript("OnEvent", nil)
|
|
self:SetScript("OnUpdate", nil)
|
|
self:SetScript("OnHide", nil)
|
|
end,
|
|
|
|
--call every tick
|
|
OnTick = function(self, deltaTime) end, --if overrided, set 'CanTick' to true on the settings table
|
|
|
|
--when an event happen for this unit, send it to the apropriate function
|
|
OnEvent = function(self, event, ...)
|
|
--run the function for this event
|
|
local eventFunc = self[event]
|
|
if (eventFunc) then
|
|
--is this event an unit event?
|
|
if (self.IsUnitEvent[event]) then
|
|
local unit = ...
|
|
--check if is for this unit (even if the event is registered only for the unit)
|
|
if (unit == self.unit or unit == self.displayedUnit) then
|
|
eventFunc(self, ...)
|
|
end
|
|
else
|
|
eventFunc(self, ...)
|
|
end
|
|
end
|
|
end,
|
|
|
|
OnHide = function(self)
|
|
if (self.Settings.ClearUnitOnHide) then
|
|
self:SetUnit(nil)
|
|
end
|
|
end,
|
|
|
|
--run if the unit currently shown is different than the new one
|
|
SetUnit = function(self, unit)
|
|
if (unit ~= self.unit or unit == nil) then
|
|
self.unit = unit --absolute unit
|
|
self.displayedUnit = unit --~todo rename to 'displayedUnit' for back compatibility with older scripts in Plater
|
|
self.unitInVehicle = nil --true when the unit is in a vehicle
|
|
|
|
if (unit) then
|
|
self:RegisterEvents()
|
|
|
|
self.guid = UnitGUID(unit)
|
|
self.class = select(2, UnitClass(unit))
|
|
self.name = UnitName(unit)
|
|
|
|
self.healthBar:SetUnit(unit, self.displayedUnit)
|
|
|
|
--is using castbars?
|
|
if (self.Settings.ShowCastBar) then
|
|
self.castBar:SetUnit(unit, self.displayedUnit)
|
|
else
|
|
self.castBar:SetUnit(nil)
|
|
end
|
|
|
|
--is using powerbars?
|
|
if (self.Settings.ShowPowerBar) then
|
|
self.powerBar:SetUnit(unit, self.displayedUnit)
|
|
else
|
|
self.powerBar:SetUnit(nil)
|
|
end
|
|
|
|
--is using the border?
|
|
if (self.Settings.ShowBorder) then
|
|
self.border:Show()
|
|
else
|
|
self.border:Hide()
|
|
end
|
|
|
|
if (not self.Settings.ShowUnitName) then
|
|
self.unitName:Hide()
|
|
end
|
|
else
|
|
self:UnregisterEvents()
|
|
self.healthBar:SetUnit(nil)
|
|
self.castBar:SetUnit(nil)
|
|
self.powerBar:SetUnit(nil)
|
|
end
|
|
|
|
self:UpdateUnitFrame()
|
|
end
|
|
end,
|
|
|
|
--if the unit is controlling a vehicle, need to show the vehicle instead
|
|
--.unit and .displayedUnit is always the same execept when the unit is controlling a vehicle, then .displayedUnit is the unitID for the vehicle
|
|
--todo: see what 'UnitTargetsVehicleInRaidUI' is, there's a call for this in the CompactUnitFrame.lua but zero documentation
|
|
CheckVehiclePossession = function(self)
|
|
--this unit is possessing a vehicle?
|
|
local unitPossessVehicle = (IS_WOW_PROJECT_MAINLINE) and UnitHasVehicleUI(self.unit) or false
|
|
if (unitPossessVehicle) then
|
|
if (not self.unitInVehicle) then
|
|
if (UnitIsUnit("player", self.unit)) then
|
|
self.displayedUnit = "vehicle"
|
|
self.unitInVehicle = true
|
|
self:RegisterEvents()
|
|
self:UpdateAllWidgets()
|
|
return true
|
|
end
|
|
|
|
local prefix, id, suffix = string.match(self.unit, "([^%d]+)([%d]*)(.*)") --CompactUnitFrame.lua
|
|
local vehicleUnitID = prefix .. "pet" .. id .. suffix
|
|
if (UnitExists(vehicleUnitID)) then
|
|
self.displayedUnit = vehicleUnitID
|
|
self.unitInVehicle = true
|
|
self:RegisterEvents()
|
|
self:UpdateAllWidgets()
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
if (self.unitInVehicle) then
|
|
self.displayedUnit = self.unit
|
|
self.unitInVehicle = nil
|
|
self:RegisterEvents()
|
|
self:UpdateAllWidgets()
|
|
end
|
|
end,
|
|
|
|
--find a color for the health bar, if a color has been passed in the arguments use it instead, 'CanModifyHealhBarColor' must be true for this function run
|
|
UpdateHealthColor = function(self, r, g, b)
|
|
--check if color changes is disabled
|
|
if (not self.Settings.CanModifyHealhBarColor) then
|
|
return
|
|
end
|
|
|
|
local unit = self.displayedUnit
|
|
|
|
--check if a color has been passed within the parameters
|
|
if (r) then
|
|
--check if passed a special color
|
|
if (type(r) ~= "number") then
|
|
r, g, b = detailsFramework:ParseColors(r)
|
|
end
|
|
|
|
self:SetHealthBarColor(r, g, b)
|
|
return
|
|
end
|
|
|
|
--check if there is a color override in the settings
|
|
if (self.Settings.FixedHealthColor) then
|
|
local FixedHealthColor = self.Settings.FixedHealthColor
|
|
r, g, b = FixedHealthColor.r, FixedHealthColor.g, FixedHealthColor.b
|
|
self:SetHealthBarColor(r, g, b)
|
|
return
|
|
end
|
|
|
|
--check if the unit is a player
|
|
if (UnitIsPlayer(unit)) then
|
|
--check if the unit is disconnected (in case it is a player
|
|
if (not UnitIsConnected(unit)) then
|
|
self:SetHealthBarColor(.5, .5, .5)
|
|
return
|
|
end
|
|
|
|
--is a friendly or enemy player?
|
|
if (UnitIsFriend ("player", unit)) then
|
|
if (self.Settings.UseFriendlyClassColor) then
|
|
local _, className = UnitClass(unit)
|
|
if (className) then
|
|
local classColor = RAID_CLASS_COLORS[className]
|
|
if (classColor) then
|
|
self:SetHealthBarColor(classColor.r, classColor.g, classColor.b)
|
|
return
|
|
end
|
|
end
|
|
else
|
|
self:SetHealthBarColor(0, 1, 0)
|
|
return
|
|
end
|
|
else
|
|
if (self.Settings.UseEnemyClassColor) then
|
|
local _, className = UnitClass(unit)
|
|
if (className) then
|
|
local classColor = RAID_CLASS_COLORS[className]
|
|
if (classColor) then
|
|
self:SetHealthBarColor(classColor.r, classColor.g, classColor.b)
|
|
return
|
|
end
|
|
end
|
|
else
|
|
self:SetHealthBarColor(1, 0, 0)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
--is tapped?
|
|
if (isUnitTapDenied(unit)) then
|
|
self:SetHealthBarColor(.6, .6, .6)
|
|
return
|
|
end
|
|
|
|
--is this is a npc attacking the player?
|
|
if (self.Settings.ColorByAggro) then
|
|
local _, threatStatus = UnitDetailedThreatSituation("player", unit)
|
|
if (threatStatus) then
|
|
self:SetHealthBarColor(1, 0, 0)
|
|
return
|
|
end
|
|
end
|
|
|
|
-- get the regular color by selection
|
|
r, g, b = UnitSelectionColor(unit)
|
|
self:SetHealthBarColor (r, g, b)
|
|
end,
|
|
|
|
--misc
|
|
UpdateName = function(self)
|
|
if (not self.Settings.ShowUnitName) then
|
|
return
|
|
end
|
|
|
|
--unit name without realm names by default
|
|
local name = UnitName(self.unit)
|
|
self.unitName:SetText(name)
|
|
self.unitName:Show()
|
|
end,
|
|
|
|
--this runs when the player it self changes its target, need to update the current target overlay
|
|
--todo: add focus overlay
|
|
UpdateTargetOverlay = function(self)
|
|
if (not self.Settings.ShowTargetOverlay) then
|
|
self.targetOverlay:Hide()
|
|
return
|
|
end
|
|
|
|
if (UnitIsUnit(self.displayedUnit, "target")) then
|
|
self.targetOverlay:Show()
|
|
else
|
|
self.targetOverlay:Hide()
|
|
end
|
|
end,
|
|
|
|
UpdateAllWidgets = function(self)
|
|
if (UnitExists(self.displayedUnit)) then
|
|
local unit = self.unit
|
|
local displayedUnit = self.displayedUnit
|
|
|
|
self:SetUnit(unit, displayedUnit)
|
|
|
|
--is using castbars?
|
|
if (self.Settings.ShowCastBar) then
|
|
self.castBar:SetUnit(unit, displayedUnit)
|
|
end
|
|
|
|
--is using powerbars?
|
|
if (self.Settings.ShowPowerBar) then
|
|
self.powerBar:SetUnit(unit, displayedUnit)
|
|
end
|
|
|
|
self:UpdateName()
|
|
self:UpdateTargetOverlay()
|
|
self:UpdateHealthColor()
|
|
end
|
|
end,
|
|
|
|
--update the unit frame and its widgets
|
|
UpdateUnitFrame = function(self)
|
|
local unitInVehicle = self:CheckVehiclePossession()
|
|
|
|
--if the unit is inside a vehicle, the vehicle possession function will call an update on all widgets
|
|
if (not unitInVehicle) then
|
|
self:UpdateAllWidgets()
|
|
end
|
|
end,
|
|
|
|
--event handles
|
|
PLAYER_ENTERING_WORLD = function(self, ...)
|
|
self:UpdateUnitFrame()
|
|
end,
|
|
|
|
--update overlays when the player changes its target
|
|
PLAYER_TARGET_CHANGED = function(self, ...)
|
|
self:UpdateTargetOverlay()
|
|
end,
|
|
|
|
--unit received a name update
|
|
UNIT_NAME_UPDATE = function(self, ...)
|
|
self:UpdateName()
|
|
end,
|
|
|
|
--this is registered only if .settings.ColorByAggro is true
|
|
UNIT_THREAT_LIST_UPDATE = function(self, ...)
|
|
if (self.Settings.ColorByAggro) then
|
|
self:UpdateHealthColor()
|
|
end
|
|
end,
|
|
|
|
--vehicle
|
|
UNIT_ENTERED_VEHICLE = function(self, ...)
|
|
self:UpdateUnitFrame()
|
|
end,
|
|
UNIT_EXITED_VEHICLE = function(self, ...)
|
|
self:UpdateUnitFrame()
|
|
end,
|
|
|
|
--pet
|
|
UNIT_PET = function(self, ...)
|
|
self:UpdateUnitFrame()
|
|
end,
|
|
|
|
--player connection
|
|
UNIT_CONNECTION = function(self, ...)
|
|
if (UnitIsConnected (self.unit)) then
|
|
self:UpdateUnitFrame()
|
|
end
|
|
end,
|
|
|
|
PARTY_MEMBER_ENABLE = function(self, ...)
|
|
if (UnitIsConnected(self.unit)) then
|
|
self:UpdateName()
|
|
end
|
|
end,
|
|
}
|
|
|
|
---@class df_unitframe : button, df_unitframemixin
|
|
---@field unit string
|
|
---@field displayedUnit string
|
|
---@field guid guid
|
|
---@field class class
|
|
---@field name actorname
|
|
---@field unitInVehicle boolean
|
|
---@field border frame
|
|
---@field overlayFrame frame
|
|
---@field unitName fontstring
|
|
---@field healthBar df_healthbar
|
|
---@field castBar df_castbar
|
|
---@field powerBar df_powerbar
|
|
---@field targetOverlay texture
|
|
---@field Settings df_unitframesettings
|
|
|
|
local globalBaseFrameLevel = 1 -- to be increased + used across each new plate
|
|
|
|
-- ~unitframe
|
|
---create a unit frame with a health bar, cast bar and power bar
|
|
---@param parent frame
|
|
---@param name string?
|
|
---@param unitFrameSettingsOverride table?
|
|
---@param healthBarSettingsOverride table?
|
|
---@param castBarSettingsOverride table?
|
|
---@param powerBarSettingsOverride table?
|
|
---@return df_unitframe
|
|
function detailsFramework:CreateUnitFrame(parent, name, unitFrameSettingsOverride, healthBarSettingsOverride, castBarSettingsOverride, powerBarSettingsOverride)
|
|
local parentName = name or ("DetailsFrameworkUnitFrame" .. tostring(math.random(1, 100000000)))
|
|
|
|
--create the main unit frame
|
|
local mewUnitFrame = CreateFrame("button", parentName, parent, "BackdropTemplate")
|
|
|
|
--base level
|
|
--local baseFrameLevel = f:GetFrameLevel()
|
|
local baseFrameLevel = globalBaseFrameLevel
|
|
globalBaseFrameLevel = globalBaseFrameLevel + 10
|
|
|
|
mewUnitFrame:SetFrameLevel(baseFrameLevel)
|
|
|
|
--create the healthBar
|
|
local healthBar = detailsFramework:CreateHealthBar(mewUnitFrame, nil, healthBarSettingsOverride)
|
|
healthBar:SetFrameLevel(baseFrameLevel + 1)
|
|
mewUnitFrame.healthBar = healthBar
|
|
|
|
--create the power bar
|
|
local powerBar = detailsFramework:CreatePowerBar(mewUnitFrame, nil, powerBarSettingsOverride)
|
|
powerBar:SetFrameLevel(baseFrameLevel + 2)
|
|
mewUnitFrame.powerBar = powerBar
|
|
|
|
--create the castBar
|
|
local castBar = detailsFramework:CreateCastBar(mewUnitFrame, nil, castBarSettingsOverride)
|
|
castBar:SetFrameLevel(baseFrameLevel + 3)
|
|
mewUnitFrame.castBar = castBar
|
|
|
|
--border frame
|
|
local borderFrame = detailsFramework:CreateBorderFrame(mewUnitFrame, mewUnitFrame:GetName() .. "Border")
|
|
borderFrame:SetFrameLevel(mewUnitFrame:GetFrameLevel() + 5)
|
|
mewUnitFrame.border = borderFrame
|
|
|
|
--overlay frame (widgets that need to stay above the unit frame)
|
|
local overlayFrame = CreateFrame("frame", "$parentOverlayFrame", mewUnitFrame, "BackdropTemplate")
|
|
overlayFrame:SetFrameLevel(mewUnitFrame:GetFrameLevel() + 6)
|
|
mewUnitFrame.overlayFrame = overlayFrame
|
|
|
|
--unit frame layers
|
|
do
|
|
--artwork
|
|
mewUnitFrame.unitName = mewUnitFrame:CreateFontString(nil, "artwork", "GameFontHighlightSmall")
|
|
PixelUtil.SetPoint(mewUnitFrame.unitName, "topleft", healthBar, "topleft", 2, -2, 1, 1)
|
|
|
|
--target overlay - it's parented in the healthbar so other widgets won't get the overlay
|
|
mewUnitFrame.targetOverlay = overlayFrame:CreateTexture(nil, "artwork")
|
|
mewUnitFrame.targetOverlay:SetTexture(healthBar:GetTexture())
|
|
mewUnitFrame.targetOverlay:SetBlendMode("ADD")
|
|
mewUnitFrame.targetOverlay:SetAlpha(.5)
|
|
mewUnitFrame.targetOverlay:SetAllPoints(healthBar)
|
|
end
|
|
|
|
--mixins
|
|
--inject mixins
|
|
detailsFramework:Mixin(mewUnitFrame, detailsFramework.UnitFrameFunctions)
|
|
|
|
--create the settings table and copy the overrides into it, the table is set into the frame after the mixin
|
|
local unitFrameSettings = detailsFramework.table.copy({}, detailsFramework.UnitFrameFunctions.Settings)
|
|
if (unitFrameSettingsOverride) then
|
|
unitFrameSettings = detailsFramework.table.copy(unitFrameSettings, unitFrameSettingsOverride)
|
|
end
|
|
mewUnitFrame.Settings = unitFrameSettings
|
|
|
|
--initialize scripts
|
|
--unitframe
|
|
mewUnitFrame:Initialize()
|
|
|
|
return mewUnitFrame
|
|
end
|
|
|