功能更新堆叠

This commit is contained in:
rucky
2026-03-25 00:57:17 +08:00
parent 017e37a365
commit b3cd6fbfc7
5 changed files with 256 additions and 8 deletions

View File

@@ -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()