Files
BlizzNameplatesPlus/core/libnameplate.lua
T
2026-09-10 17:48:35 +02:00

174 lines
5.2 KiB
Lua

BNP = BNP or {}
BNP.libnameplate = BNP.libnameplate or {
OnInit = {},
OnShow = {},
OnUpdate = {},
}
local lib = BNP.libnameplate
local known = {}
local initializedChildren = 0
local function FindHealthBar(frame)
if not frame or not frame.GetChildren then return nil end
local children = { frame:GetChildren() }
local i
for i = 1, table.getn(children) do
local child = children[i]
if child and child.GetObjectType and child:GetObjectType() == "StatusBar" then
return child
end
end
return nil
end
local function IsNamePlate(frame)
if not frame or not frame.GetObjectType then return false end
if frame:GetObjectType() ~= "Button" then return false end
-- Fast path for untouched Blizzard nameplates.
local firstRegion = frame:GetRegions()
if firstRegion and firstRegion.GetObjectType and firstRegion.GetTexture and
firstRegion:GetObjectType() == "Texture" and
firstRegion:GetTexture() == "Interface\\Tooltips\\Nameplate-Border" then
return true
end
-- Compatibility fallback: some UI/skin addons replace, blank or hide the
-- native border before BNP's WorldFrame scan sees the plate. Do not make
-- nameplate discovery depend on a mutable texture path.
local replacement = frame.nameplate
local original = replacement and replacement.original
-- Known replacement-overlay shape: the addon kept references to Blizzard's
-- original health/name/level objects even if it already reparented them.
if original and (original.health or original.healthbar) and
original.name and original.level then
return true
end
-- Generic safe fallback. Keep the structural test deliberately strict to
-- avoid mistaking unrelated WorldFrame Buttons for nameplates.
if not firstRegion or not firstRegion.GetObjectType or
firstRegion:GetObjectType() ~= "Texture" then
return false
end
if not FindHealthBar(frame) then return false end
local fontStrings = 0
local regions = { frame:GetRegions() }
local i
for i = 1, table.getn(regions) do
local region = regions[i]
if region and region.GetObjectType and region:GetObjectType() == "FontString" then
fontStrings = fontStrings + 1
if fontStrings >= 2 then return true end
end
end
return false
end
local function AttachPlateShortcuts(plate)
if plate.BNPShortcutsReady then return end
-- Do not assume another addon left the native StatusBar as the first child.
local replacement = plate.nameplate
local original = replacement and replacement.original
plate.healthbar = (original and (original.health or original.healthbar)) or
FindHealthBar(plate)
-- Vanilla region order:
-- 1 border, 2 glow, 3 name, 4 level, 5 level icon, 6 raid icon.
-- Fall back to common replacement-overlay references only when direct native
-- regions are unavailable; normal Blizzard plates keep the exact old path.
local regions = { plate:GetRegions() }
plate.border = (original and original.border) or regions[1]
plate.glow = regions[2] or (original and original.glow)
plate.name = regions[3] or (original and original.name)
plate.level = regions[4] or (original and original.level)
plate.levelicon = regions[5] or (original and (original.levelicon or original.bossicon))
plate.raidicon = regions[6] or (replacement and replacement.raidicon) or
(original and original.raidicon)
plate.BNPShortcutsReady = true
end
local function Fire(list, plate)
plate = plate or this
if not plate then return end
local i
for i = 1, table.getn(list) do
if list[i] then
pcall(list[i], plate)
end
end
end
local function RegisterPlate(plate)
plate = plate or this
if not plate then return end
if known[plate] then return end
known[plate] = true
AttachPlateShortcuts(plate)
if BNP.RegisterPlate then
BNP:RegisterPlate(plate)
end
Fire(lib.OnInit, plate)
if plate:IsShown() then
Fire(lib.OnShow, plate)
end
-- Run OnShow callbacks when Blizzard reuses the same plate.
local oldOnShow = plate:GetScript("OnShow")
plate:SetScript("OnShow", function(self)
local current = self or this or plate
if not current then return end
if oldOnShow then oldOnShow(current) end
AttachPlateShortcuts(current)
Fire(lib.OnShow, current)
end)
end
local scanner = CreateFrame("Frame")
local scanElapsed = 0
scanner:SetScript("OnUpdate", function()
scanElapsed = scanElapsed + arg1
if scanElapsed < 0.10 then return end
scanElapsed = 0
local count = WorldFrame:GetNumChildren()
-- Full scan only when WorldFrame gained children. This mirrors the lightweight
-- discovery strategy used by Shagu's Vanilla nameplate code.
if count > initializedChildren then
local children = { WorldFrame:GetChildren() }
local i
for i = 1, table.getn(children) do
local frame = children[i]
if not known[frame] and IsNamePlate(frame) then
RegisterPlate(frame)
end
end
initializedChildren = count
end
-- Compatibility callback list for modules that want a throttled plate update.
if table.getn(lib.OnUpdate) > 0 then
local plate
for plate in pairs(known) do
if plate:IsShown() then
Fire(lib.OnUpdate, plate)
end
end
end
end)