MxW_Addon/Libraries/oUF/elements/castbar.lua

602 lines
17 KiB
Lua
Raw Normal View History

2018-01-05 06:40:15 -05:00
--[[ Element: Castbar
Handles updating and visibility of unit castbars.
Widget
Castbar - A StatusBar to represent spell progress.
Sub-Widgets
.Text - A FontString to represent spell name.
.Icon - A Texture to represent spell icon.
.Time - A FontString to represent spell duration.
.Shield - A Texture to represent if it's possible to interrupt or spell
steal.
.SafeZone - A Texture to represent latency.
Options
.timeToHold - A Number to indicate for how many seconds the castbar should be
visible after a _FAILED or _INTERRUPTED event. Defaults to 0.
Credits
Based upon oUF_Castbar by starlon.
Notes
The default texture will be applied if the UI widget doesn't have a texture or
color defined.
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")
-- Add Shield
local Shield = Castbar:CreateTexture(nil, "OVERLAY")
Shield:SetSize(20, 20)
Shield:SetPoint("CENTER", Castbar)
-- Add safezone
local SafeZone = Castbar:CreateTexture(nil, "OVERLAY")
-- Register it with oUF
self.Castbar = Castbar
self.Castbar.bg = Background
self.Castbar.Spark = Spark
self.Castbar.Time = Time
self.Castbar.Text = Text
self.Castbar.Icon = Icon
self.Castbar.SafeZone = SafeZone
Hooks and Callbacks
]]
local _, ns = ...
local oUF = ns.oUF
local GetNetStats = GetNetStats
local GetTime = GetTime
local UnitCastingInfo = UnitCastingInfo
local UnitChannelInfo = UnitChannelInfo
local tradeskillCurrent, tradeskillTotal, mergeTradeskill = 0, 0, false
local updateSafeZone = function(self)
local sz = self.SafeZone
local width = self:GetWidth()
local _, _, _, ms = GetNetStats()
-- Guard against GetNetStats returning latencies of 0.
if(ms ~= 0) then
-- MADNESS!
local safeZonePercent = (width / self.max) * (ms / 1e5)
if(safeZonePercent > 1) then safeZonePercent = 1 end
sz:SetWidth(width * safeZonePercent)
sz:Show()
else
sz:Hide()
end
end
local UNIT_SPELLCAST_SENT = function (self, event, unit, spell, rank, target, castid)
local castbar = self.Castbar
castbar.curTarget = (target and target ~= "") and target or nil
if castbar.isTradeSkill then
castbar.tradeSkillCastId = castid
end
end
local UNIT_SPELLCAST_START = function(self, event, unit)
if(self.unit ~= unit and self.realUnit ~= unit) then return end
local castbar = self.Castbar
local name, _, text, texture, startTime, endTime, isTradeSkill, castid, notInterruptible, spellid = UnitCastingInfo(unit)
if(not name) then
return castbar:Hide()
end
endTime = endTime / 1e3
startTime = startTime / 1e3
local max = endTime - startTime
castbar.castid = castid
castbar.duration = GetTime() - startTime
castbar.max = max
castbar.delay = 0
castbar.casting = true
castbar.interrupt = notInterruptible -- NOTE: deprecated; to be removed
castbar.notInterruptible = notInterruptible
castbar.holdTime = 0
castbar.isTradeSkill = isTradeSkill
if(mergeTradeskill and isTradeSkill and UnitIsUnit(unit, "player")) then
castbar.duration = castbar.duration + (castbar.max * tradeskillCurrent);
castbar.max = max * tradeskillTotal;
if(unit == "player") then
tradeskillCurrent = tradeskillCurrent + 1;
end
castbar:SetValue(castbar.duration)
else
castbar:SetValue(0)
end
castbar:SetValue(0)
castbar:SetMinMaxValues(0, castbar.max)
if(castbar.Text) then castbar.Text:SetText(text) end
if(castbar.Icon) then castbar.Icon:SetTexture(texture) end
if(castbar.Time) then castbar.Time:SetText() end
local shield = castbar.Shield
if(shield and notInterruptible) then
shield:Show()
elseif(shield) then
shield:Hide()
end
local sf = castbar.SafeZone
if(sf) then
sf:ClearAllPoints()
sf:SetPoint'RIGHT'
sf:SetPoint'TOP'
sf:SetPoint'BOTTOM'
updateSafeZone(castbar)
end
if(castbar.PostCastStart) then
castbar:PostCastStart(unit, name, castid, spellid)
end
castbar:Show()
end
local UNIT_SPELLCAST_FAILED = function(self, event, unit, spellname, _, castid, spellid)
if(self.unit ~= unit and self.realUnit ~= unit) then return end
local castbar = self.Castbar
if (castbar.castid ~= castid) and (castbar.tradeSkillCastId ~= castid) then
return
end
if(mergeTradeskill and UnitIsUnit(unit, "player")) then
mergeTradeskill = false;
castbar.tradeSkillCastId = nil
end
local text = castbar.Text
if(text) then
text:SetText(FAILED)
end
castbar.casting = nil
castbar.interrupt = nil -- NOTE: deprecated; to be removed
castbar.notInterruptible = nil
castbar.holdTime = castbar.timeToHold or 0
if(castbar.PostCastFailed) then
return castbar:PostCastFailed(unit, spellname, castid, spellid)
end
end
local UNIT_SPELLCAST_FAILED_QUIET = function(self, event, unit, spellname, _, castid)
if(self.unit ~= unit and self.realUnit ~= unit) then return end
local castbar = self.Castbar
if (castbar.castid ~= castid) and (castbar.tradeSkillCastId ~= castid) then
return
end
if(mergeTradeskill and UnitIsUnit(unit, "player")) then
mergeTradeskill = false;
castbar.tradeSkillCastId = nil
end
castbar.casting = nil
castbar.interrupt = nil -- NOTE: deprecated; to be removed
castbar.notInterruptible = nil
castbar:SetValue(0)
castbar:Hide()
end
local UNIT_SPELLCAST_INTERRUPTED = function(self, event, unit, spellname, _, castid, spellid)
if(self.unit ~= unit and self.realUnit ~= unit) then return end
local castbar = self.Castbar
if (castbar.castid ~= castid) then
return
end
local text = castbar.Text
if(text) then
text:SetText(INTERRUPTED)
end
castbar.casting = nil
castbar.channeling = nil
castbar.holdTime = castbar.timeToHold or 0
if(castbar.PostCastInterrupted) then
return castbar:PostCastInterrupted(unit, spellname, castid, spellid)
end
end
local UNIT_SPELLCAST_INTERRUPTIBLE = function(self, event, unit)
if(self.unit ~= unit and self.realUnit ~= unit) then return end
local castbar = self.Castbar
local shield = castbar.Shield
if(shield) then
shield:Hide()
end
castbar.interrupt = nil -- NOTE: deprecated; to be removed
castbar.notInterruptible = nil
if(castbar.PostCastInterruptible) then
return castbar:PostCastInterruptible(unit)
end
end
local UNIT_SPELLCAST_NOT_INTERRUPTIBLE = function(self, event, unit)
if(self.unit ~= unit and self.realUnit ~= unit) then return end
local castbar = self.Castbar
local shield = castbar.Shield
if(shield) then
shield:Show()
end
castbar.interrupt = nil -- NOTE: deprecated; to be removed
castbar.notInterruptible = nil
if(castbar.PostCastNotInterruptible) then
return castbar:PostCastNotInterruptible(unit)
end
end
local UNIT_SPELLCAST_DELAYED = function(self, event, unit, _, _, _, spellid)
if(self.unit ~= unit and self.realUnit ~= unit) then return end
local castbar = self.Castbar
local name, _, _, _, startTime, _, _, castid = UnitCastingInfo(unit)
if(not startTime or not castbar:IsShown()) then return end
local duration = GetTime() - (startTime / 1000)
if(duration < 0) then duration = 0 end
castbar.delay = castbar.delay + castbar.duration - duration
castbar.duration = duration
castbar:SetValue(duration)
if(castbar.PostCastDelayed) then
return castbar:PostCastDelayed(unit, name, castid, spellid)
end
end
local UNIT_SPELLCAST_STOP = function(self, event, unit, spellname, _, castid, spellid)
if(self.unit ~= unit and self.realUnit ~= unit) then return end
local castbar = self.Castbar
if (castbar.castid ~= castid) then
return
end
if(mergeTradeskill and UnitIsUnit(unit, "player")) then
if(tradeskillCurrent == tradeskillTotal) then
mergeTradeskill = false;
end
else
castbar.casting = nil
castbar.interrupt = nil -- NOTE: deprecated; to be removed
castbar.notInterruptible = nil
end
if(castbar.PostCastStop) then
return castbar:PostCastStop(unit, spellname, castid, spellid)
end
end
local UNIT_SPELLCAST_CHANNEL_START = function(self, event, unit, _, _, _, spellid)
if(self.unit ~= unit and self.realUnit ~= unit) then return end
local castbar = self.Castbar
local name, _, _, texture, startTime, endTime, _, notInterruptible = UnitChannelInfo(unit)
if(not name) then
return
end
endTime = endTime / 1e3
startTime = startTime / 1e3
local max = (endTime - startTime)
local duration = endTime - GetTime()
castbar.duration = duration
castbar.max = max
castbar.delay = 0
castbar.startTime = startTime
castbar.endTime = endTime
castbar.extraTickRatio = 0
castbar.channeling = true
castbar.interrupt = notInterruptible -- NOTE: deprecated; to be removed
castbar.notInterruptible = notInterruptible
castbar.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.
castbar.casting = nil
castbar.castid = nil
castbar:SetMinMaxValues(0, max)
castbar:SetValue(duration)
if(castbar.Text) then castbar.Text:SetText(name) end
if(castbar.Icon) then castbar.Icon:SetTexture(texture) end
if(castbar.Time) then castbar.Time:SetText() end
local shield = castbar.Shield
if(shield and notInterruptible) then
shield:Show()
elseif(shield) then
shield:Hide()
end
local sf = castbar.SafeZone
if(sf) then
sf:ClearAllPoints()
sf:SetPoint'LEFT'
sf:SetPoint'TOP'
sf:SetPoint'BOTTOM'
updateSafeZone(castbar)
end
if(castbar.PostChannelStart) then castbar:PostChannelStart(unit, name, spellid) end
castbar:Show()
end
local UNIT_SPELLCAST_CHANNEL_UPDATE = function(self, event, unit, _, _, _, spellid)
if(self.unit ~= unit and self.realUnit ~= unit) then return end
local castbar = self.Castbar
local name, _, _, _, startTime, endTime = UnitChannelInfo(unit)
if(not name or not castbar:IsShown()) then
return
end
local duration = (endTime / 1000) - GetTime()
local startDelay = castbar.startTime - startTime / 1000
castbar.startTime = startTime / 1000
castbar.endTime = endTime / 1000
castbar.delay = castbar.delay + startDelay
castbar.duration = duration
castbar.max = (endTime - startTime) / 1000
castbar:SetMinMaxValues(0, castbar.max)
castbar:SetValue(duration)
if(castbar.PostChannelUpdate) then
return castbar:PostChannelUpdate(unit, name, spellid)
end
end
local UNIT_SPELLCAST_CHANNEL_STOP = function(self, event, unit, spellname, _, _, spellid)
if(self.unit ~= unit and self.realUnit ~= unit) then return end
local castbar = self.Castbar
if(castbar:IsShown()) then
castbar.channeling = nil
castbar.interrupt = nil -- NOTE: deprecated; to be removed
castbar.notInterruptible = nil
if(castbar.PostChannelStop) then
return castbar:PostChannelStop(unit, spellname, spellid)
end
end
end
local onUpdate = function(self, elapsed)
if(self.casting) then
local duration = self.duration + elapsed
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:SetFormattedText("%.1f|cffff0000-%.1f|r", duration, self.delay)
end
else
if(self.CustomTimeText) then
self:CustomTimeText(duration)
else
self.Time:SetFormattedText("%.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.channeling) then
local duration = self.duration - elapsed
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:SetFormattedText("%.1f|cffff0000-%.1f|r", duration, self.delay)
end
else
if(self.CustomTimeText) then
self:CustomTimeText(duration)
else
self.Time:SetFormattedText("%.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.castid = nil
self.channeling = nil
self:Hide()
end
end
local Update = function(self, ...)
UNIT_SPELLCAST_START(self, ...)
return UNIT_SPELLCAST_CHANNEL_START(self, ...)
end
local ForceUpdate = function(element)
return Update(element.__owner, 'ForceUpdate', element.__owner.unit)
end
local Enable = function(self, unit)
local castbar = self.Castbar
if(castbar) then
castbar.__owner = self
castbar.ForceUpdate = ForceUpdate
if(not (unit and unit:match'%wtarget$')) then
self:RegisterEvent("UNIT_SPELLCAST_SENT", UNIT_SPELLCAST_SENT, true)
self:RegisterEvent("UNIT_SPELLCAST_START", UNIT_SPELLCAST_START)
self:RegisterEvent("UNIT_SPELLCAST_FAILED", UNIT_SPELLCAST_FAILED)
self:RegisterEvent("UNIT_SPELLCAST_FAILED_QUIET", UNIT_SPELLCAST_FAILED_QUIET)
self:RegisterEvent("UNIT_SPELLCAST_STOP", UNIT_SPELLCAST_STOP)
self:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED", UNIT_SPELLCAST_INTERRUPTED)
self:RegisterEvent("UNIT_SPELLCAST_INTERRUPTIBLE", UNIT_SPELLCAST_INTERRUPTIBLE)
self:RegisterEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", UNIT_SPELLCAST_NOT_INTERRUPTIBLE)
self:RegisterEvent("UNIT_SPELLCAST_DELAYED", UNIT_SPELLCAST_DELAYED)
self:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START", UNIT_SPELLCAST_CHANNEL_START)
self:RegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE", UNIT_SPELLCAST_CHANNEL_UPDATE)
self:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP", UNIT_SPELLCAST_CHANNEL_STOP)
end
castbar.holdTime = 0
castbar:SetScript("OnUpdate", castbar.OnUpdate or onUpdate)
if(self.unit == "player") then
CastingBarFrame:UnregisterAllEvents()
CastingBarFrame.Show = CastingBarFrame.Hide
CastingBarFrame:Hide()
PetCastingBarFrame:UnregisterAllEvents()
PetCastingBarFrame.Show = PetCastingBarFrame.Hide
PetCastingBarFrame:Hide()
end
if(castbar:IsObjectType'StatusBar' and not castbar:GetStatusBarTexture()) then
castbar:SetStatusBarTexture[[Interface\TargetingFrame\UI-StatusBar]]
end
local spark = castbar.Spark
if(spark and spark:IsObjectType'Texture' and not spark:GetTexture()) then
spark:SetTexture[[Interface\CastingBar\UI-CastingBar-Spark]]
end
local shield = castbar.Shield
if(shield and shield:IsObjectType'Texture' and not shield:GetTexture()) then
shield:SetTexture[[Interface\CastingBar\UI-CastingBar-Small-Shield]]
end
local sz = castbar.SafeZone
if(sz and sz:IsObjectType'Texture' and not sz:GetTexture()) then
sz:SetColorTexture(1, 0, 0)
end
castbar:Hide()
return true
end
end
local Disable = function(self)
local castbar = self.Castbar
if(castbar) then
castbar:Hide()
self:UnregisterEvent("UNIT_SPELLCAST_SENT", UNIT_SPELLCAST_SENT)
self:UnregisterEvent("UNIT_SPELLCAST_START", UNIT_SPELLCAST_START)
self:UnregisterEvent("UNIT_SPELLCAST_FAILED", UNIT_SPELLCAST_FAILED)
self:UnregisterEvent("UNIT_SPELLCAST_FAILED_QUIET", UNIT_SPELLCAST_FAILED_QUIET)
self:UnregisterEvent("UNIT_SPELLCAST_STOP", UNIT_SPELLCAST_STOP)
self:UnregisterEvent("UNIT_SPELLCAST_INTERRUPTED", UNIT_SPELLCAST_INTERRUPTED)
self:UnregisterEvent("UNIT_SPELLCAST_INTERRUPTIBLE", UNIT_SPELLCAST_INTERRUPTIBLE)
self:UnregisterEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", UNIT_SPELLCAST_NOT_INTERRUPTIBLE)
self:UnregisterEvent("UNIT_SPELLCAST_DELAYED", UNIT_SPELLCAST_DELAYED)
self:UnregisterEvent("UNIT_SPELLCAST_CHANNEL_START", UNIT_SPELLCAST_CHANNEL_START)
self:UnregisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE", UNIT_SPELLCAST_CHANNEL_UPDATE)
self:UnregisterEvent("UNIT_SPELLCAST_CHANNEL_STOP", UNIT_SPELLCAST_CHANNEL_STOP)
castbar:SetScript("OnUpdate", nil)
end
end
hooksecurefunc(C_TradeSkillUI, "CraftRecipe", function(_, num)
tradeskillCurrent = 0
tradeskillTotal = num or 1
mergeTradeskill = true
end)
oUF:AddElement('Castbar', Update, Enable, Disable)