Files
BlizzNameplatesPlus/modules/combopoints.lua
T
2026-08-18 20:56:43 +02:00

300 lines
7.6 KiB
Lua

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