Add files via upload
This commit is contained in:
@@ -0,0 +1,386 @@
|
||||
BNP = BNP or {}
|
||||
|
||||
local casts = {}
|
||||
local BAR_HEIGHT = 6
|
||||
|
||||
-- Runtime-only visual preview. This is intentionally not saved in BNP_DB so
|
||||
-- a /reload always returns to normal cast detection.
|
||||
local castbarTestMode = false
|
||||
local testCastStart = 0
|
||||
local TEST_CAST_DURATION = 4
|
||||
local TEST_CAST_TEXTURE = "Interface\\Icons\\Spell_Fire_Fireball02"
|
||||
|
||||
local testCast = {
|
||||
name = "Test Cast",
|
||||
texture = TEST_CAST_TEXTURE,
|
||||
startTime = 0,
|
||||
endTime = 0,
|
||||
duration = TEST_CAST_DURATION,
|
||||
channel = false,
|
||||
}
|
||||
|
||||
local function ResetTestCast(now)
|
||||
now = now or GetTime()
|
||||
testCastStart = now
|
||||
testCast.startTime = now
|
||||
testCast.endTime = now + TEST_CAST_DURATION
|
||||
testCast.duration = TEST_CAST_DURATION
|
||||
end
|
||||
|
||||
local function GetTestCast(now)
|
||||
if testCastStart == 0 or now >= testCast.endTime then
|
||||
ResetTestCast(now)
|
||||
end
|
||||
return testCast
|
||||
end
|
||||
|
||||
local function GetCastbarHeight()
|
||||
if BNP.GetCastbarHeight then return BNP:GetCastbarHeight() end
|
||||
return BAR_HEIGHT
|
||||
end
|
||||
local BAR_GAP = 4
|
||||
|
||||
local function GetCastbarSpacing()
|
||||
if BNP.GetCastbarSpacing then return BNP:GetCastbarSpacing() end
|
||||
return BAR_GAP
|
||||
end
|
||||
|
||||
local BOTTOM_AURA_EDGE_GAP = 4
|
||||
local BOTTOM_AURA_ROW_SPACING = 2
|
||||
|
||||
local function FrameShown(frame)
|
||||
return frame and frame.IsShown and frame:IsShown() and true or false
|
||||
end
|
||||
|
||||
local function IsBottomDebuffPosition(position)
|
||||
return position == "bottom_mid" or position == "bottom_left" or position == "bottom_right"
|
||||
end
|
||||
|
||||
local function GetBottomAuraClearance(plate)
|
||||
if not plate then return 0 end
|
||||
|
||||
local debuffPosition = BNP.GetDebuffPosition and BNP:GetDebuffPosition() or "top"
|
||||
local debuffsEnabled = not BNP.AreDebuffsEnabled or BNP:AreDebuffsEnabled()
|
||||
local auraSize = BNP.GetIconSize and BNP:GetIconSize() or 18
|
||||
|
||||
if debuffsEnabled and IsBottomDebuffPosition(debuffPosition) and FrameShown(plate.BNPAuraContainer) then
|
||||
return BOTTOM_AURA_EDGE_GAP + auraSize
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function ApplyCastbarLayout(plate, bar)
|
||||
if not plate or not plate.healthbar or not bar then return end
|
||||
local gap = GetCastbarSpacing() + GetBottomAuraClearance(plate)
|
||||
|
||||
bar:ClearAllPoints()
|
||||
bar:SetPoint("TOPLEFT", plate.healthbar, "BOTTOMLEFT", 15, -gap)
|
||||
bar:SetPoint("TOPRIGHT", plate.healthbar, "BOTTOMRIGHT", 14, -gap)
|
||||
|
||||
if bar.icon then
|
||||
bar.icon:ClearAllPoints()
|
||||
bar.icon:SetPoint("TOPLEFT", plate.healthbar, "BOTTOMLEFT", 0, -gap)
|
||||
end
|
||||
bar.BNPBottomAuraClearance = GetBottomAuraClearance(plate)
|
||||
end
|
||||
|
||||
local function Enabled()
|
||||
if BNP.AreCastbarsEnabled then return BNP:AreCastbarsEnabled() end
|
||||
return not BNP_DB or BNP_DB.castbars ~= false
|
||||
end
|
||||
|
||||
local function GetPlateGUID(plate)
|
||||
if not plate or not plate.GetName then return nil end
|
||||
|
||||
local token = plate:GetName(1)
|
||||
if not token then return nil end
|
||||
|
||||
local exists, guid = UnitExists(token)
|
||||
if exists and guid then return guid end
|
||||
|
||||
-- On SuperWoW the plate token itself can already be the GUID.
|
||||
return token
|
||||
end
|
||||
|
||||
local function SpellData(spellID)
|
||||
if SpellInfo and spellID then
|
||||
local name, _, texture = SpellInfo(spellID)
|
||||
return name or "Casting", texture
|
||||
end
|
||||
return "Casting", nil
|
||||
end
|
||||
|
||||
local function CreateCastbar(plate)
|
||||
if plate.BNPCastbar then return plate.BNPCastbar end
|
||||
if not plate.healthbar then return nil end
|
||||
|
||||
local parent = plate.BNPScaleWrapper or plate
|
||||
local bar = CreateFrame("StatusBar", nil, parent)
|
||||
bar:SetHeight(GetCastbarHeight())
|
||||
-- Center the castbar under the complete Blizzard nameplate.
|
||||
-- Extend beneath the level badge instead of stopping at the healthbar edge.
|
||||
-- Compact row: icon + castbar together span the complete plate width.
|
||||
-- The icon begins at the healthbar's left edge; the bar continues directly
|
||||
-- beside it and ends beneath the level badge.
|
||||
local gap = GetCastbarSpacing() + GetBottomAuraClearance(plate)
|
||||
bar:SetPoint("TOPLEFT", plate.healthbar, "BOTTOMLEFT", 15, -gap)
|
||||
bar:SetPoint("TOPRIGHT", plate.healthbar, "BOTTOMRIGHT", 14, -gap)
|
||||
bar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")
|
||||
bar:SetStatusBarColor(1.0, 0.8, 0.0)
|
||||
bar:SetMinMaxValues(0, 1)
|
||||
bar:SetValue(0)
|
||||
bar:SetFrameLevel((plate.healthbar:GetFrameLevel() or 1) + 1)
|
||||
bar:Hide()
|
||||
|
||||
local bg = bar:CreateTexture(nil, "BACKGROUND")
|
||||
bg:SetTexture("Interface\\TargetingFrame\\UI-StatusBar")
|
||||
bg:SetVertexColor(0.10, 0.10, 0.00, 0.85)
|
||||
bg:SetAllPoints(bar)
|
||||
|
||||
local text = bar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
||||
text:SetPoint("CENTER", bar, "CENTER", 0, 0)
|
||||
text:SetTextColor(1, 1, 1)
|
||||
bar.text = text
|
||||
|
||||
-- Spell icon to the left of the castbar.
|
||||
local icon = CreateFrame("Frame", nil, parent)
|
||||
icon:SetWidth(14)
|
||||
icon:SetHeight(14)
|
||||
icon:SetPoint("TOPLEFT", plate.healthbar, "BOTTOMLEFT", 0, -gap)
|
||||
icon:SetFrameLevel(bar:GetFrameLevel() + 1)
|
||||
|
||||
local texture = icon:CreateTexture(nil, "ARTWORK")
|
||||
texture:SetAllPoints(icon)
|
||||
icon.texture = texture
|
||||
|
||||
local border = icon:CreateTexture(nil, "OVERLAY")
|
||||
border:SetTexture("Interface\\Buttons\\UI-Quickslot2")
|
||||
border:SetPoint("CENTER", icon, "CENTER", 0, 0)
|
||||
border:SetWidth(23)
|
||||
border:SetHeight(23)
|
||||
icon.border = border
|
||||
|
||||
icon:Hide()
|
||||
bar.icon = icon
|
||||
|
||||
plate.BNPCastbar = bar
|
||||
return bar
|
||||
end
|
||||
|
||||
local function Hide(plate)
|
||||
if plate and plate.BNPCastbar then
|
||||
plate.BNPCastbar:Hide()
|
||||
if plate.BNPCastbar.icon then
|
||||
plate.BNPCastbar.icon:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Exact SuperWoW event path used by ShaguTweaks' superwow module:
|
||||
-- START / CAST / CHANNEL create cast state; FAIL removes it.
|
||||
local eventFrame = CreateFrame("Frame")
|
||||
eventFrame:RegisterEvent("UNIT_CASTEVENT")
|
||||
eventFrame:SetScript("OnEvent", function()
|
||||
local guid = arg1
|
||||
local eventType = arg3
|
||||
local spellID = arg4
|
||||
local timerMS = tonumber(arg5) or 0
|
||||
|
||||
if not guid then return end
|
||||
|
||||
if eventType == "START" or eventType == "CAST" or eventType == "CHANNEL" then
|
||||
if timerMS <= 0 then
|
||||
-- Instant casts do not need a visible castbar.
|
||||
casts[guid] = nil
|
||||
return
|
||||
end
|
||||
|
||||
local name, texture = SpellData(spellID)
|
||||
local now = GetTime()
|
||||
local duration = timerMS / 1000
|
||||
|
||||
casts[guid] = {
|
||||
name = name,
|
||||
texture = texture,
|
||||
startTime = now,
|
||||
endTime = now + duration,
|
||||
duration = duration,
|
||||
channel = eventType == "CHANNEL",
|
||||
}
|
||||
elseif eventType == "FAIL" then
|
||||
casts[guid] = nil
|
||||
end
|
||||
end)
|
||||
|
||||
local activeCastPlates = {}
|
||||
|
||||
local function MarkCastPlateActive(plate)
|
||||
if not plate then return end
|
||||
activeCastPlates[plate] = true
|
||||
end
|
||||
|
||||
local function MarkCastPlateInactive(plate)
|
||||
if not plate then return end
|
||||
activeCastPlates[plate] = nil
|
||||
Hide(plate)
|
||||
end
|
||||
|
||||
-- Lightweight discovery pass. This does NOT animate bars; it only checks
|
||||
-- visible plates for active cast state at 20 Hz.
|
||||
local discovery = CreateFrame("Frame")
|
||||
local discoveryElapsed = 0
|
||||
discovery:SetScript("OnUpdate", function()
|
||||
discoveryElapsed = discoveryElapsed + arg1
|
||||
if discoveryElapsed < 0.05 then return end
|
||||
discoveryElapsed = 0
|
||||
|
||||
if not Enabled() and not castbarTestMode then
|
||||
local plate
|
||||
for plate in pairs(activeCastPlates) do
|
||||
MarkCastPlateInactive(plate)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local now = GetTime()
|
||||
local plate
|
||||
for plate in pairs(BNP.plates) do
|
||||
if plate:IsShown() then
|
||||
if castbarTestMode then
|
||||
-- Preview every visible nameplate. No GUID/cast lookup is needed.
|
||||
MarkCastPlateActive(plate)
|
||||
else
|
||||
local guid = GetPlateGUID(plate)
|
||||
local cast = guid and casts[guid]
|
||||
|
||||
if cast and now < cast.endTime then
|
||||
MarkCastPlateActive(plate)
|
||||
elseif activeCastPlates[plate] then
|
||||
MarkCastPlateInactive(plate)
|
||||
end
|
||||
end
|
||||
elseif activeCastPlates[plate] then
|
||||
MarkCastPlateInactive(plate)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Frame-perfect animation remains unchanged visually, but only active
|
||||
-- castbars are updated every rendered frame.
|
||||
local updater = CreateFrame("Frame")
|
||||
updater:SetScript("OnUpdate", function()
|
||||
if not Enabled() and not castbarTestMode then return end
|
||||
|
||||
local now = GetTime()
|
||||
local plate
|
||||
for plate in pairs(activeCastPlates) do
|
||||
if not plate:IsShown() then
|
||||
MarkCastPlateInactive(plate)
|
||||
else
|
||||
local guid
|
||||
local cast
|
||||
|
||||
if castbarTestMode then
|
||||
cast = GetTestCast(now)
|
||||
else
|
||||
guid = GetPlateGUID(plate)
|
||||
cast = guid and casts[guid]
|
||||
end
|
||||
|
||||
if not cast or now >= cast.endTime then
|
||||
if (not castbarTestMode) and guid and cast and now >= cast.endTime then
|
||||
casts[guid] = nil
|
||||
end
|
||||
MarkCastPlateInactive(plate)
|
||||
else
|
||||
local bar = CreateCastbar(plate)
|
||||
if bar then
|
||||
local bottomClearance = GetBottomAuraClearance(plate)
|
||||
if bar.BNPBottomAuraClearance ~= bottomClearance then
|
||||
ApplyCastbarLayout(plate, bar)
|
||||
end
|
||||
|
||||
local current = now - cast.startTime
|
||||
if current < 0 then current = 0 end
|
||||
if current > cast.duration then current = cast.duration end
|
||||
|
||||
bar:SetMinMaxValues(0, cast.duration)
|
||||
|
||||
if cast.channel then
|
||||
bar:SetValue(cast.duration - current)
|
||||
else
|
||||
bar:SetValue(current)
|
||||
end
|
||||
|
||||
bar.text:SetText(cast.name)
|
||||
bar:SetAlpha(1)
|
||||
|
||||
if bar.icon then
|
||||
if cast.texture then
|
||||
bar.icon.texture:SetTexture(cast.texture)
|
||||
bar.icon:SetAlpha(1)
|
||||
bar.icon:Show()
|
||||
else
|
||||
bar.icon:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
bar:Show()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
function BNP:IsCastbarTestMode()
|
||||
return castbarTestMode and true or false
|
||||
end
|
||||
|
||||
function BNP:SetCastbarTestMode(enabled)
|
||||
castbarTestMode = enabled and true or false
|
||||
|
||||
if castbarTestMode then
|
||||
ResetTestCast(GetTime())
|
||||
else
|
||||
-- Remove every preview immediately. Real casts are rediscovered on the
|
||||
-- next lightweight discovery pass without touching their stored state.
|
||||
local plate
|
||||
for plate in pairs(activeCastPlates) do
|
||||
MarkCastPlateInactive(plate)
|
||||
end
|
||||
end
|
||||
|
||||
if self.optionsFrame and self.optionsFrame.castbarTestCheck then
|
||||
self.optionsFrame.castbarTestCheck:SetChecked(castbarTestMode)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function BNP:RefreshCastbarLayout()
|
||||
local plate
|
||||
local height = GetCastbarHeight()
|
||||
|
||||
for plate in pairs(BNP.plates or {}) do
|
||||
if plate and plate.BNPCastbar then
|
||||
plate.BNPCastbar:SetHeight(height)
|
||||
ApplyCastbarLayout(plate, plate.BNPCastbar)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BNP:RefreshCastbarHeights()
|
||||
self:RefreshCastbarLayout()
|
||||
end
|
||||
|
||||
function BNP:SetCastbarsEnabled(enabled)
|
||||
BNP_DB = BNP_DB or {}
|
||||
BNP_DB.castbars = enabled and true or false
|
||||
|
||||
if not enabled then
|
||||
local plate
|
||||
for plate in pairs(BNP.plates) do Hide(plate) end
|
||||
end
|
||||
|
||||
self:Print("Nameplate castbars " .. (enabled and "enabled." or "disabled."))
|
||||
end
|
||||
Reference in New Issue
Block a user