mirror of
https://github.com/brues-code/pfUI.git
synced 2026-09-22 07:36:56 +00:00
0156d9dfec
BigWigs (SpellRequests) redefines the global string:split to return a table, and another addon clobbers the strsplit global too. pfUI.api.strsplit and the bare strsplit callers inherited the broken versions depending on load order, producing 'attempt to compare number with nil' from GetStringColor. - Make pfUI.api.strsplit fully self-contained (no delegation to global strsplit / string.split), so no override can reach it. - Optimize the hot path: single-char delimiters use plain-text find (no pattern compilation, no per-call char-class string); localize string.find and string.sub. - Route the per-frame castbar/nameplate color splits through GetStringColor, which caches, instead of re-splitting a constant string every update.
555 lines
23 KiB
Lua
555 lines
23 KiB
Lua
pfUI:RegisterModule("castbar", function ()
|
||
|
||
local font = C.castbar.use_unitfonts == "1" and pfUI.font_unit or pfUI.font_default
|
||
local font_size = C.castbar.use_unitfonts == "1" and C.global.font_unit_size or C.global.font_size
|
||
local rawborder, default_border = GetBorderSize("unitframes")
|
||
local cbtexture = pfUI.media[C.appearance.castbar.texture]
|
||
|
||
-- Helper function for castbar timer formatting
|
||
local function FormatCastbarTime(value)
|
||
if C.unitframes.castbardecimals == "1" then
|
||
-- 1 decimal, round half up (matches Blizzard spellbook display)
|
||
return string.format("%.1f", floor(value * 10 + 0.5) / 10)
|
||
else
|
||
-- 2 decimals (default)
|
||
return string.format("%.2f", value)
|
||
end
|
||
end
|
||
|
||
-- Clear cast state on the bar. Shows the bar full for one frame, then
|
||
-- OnUpdate fades it out.
|
||
local function ClearBar(cb)
|
||
cb.startTime, cb.endTime, cb.isChannel = nil, nil, nil
|
||
cb.activeName, cb.spellID = nil, nil
|
||
cb.isTradeskill = nil
|
||
cb.tradeskillTotal, cb.tradeskillCompleted, cb.tradeskillSpellID = nil, nil, nil
|
||
cb.tradeskillSingleMs, cb.currentCraftStart = nil, nil
|
||
cb.lastMax = nil
|
||
cb.delay = 0
|
||
cb.bar:SetMinMaxValues(1, 100)
|
||
cb.bar:SetValue(100)
|
||
if cb.bar.spark then cb.bar.spark:Hide() end
|
||
cb.fadeout = 1
|
||
end
|
||
|
||
-- "Spellname (N)" label for an active tradeskill merge — N is the number
|
||
-- of crafts remaining. Falls back to the bare spell name when ≤1 left.
|
||
local function UpdateTradeskillLabel(cb)
|
||
if not cb.tradeskillTotal or not cb.activeName or not cb.showname then return end
|
||
local remaining = cb.tradeskillTotal - (cb.tradeskillCompleted or 0)
|
||
if remaining > 1 then
|
||
cb.bar.left:SetText(string.format("%s (%d)", cb.activeName, remaining))
|
||
else
|
||
cb.bar.left:SetText(cb.activeName)
|
||
end
|
||
end
|
||
|
||
-- Stretch the bar to span all queued crafts (Quartz-style merge): the bar
|
||
-- fills continuously across the chain instead of resetting per craft.
|
||
-- Also arms the per-craft spark — a thin marker that crosses the bar once
|
||
-- per craft (so it moves N× faster than the main fill on an N-stack).
|
||
local function EnterTradeskillMerge(cb, startMs, endMs, count)
|
||
cb.tradeskillTotal = count
|
||
cb.tradeskillCompleted = 0
|
||
cb.tradeskillSpellID = cb.spellID
|
||
local single = endMs - startMs
|
||
cb.tradeskillSingleMs = single
|
||
cb.currentCraftStart = startMs
|
||
local mergedEnd = startMs + single * count
|
||
cb.endTime = mergedEnd
|
||
local duration = (mergedEnd - startMs) / 1000
|
||
cb.bar:SetMinMaxValues(0, duration)
|
||
cb.lastMax = duration
|
||
if cb.bar.spark then cb.bar.spark:Show() end
|
||
UpdateTradeskillLabel(cb)
|
||
end
|
||
|
||
-- Mark the start of craft 2..N within an active merge (called from the
|
||
-- SPELLCAST_START / SPELL_START_SELF handlers). Resets the per-craft
|
||
-- spark to the left edge of the bar.
|
||
local function StartTradeskillCraft(cb)
|
||
cb.currentCraftStart = GetTime() * 1000
|
||
local remaining = cb.tradeskillTotal - (cb.tradeskillCompleted or 0)
|
||
cb.endTime = cb.currentCraftStart + cb.tradeskillSingleMs * remaining
|
||
local duration = (cb.endTime - cb.startTime) / 1000
|
||
cb.bar:SetMinMaxValues(0, duration)
|
||
cb.lastMax = duration
|
||
UpdateTradeskillLabel(cb)
|
||
end
|
||
|
||
-- Stamp the bar with cast data and render text/icon/lag once. OnUpdate
|
||
-- then animates the fill from this state without touching C_Spell.
|
||
local function StampBar(cb, name, tex, startMs, endMs, spellID, isChannel, delayMs, isTradeskill)
|
||
cb.startTime = startMs
|
||
cb.endTime = endMs
|
||
cb.isChannel = isChannel
|
||
cb.spellID = spellID
|
||
cb.activeName = name
|
||
cb.isTradeskill = isTradeskill
|
||
cb.delay = (delayMs or 0) / 1000
|
||
cb:SetAlpha(1)
|
||
cb.fadeout = nil
|
||
|
||
cb.bar:SetStatusBarColor(GetStringColor(C.appearance.castbar[isChannel and "channelcolor" or "castbarcolor"]))
|
||
|
||
local rank = ""
|
||
if spellID then
|
||
rank = C_Spell.GetSpellSubtext(spellID) or ""
|
||
end
|
||
local spellname = (cb.showname and name) and (name .. " ") or ""
|
||
local rankstr = (cb.showrank and rank ~= "") and string.format("|cffaaffcc[%s]|r", rank) or ""
|
||
cb.bar.left:SetText(spellname .. rankstr)
|
||
|
||
if tex and cb.showicon then
|
||
local size = cb:GetHeight()
|
||
cb.icon:Show()
|
||
cb.icon:SetSize(size, size)
|
||
cb.icon.texture:SetTexture(tex)
|
||
cb.bar:SetPoint("TOPLEFT", cb.icon, "TOPRIGHT", cb.spacing, 0)
|
||
else
|
||
cb.bar:SetPoint("TOPLEFT", cb, 0, 0)
|
||
cb.icon:Hide()
|
||
end
|
||
|
||
local duration = (endMs - startMs) / 1000
|
||
if cb.showlag then
|
||
local _, _, lag = GetNetStats()
|
||
cb.bar.lag:SetWidth(math.min(cb:GetWidth(), cb:GetWidth() / duration * (lag/1000)))
|
||
cb.bar.lag:Show()
|
||
else
|
||
cb.bar.lag:Hide()
|
||
end
|
||
|
||
cb.bar:SetMinMaxValues(0, duration)
|
||
cb.lastMax = duration
|
||
end
|
||
|
||
-- One-shot poll: read C_Spell for the bar's unit, stamp or clear. Called
|
||
-- from event handlers (cast start, target/focus change), never per-frame.
|
||
local function RefreshBar(cb)
|
||
local query = cb.unitstr ~= "" and cb.unitstr or cb.unitname
|
||
if not query or (cb.unitstr ~= "" and not UnitExists(cb.unitstr)) then
|
||
ClearBar(cb)
|
||
return
|
||
end
|
||
local name, _, tex, startMs, endMs, isTradeskill, _, _, spellID, _, delayMs = C_Spell.UnitCastingInfo(query)
|
||
local isChan
|
||
if not name then
|
||
name, _, tex, startMs, endMs, _, _, spellID = C_Spell.UnitChannelInfo(query)
|
||
isChan = true
|
||
end
|
||
-- Synthetic fallback for abilities the engine treats as instant-cast but
|
||
-- that have a meaningful wait window (e.g. Turtle WoW Steady Shot on the
|
||
-- ranged-swing queue). Per-unit table populated by module-side hooks.
|
||
if not name and pfUI.synthetic_casts and pfUI.synthetic_casts[query] then
|
||
local s = pfUI.synthetic_casts[query]
|
||
if s.endMs > GetTime() * 1000 then
|
||
name, tex, startMs, endMs, spellID = s.name, s.icon, s.startMs, s.endMs, s.spellID
|
||
isChan = nil
|
||
end
|
||
end
|
||
if name and startMs and endMs then
|
||
StampBar(cb, name, tex, startMs, endMs, spellID, isChan, delayMs, isTradeskill)
|
||
else
|
||
ClearBar(cb)
|
||
end
|
||
end
|
||
|
||
local function CreateCastbar(name, parent, unitstr, unitname)
|
||
local cb = CreateFrame("Frame", name, parent or UIParent)
|
||
|
||
cb:SetHeight(C.global.font_size * 1.5)
|
||
cb:SetFrameStrata("MEDIUM")
|
||
cb:SetFrameLevel(8)
|
||
|
||
cb.unitstr = unitstr
|
||
cb.unitname = unitname
|
||
|
||
-- icon
|
||
cb.icon = CreateFrame("Frame", nil, cb)
|
||
cb.icon:SetPoint("TOPLEFT", 0, 0)
|
||
cb.icon:SetSize(16, 16)
|
||
|
||
cb.icon.texture = cb.icon:CreateTexture(nil, "OVERLAY")
|
||
cb.icon.texture:SetAllPoints()
|
||
cb.icon.texture:SetTexCoord(.08, .92, .08, .92)
|
||
CreateBackdrop(cb.icon, default_border)
|
||
|
||
-- statusbar
|
||
cb.bar = CreateFrame("StatusBar", nil, cb)
|
||
cb.bar:SetStatusBarTexture(cbtexture)
|
||
cb.bar:ClearAllPoints()
|
||
cb.bar:SetAllPoints(cb)
|
||
cb.bar:SetMinMaxValues(0, 100)
|
||
cb.bar:SetValue(20)
|
||
local r,g,b,a = strsplit(",", C.appearance.castbar.castbarcolor)
|
||
cb.bar:SetStatusBarColor(r,g,b,a)
|
||
CreateBackdrop(cb.bar, default_border)
|
||
CreateBackdropShadow(cb.bar)
|
||
|
||
-- text left
|
||
cb.bar.left = cb.bar:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||
cb.bar.left:ClearAllPoints()
|
||
cb.bar.left:SetPoint("TOPLEFT", cb.bar, "TOPLEFT", 3 + C.castbar[unitstr].txtleftoffx, C.castbar[unitstr].txtleftoffy)
|
||
cb.bar.left:SetPoint("BOTTOMRIGHT", cb.bar, "BOTTOMRIGHT", -3 + C.castbar[unitstr].txtleftoffx, C.castbar[unitstr].txtleftoffy)
|
||
cb.bar.left:SetNonSpaceWrap(false)
|
||
cb.bar.left:SetFontObject(GameFontWhite)
|
||
cb.bar.left:SetTextColor(1,1,1,1)
|
||
cb.bar.left:SetFont(font, font_size, "OUTLINE")
|
||
cb.bar.left:SetJustifyH(C.castbar[unitstr].namealign or "LEFT")
|
||
|
||
-- text right
|
||
cb.bar.right = cb.bar:CreateFontString("Status", "DIALOG", "GameFontNormal")
|
||
cb.bar.right:ClearAllPoints()
|
||
cb.bar.right:SetPoint("TOPLEFT", cb.bar, "TOPLEFT", 3 + C.castbar[unitstr].txtrightoffx, C.castbar[unitstr].txtrightoffy)
|
||
cb.bar.right:SetPoint("BOTTOMRIGHT", cb.bar, "BOTTOMRIGHT", -3 + C.castbar[unitstr].txtrightoffx, C.castbar[unitstr].txtrightoffy)
|
||
cb.bar.right:SetNonSpaceWrap(false)
|
||
cb.bar.right:SetFontObject(GameFontWhite)
|
||
cb.bar.right:SetTextColor(1,1,1,1)
|
||
cb.bar.right:SetFont(font, font_size, "OUTLINE")
|
||
cb.bar.right:SetJustifyH(C.castbar[unitstr].timealign or "RIGHT")
|
||
|
||
cb.bar.lag = cb.bar:CreateTexture(nil, "OVERLAY")
|
||
cb.bar.lag:SetPoint("TOPRIGHT", cb.bar, "TOPRIGHT", 0, 0)
|
||
cb.bar.lag:SetPoint("BOTTOMRIGHT", cb.bar, "BOTTOMRIGHT", 0, 0)
|
||
cb.bar.lag:SetTexture(1,.2,.2,.2)
|
||
|
||
-- Per-craft progress spark for tradeskill merge — a thin vertical line
|
||
-- that crosses the bar once per craft in the chain (so on a 5-stack it
|
||
-- moves 5x faster than the main fill). Position is updated by OnUpdate
|
||
-- while a merge is active; hidden otherwise.
|
||
cb.bar.spark = cb.bar:CreateTexture(nil, "OVERLAY")
|
||
cb.bar.spark:SetTexture(1, 1, 1, 0.8)
|
||
cb.bar.spark:SetWidth(2)
|
||
cb.bar.spark:SetPoint("TOP", cb.bar, "TOPLEFT", 0, 0)
|
||
cb.bar.spark:SetPoint("BOTTOM", cb.bar, "BOTTOMLEFT", 0, 0)
|
||
cb.bar.spark:Hide()
|
||
|
||
-- OnUpdate animates the bar fill and fades it out on completion. All
|
||
-- state transitions (cast start/stop/interrupt, channel start/stop,
|
||
-- pushback) come from the event handler below — we never poll C_Spell
|
||
-- here.
|
||
cb:SetScript("OnUpdate", function()
|
||
if (this.tick or 0) > GetTime() then return end
|
||
this.tick = GetTime() + 0.020 -- ~50 FPS
|
||
|
||
if this.drag and this.drag:IsShown() then
|
||
this:SetAlpha(1)
|
||
return
|
||
end
|
||
|
||
if this.fadeout and this:GetAlpha() > 0 then
|
||
this:SetAlpha(this:GetAlpha() - 0.05)
|
||
if this:GetAlpha() <= 0 then this.fadeout = nil end
|
||
return
|
||
end
|
||
|
||
if not this.endTime then
|
||
if this:GetAlpha() ~= 0 then this:SetAlpha(0) end
|
||
return
|
||
end
|
||
|
||
-- Non-player bars: if the unit disappears (target died / detarget),
|
||
-- drop the bar immediately.
|
||
if this.unitstr ~= "" and this.unitstr ~= "player" and not UnitExists(this.unitstr) then
|
||
ClearBar(this)
|
||
return
|
||
end
|
||
|
||
local now = GetTime()
|
||
local endSec = this.endTime / 1000
|
||
if now >= endSec then
|
||
ClearBar(this)
|
||
return
|
||
end
|
||
|
||
local startSec = this.startTime / 1000
|
||
local max = endSec - startSec
|
||
local cur = this.isChannel and (endSec - now) or (now - startSec)
|
||
if cur > max then cur = max end
|
||
if cur < 0 then cur = 0 end
|
||
|
||
this.bar:SetValue(cur)
|
||
|
||
-- Per-craft spark for a tradeskill merge: position by (now - current
|
||
-- craft's start) / single-craft duration, clamped to [0, 1]. Snaps back
|
||
-- to the left edge each time StartTradeskillCraft fires for craft N+1.
|
||
if this.tradeskillTotal and this.tradeskillSingleMs and this.currentCraftStart then
|
||
local craftElapsed = now * 1000 - this.currentCraftStart
|
||
local p = craftElapsed / this.tradeskillSingleMs
|
||
if p < 0 then p = 0 elseif p > 1 then p = 1 end
|
||
local x = p * this.bar:GetWidth() - 1
|
||
this.bar.spark:ClearAllPoints()
|
||
this.bar.spark:SetPoint("TOP", this.bar, "TOPLEFT", x, 0)
|
||
this.bar.spark:SetPoint("BOTTOM", this.bar, "BOTTOMLEFT", x, 0)
|
||
end
|
||
|
||
if this.showtimer then
|
||
if (this.delay or 0) > 0 then
|
||
local prefix = "|cffffaaaa" .. (this.isChannel and "-" or "+") .. FormatCastbarTime(this.delay) .. " |r "
|
||
this.bar.right:SetText(prefix .. FormatCastbarTime(cur) .. " / " .. FormatCastbarTime(max))
|
||
else
|
||
this.bar.right:SetText(FormatCastbarTime(cur) .. " / " .. FormatCastbarTime(max))
|
||
end
|
||
end
|
||
end)
|
||
|
||
-- Cast lifecycle events. Player bars react to vanilla SPELLCAST_*; non-
|
||
-- player bars also react to Nampower SPELL_*_OTHER (gated by the
|
||
-- NP_EnableSpell{Start,Go}Events CVars, enabled by libdebuff) plus the
|
||
-- retarget event. Player events also feed non-player bars for the
|
||
-- target=self case.
|
||
cb:RegisterEvent("SPELLCAST_START")
|
||
cb:RegisterEvent("SPELLCAST_STOP")
|
||
cb:RegisterEvent("SPELLCAST_FAILED")
|
||
cb:RegisterEvent("SPELLCAST_INTERRUPTED")
|
||
cb:RegisterEvent("SPELLCAST_CHANNEL_START")
|
||
cb:RegisterEvent("SPELLCAST_CHANNEL_STOP")
|
||
cb:RegisterEvent("SPELLCAST_CHANNEL_UPDATE")
|
||
cb:RegisterEvent("SPELL_DELAYED_SELF")
|
||
-- Chained same-spell recasts never run the client cast path (the 1.12
|
||
-- engine short-circuits at spellID == current-cast), so vanilla
|
||
-- SPELLCAST_START never fires for them. nampower's SPELL_START_SELF
|
||
-- (server-driven) is the only signal that shows them.
|
||
cb:RegisterEvent("SPELL_START_SELF")
|
||
if unitstr == "player" then
|
||
cb:RegisterEvent("SPELL_GO_SELF")
|
||
end
|
||
if unitstr ~= "player" and unitstr ~= "" then
|
||
cb:RegisterEvent("SPELL_START_OTHER")
|
||
cb:RegisterEvent("SPELL_FAILED_OTHER")
|
||
if unitstr == "target" then
|
||
cb:RegisterEvent("PLAYER_TARGET_CHANGED")
|
||
elseif unitstr == "focus" then
|
||
cb:RegisterEvent("PLAYER_FOCUS_CHANGED")
|
||
end
|
||
end
|
||
|
||
cb:SetScript("OnEvent", function()
|
||
local unit = this.unitstr
|
||
|
||
if event == "PLAYER_TARGET_CHANGED" or event == "PLAYER_FOCUS_CHANGED" then
|
||
RefreshBar(this)
|
||
return
|
||
end
|
||
|
||
if event == "SPELL_START_OTHER" then
|
||
-- arg3=casterGuid. Defer one frame so ClassicAPI's UnitChannelInfo
|
||
-- can see the engine's +0x228 broadcast for remote-unit channels
|
||
-- (the cohook+packet handler runs in the same frame; the broadcast
|
||
-- propagates after).
|
||
if arg3 == UnitGUID(unit) then
|
||
local target = this
|
||
RunNextFrame(function() RefreshBar(target) end)
|
||
end
|
||
return
|
||
end
|
||
|
||
if event == "SPELL_FAILED_OTHER" then
|
||
if arg1 == UnitGUID(unit) then ClearBar(this) end
|
||
return
|
||
end
|
||
|
||
-- Vanilla SPELLCAST_* + SPELL_DELAYED_SELF fire only for the local
|
||
-- player. Non-player bars handle them only when their unit currently
|
||
-- resolves to the player (target=self / focus=self).
|
||
if not UnitIsUnit(unit, 'player') then return end
|
||
|
||
if event == "SPELLCAST_START" or event == "SPELLCAST_CHANNEL_START" then
|
||
if this.tradeskillTotal then
|
||
-- Mid-chain craft N+1 of N. Keep the merged bar; resync the spark
|
||
-- to the new craft's start and refresh the "(N)" count label.
|
||
StartTradeskillCraft(this)
|
||
else
|
||
RefreshBar(this)
|
||
if this.isTradeskill and (this.pendingTradeskillCount or 0) > 1
|
||
and C.castbar.player.mergetradeskill == "1" then
|
||
EnterTradeskillMerge(this, this.startTime, this.endTime, this.pendingTradeskillCount)
|
||
end
|
||
this.pendingTradeskillCount = nil
|
||
end
|
||
elseif event == "SPELL_START_SELF" then
|
||
-- Catches chained same-spell recasts (no SPELLCAST_START fires) —
|
||
-- including tradeskill chaining where craft 2..N reuse one spell.
|
||
-- Defer one frame so ClassicAPI's SMSG_SPELL_START co-hook has
|
||
-- stamped g_cast before we poll, regardless of co-hook order.
|
||
if this.tradeskillTotal then
|
||
StartTradeskillCraft(this)
|
||
else
|
||
local target = this
|
||
RunNextFrame(function()
|
||
-- Re-check: SPELL_START_SELF (nampower co-hook) fires before vanilla
|
||
-- SPELLCAST_START on the same packet, so SPELLCAST_START may have
|
||
-- entered merge in this same frame. Don't restamp over it.
|
||
if target.tradeskillTotal then
|
||
StartTradeskillCraft(target)
|
||
return
|
||
end
|
||
RefreshBar(target)
|
||
if target.unitstr == "player" and target.isTradeskill
|
||
and (target.pendingTradeskillCount or 0) > 1
|
||
and C.castbar.player.mergetradeskill == "1" then
|
||
EnterTradeskillMerge(target, target.startTime, target.endTime, target.pendingTradeskillCount)
|
||
end
|
||
target.pendingTradeskillCount = nil
|
||
end)
|
||
end
|
||
elseif event == "SPELLCAST_CHANNEL_STOP" then
|
||
-- A channel's stop can arrive after a following cast already claimed
|
||
-- the bar (channel->cast transition); only clear if a channel is
|
||
-- actually being shown, so it doesn't wipe an active cast bar.
|
||
if this.isChannel then ClearBar(this) end
|
||
elseif event == "SPELLCAST_STOP" then
|
||
-- During a tradeskill chain, SPELL_GO_SELF already counted this craft
|
||
-- and either cleared the bar (chain done) or kept it running. Only a
|
||
-- non-merge cast clears here.
|
||
if not this.tradeskillTotal then ClearBar(this) end
|
||
elseif event == "SPELLCAST_FAILED" or event == "SPELLCAST_INTERRUPTED" then
|
||
ClearBar(this)
|
||
elseif event == "SPELL_GO_SELF" then
|
||
-- arg2 = spellId. In a tradeskill merge, count each successful craft
|
||
-- and clear when the chain is done.
|
||
if this.tradeskillTotal and arg2 == this.tradeskillSpellID then
|
||
this.tradeskillCompleted = (this.tradeskillCompleted or 0) + 1
|
||
if this.tradeskillCompleted >= this.tradeskillTotal then
|
||
ClearBar(this)
|
||
else
|
||
UpdateTradeskillLabel(this)
|
||
end
|
||
end
|
||
elseif event == "SPELL_DELAYED_SELF" then
|
||
-- Cast pushback. nampower's event carries the delay (arg2); apply it
|
||
-- locally rather than re-polling, so the bar doesn't depend on
|
||
-- ClassicAPI's SMSG_SPELL_DELAYED co-hook having bumped g_cast before
|
||
-- this fires (co-hook order vs nampower is not guaranteed).
|
||
if not this.endTime or not arg2 then return end
|
||
local delayMs = tonumber(arg2) or 0
|
||
if delayMs > 0 then
|
||
this.delay = (this.delay or 0) + delayMs / 1000
|
||
this.endTime = this.endTime + delayMs
|
||
local newDuration = (this.endTime - this.startTime) / 1000
|
||
this.bar:SetMinMaxValues(0, newDuration)
|
||
this.lastMax = newDuration
|
||
end
|
||
elseif event == "SPELLCAST_CHANNEL_UPDATE" then
|
||
-- Channel pushback. ClassicAPI doesn't track channel delay in
|
||
-- g_channel, so we adjust endTime + delay locally and resize the
|
||
-- bar so OnUpdate animates against the new total.
|
||
if not this.endTime or not arg1 then return end
|
||
local newEndMs = GetTime() * 1000 + arg1
|
||
local diff = this.endTime - newEndMs
|
||
if diff > 50 then
|
||
this.delay = (this.delay or 0) + diff / 1000
|
||
this.endTime = newEndMs
|
||
local newDuration = (this.endTime - this.startTime) / 1000
|
||
this.bar:SetMinMaxValues(0, newDuration)
|
||
this.lastMax = newDuration
|
||
end
|
||
end
|
||
end)
|
||
|
||
cb:SetAlpha(0)
|
||
return cb
|
||
end
|
||
|
||
pfUI.castbar = CreateFrame("Frame", "pfCastBar", UIParent)
|
||
|
||
-- hide blizzard
|
||
if C.castbar.player.hide_blizz == "1" then
|
||
CastingBarFrame:SetScript("OnShow", function() CastingBarFrame:Hide() end)
|
||
CastingBarFrame:UnregisterAllEvents()
|
||
CastingBarFrame:Hide()
|
||
end
|
||
|
||
-- [[ pfPlayerCastbar ]] --
|
||
if C.castbar.player.hide_pfui == "0" then
|
||
pfUI.castbar.player = CreateCastbar("pfPlayerCastbar", UIParent, "player")
|
||
pfUI.castbar.player.showicon = C.castbar.player.showicon == "1" and true or nil
|
||
pfUI.castbar.player.showname = C.castbar.player.showname == "1" and true or nil
|
||
pfUI.castbar.player.showtimer = C.castbar.player.showtimer == "1" and true or nil
|
||
pfUI.castbar.player.showlag = C.castbar.player.showlag == "1" and true or nil
|
||
pfUI.castbar.player.showrank = C.castbar.player.showrank == "1" and true or nil
|
||
pfUI.castbar.player.spacing = default_border * 2 + tonumber(C.unitframes.player.pspace) * GetPerfectPixel()
|
||
|
||
if pfUI.uf.player then
|
||
local anchor = pfUI.uf.player.portrait:GetHeight() > pfUI.uf.player:GetHeight() and pfUI.uf.player.power or pfUI.uf.player
|
||
local width = C.castbar.player.width ~= "-1" and C.castbar.player.width or anchor:GetWidth()
|
||
pfUI.castbar.player:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, -pfUI.castbar.player.spacing)
|
||
pfUI.castbar.player:SetWidth(width)
|
||
else
|
||
local width = C.castbar.player.width ~= "-1" and C.castbar.player.width or 200
|
||
pfUI.castbar.player:SetPoint("CENTER", 0, -200)
|
||
pfUI.castbar.player:SetWidth(width)
|
||
end
|
||
|
||
if C.castbar.player.height ~= "-1" then
|
||
pfUI.castbar.player:SetHeight(C.castbar.player.height)
|
||
end
|
||
|
||
UpdateMovable(pfUI.castbar.player)
|
||
|
||
-- Tradeskill merge: hook DoTradeSkill so the player castbar knows the
|
||
-- requested count before the first SPELLCAST_START fires. Always-on hook
|
||
-- (the config knob is read at event time so toggling takes effect on the
|
||
-- next craft without a /reload). DoTradeSkill is synchronous; the server
|
||
-- roundtrip to SPELLCAST_START gives us plenty of time after this hook.
|
||
hooksecurefunc("DoTradeSkill", function(index, num)
|
||
if pfUI.castbar.player then
|
||
pfUI.castbar.player.pendingTradeskillCount = tonumber(num) or 1
|
||
end
|
||
end)
|
||
end
|
||
|
||
-- [[ pfTargetCastbar ]] --
|
||
if C.castbar.target.hide_pfui == "0" then
|
||
pfUI.castbar.target = CreateCastbar("pfTargetCastbar", UIParent, "target")
|
||
pfUI.castbar.target.showicon = C.castbar.target.showicon == "1" and true or nil
|
||
pfUI.castbar.target.showname = C.castbar.target.showname == "1" and true or nil
|
||
pfUI.castbar.target.showtimer = C.castbar.target.showtimer == "1" and true or nil
|
||
pfUI.castbar.target.showlag = C.castbar.target.showlag == "1" and true or nil
|
||
pfUI.castbar.target.showrank = C.castbar.target.showrank == "1" and true or nil
|
||
pfUI.castbar.target.spacing = default_border * 2 + tonumber(C.unitframes.target.pspace) * GetPerfectPixel()
|
||
|
||
if pfUI.uf.target then
|
||
local anchor = pfUI.uf.target.portrait:GetHeight() > pfUI.uf.target:GetHeight() and pfUI.uf.target.power or pfUI.uf.target
|
||
local width = C.castbar.target.width ~= "-1" and C.castbar.target.width or anchor:GetWidth()
|
||
pfUI.castbar.target:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, -pfUI.castbar.target.spacing)
|
||
pfUI.castbar.target:SetWidth(width)
|
||
else
|
||
local width = C.castbar.target.width ~= "-1" and C.castbar.target.width or 200
|
||
pfUI.castbar.target:SetPoint("CENTER", 0, -225)
|
||
pfUI.castbar.target:SetWidth(width)
|
||
end
|
||
|
||
if C.castbar.target.height ~= "-1" then
|
||
pfUI.castbar.target:SetHeight(C.castbar.target.height)
|
||
end
|
||
|
||
UpdateMovable(pfUI.castbar.target)
|
||
end
|
||
|
||
-- [[ pfFocusCastbar ]] --
|
||
if C.castbar.focus.hide_pfui == "0" and pfUI.uf.focus then
|
||
pfUI.castbar.focus = CreateCastbar("pfFocusCastbar", UIParent, "focus")
|
||
pfUI.castbar.focus.showicon = C.castbar.focus.showicon == "1" and true or nil
|
||
pfUI.castbar.focus.showname = C.castbar.focus.showname == "1" and true or nil
|
||
pfUI.castbar.focus.showtimer = C.castbar.focus.showtimer == "1" and true or nil
|
||
pfUI.castbar.focus.showlag = C.castbar.focus.showlag == "1" and true or nil
|
||
pfUI.castbar.focus.showrank = C.castbar.focus.showrank == "1" and true or nil
|
||
pfUI.castbar.focus.spacing = default_border * 2 + tonumber(C.unitframes.focus.pspace) * GetPerfectPixel()
|
||
|
||
local anchor = pfUI.uf.focus.portrait:GetHeight() > pfUI.uf.focus:GetHeight() and pfUI.uf.focus.power or pfUI.uf.focus
|
||
local width = C.castbar.focus.width ~= "-1" and C.castbar.focus.width or anchor:GetWidth()
|
||
pfUI.castbar.focus:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, -pfUI.castbar.focus.spacing)
|
||
pfUI.castbar.focus:SetWidth(width)
|
||
|
||
if C.castbar.focus.height ~= "-1" then
|
||
pfUI.castbar.focus:SetHeight(C.castbar.focus.height)
|
||
end
|
||
|
||
-- bind castbar to the "focus" unit token; the GUID resolves at read time
|
||
pfUI.castbar.focus.unitstr = "focus"
|
||
|
||
UpdateMovable(pfUI.castbar.focus)
|
||
end
|
||
end) |