--[[ # 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, ...) SPELLCAST_START(self, unpack(arg)) return SPELLCAST_CHANNEL_START(self, unpack(arg)) 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)