Add files via upload

This commit is contained in:
Wurmschwanz
2026-09-04 20:52:41 +02:00
committed by GitHub
parent ec365b7e93
commit a3dfb369a7
37 changed files with 12583 additions and 0 deletions
+386
View File
@@ -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