功能更新堆叠
This commit is contained in:
220
Plates.lua
220
Plates.lua
@@ -447,9 +447,10 @@ local function HandleNamePlate(frame)
|
||||
original.name = original.name or r3
|
||||
original.level = original.level or r4
|
||||
|
||||
-- Shrink original frame to reduce game engine's anti-overlap stacking
|
||||
-- Original ~35px, lower = more overlap allowed, higher = more separation
|
||||
frame:SetHeight(14)
|
||||
-- Set parent to 1x1 to fully disable engine anti-overlap (ShaguPlates technique)
|
||||
-- Custom stacking in ResolveStacking() handles overlap instead
|
||||
frame:SetHeight(1)
|
||||
frame:SetWidth(1)
|
||||
|
||||
-- Create overlay
|
||||
local np = CreateFrame("Button", plateName, frame)
|
||||
@@ -539,6 +540,14 @@ local function HandleNamePlate(frame)
|
||||
healthText:SetTextColor(1, 1, 1, 1)
|
||||
np.healthText = healthText
|
||||
|
||||
-- Threat percentage text
|
||||
local threatPctText = health:CreateFontString(nil, "OVERLAY")
|
||||
threatPctText:SetFont(NP.GetFont(), Settings.healthFontSize, NP.GetFontOutline())
|
||||
threatPctText:SetPoint("CENTER", health, "CENTER", 0, 0)
|
||||
threatPctText:SetTextColor(1, 1, 1, 1)
|
||||
threatPctText:SetText("")
|
||||
np.threatPctText = threatPctText
|
||||
|
||||
-- Name text
|
||||
local name = np:CreateFontString(nil, "OVERLAY")
|
||||
name:SetFont(NP.GetFont(), Settings.nameFontSize, NP.GetFontOutline())
|
||||
@@ -680,7 +689,8 @@ local function HandleNamePlate(frame)
|
||||
local oldOnShow = frame:GetScript("OnShow")
|
||||
frame:SetScript("OnShow", function()
|
||||
if oldOnShow then oldOnShow() end
|
||||
this:SetHeight(14)
|
||||
this:SetHeight(1)
|
||||
this:SetWidth(1)
|
||||
local nameplate = this.nameplate
|
||||
if nameplate then
|
||||
Healthbar.ResetCache(nameplate)
|
||||
@@ -692,6 +702,10 @@ local function HandleNamePlate(frame)
|
||||
nameplate._lastManaShowing = nil
|
||||
nameplate._lastFriendly_resize = nil
|
||||
nameplate._lastAlpha = nil
|
||||
nameplate._stackY = nil
|
||||
nameplate._appliedOffY = nil
|
||||
nameplate.healthBG:ClearAllPoints()
|
||||
nameplate.healthBG:SetPoint("CENTER", nameplate, "CENTER", 0, Settings.nameplateYOffset or 0)
|
||||
if nameplate._defaultStrata then
|
||||
nameplate:SetFrameStrata(nameplate._defaultStrata)
|
||||
end
|
||||
@@ -813,9 +827,10 @@ local function UpdateNamePlate(frame)
|
||||
local original = np.original
|
||||
if not original or not original.healthbar then return end
|
||||
|
||||
-- Keep original frame small to reduce anti-overlap stacking jumps
|
||||
if frame:GetHeight() > 16 then
|
||||
frame:SetHeight(14)
|
||||
-- Keep parent 1x1 to disable engine anti-overlap entirely
|
||||
if frame:GetHeight() > 1 or frame:GetWidth() > 1 then
|
||||
frame:SetHeight(1)
|
||||
frame:SetWidth(1)
|
||||
end
|
||||
|
||||
-- Continuously enforce hiding of original elements
|
||||
@@ -1024,17 +1039,21 @@ local function UpdateNamePlate(frame)
|
||||
if isHostile and not isTapped then
|
||||
local threatColor = nil
|
||||
local mobGUID = unitstr
|
||||
local threatPctValue = nil
|
||||
local threatPctRole = NP.playerRole or "DPS"
|
||||
|
||||
if NanamiPlates_Threat then
|
||||
local hasData, playerHasAggro, otherName, otherPct = NanamiPlates_Threat.GetTWTankModeThreat(mobGUID, plateName)
|
||||
|
||||
if hasData then
|
||||
local role = NP.playerRole or "DPS"
|
||||
local role = threatPctRole
|
||||
if role == "TANK" then
|
||||
if playerHasAggro then
|
||||
threatColor = THREAT_COLORS.TANK.AGGRO
|
||||
threatPctValue = 100
|
||||
elseif otherPct and otherPct > 70 then
|
||||
threatColor = THREAT_COLORS.TANK.LOSING_AGGRO
|
||||
threatPctValue = otherPct
|
||||
elseif otherName and NP.TANK_CLASSES and NP.GetPlayerClassByName then
|
||||
local otherClass = NP.GetPlayerClassByName(otherName)
|
||||
if otherClass and NP.TANK_CLASSES[otherClass] then
|
||||
@@ -1042,16 +1061,61 @@ local function UpdateNamePlate(frame)
|
||||
else
|
||||
threatColor = THREAT_COLORS.TANK.NO_AGGRO
|
||||
end
|
||||
threatPctValue = otherPct or 0
|
||||
else
|
||||
threatColor = THREAT_COLORS.TANK.NO_AGGRO
|
||||
threatPctValue = otherPct or 0
|
||||
end
|
||||
else
|
||||
if playerHasAggro then
|
||||
threatColor = THREAT_COLORS.DPS.AGGRO
|
||||
threatPctValue = 100
|
||||
elseif otherPct and otherPct > 70 then
|
||||
threatColor = THREAT_COLORS.DPS.HIGH_THREAT
|
||||
threatPctValue = otherPct
|
||||
else
|
||||
threatColor = THREAT_COLORS.DPS.NO_AGGRO
|
||||
threatPctValue = otherPct or 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not hasData and NanamiDPS and NanamiDPS.ThreatEngine then
|
||||
local TE = NanamiDPS.ThreatEngine
|
||||
if TE.inCombat and unitstr then
|
||||
local data = TE:QueryUnitThreat(unitstr)
|
||||
if not data and plateName then
|
||||
local tk = TE.GetTargetKey and TE.GetTargetKey(unitstr)
|
||||
if tk then
|
||||
local t, isTk, pct = TE:QueryNameThreat(tk, UnitName("player"))
|
||||
if t > 0 then
|
||||
data = { pct = pct, isTanking = isTk, threat = t,
|
||||
tankThreat = 0, secondPct = 0, secondName = nil }
|
||||
end
|
||||
end
|
||||
end
|
||||
if data then
|
||||
local role = threatPctRole
|
||||
if role == "TANK" then
|
||||
if data.isTanking then
|
||||
threatColor = THREAT_COLORS.TANK.AGGRO
|
||||
threatPctValue = data.secondPct or 0
|
||||
else
|
||||
threatColor = THREAT_COLORS.TANK.NO_AGGRO
|
||||
threatPctValue = data.pct or 0
|
||||
end
|
||||
else
|
||||
if data.isTanking then
|
||||
threatColor = THREAT_COLORS.DPS.AGGRO
|
||||
threatPctValue = 100
|
||||
elseif data.pct and data.pct > 70 then
|
||||
threatColor = THREAT_COLORS.DPS.HIGH_THREAT
|
||||
threatPctValue = data.pct
|
||||
else
|
||||
threatColor = THREAT_COLORS.DPS.NO_AGGRO
|
||||
threatPctValue = data.pct or 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1064,6 +1128,7 @@ local function UpdateNamePlate(frame)
|
||||
if data and data.start and data.duration then
|
||||
if data.start + data.duration > GetTime() then
|
||||
threatColor = THREAT_COLORS.STUN
|
||||
threatPctValue = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
@@ -1073,6 +1138,22 @@ local function UpdateNamePlate(frame)
|
||||
if threatColor then
|
||||
barR, barG, barB = threatColor[1], threatColor[2], threatColor[3]
|
||||
end
|
||||
|
||||
if np.threatPctText then
|
||||
if threatPctValue and threatColor then
|
||||
np.threatPctText:SetText(string.format("%.0f%%", threatPctValue))
|
||||
np.threatPctText:SetTextColor(threatColor[1], threatColor[2], threatColor[3], 1)
|
||||
np.threatPctText:Show()
|
||||
else
|
||||
np.threatPctText:SetText("")
|
||||
np.threatPctText:Hide()
|
||||
end
|
||||
end
|
||||
else
|
||||
if np.threatPctText then
|
||||
np.threatPctText:SetText("")
|
||||
np.threatPctText:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
np.health:SetStatusBarColor(barR, barG, barB, 1)
|
||||
@@ -1286,6 +1367,126 @@ local function UpdateNamePlate(frame)
|
||||
end
|
||||
end
|
||||
|
||||
-- Nameplate vertical stacking resolution
|
||||
-- Engine anti-overlap is fully disabled (parent set to 1x1 like ShaguPlates),
|
||||
-- so we resolve overlaps ourselves with a stable algorithm.
|
||||
local stackPool = {}
|
||||
local stackN = 0
|
||||
local STACK_SMOOTH = 0.15
|
||||
|
||||
local function ResolveStacking()
|
||||
if not Settings.enableStacking then
|
||||
-- Stacking disabled: decay any residual offsets from when it was enabled
|
||||
for frame, np in pairs(registry) do
|
||||
if np._stackY and np._stackY ~= 0 then
|
||||
local new = np._stackY * (1 - STACK_SMOOTH * 3)
|
||||
if new > -0.5 and new < 0.5 then new = 0 end
|
||||
np._stackY = new
|
||||
np._appliedOffY = (Settings.nameplateYOffset or 0) + new
|
||||
np.healthBG:ClearAllPoints()
|
||||
np.healthBG:SetPoint("CENTER", np, "CENTER", 0, np._appliedOffY)
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
stackN = 0
|
||||
for frame, np in pairs(registry) do
|
||||
if frame:IsShown() and np:IsShown() then
|
||||
local cx, cy = frame:GetCenter()
|
||||
if cx and cy then
|
||||
stackN = stackN + 1
|
||||
if not stackPool[stackN] then stackPool[stackN] = {} end
|
||||
local e = stackPool[stackN]
|
||||
e.np = np
|
||||
e.cx = cx
|
||||
e.cy = cy
|
||||
e.name = np.plateName or ""
|
||||
e.stackTarget = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if stackN < 1 then return end
|
||||
|
||||
-- Single plate: decay any residual stacking offset
|
||||
if stackN < 2 then
|
||||
local np = stackPool[1].np
|
||||
if np._stackY and np._stackY ~= 0 then
|
||||
local new = np._stackY * (1 - STACK_SMOOTH * 3)
|
||||
if new > -0.5 and new < 0.5 then new = 0 end
|
||||
np._stackY = new
|
||||
np._appliedOffY = (Settings.nameplateYOffset or 0) + new
|
||||
np.healthBG:ClearAllPoints()
|
||||
np.healthBG:SetPoint("CENTER", np, "CENTER", 0, np._appliedOffY)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Stable bubble sort: Y descending (higher on screen first), name as tiebreaker
|
||||
for i = stackN, 2, -1 do
|
||||
for j = 1, i - 1 do
|
||||
local a = stackPool[j]
|
||||
local b = stackPool[j + 1]
|
||||
local ay = math_floor(a.cy)
|
||||
local by = math_floor(b.cy)
|
||||
if by > ay or (by == ay and b.name < a.name) then
|
||||
stackPool[j] = b
|
||||
stackPool[j + 1] = a
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Resolve vertical overlaps: push lower plates down when horizontally close
|
||||
local minSep = (Settings.healthbarHeight or 12) + BORDER_PAD * 2
|
||||
+ (Settings.nameFontSize or 9) + 6
|
||||
local overlapW = (Settings.healthbarWidth or 120) * 0.8
|
||||
|
||||
for i = 1, stackN do
|
||||
for j = i + 1, stackN do
|
||||
local a = stackPool[i]
|
||||
local b = stackPool[j]
|
||||
local dx = a.cx - b.cx
|
||||
if dx < 0 then dx = -dx end
|
||||
if dx < overlapW then
|
||||
local ya = a.cy + a.stackTarget
|
||||
local yb = b.cy + b.stackTarget
|
||||
local gap = ya - yb
|
||||
if gap < minSep then
|
||||
b.stackTarget = b.stackTarget - (minSep - gap)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Apply stacking offsets with smooth convergence
|
||||
for i = 1, stackN do
|
||||
local e = stackPool[i]
|
||||
local np = e.np
|
||||
|
||||
local curStack = np._stackY or 0
|
||||
local tarStack = e.stackTarget
|
||||
local d = tarStack - curStack
|
||||
local newStack
|
||||
if d > -0.5 and d < 0.5 then
|
||||
newStack = tarStack
|
||||
else
|
||||
newStack = curStack + d * STACK_SMOOTH
|
||||
end
|
||||
if newStack > -0.3 and newStack < 0.3 then newStack = 0 end
|
||||
np._stackY = newStack
|
||||
|
||||
local offY = (Settings.nameplateYOffset or 0) + newStack
|
||||
|
||||
local pY = np._appliedOffY or (Settings.nameplateYOffset or 0)
|
||||
if (offY - pY) > 0.5 or (offY - pY) < -0.5 then
|
||||
np._appliedOffY = offY
|
||||
np.healthBG:ClearAllPoints()
|
||||
np.healthBG:SetPoint("CENTER", np, "CENTER", 0, offY)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Main OnUpdate loop
|
||||
local scanThrottle = 0
|
||||
local SCAN_INTERVAL = 0.1
|
||||
@@ -1312,6 +1513,9 @@ local function OnUpdate()
|
||||
end
|
||||
end
|
||||
|
||||
-- Resolve stacking overlaps and smooth position jitter
|
||||
ResolveStacking()
|
||||
|
||||
-- Cleanup debuff timers
|
||||
if NanamiPlates_Auras then
|
||||
NanamiPlates_Auras:CleanupTimers()
|
||||
|
||||
Reference in New Issue
Block a user