Files
ElvUIModernized/ElvUI/Libraries/oUF/elements/castbar.lua
T
2026-07-26 15:47:37 +02:00

443 lines
11 KiB
Lua

--[[
# Element: Castbar
Handles the visibility and updating of spell castbars.
Based upon oUF_Castbar by starlon.
## Widget
Castbar - A `StatusBar` to represent spell cast/channel progress.
## Sub-Widgets
.Text - A `FontString` to represent spell name.
.Time - A `FontString` to represent spell duration.
## Notes
A default texture will be applied to the StatusBar and Texture widgets if they don't have a texture or a color set.
## Options
.timeToHold - indicates for how many seconds the castbar should be visible after a _FAILED or _INTERRUPTED
event. Defaults to 0 (number)
## Examples
-- Position and size
local Castbar = CreateFrame("StatusBar", nil, self)
Castbar:SetSize(20, 20)
Castbar:SetPoint("TOP")
Castbar:SetPoint("LEFT")
Castbar:SetPoint("RIGHT")
-- Add a background
local Background = Castbar:CreateTexture(nil, "BACKGROUND")
Background:SetAllPoints(Castbar)
Background:SetTexture(1, 1, 1, .5)
-- Add a spark
local Spark = Castbar:CreateTexture(nil, "OVERLAY")
Spark:SetSize(20, 20)
Spark:SetBlendMode("ADD")
-- Add a timer
local Time = Castbar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
Time:SetPoint("RIGHT", Castbar)
-- Add spell text
local Text = Castbar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
Text:SetPoint("LEFT", Castbar)
-- Add spell icon
local Icon = Castbar:CreateTexture(nil, 'OVERLAY')
Icon:SetSize(20, 20)
Icon:SetPoint('TOPLEFT', Castbar, 'TOPLEFT')
-- Register it with oUF
Castbar.bg = Background
Castbar.Spark = Spark
Castbar.Time = Time
Castbar.Text = Text
Castbar.Icon = Icon
self.Castbar = Castbar
--]]
local ns = oUF
local oUF = ns.oUF
local GetActionTexture = GetActionTexture
local GetContainerItemInfo = GetContainerItemInfo
local GetSpellTexture = GetSpellTexture
local GetTime = GetTime
local iconTexture
hooksecurefunc("UseAction", function(id)
iconTexture = GetActionTexture(id)
end)
hooksecurefunc("CastSpell", function(id, bookType)
iconTexture = GetSpellTexture(id, bookType)
end)
hooksecurefunc("UseContainerItem", function(id, index)
iconTexture = GetContainerItemInfo(id, index)
end)
local function SPELLCAST_START(self, event, name, endTime)
local element = self.Castbar
if(not name) then
return element:Hide()
end
endTime = endTime / 1000
element.startTime = GetTime()
element.duration = element.startTime
element.max = endTime
element.delay = 0
element.casting = true
element.holdTime = 0
element:SetMinMaxValues(0, element.max)
element:SetValue(0)
if(element.Text) then element.Text:SetText(name) end
if(element.Icon) then
if(not iconTexture) then iconTexture = "Interface\\Icons\\Ability_Ambush" end
element.Icon:SetTexture(iconTexture)
iconTexture = nil
end
if(element.Time) then element.Time:SetText() end
--[[ Callback: Castbar:PostCastStart(unit, name)
Called after the element has been updated upon a spell cast start.
* self - the Castbar widget
* name - name of the spell being cast (string)
--]]
if(element.PostCastStart) then
element:PostCastStart(name)
end
element:Show()
end
local function SPELLCAST_FAILED(self, event)
if(not self.casting) then return end
local element = self.Castbar
local text = element.Text
if(text) then
text:SetText(FAILED)
end
element.casting = nil
element.holdTime = element.timeToHold or 0
--[[ Callback: Castbar:PostCastFailed(unit, name)
Called after the element has been updated upon a failed spell cast.
* self - the Castbar widget
--]]
if(element.PostCastFailed) then
return element:PostCastFailed()
end
end
local function SPELLCAST_INTERRUPTED(self, event)
local element = self.Castbar
local text = element.Text
if(text) then
text:SetText(INTERRUPTED)
end
element.casting = nil
element.channeling = nil
element.holdTime = element.timeToHold or 0
--[[ Callback: Castbar:PostCastInterrupted(unit, name)
Called after the element has been updated upon an interrupted spell cast.
* self - the Castbar widget
--]]
if(element.PostCastInterrupted) then
return element:PostCastInterrupted()
end
end
local function SPELLCAST_DELAYED(self, event, delay)
local element = self.Castbar
if(not delay or not element:IsShown()) then return end
delay = delay / 1000
local duration = GetTime() - (element.startTime / 1000)
if(duration < 0) then duration = 0 end
element.startTime = element.startTime + delay
element.delay = delay
element.duration = duration
element:SetValue(duration)
--[[ Callback: Castbar:PostCastDelayed(unit, name)
Called after the element has been updated when a spell cast has been delayed.
* self - the Castbar widget
--]]
if(element.PostCastDelayed) then
return element:PostCastDelayed()
end
end
local function SPELLCAST_STOP(self, event)
local element = self.Castbar
if(element.casting or element.channeling) then
iconTexture = nil
end
if(element:IsShown()) then
element.casting = nil
end
--[[ Callback: Castbar:PostCastStop(unit, name)
Called after the element has been updated when a spell cast has finished.
* self - the Castbar widget
--]]
if(element.PostCastStop) then
return element:PostCastStop()
end
end
local function SPELLCAST_CHANNEL_START(self, event, endTime, name)
local element = self.Castbar
if(not name) then
return
end
endTime = endTime / 1000
element.startTime = GetTime()
element.duration = element.startTime + endTime
element.max = endTime
element.delay = 0
element.channeling = true
element.holdTime = 0
-- We have to do this, as it's possible for spell casts to never have _STOP
-- executed or be fully completed by the OnUpdate handler before CHANNEL_START
-- is called.
element.casting = nil
element:SetMinMaxValues(0, endTime)
element:SetValue(endTime)
if(element.Text) then element.Text:SetText(name) end
if(element.Icon) then
if(not iconTexture) then iconTexture = "Interface\\Icons\\Ability_Ambush" end
element.Icon:SetTexture(iconTexture)
iconTexture = nil
end
if(element.Time) then element.Time:SetText() end
--[[ Callback: Castbar:PostChannelStart(unit, name)
Called after the element has been updated upon a spell channel start.
* self - the Castbar widget
* name - name of the channeled spell (string)
--]]
if(element.PostChannelStart) then
element:PostChannelStart(name)
end
element:Show()
end
local function SPELLCAST_CHANNEL_UPDATE(self, event, delay)
local element = self.Castbar
if(not element:IsShown()) then
return
end
delay = delay / 1000
local duration = element.startTime + (element.max - GetTime()) - delay
element.delay = element.delay - duration
element.duration = duration
element.startTime = element.startTime - duration
element:SetValue(duration)
--[[ Callback: Castbar:PostChannelUpdate(unit, name)
Called after the element has been updated after a channeled spell has been delayed or interrupted.
* self - the Castbar widget
--]]
if(element.PostChannelUpdate) then
return element:PostChannelUpdate()
end
end
local function SPELLCAST_CHANNEL_STOP(self, event)
local element = self.Castbar
if(element:IsShown()) then
element.channeling = nil
--[[ Callback: Castbar:PostChannelUpdate(unit, name)
Called after the element has been updated after a channeled spell has been completed.
* self - the Castbar widget
--]]
if(element.PostChannelStop) then
return element:PostChannelStop()
end
end
end
local function onUpdate(self, elapsed)
if(self.casting) then
local duration = GetTime() - self.startTime
if(duration >= self.max) then
self.casting = nil
self:Hide()
if(self.PostCastStop) then self:PostCastStop(self.__owner.unit) end
return
end
if(self.Time) then
if(self.delay ~= 0) then
if(self.CustomDelayText) then
self:CustomDelayText(duration)
else
self.Time:SetText(format("%.1f|cffff0000-%.1f|r", duration, self.delay))
end
else
if(self.CustomTimeText) then
self:CustomTimeText(duration)
else
self.Time:SetText(format("%.1f", duration))
end
end
end
self.duration = duration
self:SetValue(duration)
if(self.Spark) then
self.Spark:SetPoint("CENTER", self, "LEFT", (duration / ((self.startTime + self.max) - self.startTime)) * self:GetWidth(), 0)
end
elseif(self.channeling) then
local duration = self.startTime + (self.max - GetTime())
if(duration <= 0) then
self.channeling = nil
self:Hide()
if(self.PostChannelStop) then self:PostChannelStop(self.__owner.unit) end
return
end
if(self.Time) then
if(self.delay ~= 0) then
if(self.CustomDelayText) then
self:CustomDelayText(duration)
else
self.Time:SetText(format("%.1f|cffff0000-%.1f|r", duration, self.delay))
end
else
if(self.CustomTimeText) then
self:CustomTimeText(duration)
else
self.Time:SetText(format("%.1f", duration))
end
end
end
self.duration = duration
self:SetValue(duration)
if(self.Spark) then
self.Spark:SetPoint("CENTER", self, "LEFT", (duration / self.max) * self:GetWidth(), 0)
end
elseif(self.holdTime > 0) then
self.holdTime = self.holdTime - elapsed
else
self.casting = nil
self.castName = nil
self.channeling = nil
self:Hide()
end
end
local function Update(self, event, a, b, c, d, e)
SPELLCAST_START(self, event, a, b, c, d, e)
return SPELLCAST_CHANNEL_START(self, event, a, b, c, d, e)
end
local function ForceUpdate(element)
return Update(element.__owner, "ForceUpdate", element.__owner.unit)
end
local function Enable(self, unit)
local element = self.Castbar
if(element) then
element.__owner = self
element.ForceUpdate = ForceUpdate
element.holdTime = 0
element:SetScript("OnUpdate", function()
if element.OnUpdate then
else
onUpdate(this, arg1)
end
end)
if(unit == "player") then
self:RegisterEvent("SPELLCAST_START", SPELLCAST_START)
self:RegisterEvent("SPELLCAST_STOP", SPELLCAST_STOP)
self:RegisterEvent("SPELLCAST_FAILED", SPELLCAST_FAILED)
self:RegisterEvent("SPELLCAST_INTERRUPTED", SPELLCAST_INTERRUPTED)
self:RegisterEvent("SPELLCAST_DELAYED", SPELLCAST_DELAYED)
self:RegisterEvent("SPELLCAST_CHANNEL_START", SPELLCAST_CHANNEL_START)
self:RegisterEvent("SPELLCAST_CHANNEL_UPDATE", SPELLCAST_CHANNEL_UPDATE)
self:RegisterEvent("SPELLCAST_CHANNEL_STOP", SPELLCAST_CHANNEL_STOP)
CastingBarFrame:UnregisterAllEvents()
CastingBarFrame.Show = CastingBarFrame.Hide
CastingBarFrame:Hide()
end
if(element:IsObjectType("StatusBar") and not element:GetStatusBarTexture()) then
element:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]])
end
local spark = element.Spark
if(spark and spark:IsObjectType("Texture") and not spark:GetTexture()) then
spark:SetTexture([[Interface\CastingBar\UI-CastingBar-Spark]])
end
element:Hide()
return true
end
end
local function Disable(self)
local element = self.Castbar
if(element) then
element:Hide()
self:UnregisterEvent("SPELLCAST_START", SPELLCAST_START)
self:UnregisterEvent("SPELLCAST_FAILED", SPELLCAST_FAILED)
self:UnregisterEvent("SPELLCAST_STOP", SPELLCAST_STOP)
self:UnregisterEvent("SPELLCAST_INTERRUPTED", SPELLCAST_INTERRUPTED)
self:UnregisterEvent("SPELLCAST_DELAYED", SPELLCAST_DELAYED)
self:UnregisterEvent("SPELLCAST_CHANNEL_START", SPELLCAST_CHANNEL_START)
self:UnregisterEvent("SPELLCAST_CHANNEL_UPDATE", SPELLCAST_CHANNEL_UPDATE)
self:UnregisterEvent("SPELLCAST_CHANNEL_STOP", SPELLCAST_CHANNEL_STOP)
element:SetScript("OnUpdate", nil)
end
end
oUF:AddElement("Castbar", function() end, Enable, Disable)