Delete modules directory

This commit is contained in:
Wurmschwanz
2026-08-19 00:00:41 +02:00
committed by GitHub
parent cdadb8ecfd
commit da89860f5a
14 changed files with 0 additions and 5018 deletions
-2279
View File
File diff suppressed because it is too large Load Diff
-264
View File
@@ -1,264 +0,0 @@
BNP = BNP or {}
local casts = {}
local BAR_HEIGHT = 6
local function GetCastbarHeight()
if BNP.GetCastbarHeight then return BNP:GetCastbarHeight() end
return BAR_HEIGHT
end
local BAR_GAP = 2
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.
bar:SetPoint("TOPLEFT", plate.healthbar, "BOTTOMLEFT", 15, -BAR_GAP)
bar:SetPoint("TOPRIGHT", plate.healthbar, "BOTTOMRIGHT", 14, -BAR_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, -BAR_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() 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
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
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() then return end
local now = GetTime()
local plate
for plate in pairs(activeCastPlates) do
if not plate:IsShown() then
MarkCastPlateInactive(plate)
else
local guid = GetPlateGUID(plate)
local cast = guid and casts[guid]
if not cast or now >= cast.endTime then
if guid and cast and now >= cast.endTime then
casts[guid] = nil
end
MarkCastPlateInactive(plate)
else
local bar = CreateCastbar(plate)
if bar then
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:RefreshCastbarHeights()
local plate
local height = GetCastbarHeight()
for plate in pairs(BNP.plates or {}) do
if plate and plate.BNPCastbar then
plate.BNPCastbar:SetHeight(height)
end
end
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
-169
View File
@@ -1,169 +0,0 @@
BNP = BNP or {}
local fallbackColors = {
WARRIOR = { 0.78, 0.61, 0.43 },
MAGE = { 0.41, 0.80, 0.94 },
ROGUE = { 1.00, 0.96, 0.41 },
DRUID = { 1.00, 0.49, 0.04 },
HUNTER = { 0.67, 0.83, 0.45 },
SHAMAN = { 0.00, 0.44, 0.87 },
PRIEST = { 1.00, 1.00, 1.00 },
WARLOCK = { 0.58, 0.51, 0.79 },
PALADIN = { 0.96, 0.55, 0.73 },
}
local function GetColor(class)
if RAID_CLASS_COLORS and RAID_CLASS_COLORS[class] then
local c = RAID_CLASS_COLORS[class]
return c.r, c.g, c.b
end
local c = fallbackColors[class]
if c then return c[1], c[2], c[3] end
end
local function Enabled()
if BNP.AreClassColorsEnabled then return BNP:AreClassColorsEnabled() end
return not BNP_DB or BNP_DB.classColors ~= false
end
local function ClearClassCache(plate)
if not plate then return end
plate.BNPClassColorApplied = nil
plate.BNPClassColorToken = nil
plate.BNPClassGUID = nil
plate.BNPClassR = nil
plate.BNPClassG = nil
plate.BNPClassB = nil
end
local function ResolvePlateUnit(plate)
if not plate or not plate.GetName then return nil, nil end
local token = plate:GetName(1)
if not token or not UnitExists(token) then
return token, nil
end
local exists, guid = UnitExists(token)
if exists then
return token, guid
end
return token, nil
end
local function ApplyClassColor(plate)
if not Enabled() then
ClearClassCache(plate)
return false
end
if not plate or not plate.healthbar then return false end
local token, guid = ResolvePlateUnit(plate)
-- BNP only owns PLAYER class colors.
-- NPC/neutral/friendly-NPC colors stay completely under Blizzard control.
if not token or not guid or not UnitIsPlayer(token) then
ClearClassCache(plate)
return false
end
local _, class = UnitClass(token)
local r, g, b = GetColor(class)
if not r then
ClearClassCache(plate)
return false
end
plate.BNPClassColorApplied = true
plate.BNPClassColorToken = token
plate.BNPClassGUID = guid
plate.BNPClassR = r
plate.BNPClassG = g
plate.BNPClassB = b
plate.healthbar:SetStatusBarColor(r, g, b)
return true
end
BNP.ApplyClassColor = ApplyClassColor
local function EnforceClassColor(plate)
if not Enabled() then return end
if not plate or not plate.healthbar or not plate.BNPClassGUID then return end
local token, guid = ResolvePlateUnit(plate)
-- Never enforce a cached color after Blizzard recycled the frame.
if not token or not guid or guid ~= plate.BNPClassGUID or not UnitIsPlayer(token) then
ClearClassCache(plate)
return
end
local r, g, b = plate.healthbar:GetStatusBarColor()
if math.abs(r - plate.BNPClassR) > 0.01
or math.abs(g - plate.BNPClassG) > 0.01
or math.abs(b - plate.BNPClassB) > 0.01 then
plate.healthbar:SetStatusBarColor(
plate.BNPClassR,
plate.BNPClassG,
plate.BNPClassB
)
end
end
if BNP.libnameplate then
table.insert(BNP.libnameplate.OnInit, function(plate)
local current = plate or this
if not current then return end
ClearClassCache(current)
ApplyClassColor(current)
local bar = current.healthbar
if bar and not current.BNPClassColorHooked then
local oldValueChanged = bar:GetScript("OnValueChanged")
bar:SetScript("OnValueChanged", function()
if oldValueChanged then oldValueChanged() end
EnforceClassColor(current)
end)
current.BNPClassColorHooked = true
end
end)
table.insert(BNP.libnameplate.OnShow, function(plate)
local current = plate or this
if not current then return end
ClearClassCache(current)
ApplyClassColor(current)
end)
end
-- Blizzard may rewrite player colors during combat/threat updates.
-- Re-apply only while the plate still resolves to the SAME player GUID.
-- If a recycled frame now represents an NPC/neutral unit, drop the cache
-- immediately and never touch its Blizzard color.
local elapsed = 0
local updater = CreateFrame("Frame")
updater:SetScript("OnUpdate", function()
elapsed = elapsed + arg1
if elapsed < 0.10 then return end
elapsed = 0
if not Enabled() then return end
local plate
for plate in pairs(BNP.plates) do
if plate:IsShown() and plate.healthbar then
local token, guid = ResolvePlateUnit(plate)
if plate.BNPClassGUID then
EnforceClassColor(plate)
elseif token and guid and UnitIsPlayer(token) then
ApplyClassColor(plate)
end
end
end
end)
-299
View File
@@ -1,299 +0,0 @@
BNP = BNP or {}
local _, playerClass = UnitClass("player")
local COMBO_CLASS = (playerClass == "ROGUE" or playerClass == "DRUID")
local MAX_POINTS = 5
local POINT_GAP = 1
local function Enabled()
return COMBO_CLASS and BNP.AreComboPointsEnabled and BNP:AreComboPointsEnabled()
end
local function GetTargetGUID()
local exists, guid = UnitExists("target")
if exists and guid then return guid end
return nil
end
local function GetComboCount()
if not GetComboPoints then return 0 end
local count = tonumber(GetComboPoints()) or 0
if count < 0 then count = 0 end
if count > MAX_POINTS then count = MAX_POINTS end
return count
end
local function GetSourcePoint()
if getglobal then
return getglobal("TargetFrameComboPoint1")
end
return nil
end
local function GetSourceSize()
local source = GetSourcePoint()
local w, h = 12, 16
if source then
if source.GetWidth then w = tonumber(source:GetWidth()) or w end
if source.GetHeight then h = tonumber(source:GetHeight()) or h end
end
if w <= 0 then w = 12 end
if h <= 0 then h = 16 end
return w, h
end
local function ApplyTargetFrameScale(holder)
if not holder then return end
local source = GetSourcePoint()
local parent = holder:GetParent()
local sourceScale = 1.0
local parentScale = 1.0
-- Match the actual on-screen scale of Blizzard's TargetFrame combo points.
if source and source.GetEffectiveScale then
sourceScale = tonumber(source:GetEffectiveScale()) or 1.0
elseif UIParent and UIParent.GetEffectiveScale then
sourceScale = tonumber(UIParent:GetEffectiveScale()) or 1.0
elseif UIParent and UIParent.GetScale then
sourceScale = tonumber(UIParent:GetScale()) or 1.0
end
if parent and parent.GetEffectiveScale then
parentScale = tonumber(parent:GetEffectiveScale()) or 1.0
end
if sourceScale <= 0 then sourceScale = 1.0 end
if parentScale <= 0 then parentScale = 1.0 end
holder:SetScale(sourceScale / parentScale)
end
local function CopySourceTextureAppearance(dst, src)
if not dst or not src then return end
if src.GetTexture and dst.SetTexture then
dst:SetTexture(src:GetTexture())
end
if src.GetTexCoord and dst.SetTexCoord then
local l, r, t, b = src:GetTexCoord()
if l then dst:SetTexCoord(l, r, t, b) end
end
if src.GetVertexColor and dst.SetVertexColor then
local r, g, b, a = src:GetVertexColor()
if r then dst:SetVertexColor(r, g, b, a or 1) end
end
if src.GetBlendMode and dst.SetBlendMode then
local mode = src:GetBlendMode()
if mode then dst:SetBlendMode(mode) end
end
end
local function CreateExactPoint(parent)
local source = GetSourcePoint()
local pointW, pointH = GetSourceSize()
local point = CreateFrame("Frame", nil, parent)
point:SetWidth(pointW)
point:SetHeight(pointH)
local copied = false
-- Clone the actual texture regions from Blizzard's TargetFrame combo point.
-- This guarantees the same client artwork/skin instead of guessing atlas cuts.
if source and source.GetRegions then
local regions = { source:GetRegions() }
local i
for i = 1, table.getn(regions) do
local src = regions[i]
if src and src.GetObjectType and src:GetObjectType() == "Texture" then
local tex = point:CreateTexture(nil, "ARTWORK")
CopySourceTextureAppearance(tex, src)
local w = src.GetWidth and tonumber(src:GetWidth()) or pointW
local h = src.GetHeight and tonumber(src:GetHeight()) or pointH
if not w or w <= 0 then w = pointW end
if not h or h <= 0 then h = pointH end
tex:SetWidth(w)
tex:SetHeight(h)
-- Preserve the standard TargetFrame relative offsets where possible.
local anchored = false
if src.GetPoint then
local pnt, rel, relp, x, y = src:GetPoint(1)
if pnt then
-- Source textures are normally anchored to their own combo frame.
-- Re-anchor them to our clone with the same point/offset.
tex:SetPoint(pnt, point, relp or pnt, x or 0, y or 0)
anchored = true
end
end
if not anchored then
tex:SetPoint("CENTER", point, "CENTER", 0, 0)
end
copied = true
end
end
end
-- Fallback only if the client does not expose TargetFrameComboPoint1 regions.
if not copied then
local base = point:CreateTexture(nil, "BACKGROUND")
base:SetTexture("Interface\\ComboFrame\\ComboPoint")
base:SetWidth(12)
base:SetHeight(16)
base:SetPoint("TOPLEFT", point, "TOPLEFT", 0, 0)
base:SetTexCoord(0, 0.375, 0, 1)
local highlight = point:CreateTexture(nil, "ARTWORK")
highlight:SetTexture("Interface\\ComboFrame\\ComboPoint")
highlight:SetWidth(8)
highlight:SetHeight(16)
highlight:SetPoint("TOPLEFT", point, "TOPLEFT", 2, 0)
highlight:SetTexCoord(0.375, 0.5625, 0, 1)
end
point:Hide()
return point
end
local function EnsurePoints(plate)
if plate.BNPComboPoints then
ApplyTargetFrameScale(plate.BNPComboPoints)
return plate.BNPComboPoints
end
if not plate or not plate.name then return nil end
local pointW, pointH = GetSourceSize()
local parent = plate.BNPScaleWrapper or plate
local holder = CreateFrame("Frame", nil, parent)
holder:SetWidth(MAX_POINTS * pointW + (MAX_POINTS - 1) * POINT_GAP)
holder:SetHeight(pointH)
-- Keep the position the user already approved.
holder:SetPoint("BOTTOM", plate.name, "TOP", 0, 2)
holder:SetFrameLevel((plate:GetFrameLevel() or 1) + 12)
holder.points = {}
holder:Hide()
local i
for i = 1, MAX_POINTS do
local point = CreateExactPoint(holder)
if i == 1 then
point:SetPoint("LEFT", holder, "LEFT", 0, 0)
else
point:SetPoint("LEFT", holder.points[i - 1], "RIGHT", POINT_GAP, 0)
end
holder.points[i] = point
end
ApplyTargetFrameScale(holder)
plate.BNPComboPoints = holder
return holder
end
local function HidePoints(plate)
local holder = plate and plate.BNPComboPoints
if not holder then return end
local i
for i = 1, MAX_POINTS do
holder.points[i]:Hide()
end
holder:Hide()
end
local function UpdatePlate(plate)
if not plate or not plate:IsShown() then return end
if not Enabled() then
HidePoints(plate)
return
end
local targetGUID = GetTargetGUID()
local plateGUID = plate.GetName and plate:GetName(1) or nil
if not targetGUID or not plateGUID or targetGUID ~= plateGUID then
HidePoints(plate)
return
end
local count = GetComboCount()
if count <= 0 then
HidePoints(plate)
return
end
local holder = EnsurePoints(plate)
if not holder then return end
ApplyTargetFrameScale(holder)
local i
for i = 1, MAX_POINTS do
if i <= count then
holder.points[i]:Show()
else
holder.points[i]:Hide()
end
end
holder:Show()
end
function BNP:RefreshComboPoints()
local plate
for plate in pairs(BNP.plates or {}) do
if plate:IsShown() then
UpdatePlate(plate)
else
HidePoints(plate)
end
end
if BNP.RefreshAllAuraLayouts then
BNP:RefreshAllAuraLayouts()
end
end
table.insert(BNP.libnameplate.OnInit, function(plate)
if COMBO_CLASS then
EnsurePoints(plate)
HidePoints(plate)
end
end)
table.insert(BNP.libnameplate.OnShow, function(plate)
if COMBO_CLASS then
HidePoints(plate)
UpdatePlate(plate)
end
end)
table.insert(BNP.libnameplate.OnUpdate, function(plate)
if COMBO_CLASS then UpdatePlate(plate) end
end)
if COMBO_CLASS then
local events = CreateFrame("Frame")
events:RegisterEvent("PLAYER_TARGET_CHANGED")
events:RegisterEvent("PLAYER_COMBO_POINTS")
events:SetScript("OnEvent", function()
BNP:RefreshComboPoints()
end)
end
-146
View File
@@ -1,146 +0,0 @@
SLASH_BLIZZNAMEPLATESPLUS1 = "/bnp"
SlashCmdList["BLIZZNAMEPLATESPLUS"] = function(msg)
msg = string.lower(msg or "")
if msg == "" or msg == "config" or msg == "options" then
if BNP.ToggleOptions then BNP:ToggleOptions() end
elseif msg == "spellprobe" or msg == "spellbook" then
if BNP.SpellbookProbe then BNP:SpellbookProbe() else BNP:Print("Spellbook probe is not loaded.") end
elseif msg == "spellprobe corruption" then
if BNP.SpellbookProbe then BNP:SpellbookProbe("corruption") end
elseif msg == "spellprobe agony" then
if BNP.SpellbookProbe then BNP:SpellbookProbe("agony") end
elseif msg == "spellprobe siphon" then
if BNP.SpellbookProbe then BNP:SpellbookProbe("siphon") end
elseif msg == "unknown" or msg == "learn" then
if BNP.PrintUnknownBeta then BNP:PrintUnknownBeta(false) else BNP:Print("Unknown collector is not loaded.") end
elseif msg == "unknown verbose" or msg == "learn verbose" then
if BNP.PrintUnknownBeta then BNP:PrintUnknownBeta(true) else BNP:Print("Unknown collector is not loaded.") end
elseif msg == "unknown reset" or msg == "learn reset" then
if BNP.ResetUnknownBeta then BNP:ResetUnknownBeta() end
BNP.unknownDirectEvents = {}
elseif msg == "auras" or msg == "targetauras" then
if BNP.PrintTargetAuraProbe then
BNP:PrintTargetAuraProbe()
else
BNP:Print("Target aura probe is not loaded.")
end
elseif msg == "raidcheck" then
local exists, guid = UnitExists("target")
DEFAULT_CHAT_FRAME:AddMessage("|cff66ccffBNP RAIDCHECK|r")
if not exists or not guid then
DEFAULT_CHAT_FRAME:AddMessage("No target / no target GUID.")
return
end
DEFAULT_CHAT_FRAME:AddMessage("Target: "..tostring(UnitName("target") or "?").." | GUID: "..tostring(guid))
local cache=BNP.guidAuras and BNP.guidAuras[guid]
local n=0
if cache then
local key,aura
for key,aura in pairs(cache) do
if type(aura)=="table" and aura.spellID then
n=n+1
local mode = aura.castPrimary and "CAST" or (aura.liveConfirmed and "LIVE" or "?")
DEFAULT_CHAT_FRAME:AddMessage(
"CACHE | "..tostring(key).." | ID "..tostring(aura.spellID).." | "..mode
)
end
end
end
if n==0 then DEFAULT_CHAT_FRAME:AddMessage("CACHE: EMPTY") end
local pending = BNP.pendingAuras and BNP.pendingAuras[guid]
if pending then
DEFAULT_CHAT_FRAME:AddMessage("-- PENDING FOR TARGET --")
local pk,pv
for pk,pv in pairs(pending) do
if type(pv)=="table" then
DEFAULT_CHAT_FRAME:AddMessage(
"PENDING | "..tostring(pk)..
" | ID "..tostring(pv.spellID or "?")..
" | age "..string.format("%.1fs", GetTime()-(pv.created or GetTime()))
)
end
end
else
DEFAULT_CHAT_FRAME:AddMessage("PENDING: none")
end
DEFAULT_CHAT_FRAME:AddMessage("-- LAST TRACKED CAST EVENTS --")
local trace=BNP.raidTrace or {}
local start=math.max(1,table.getn(trace)-14)
local ti
for ti=start,table.getn(trace) do
local e=trace[ti]
DEFAULT_CHAT_FRAME:AddMessage(
tostring(e.stage)..
" | "..tostring(e.key or "-")..
" | ID "..tostring(e.spellID or "?")..
" | GUID "..tostring(e.guid or "nil")..
" | "..string.format("%.1fs ago",GetTime()-(e.time or GetTime()))
)
end
DEFAULT_CHAT_FRAME:AddMessage("-- LIVE TARGET AURAS (up to 64) --")
local i
for i=1,64 do
local tex,stacks,dtype,id=UnitDebuff("target",i)
if not tex then break end
local name=(id and SpellInfo and SpellInfo(id)) or "?"
DEFAULT_CHAT_FRAME:AddMessage("LIVE "..tostring(i).." | ID "..tostring(id or "?").." | "..tostring(name))
end
elseif msg == "direct" then
if BNP.PrintUnknownDirectEvents then
BNP:PrintUnknownDirectEvents()
else
BNP:Print("Direct event collector is not loaded.")
end
elseif msg == "spelldb" or msg == "audit" then
if BNP.PrintSpellDBAudit then BNP:PrintSpellDBAudit() else BNP:Print("SpellDB audit is not loaded.") end
elseif msg == "spelldb reset" or msg == "audit reset" then
if BNP.ResetSpellDBAudit then BNP:ResetSpellDBAudit() end
elseif msg == "guid" or msg == "guiddebug" or msg == "diag" then
if BNP.PrintGUIDDiagnostic then BNP:PrintGUIDDiagnostic() else BNP:Print("GUID diagnostics are not loaded.") end
elseif msg == "status" then
local visible = 0
local plate
for plate in pairs(BNP.plates) do
if plate:IsShown() then visible = visible + 1 end
end
BNP:Print("registered: " .. BNP.detected .. ", visible: " .. visible .. ", test spell: " .. tostring(BNP.corruptionName or "not found"))
elseif msg == "cache" then
local name = UnitName("target")
if not name then
BNP:Print("No target selected.")
return
end
local remaining = BNP.FindCachedCorruption and BNP.FindCachedCorruption(name)
BNP:Print("Cache for " .. name .. ": " .. tostring(remaining or "no Corruption found"))
elseif msg == "castbars on" then
if BNP.SetCastbarsEnabled then BNP:SetCastbarsEnabled(true) end
elseif msg == "castbars off" then
if BNP.SetCastbarsEnabled then BNP:SetCastbarsEnabled(false) end
elseif msg == "scale" then
BNP:Print("Current nameplate scale: " .. tostring(BNP:GetNameplateScale()))
elseif msg == "scale reset" then
if BNP.ResetNameplateScale then BNP:ResetNameplateScale() end
elseif string.find(msg, "^scale%s+") then
local _, _, value = string.find(msg, "^scale%s+([0-9%.]+)")
if BNP.SetNameplateScale then BNP:SetNameplateScale(value) end
elseif msg == "classcolors on" then
BNP_DB = BNP_DB or {}
BNP_DB.classColors = true
BNP:Print("Class colors enabled.")
elseif msg == "classcolors off" then
BNP_DB = BNP_DB or {}
BNP_DB.classColors = false
BNP:Print("Class colors disabled.")
elseif msg == "debug on" then
BNP:SetDebug(true)
elseif msg == "debug off" then
BNP:SetDebug(false)
elseif msg == "debug" then
BNP:SetDebug(not BNP.debugEnabled)
else
BNP:Print("Commands: /bnp unknown, /bnp direct, /bnp spellprobe, /bnp spelldb, /bnp guid, /bnp status, /bnp castbars on/off, /bnp scale <0.5-1.5>, /bnp scale reset, /bnp classcolors on/off, /bnp debug on, /bnp debug off")
end
end
-43
View File
@@ -1,43 +0,0 @@
if not BNP.libnameplate then return end
local function CreateMarker(plate)
if not plate then return end
BNP:RegisterPlate(plate)
if not plate.BNPMarker then
local marker = CreateFrame("Frame", nil, plate)
marker:SetWidth(14)
marker:SetHeight(14)
marker:SetPoint("BOTTOM", plate, "TOP", 0, 34)
marker:SetFrameLevel(30)
marker.texture = marker:CreateTexture(nil, "OVERLAY")
marker.texture:SetAllPoints(marker)
marker.texture:SetTexture("Interface\\Buttons\\WHITE8X8")
marker.texture:SetVertexColor(1, 0, 0, 1)
marker.text = marker:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
marker.text:SetPoint("CENTER", marker, "CENTER", 0, 0)
marker.text:SetText("+")
marker.text:SetTextColor(1, 1, 1)
plate.BNPMarker = marker
end
if BNP.debugEnabled then plate.BNPMarker:Show() else plate.BNPMarker:Hide() end
end
table.insert(BNP.libnameplate.OnInit, CreateMarker)
table.insert(BNP.libnameplate.OnShow, CreateMarker)
function BNP:SetDebug(enabled)
BNP.debugEnabled = enabled
BNP_DB = BNP_DB or {}
BNP_DB.debugEnabled = enabled
local plate
for plate in pairs(BNP.plates) do
if plate.BNPMarker then
if enabled then plate.BNPMarker:Show() else plate.BNPMarker:Hide() end
end
end
BNP:Print("Debug-Marker " .. (enabled and "aktiviert" or "deaktiviert") .. ".")
end
-324
View File
@@ -1,324 +0,0 @@
BNP = BNP or {}
local NON_TARGET_ALPHA = 0.55
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
return token
end
local function GetTargetGUID()
local exists, guid = UnitExists("target")
if exists and guid then return guid end
return nil
end
local function IsForeignTagged(plate)
if not plate or not plate.GetName then return false end
local token = plate:GetName(1)
if not token then return false end
local exists = UnitExists(token)
if not exists then return false end
if UnitCanAttack and not UnitCanAttack("player", token) then
return false
end
if not UnitIsTapped or not UnitIsTapped(token) then
return false
end
if UnitIsTappedByPlayer and UnitIsTappedByPlayer(token) then
return false
end
return true
end
local function DesiredAlpha(plate)
-- No selected target: keep all nameplates fully visible.
local targetGUID = GetTargetGUID()
if not targetGUID then return 1 end
-- Current target always stays fully visible.
local plateGUID = GetPlateGUID(plate)
if plateGUID and plateGUID == targetGUID then
return 1
end
-- Only non-target plates use the configured alpha.
if BNP.GetNonTargetAlpha then
return BNP:GetNonTargetAlpha()
end
return 1
end
local function SetLevelSafe(frame, level)
if frame and frame.SetFrameLevel and level then
if frame:GetFrameLevel() ~= level then
frame:SetFrameLevel(level)
end
end
end
local function ApplyTargetFrameLevel(plate)
if not plate or not plate:IsShown() then return end
-- IMPORTANT:
-- Never change the FrameStrata of projected Vanilla nameplates.
-- Their native client/world layer keeps them behind Blizzard UI frames.
-- Target Plate on Top is therefore implemented only with FrameLevel.
if plate.BNPBaseFrameLevel == nil then
plate.BNPBaseFrameLevel = plate:GetFrameLevel() or 0
end
local wrapper = plate.BNPScaleWrapper
if wrapper and plate.BNPBaseWrapperLevel == nil then
plate.BNPBaseWrapperLevel = wrapper:GetFrameLevel() or plate.BNPBaseFrameLevel
end
local target = false
if BNP.IsTargetOnTopEnabled and BNP:IsTargetOnTopEnabled() then
local targetGUID = GetTargetGUID()
local plateGUID = GetPlateGUID(plate)
target = targetGUID and plateGUID and targetGUID == plateGUID
end
local plateLevel = plate.BNPBaseFrameLevel
local wrapperLevel = plate.BNPBaseWrapperLevel or plateLevel
if target then
-- Raise the target only relative to other nameplates while preserving the
-- native projected-nameplate strata beneath the Blizzard interface.
plateLevel = plateLevel + 50
wrapperLevel = wrapperLevel + 50
end
SetLevelSafe(plate, plateLevel)
if wrapper then
SetLevelSafe(wrapper, wrapperLevel)
end
end
local function ApplyForeignTagVisual(plate)
if not plate or not plate:IsShown() then return end
local bar = plate.healthbar
if not bar or not bar.SetStatusBarColor then return end
local foreign = IsForeignTagged(plate)
if foreign then
if not plate.BNPForeignTagGrey then
local r, g, b, a = bar:GetStatusBarColor()
plate.BNPForeignTagOldR = r
plate.BNPForeignTagOldG = g
plate.BNPForeignTagOldB = b
plate.BNPForeignTagOldA = a
plate.BNPForeignTagGrey = true
end
-- Neutral grey: visually distinct from Target Focus transparency.
bar:SetStatusBarColor(0.45, 0.45, 0.45, plate.BNPForeignTagOldA or 1)
elseif plate.BNPForeignTagGrey then
-- Restore the color that BNP/Shagu/class-color logic had before the mob
-- became foreign-tagged. Clear state so future color changes can be learned.
bar:SetStatusBarColor(
plate.BNPForeignTagOldR or 1,
plate.BNPForeignTagOldG or 0,
plate.BNPForeignTagOldB or 0,
plate.BNPForeignTagOldA or 1
)
plate.BNPForeignTagGrey = nil
plate.BNPForeignTagOldR = nil
plate.BNPForeignTagOldG = nil
plate.BNPForeignTagOldB = nil
plate.BNPForeignTagOldA = nil
end
end
local function GetGlowVisualParent(plate)
if not plate then return nil end
local anchor = plate.healthbar or plate
local visualParent = plate.BNPScaleWrapper
if not visualParent and anchor and anchor.GetParent then
visualParent = anchor:GetParent()
end
return visualParent or plate
end
local function KeepShaguGlowLayering(plate)
if not plate then return end
local anchor = plate.healthbar
local visualParent = GetGlowVisualParent(plate)
if not anchor or not visualParent then return end
-- ShaguPlates creates its health StatusBar at FrameLevel 4 specifically
-- "to keep above glow". Reproduce that relationship relative to BNP's
-- visual wrapper, including when Target Plate on Top changes its level.
if anchor.SetFrameLevel and visualParent.GetFrameLevel then
local wanted = (visualParent:GetFrameLevel() or 0) + 4
if anchor:GetFrameLevel() ~= wanted then
anchor:SetFrameLevel(wanted)
end
end
end
local function EnsureTargetGlow(plate)
if not plate then return nil end
if plate.BNPTargetGlow then return plate.BNPTargetGlow end
local anchor = plate.healthbar or plate
local visualParent = GetGlowVisualParent(plate)
if not anchor or not visualParent then return nil end
-- Direct port of ShaguPlates' target glow:
-- nameplate.glow = nameplate:CreateTexture(nil, "BACKGROUND")
-- glow anchored to health CENTER
-- width = health width + 60
-- height = health height + 30
--
-- BNP's only intentional visual change is +3 px on X.
local glow = visualParent:CreateTexture(nil, "BACKGROUND")
glow:SetTexture("Interface\\AddOns\\BlizzNameplatesPlus\\media\\shagu_target_glow.tga")
glow:SetPoint("CENTER", anchor, "CENTER", 3, 0)
local width = anchor.GetWidth and anchor:GetWidth() or 120
local height = anchor.GetHeight and anchor:GetHeight() or 8
glow:SetWidth(width + 60)
glow:SetHeight(height + 30)
local r, g, b = 1, 1, 1
if BNP.GetTargetGlowColor then
r, g, b = BNP:GetTargetGlowColor()
end
glow:SetVertexColor(r, g, b, 1)
glow:Hide()
plate.BNPTargetGlow = glow
KeepShaguGlowLayering(plate)
return glow
end
local function ApplyTargetGlow(plate)
if not plate or not plate:IsShown() then return end
local glow = EnsureTargetGlow(plate)
if not glow then return end
local enabled = BNP.IsTargetFocusEnabled and BNP:IsTargetFocusEnabled()
if not enabled then
glow:Hide()
return
end
local targetGUID = GetTargetGUID()
local plateGUID = GetPlateGUID(plate)
if targetGUID and plateGUID and targetGUID == plateGUID then
local anchor = plate.healthbar or plate
local width = anchor.GetWidth and anchor:GetWidth() or 120
local height = anchor.GetHeight and anchor:GetHeight() or 8
KeepShaguGlowLayering(plate)
glow:ClearAllPoints()
glow:SetPoint("CENTER", anchor, "CENTER", 3, 0)
glow:SetWidth(width + 60)
glow:SetHeight(height + 30)
if BNP.GetTargetGlowColor then
local r, g, b = BNP:GetTargetGlowColor()
glow:SetVertexColor(r, g, b, 1)
end
glow:SetAlpha(1)
glow:Show()
else
glow:Hide()
end
end
local function ApplyTargetAlpha(plate)
if not plate or not plate:IsShown() then return end
local wanted = DesiredAlpha(plate)
if plate:GetAlpha() ~= wanted then
plate:SetAlpha(wanted)
end
end
local function InstallAlphaGuard(plate)
plate = plate or this
if not plate or plate.BNPFullAlphaGuard then return end
local oldOnUpdate = plate:GetScript("OnUpdate")
plate:SetScript("OnUpdate", function()
if oldOnUpdate then oldOnUpdate() end
local current = this or plate
-- Apply the optional Y-offset after Blizzard has had a chance to update
-- the projected nameplate for this frame. This uses the OnUpdate BNP
-- already needs for alpha/glow/target-level handling.
if BNP.MaintainNameplateYOffset then
BNP:MaintainNameplateYOffset(current)
end
ApplyTargetAlpha(current)
ApplyTargetGlow(current)
ApplyForeignTagVisual(current)
ApplyTargetFrameLevel(current)
end)
plate.BNPFullAlphaGuard = true
ApplyTargetAlpha(plate)
end
if BNP.libnameplate then
table.insert(BNP.libnameplate.OnInit, function(plate)
InstallAlphaGuard(plate or this)
end)
table.insert(BNP.libnameplate.OnShow, function(plate)
local current = plate or this
if not current then return end
InstallAlphaGuard(current)
ApplyTargetAlpha(current)
ApplyTargetGlow(current)
ApplyForeignTagVisual(current)
ApplyTargetFrameLevel(current)
end)
end
function BNP:RefreshTargetFocus()
local plate
for plate in pairs(self.plates or {}) do
ApplyTargetAlpha(plate)
ApplyTargetGlow(plate)
ApplyForeignTagVisual(plate)
end
end
function BNP:RefreshNonTargetAlpha()
local plate
for plate in pairs(self.plates or {}) do
ApplyTargetAlpha(plate)
end
end
function BNP:RefreshTargetOnTop()
local plate
for plate in pairs(self.plates or {}) do
ApplyTargetFrameLevel(plate)
end
end
-73
View File
@@ -1,73 +0,0 @@
-- SuperWoW GUID probe.
-- This module does not alter aura tracking or rendering. It only inspects the
-- extended UnitExists return value and the unit token exposed by nameplates.
local function CleanText(text)
if not text then return nil end
text = string.gsub(text, "|c%x%x%x%x%x%x%x%x", "")
text = string.gsub(text, "|r", "")
return text
end
local function SafePlateToken(plate)
if not plate or not plate.GetName then return nil end
-- SuperWoW extends frame:GetName(index). ShaguTweaks already uses
-- plate:GetName(1) for its SuperWoW nameplate castbar implementation.
local ok, token = pcall(function() return plate:GetName(1) end)
if ok then return token end
return nil
end
local function UnitExistsData(unit)
if not unit then return nil, nil end
local ok, exists, guid = pcall(UnitExists, unit)
if not ok then return nil, nil end
return exists, guid
end
function BNP:GetSuperWoWGUID(unit)
local exists, guid = UnitExistsData(unit)
if exists and guid then return guid end
return nil
end
function BNP:GetPlateSuperWoWData(plate)
local token = SafePlateToken(plate)
local exists, guid = UnitExistsData(token)
return token, exists, guid
end
function BNP:PrintGUIDDiagnostic()
BNP:Print("=== SuperWoW GUID Diagnose ===")
local targetName = UnitName("target")
local targetExists, targetGUID = UnitExistsData("target")
BNP:Print("Target: " .. tostring(targetName or "kein Ziel"))
BNP:Print("UnitExists(target): " .. tostring(targetExists) .. " | GUID: " .. tostring(targetGUID))
local visible = 0
local sameName = 0
local guidMatches = 0
local index = 0
for plate in pairs(BNP.plates) do
if plate and plate:IsShown() then
visible = visible + 1
local plateName = plate.name and plate.name.GetText and CleanText(plate.name:GetText()) or nil
if not targetName or plateName == targetName then
sameName = sameName + 1
index = index + 1
local token, exists, guid = BNP:GetPlateSuperWoWData(plate)
if targetGUID and guid and targetGUID == guid then guidMatches = guidMatches + 1 end
BNP:Print("Plate " .. index .. ": " .. tostring(plateName))
BNP:Print(" Token: " .. tostring(token) .. " | Exists: " .. tostring(exists))
BNP:Print(" GUID: " .. tostring(guid) .. " | Target-Match: " .. tostring(targetGUID and guid == targetGUID or false))
end
end
end
BNP:Print("Sichtbar: " .. visible .. " | gleicher Name: " .. sameName .. " | GUID-Matches: " .. guidMatches)
end
-224
View File
@@ -1,224 +0,0 @@
BNP = BNP or {}
local function Enabled()
return BNP.IsHealthTextEnabled and BNP:IsHealthTextEnabled()
end
local function CreateHealthText(plate)
if plate.BNPHealthPercent then return plate.BNPHealthPercent end
if not plate or not plate.healthbar then return nil end
-- Parent the text directly to the Blizzard health StatusBar. This keeps it
-- above the bar texture and automatically follows every frame-level change
-- made for Target Glow / Target Plate on Top. No separate holder level can
-- become stale during target switches or nameplate recycling.
local anchor = plate.healthbar
local text = anchor:CreateFontString(nil, "OVERLAY")
text:SetFont("Fonts\\FRIZQT__.TTF", 9, "OUTLINE")
text:SetPoint("CENTER", anchor, "CENTER", 3, 0)
text:SetTextColor(1, 1, 1)
text:Hide()
plate.BNPHealthPercent = text
return text
end
local function GetPercentFromBar(plate)
if not plate or not plate.healthbar then return nil end
local minValue, maxValue = plate.healthbar:GetMinMaxValues()
local value = plate.healthbar:GetValue()
if value and minValue and maxValue and maxValue > minValue then
local pct = ((value - minValue) / (maxValue - minValue)) * 100
return math.floor(pct + 0.5)
end
return nil
end
local function GetPercentFromUnit(plate)
if not plate or not plate.GetName then return nil end
local token = plate:GetName(1)
if not token or not UnitExists(token) then return nil end
if not UnitHealth or not UnitHealthMax then return nil end
local health = UnitHealth(token)
local maxHealth = UnitHealthMax(token)
if health and maxHealth and maxHealth > 0 then
return math.floor((health / maxHealth) * 100 + 0.5)
end
return nil
end
local function GetHealthFromUnit(plate)
if not plate or not plate.GetName then return nil, nil end
local token = plate:GetName(1)
if not token or not UnitExists(token) then return nil, nil end
if not UnitHealth or not UnitHealthMax then return nil, nil end
local health = tonumber(UnitHealth(token))
local maxHealth = tonumber(UnitHealthMax(token))
if health and maxHealth and maxHealth > 0 then
return health, maxHealth
end
return nil, nil
end
local function GetHealthFromBar(plate)
if not plate or not plate.healthbar then return nil, nil end
local minValue, maxValue = plate.healthbar:GetMinMaxValues()
local value = plate.healthbar:GetValue()
value = tonumber(value)
minValue = tonumber(minValue)
maxValue = tonumber(maxValue)
if value and minValue and maxValue and maxValue > minValue then
return value - minValue, maxValue - minValue
end
return nil, nil
end
local function FormatHealth(value)
value = tonumber(value)
if not value then return nil end
if value < 0 then value = 0 end
-- Keep boss/player values readable inside the compact Blizzard healthbar.
if value >= 1000000 then
local shown = math.floor((value / 1000000) * 10 + 0.5) / 10
return tostring(shown) .. "m"
elseif value >= 10000 then
return tostring(math.floor(value / 1000 + 0.5)) .. "k"
elseif value >= 1000 then
local shown = math.floor((value / 1000) * 10 + 0.5) / 10
return tostring(shown) .. "k"
end
return tostring(math.floor(value + 0.5))
end
local function UpdatePlate(plate)
if not plate or not plate.healthbar then return end
local text = plate.BNPHealthPercent
if not Enabled() or not plate:IsShown() then
if text then text:Hide() end
return
end
text = text or CreateHealthText(plate)
if not text then return end
local mode = BNP:GetHealthTextMode()
local health, maxHealth = GetHealthFromUnit(plate)
-- Fallback for clients/units where the SuperWoW unit token is temporarily
-- unavailable. This may be normalized health on stock Vanilla.
if health == nil or maxHealth == nil then
health, maxHealth = GetHealthFromBar(plate)
end
local pct = nil
if health and maxHealth and maxHealth > 0 then
pct = math.floor((health / maxHealth) * 100 + 0.5)
else
pct = GetPercentFromBar(plate)
if pct == nil then pct = GetPercentFromUnit(plate) end
end
if pct ~= nil then
if pct < 0 then pct = 0 end
if pct > 100 then pct = 100 end
end
local hpText = FormatHealth(health)
local display = nil
if mode == "percent" then
if pct ~= nil then display = tostring(pct) .. "%" end
elseif mode == "hp" then
display = hpText
elseif mode == "both" then
if hpText and pct ~= nil then
display = hpText .. " | " .. tostring(pct) .. "%"
elseif hpText then
display = hpText
elseif pct ~= nil then
display = tostring(pct) .. "%"
end
end
if not display or display == "" then
text:Hide()
return
end
if text.BNPLastHealthText ~= display then
text:SetText(display)
text.BNPLastHealthText = display
end
text:Show()
end
-- Intentionally simple fallback: only reads values and updates text.
-- No SetScript calls on healthbar, no SetAlpha, no color changes.
local updater = CreateFrame("Frame")
local elapsed = 0
updater:SetScript("OnUpdate", function()
elapsed = elapsed + arg1
if elapsed < 0.10 then return end
elapsed = 0
local plate
for plate in pairs(BNP.plates or {}) do
if plate:IsShown() then
UpdatePlate(plate)
elseif plate.BNPHealthPercent then
plate.BNPHealthPercent:Hide()
end
end
end)
function BNP:RefreshHealthPercent()
local plate
for plate in pairs(self.plates or {}) do
if plate and plate:IsShown() then
UpdatePlate(plate)
elseif plate and plate.BNPHealthPercent then
plate.BNPHealthPercent:Hide()
end
end
end
-- Refresh immediately when Blizzard reuses a nameplate instead of waiting for
-- the next 0.10 s fallback pass.
if BNP.libnameplate then
table.insert(BNP.libnameplate.OnShow, function(plate)
local current = plate or this
if current then UpdatePlate(current) end
end)
end
-- Target switches can change nameplate frame levels in the same frame. Because
-- the text now belongs to the healthbar this is only a value/visibility refresh,
-- not a layering workaround.
local healthEvents = CreateFrame("Frame")
healthEvents:RegisterEvent("PLAYER_TARGET_CHANGED")
healthEvents:RegisterEvent("PLAYER_ENTERING_WORLD")
healthEvents:SetScript("OnEvent", function()
if BNP.RefreshHealthPercent then
BNP:RefreshHealthPercent()
end
end)
-274
View File
@@ -1,274 +0,0 @@
BNP = BNP or {}
local MIN_SCALE = 0.50
local MAX_SCALE = 1.50
local DEFAULT_SCALE = 1.00
local function Clamp(value)
if value < MIN_SCALE then return MIN_SCALE end
if value > MAX_SCALE then return MAX_SCALE end
return value
end
function BNP:GetNameplateScale()
BNP_DB = BNP_DB or {}
return Clamp(tonumber(BNP_DB.nameplateScale) or DEFAULT_SCALE)
end
local function EffectiveScale()
local uiScale = 1.0
if UIParent and UIParent.GetScale then
uiScale = UIParent:GetScale() or 1.0
end
return uiScale * BNP:GetNameplateScale()
end
local function CaptureAnchorState(object, plate, wrapper)
if not object or not object.GetPoint then return nil end
local state = {
object = object,
points = {},
}
local count = 1
if object.GetNumPoints then
count = object:GetNumPoints() or 1
if count < 1 then count = 1 end
end
local i
for i = 1, count do
local point, relativeTo, relativePoint, x, y = object:GetPoint(i)
if point then
if relativeTo == plate then
relativeTo = wrapper
end
state.points[table.getn(state.points) + 1] = {
point = point,
relativeTo = relativeTo or wrapper,
relativePoint = relativePoint or point,
x = x or 0,
y = y or 0,
}
end
end
return state
end
local function AnchorStateMatches(state)
if not state or not state.object or not state.object.GetPoint then return false end
local object = state.object
local expectedCount = table.getn(state.points)
local currentCount = expectedCount
if object.GetNumPoints then
currentCount = object:GetNumPoints() or 0
end
if currentCount ~= expectedCount then
return false
end
local i
for i = 1, expectedCount do
local expected = state.points[i]
local point, relativeTo, relativePoint, x, y = object:GetPoint(i)
if point ~= expected.point
or relativeTo ~= expected.relativeTo
or relativePoint ~= expected.relativePoint
or math.abs((x or 0) - expected.x) > 0.01
or math.abs((y or 0) - expected.y) > 0.01 then
return false
end
end
return true
end
local function RestoreAnchorState(state, wrapper)
if not state or not state.object then return end
local object = state.object
if object.GetParent and object.SetParent and object:GetParent() ~= wrapper then
object:SetParent(wrapper)
end
-- Do nothing in the common case. Blizzard normally only rewrites a subset
-- of nameplate anchors while moving, most notably the level FontString.
if AnchorStateMatches(state) then return end
if not object.ClearAllPoints or not object.SetPoint then return end
object:ClearAllPoints()
local i
for i = 1, table.getn(state.points) do
local p = state.points[i]
object:SetPoint(
p.point,
p.relativeTo or wrapper,
p.relativePoint or p.point,
p.x,
p.y
)
end
end
local function ApplyWrapperOffset(plate, wrapper)
if not plate or not wrapper then return end
local offset = 0
if BNP.GetNameplateYOffset then
offset = BNP:GetNameplateYOffset()
end
-- Move only the child visual container. The WorldFrame-projected Blizzard
-- nameplate itself keeps its original anchor and can continue to be
-- repositioned by the client every frame.
wrapper:ClearAllPoints()
wrapper:SetPoint("TOPLEFT", plate, "TOPLEFT", 0, offset)
wrapper:SetPoint("BOTTOMRIGHT", plate, "BOTTOMRIGHT", 0, offset)
end
local function EnsureScaleWrapper(plate)
if not plate or plate.BNPScaleWrapper then return end
local originalWidth = plate:GetWidth()
local originalHeight = plate:GetHeight()
local wrapper = CreateFrame("Frame", nil, plate)
wrapper:SetAllPoints(plate)
wrapper.plate = plate
wrapper.originalWidth = originalWidth
wrapper.originalHeight = originalHeight
wrapper.BNPAnchorStates = {}
plate.BNPScaleWrapper = wrapper
-- Capture the ORIGINAL Blizzard anchors before changing parentage.
-- Those stored anchors become BNP's authoritative visual layout whenever
-- Y-offset is enabled.
if plate.healthbar then
local state = CaptureAnchorState(plate.healthbar, plate, wrapper)
if state then
table.insert(wrapper.BNPAnchorStates, state)
end
plate.healthbar:SetParent(wrapper)
RestoreAnchorState(state, wrapper)
plate.healthbar:SetFrameLevel(1)
end
local regions = { plate:GetRegions() }
local i, object
for i, object in pairs(regions) do
if object and object.SetParent then
local state = CaptureAnchorState(object, plate, wrapper)
if state then
table.insert(wrapper.BNPAnchorStates, state)
end
object:SetParent(wrapper)
RestoreAnchorState(state, wrapper)
end
end
ApplyWrapperOffset(plate, wrapper)
end
function BNP:ApplyNameplateScale(plate)
if not plate then return end
EnsureScaleWrapper(plate)
local wrapper = plate.BNPScaleWrapper
if not wrapper then return end
local scale = EffectiveScale()
wrapper:SetScale(scale)
ApplyWrapperOffset(plate, wrapper)
-- ShaguTweaks also adjusts the original frame bounds so Blizzard's world
-- positioning remains correct while the child visuals honor UI scale.
plate:SetWidth(wrapper.originalWidth * scale)
plate:SetHeight(wrapper.originalHeight * scale)
end
function BNP:ApplyNameplateScaleAll()
local plate
for plate in pairs(self.plates) do
self:ApplyNameplateScale(plate)
end
end
function BNP:ApplyNameplateYOffsetAll()
local plate
for plate in pairs(self.plates or {}) do
if plate then
self:ApplyNameplateScale(plate)
end
end
end
function BNP:MaintainNameplateYOffset(plate)
if not plate or not plate:IsShown() then return end
if not self.GetNameplateYOffset or self:GetNameplateYOffset() <= 0 then return end
EnsureScaleWrapper(plate)
local wrapper = plate.BNPScaleWrapper
if not wrapper then return end
-- Never rediscover regions through plate:GetRegions() here. Once BNP moves
-- Blizzard regions into the wrapper they are no longer guaranteed to be
-- returned by plate:GetRegions(). Keep and maintain the original region
-- objects captured during wrapper creation instead.
local states = wrapper.BNPAnchorStates or {}
local i
for i = 1, table.getn(states) do
RestoreAnchorState(states[i], wrapper)
end
ApplyWrapperOffset(plate, wrapper)
end
function BNP:SetNameplateScale(value)
value = tonumber(value)
if not value then
self:Print("Usage: /bnp scale 0.5-1.5")
return
end
value = Clamp(value)
BNP_DB = BNP_DB or {}
BNP_DB.nameplateScale = value
self:ApplyNameplateScaleAll()
self:Print("Nameplate scale multiplier set to " .. tostring(value) .. ".")
end
function BNP:ResetNameplateScale()
BNP_DB = BNP_DB or {}
BNP_DB.nameplateScale = DEFAULT_SCALE
self:ApplyNameplateScaleAll()
self:Print("Nameplate scale multiplier reset to 1.0 (Shagu/UI scale behavior).")
end
if BNP.libnameplate then
table.insert(BNP.libnameplate.OnInit, function(plate)
local current = plate or this
if not current then return end
BNP:ApplyNameplateScale(current)
end)
table.insert(BNP.libnameplate.OnShow, function(plate)
local current = plate or this
if not current then return end
BNP:ApplyNameplateScale(current)
end)
end
-67
View File
@@ -1,67 +0,0 @@
BNP = BNP or {}
-- ShaguTweaks Darkened UI compatibility -------------------------------------
-- ShaguTweaks darkens a nameplate once and marks it with plate.darkened=true.
-- Blizzard can later recycle that same plate and restore its original gold
-- textures while the flag remains true. Shagu then skips the recycled plate.
--
-- BNP does not implement its own dark mode here. It simply asks ShaguTweaks
-- to run its own DarkenFrame() again whenever Blizzard reuses a nameplate.
local pending = {}
local function ShaguDarkModeActive()
return ShaguTweaks
and ShaguTweaks.DarkMode
and ShaguTweaks.DarkenFrame
end
local function ReapplyShaguDarkMode(plate)
if not plate or not plate:IsShown() then return end
if not ShaguDarkModeActive() then return end
-- Clear Shagu's one-time marker because this Blizzard plate may have been
-- recycled for another unit and had its textures reset.
plate.darkened = nil
-- Use ShaguTweaks' own implementation/configuration as the single source of
-- truth. Its blacklist already protects StatusBars, spell icons, portraits,
-- etc. from being incorrectly darkened.
ShaguTweaks.DarkenFrame(plate)
plate.darkened = true
end
local function QueueReapply(plate)
if not plate then return end
-- Immediate pass handles normal OnShow.
ReapplyShaguDarkMode(plate)
-- Blizzard may restore some nameplate texture state immediately after
-- OnShow. Do one delayed pass and then stop; no permanent scanner is needed.
pending[plate] = GetTime() + 0.08
end
if BNP.libnameplate then
table.insert(BNP.libnameplate.OnInit, function(plate)
QueueReapply(plate or this)
end)
table.insert(BNP.libnameplate.OnShow, function(plate)
QueueReapply(plate or this)
end)
end
local updater = CreateFrame("Frame")
updater:SetScript("OnUpdate", function()
if not next(pending) then return end
local now = GetTime()
local plate, due
for plate, due in pairs(pending) do
if now >= due then
pending[plate] = nil
ReapplyShaguDarkMode(plate)
end
end
end)
-177
View File
@@ -1,177 +0,0 @@
BNP = BNP or {}
local wanted = {
corruption = { "Corruption", "Verderbnis" },
agony = { "Curse of Agony", "Fluch der Pein" },
siphon = { "Siphon Life", "Lebensentzug" },
shadow_word_pain = { "Shadow Word: Pain", "Schattenwort: Schmerz" },
}
local function isWanted(name, list)
if not name then return false end
local lower = string.lower(name)
local i
for i = 1, table.getn(list) do
if lower == string.lower(list[i]) then return true end
end
return false
end
local function normalizeNumber(value)
if not value then return nil end
value = string.gsub(value, ",", ".")
return tonumber(value)
end
-- WoW 1.12 uses Lua 5.0. Use string.find captures instead of string.match.
local function durationFromLine(text)
if not text then return nil end
local lower = string.lower(text)
local _, _, num
-- German: 14,95 Sek. / 14.95 Sekunden
_, _, num = string.find(lower, "([0-9]+[%,%.]?[0-9]*)%s*sek")
if num then return normalizeNumber(num) end
-- English: 14.95 sec / seconds
_, _, num = string.find(lower, "([0-9]+[%,%.]?[0-9]*)%s*sec")
if num then return normalizeNumber(num) end
-- Generic short form: "over 14.95 s" / "14,95 s"
_, _, num = string.find(lower, "over%s+([0-9]+[%,%.]?[0-9]*)%s+s")
if num then return normalizeNumber(num) end
return nil
end
local function getTooltip()
local tooltip = getglobal("BNPSpellProbeTooltip")
if tooltip then return tooltip end
tooltip = CreateFrame("GameTooltip", "BNPSpellProbeTooltip", UIParent, "GameTooltipTemplate")
tooltip:SetOwner(WorldFrame, "ANCHOR_NONE")
return tooltip
end
local function scanSpell(slot)
local tooltip = getTooltip()
tooltip:ClearLines()
tooltip:SetOwner(WorldFrame, "ANCHOR_NONE")
tooltip:SetSpell(slot, BOOKTYPE_SPELL)
local lines = {}
local bestDuration = nil
local i
for i = 1, tooltip:NumLines() do
local fs = getglobal("BNPSpellProbeTooltipTextLeft" .. i)
local text = fs and fs:GetText()
if text and text ~= "" then
table.insert(lines, text)
local parsed = durationFromLine(text)
if parsed and parsed > 0 then
bestDuration = parsed
end
end
end
return bestDuration, lines
end
local function findSpell(wantedNames)
local slot = 1
local found = nil
while true do
local name, rank = GetSpellName(slot, BOOKTYPE_SPELL)
if not name then break end
if isWanted(name, wantedNames) then
found = { slot = slot, name = name, rank = rank }
-- keep scanning so the highest learned rank wins
end
slot = slot + 1
end
return found
end
BNP.spellbookDurations = BNP.spellbookDurations or {}
function BNP:RefreshSpellbookDurations()
local id
for id, names in pairs(wanted) do
local spell = findSpell(names)
if spell then
local ok, duration = pcall(scanSpell, spell.slot)
if ok and duration and duration > 0 then
BNP.spellbookDurations[id] = duration
BNP.spellbookDurations[string.lower(spell.name)] = duration
end
end
end
end
function BNP:GetSpellbookDurationForDef(def)
if not def then return nil end
BNP.spellbookDurations = BNP.spellbookDurations or {}
if def.key and BNP.spellbookDurations[def.key] then
return BNP.spellbookDurations[def.key]
end
if def.names then
local i
for i = 1, table.getn(def.names) do
local key = string.lower(def.names[i])
if BNP.spellbookDurations[key] then
return BNP.spellbookDurations[key]
end
end
-- Generic on-demand scan: works for every class, not only the original
-- Warlock/Priest probe list.
local spell = findSpell(def.names)
if spell then
local ok, duration = pcall(scanSpell, spell.slot)
if ok and duration and duration > 0 then
if def.key then BNP.spellbookDurations[def.key] = duration end
BNP.spellbookDurations[string.lower(spell.name)] = duration
return duration
end
end
end
return nil
end
function BNP:SpellbookProbe(key)
self:RefreshSpellbookDurations()
local keys = {}
if key and key ~= "" and wanted[key] then
table.insert(keys, key)
else
table.insert(keys, "corruption")
table.insert(keys, "agony")
table.insert(keys, "siphon")
table.insert(keys, "shadow_word_pain")
end
self:Print("=== Spellbook Tooltip Probe ===")
local k
for k = 1, table.getn(keys) do
local id = keys[k]
local spell = findSpell(wanted[id])
if not spell then
self:Print(id .. ": not found in spellbook")
else
local ok, duration, lines = pcall(scanSpell, spell.slot)
if not ok then
self:Print(spell.name .. ": tooltip scan ERROR: " .. tostring(duration))
else
self:Print(spell.name .. " " .. tostring(spell.rank or "") .. " | Slot " .. spell.slot .. " | Duration: " .. tostring(duration or "NOT DETECTED"))
local i
for i = 1, table.getn(lines) do
self:Print(" " .. i .. ": " .. lines[i])
end
end
end
end
end
-181
View File
@@ -1,181 +0,0 @@
BNP = BNP or {}
-- Tank Mode for SuperWoW GUID nameplates.
-- GREEN = hostile NPC currently targets the player.
-- RED = hostile NPC currently targets another unit.
-- Idle hostile NPCs keep their normal Blizzard color.
local GREEN_R, GREEN_G, GREEN_B = 0.00, 1.00, 0.00
local RED_R, RED_G, RED_B = 1.00, 0.00, 0.00
local playerGUID = nil
local function GetPlayerGUID()
local exists, guid = UnitExists("player")
if exists and guid then playerGUID = guid end
return playerGUID
end
local function GetPlateGUID(plate)
if not plate or not plate.GetName then return nil end
local guid = plate:GetName(1)
if guid and guid ~= "" then return guid end
return nil
end
local function GetTargetGUID(guid)
if not guid then return nil end
local exists, targetGUID = UnitExists(guid .. "target")
if exists then return targetGUID end
return nil
end
local function GetBar(plate)
return plate and (plate.healthbar or plate.healthBar) or nil
end
local function IsHostileNPC(guid)
if not guid or not UnitExists(guid) then return false end
if UnitIsPlayer(guid) then return false end
return UnitCanAttack("player", guid) and true or false
end
local function IsForeignTaggedGUID(guid)
if not guid or not UnitExists(guid) then return false end
if UnitCanAttack and not UnitCanAttack("player", guid) then return false end
if not UnitIsTapped or not UnitIsTapped(guid) then return false end
if UnitIsTappedByPlayer and UnitIsTappedByPlayer(guid) then return false end
return true
end
local function RestoreNormalColor(plate, bar)
-- Do not restore a cached RGB value. Outside an active Tank Mode state,
-- Blizzard owns the NPC healthbar color completely.
plate.BNPTankActive = nil
plate.BNPTankR = nil
plate.BNPTankG = nil
plate.BNPTankB = nil
end
local function ClearTankState(plate)
if not plate then return end
plate.BNPTankActive = nil
plate.BNPTankR = nil
plate.BNPTankG = nil
plate.BNPTankB = nil
end
local function ApplyTankColor(plate, bar, r, g, b)
bar:SetStatusBarColor(r, g, b, 1)
plate.BNPTankActive = true
plate.BNPTankR = r
plate.BNPTankG = g
plate.BNPTankB = b
end
local function EnforceTankColor(plate)
if not plate or not plate.BNPTankActive then return end
local bar = GetBar(plate)
if not bar or not plate.BNPTankR then return end
local r, g, b = bar:GetStatusBarColor()
if math.abs(r - plate.BNPTankR) > 0.01
or math.abs(g - plate.BNPTankG) > 0.01
or math.abs(b - plate.BNPTankB) > 0.01 then
bar:SetStatusBarColor(plate.BNPTankR, plate.BNPTankG, plate.BNPTankB, 1)
end
end
function BNP:UpdateTankModePlate(plate)
if not plate or not plate:IsShown() then return end
local bar = GetBar(plate)
if not bar then return end
if not self:IsTankModeEnabled() then
RestoreNormalColor(plate, bar)
return
end
local guid = GetPlateGUID(plate)
if not IsHostileNPC(guid) then
-- NPC reaction colors belong to Blizzard. Never restore a stale cached
-- Tank Mode color onto neutral/friendly/recycled nameplates.
ClearTankState(plate)
return
end
-- Foreign-tag grey has priority over Tank Mode. Previously Tank Mode wrote
-- RED every 0.10s while fullalpha.lua restored GREY every frame, producing
-- the visible grey/red flicker. Clear any cached tank color and let the
-- foreign-tag renderer own the bar until the mob is ours/untapped again.
if IsForeignTaggedGUID(guid) then
ClearTankState(plate)
return
end
local myGUID = GetPlayerGUID()
if not myGUID then return end
local targetGUID = GetTargetGUID(guid)
if targetGUID and targetGUID == myGUID then
ApplyTankColor(plate, bar, GREEN_R, GREEN_G, GREEN_B)
elseif targetGUID then
ApplyTankColor(plate, bar, RED_R, RED_G, RED_B)
else
RestoreNormalColor(plate, bar)
end
end
function BNP:RefreshTankMode()
local plate
for plate in pairs(self.plates or {}) do
if plate and plate:IsShown() then
self:UpdateTankModePlate(plate)
end
end
end
function BNP:UpdateTankMode()
self:RefreshTankMode()
end
function BNP:InstallTankMode()
if self.BNPTankModeInstalled then return end
if not self.libnameplate or not self.libnameplate.OnUpdate then return end
GetPlayerGUID()
table.insert(self.libnameplate.OnInit, function(plate)
local current = plate or this
if not current then return end
ClearTankState(current)
local bar = GetBar(current)
if bar and not current.BNPTankColorHooked then
local old = bar:GetScript("OnValueChanged")
bar:SetScript("OnValueChanged", function()
if old then old() end
EnforceTankColor(current)
end)
current.BNPTankColorHooked = true
end
end)
table.insert(self.libnameplate.OnShow, function(plate)
local current = plate or this
if not current then return end
ClearTankState(current)
end)
table.insert(self.libnameplate.OnUpdate, function(plate, elapsed)
local current = plate or this
if not current then return end
BNP:UpdateTankModePlate(current)
EnforceTankColor(current)
end)
self.BNPTankModeInstalled = true
end
-498
View File
@@ -1,498 +0,0 @@
BNP = BNP or {}
BNP.unknownDirectEvents = BNP.unknownDirectEvents or {}
-- Beta spell collector -------------------------------------------------------
-- Purpose: help testers report missing Turtle/custom spell IDs without
-- editing Lua files. It records unknown own cast IDs and unknown debuff IDs
-- that newly appear shortly after one of the player's casts.
--
-- It is intentionally diagnostic only: it never changes tracking/rendering.
BNP.unknownBeta = BNP.unknownBeta or {
entries = {},
order = {},
recentCasts = {},
seenByGUID = {},
activeUntil = 0,
}
local _, playerClass = UnitClass("player")
local CLASS_AURAS = {
WARLOCK = BNP.WarlockAuras,
PRIEST = BNP.PriestAuras,
WARRIOR = BNP.WarriorAuras,
ROGUE = BNP.RogueAuras,
HUNTER = BNP.HunterAuras,
MAGE = BNP.MageAuras,
DRUID = BNP.DruidAuras,
SHAMAN = BNP.ShamanAuras,
PALADIN = BNP.PaladinAuras,
}
local AURA_DEFS = CLASS_AURAS[playerClass] or {}
local function GetPlayerGUID()
local exists, guid = UnitExists("player")
if exists and guid then return guid end
return nil
end
local function GetUnitGUID(unit)
local exists, guid = UnitExists(unit)
if exists and guid then return guid end
return nil
end
local function IsExplicitKnownID(spellID)
if not spellID then return false end
-- Dark Harvest is handled by dedicated Warlock logic.
if spellID == 52552 then return true end
local i, j, def
for i = 1, table.getn(AURA_DEFS) do
def = AURA_DEFS[i]
if def.spellIDs then
for j = 1, table.getn(def.spellIDs) do
if def.spellIDs[j] == spellID then return true end
end
end
if def.durations and def.durations[spellID] then
return true
end
end
return false
end
local function SpellName(spellID)
if not spellID or not SpellInfo then return "?" end
local name = SpellInfo(spellID)
return name or "?"
end
local function AddEntry(kind, spellID, spellName, guid, trigger, confidence)
if not spellID then return end
local key = tostring(kind) .. ":" .. tostring(spellID)
local data = BNP.unknownBeta
local old = data.entries[key]
if old then
old.count = (old.count or 1) + 1
old.lastSeen = GetTime()
if confidence == "TARGET" then old.confidence = confidence end
if trigger and not old.trigger then old.trigger = trigger end
return
end
local entry = {
kind = kind,
spellID = spellID,
name = spellName or SpellName(spellID),
class = playerClass or "?",
guid = guid,
trigger = trigger,
confidence = confidence or "?",
count = 1,
firstSeen = GetTime(),
lastSeen = GetTime(),
}
data.entries[key] = entry
table.insert(data.order, key)
end
local function RememberCast(spellID, targetGUID, eventType)
local data = BNP.unknownBeta
local now = GetTime()
data.activeUntil = now + 8.0
local cast = {
time = now,
spellID = spellID,
name = SpellName(spellID),
targetGUID = targetGUID,
eventType = eventType,
}
table.insert(data.recentCasts, cast)
while table.getn(data.recentCasts) > 20 do
table.remove(data.recentCasts, 1)
end
-- Unknown own CAST/CHANNEL IDs are useful too. Some trap/proc systems expose
-- the effect as a separate cast event before the aura is seen.
if spellID and not IsExplicitKnownID(spellID) then
AddEntry("CAST", spellID, cast.name, targetGUID, nil, "OWN")
end
end
local function FindRecentTrigger(guid, now)
local casts = BNP.unknownBeta.recentCasts
local i
for i = table.getn(casts), 1, -1 do
local cast = casts[i]
local age = now - cast.time
if age > 6 then
break
end
if cast.targetGUID and guid and cast.targetGUID == guid then
return cast, "TARGET"
end
-- Short global fallback catches effects such as traps where the placement
-- cast may not carry the eventual victim GUID.
if age <= 1.5 then
return cast, "RECENT"
end
end
return nil, nil
end
local function ScanUnit(unit, guid, now)
if not unit or not guid then return end
local seen = BNP.unknownBeta.seenByGUID[guid]
if not seen then
seen = {}
BNP.unknownBeta.seenByGUID[guid] = seen
end
local current = {}
local i
for i = 1, 32 do
local texture, stacks, dtype, auraSpellID = UnitDebuff(unit, i)
if not texture then break end
if auraSpellID then
current[auraSpellID] = true
if not seen[auraSpellID] and not IsExplicitKnownID(auraSpellID) then
local trigger, confidence = FindRecentTrigger(guid, now)
if trigger then
AddEntry(
"AURA",
auraSpellID,
SpellName(auraSpellID),
guid,
tostring(trigger.name) .. " [" .. tostring(trigger.spellID) .. "]",
confidence
)
end
end
end
end
-- Keep only currently present aura IDs so a later re-application can be
-- detected again without growing this table forever.
BNP.unknownBeta.seenByGUID[guid] = current
end
-- Indirect Effect Audit: traps/procs may never expose a UnitDebuff ID.
BNP.unknownBeta.combat = BNP.unknownBeta.combat or {}
local EFFECT_WINDOW = 8.0
local function IsUsefulEffectLine(raw)
if not raw or raw == "" then return false end
local lower = string.lower(raw)
-- Ignore obvious player-action/noise lines.
if string.find(lower, "you cast ", 1, true) then return false end
if string.find(lower, "auto shot", 1, true) then return false end
if string.find(lower, "login", 1, true) then return false end
-- Keep lines that actually describe a harmful aura/effect or its periodic damage.
if string.find(lower, "afflicted by", 1, true) then return true end
if string.find(lower, "is afflicted", 1, true) then return true end
if string.find(lower, "is affected by", 1, true) then return true end
if string.find(lower, "suffers", 1, true) then return true end
if string.find(lower, "periodic", 1, true) then return true end
return false
end
local function AddCombatEffect(raw)
if not IsUsefulEffectLine(raw) then return end
local now=GetTime()
local casts=BNP.unknownBeta.recentCasts
local trigger=nil
local i
for i=table.getn(casts),1,-1 do
local c=casts[i]
if now-c.time > EFFECT_WINDOW then break end
if c.spellID ~= 75 then trigger=c break end
end
if not trigger then return end
if string.find(string.lower(raw),"auto shot",1,true) then return end
local key=tostring(trigger.spellID)..":"..raw
local old=BNP.unknownBeta.combat[key]
if old then old.count=old.count+1 return end
BNP.unknownBeta.combat[key]={
raw=raw, triggerID=trigger.spellID, triggerName=trigger.name, count=1
}
end
local combatFrame=CreateFrame("Frame")
local combatEvents={
"CHAT_MSG_COMBAT_SELF_HITS",
"CHAT_MSG_SPELL_SELF_DAMAGE",
"CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE",
"CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE",
}
local cei
for cei=1,table.getn(combatEvents) do combatFrame:RegisterEvent(combatEvents[cei]) end
combatFrame:SetScript("OnEvent",function() AddCombatEffect(arg1) end)
local castFrame = CreateFrame("Frame")
castFrame:RegisterEvent("UNIT_CASTEVENT")
castFrame:SetScript("OnEvent", function()
local playerGUID = GetPlayerGUID()
if not playerGUID or arg1 ~= playerGUID then return end
local eventType = arg3
if eventType ~= "CAST" and eventType ~= "CHANNEL" then return end
RememberCast(arg4, arg2, eventType)
end)
local scanner = CreateFrame("Frame")
local elapsed = 0
scanner:SetScript("OnUpdate", function()
elapsed = elapsed + arg1
if elapsed < 0.35 then return end
elapsed = 0
local now = GetTime()
if not BNP.unknownBeta.activeUntil or now > BNP.unknownBeta.activeUntil then
return
end
local targetGUID = GetUnitGUID("target")
if targetGUID then
ScanUnit("target", targetGUID, now)
end
local plate
for plate in pairs(BNP.plates) do
if plate:IsShown() and plate.GetName then
local token = plate:GetName(1)
if token then
local guid = GetUnitGUID(token)
if guid then
ScanUnit(token, guid, now)
end
end
end
end
end)
function BNP:PrintUnknownBeta(verbose)
local data = self.unknownBeta
self:Print("=== BNP Beta: Unknown Auras (" .. tostring(playerClass or "?") .. ") ===")
local auraCount = 0
local i
for i = 1, table.getn(data.order) do
local entry = data.entries[data.order[i]]
if entry and entry.kind == "AURA" then
auraCount = auraCount + 1
local line =
tostring(auraCount) ..
" | AURA | ID " .. tostring(entry.spellID) ..
" | " .. tostring(entry.name) ..
" | x" .. tostring(entry.count or 1)
if entry.confidence then
line = line .. " | " .. tostring(entry.confidence)
end
self:Print(line)
if entry.trigger then
self:Print(" Trigger: " .. tostring(entry.trigger))
end
end
end
local combatCount = 0
local ck
for ck in pairs(data.combat or {}) do combatCount = combatCount + 1 end
if combatCount > 0 then
self:Print("--- Indirect Effects ---")
local key, e
for key, e in pairs(data.combat) do
self:Print("EFFECT | Trigger " .. tostring(e.triggerName) .. " [" .. tostring(e.triggerID) .. "] | x" .. tostring(e.count))
self:Print(" " .. tostring(e.raw))
end
end
if auraCount == 0 and combatCount == 0 then
self:Print("No unknown negative auras/effects found since login.")
end
if verbose then
self:Print("--- Verbose: unknown player cast IDs ---")
local castCount = 0
for i = 1, table.getn(data.order) do
local entry = data.entries[data.order[i]]
if entry and entry.kind == "CAST" then
castCount = castCount + 1
self:Print(
tostring(castCount) ..
" | CAST | ID " .. tostring(entry.spellID) ..
" | " .. tostring(entry.name) ..
" | x" .. tostring(entry.count or 1)
)
end
end
if castCount == 0 then
self:Print("No unknown cast IDs.")
end
else
self:Print("A screenshot of this output is enough. Raw data: /bnp unknown verbose")
end
end
function BNP:ResetUnknownBeta()
self.unknownBeta.entries = {}
self.unknownBeta.order = {}
self.unknownBeta.recentCasts = {}
self.unknownBeta.seenByGUID = {}
self.unknownBeta.combat = {}
self.unknownBeta.activeUntil = 0
self:Print("Unknown spell/effect list cleared.")
end
-- Target Aura Probe ----------------------------------------------------------
-- Diagnostic helper for proc/talent auras that the normal unknown collector
-- may miss because they were already present in its seen-state.
function BNP:PrintTargetAuraProbe()
local exists, guid = UnitExists("target")
if not exists or not guid then
self:Print("Target Aura Probe: no target selected.")
return
end
self:Print("=== BNP Target Negative Auras ===")
self:Print("Target: " .. tostring(UnitName("target") or "?") .. " | GUID " .. tostring(guid))
local count = 0
local i
for i = 1, 32 do
local texture, stacks, dtype, auraSpellID = UnitDebuff("target", i)
if not texture then break end
count = count + 1
local name = auraSpellID and SpellName(auraSpellID) or "?"
self:Print(
tostring(count) ..
" | ID " .. tostring(auraSpellID or "?") ..
" | " .. tostring(name) ..
" | stacks " .. tostring(stacks or 0) ..
" | type " .. tostring(dtype or "?")
)
end
if count == 0 then
self:Print("No negative target auras found.")
end
end
-- Direct player spell collector --------------------------------------------
-- Captures successful player spell events that do not create a persistent
-- aura and therefore never appear in the normal unknown-aura collector.
local function GetLocalPlayerGUID()
local exists, guid = UnitExists("player")
if exists and guid then return guid end
return nil
end
local function RememberDirectEvent(eventType, spellID, targetGUID, extra)
if not spellID then return end
local name = nil
if SpellInfo then name = SpellInfo(spellID) end
name = name or ("Spell " .. tostring(spellID))
local key = tostring(eventType) .. ":" .. tostring(spellID)
local entry = BNP.unknownDirectEvents[key]
if not entry then
entry = {
eventType = eventType,
spellID = spellID,
name = name,
targetGUID = targetGUID,
count = 0,
extra = extra,
last = GetTime(),
}
BNP.unknownDirectEvents[key] = entry
end
entry.count = (entry.count or 0) + 1
entry.targetGUID = targetGUID or entry.targetGUID
entry.extra = extra or entry.extra
entry.last = GetTime()
end
local directFrame = CreateFrame("Frame")
directFrame:RegisterEvent("UNIT_CASTEVENT")
directFrame:SetScript("OnEvent", function()
local casterGUID = arg1
local targetGUID = arg2
local eventType = arg3
local spellID = arg4
local playerGUID = GetLocalPlayerGUID()
if not playerGUID or casterGUID ~= playerGUID then return end
if not spellID then return end
if eventType == "START" or eventType == "CAST" or eventType == "CHANNEL" then
RememberDirectEvent(eventType, spellID, targetGUID, arg5)
end
end)
function BNP:PrintUnknownDirectEvents()
local list = {}
local key, entry
for key, entry in pairs(self.unknownDirectEvents or {}) do
table.insert(list, entry)
end
table.sort(list, function(a, b)
return (a.last or 0) > (b.last or 0)
end)
if table.getn(list) == 0 then
self:Print("--- Direct Player Events: none captured ---")
return
end
self:Print("--- Direct Player Events ---")
local i, e
for i = 1, table.getn(list) do
e = list[i]
self:Print(
tostring(i) .. " | " ..
tostring(e.eventType) .. " | ID " ..
tostring(e.spellID) .. " | " ..
tostring(e.name) .. " | x" ..
tostring(e.count or 1)
)
end
end