From 77154b7b117709d28654a475dae16b74acfdd56d Mon Sep 17 00:00:00 2001 From: Relationship <139162359+Relationship-OctoWoW@users.noreply.github.com> Date: Fri, 10 Jul 2026 09:44:50 +0100 Subject: [PATCH] Add files via upload --- RelationshipsThreatPlates.toc | 2 +- RelationshipsThreatPlates_Core.lua | 2 +- RelationshipsThreatPlates_GUI.lua | 32 +++++++- RelationshipsThreatPlates_Nameplates.lua | 76 ++++++++++------- RelationshipsThreatPlates_Threat.lua | 95 +++++++++------------- RelationshipsThreatPlates_ThreatEngine.lua | 27 +++++- 6 files changed, 141 insertions(+), 93 deletions(-) diff --git a/RelationshipsThreatPlates.toc b/RelationshipsThreatPlates.toc index b6442b3..1900028 100644 --- a/RelationshipsThreatPlates.toc +++ b/RelationshipsThreatPlates.toc @@ -2,7 +2,7 @@ ## Title: RelationshipsThreatPlates - OctoWoW ## Notes: OctoWoW-compatible RelationshipsThreatPlates ## Author: Packaged By Relationship -## Version: 1.2 +## Version: 1.3 ## SavedVariables: RelationshipsThreatPlatesDB ## X-Website: https://github.com/Relationship-OctoWoW/RelationshipsThreatPlates ## X-GitHub: https://github.com/Relationship-OctoWoW/RelationshipsThreatPlates diff --git a/RelationshipsThreatPlates_Core.lua b/RelationshipsThreatPlates_Core.lua index b734315..86cd225 100644 --- a/RelationshipsThreatPlates_Core.lua +++ b/RelationshipsThreatPlates_Core.lua @@ -12,7 +12,7 @@ addon.MAJOR = 1 addon.MINOR = 1 -- Version string -TP_VERSION = "2.4.0" +TP_VERSION = "2.5.0" TP_ADDON_NAME = "RelationshipsThreatPlates" -- ================================================================== diff --git a/RelationshipsThreatPlates_GUI.lua b/RelationshipsThreatPlates_GUI.lua index e0a2930..390aae5 100644 --- a/RelationshipsThreatPlates_GUI.lua +++ b/RelationshipsThreatPlates_GUI.lua @@ -44,6 +44,34 @@ local FONT_SIZE = 10 -- Widget references for refresh local widgets = {} +-- Only settings that change the visual STRUCTURE of a bar need a full +-- RecreateAll (which tears every bar down and re-attaches on the next +-- scan). Everything else — filters, modes, alpha, colours — just needs +-- a soft UpdateAll, which is far less likely to leave plates without +-- bars while pooled nameplate frames are hidden. +local NEEDS_RECREATE = { + barHeight = true, + barWidthScale= true, + barOffsetY = true, + barOffsetX = true, + glowEnabled = true, + glowAsShadow = true, + glowSize = true, + showSpark = true, + showFillBG = true, + showText = true, + fontOutline = true, + fontSize = true, +} + +local function ApplySettingChange(configKey) + if NEEDS_RECREATE[configKey] then + addon.Nameplates:RecreateAll() + else + addon.Nameplates:UpdateAll() + end +end + -- ================================================================== -- Helper: safely set backdrop on a frame -- ================================================================== @@ -302,7 +330,7 @@ function addon.GUI:BuildSettings(parent) else this.checkMark:Hide() end - addon.Nameplates:RecreateAll() + ApplySettingChange(configKey) end) local label = parent:CreateFontString(nil, "OVERLAY") @@ -393,7 +421,7 @@ function addon.GUI:BuildSettings(parent) else valText:SetText(math.floor(val + 0.5) .. suffix) end - addon.Nameplates:RecreateAll() + ApplySettingChange(configKey) end) y = y + 24 diff --git a/RelationshipsThreatPlates_Nameplates.lua b/RelationshipsThreatPlates_Nameplates.lua index e05e446..90aeb4d 100644 --- a/RelationshipsThreatPlates_Nameplates.lua +++ b/RelationshipsThreatPlates_Nameplates.lua @@ -390,21 +390,24 @@ function addon.Nameplates:ScanForNameplates() local nameplate = children[i] if nameplate and not knownPlates[nameplate] then - -- Quick visibility filter - don't scan invisible frames + -- Only mark a plate as "known" once we have actually + -- attached a threat bar to it. Vanilla recycles nameplate + -- frames from a pool: a currently-invisible plate frame + -- can (and usually will) reappear later for another mob. + -- If we flagged it known while invisible, we would never + -- attach a bar when it became visible again — that is the + -- root cause of "plates stop appearing after changing a + -- setting" (RecreateAll clears knownPlates, then this + -- scan runs while some pooled plates are hidden). if nameplate:IsVisible() then - knownPlates[nameplate] = true - - -- Check if this frame is a nameplate local healthBar, nameText = self:IdentifyNameplate(nameplate) - if healthBar then + knownPlates[nameplate] = true self:AttachThreatBar(nameplate, healthBar, nameText) end - else - -- Mark as known even if invisible; we'll handle - -- visibility in the update loop - knownPlates[nameplate] = true end + -- Invisible plates are intentionally left un-flagged so + -- the periodic scan will re-check them once they show. end end end @@ -894,26 +897,34 @@ function addon.Nameplates:ApplyThreatToPlate(data, pct, situation) data.container:Show() - -- Smooth the percentage + -- Preserve the RAW threat percent (may exceed 100 when the player + -- is the top threat holder and well ahead of the runner-up). The + -- text label shows this uncapped value so the user can see e.g. + -- "185%" for a tank comfortably holding threat. The bar fill + -- itself is clamped to 0-100 because the statusbar range is fixed. + local rawPct = pct or 0 + + -- Smooth the percentage (bar fill only — clamp to 0..100) local oldPct = data.lastPct or 0 + local barPct = math.min(100, math.max(0, rawPct)) local smoothPct - -- Snap instantly when jumping to full aggro (100%) or dropping to 0 + -- Snap instantly when jumping to full aggro or dropping to 0 -- (mob switched target). Smooth intermediate transitions moderately. - if pct == 0 or pct >= 99.5 or math.abs(pct - oldPct) > 40 then - smoothPct = pct + if barPct == 0 or barPct >= 99.5 or math.abs(barPct - oldPct) > 40 then + smoothPct = barPct else - smoothPct = oldPct + (pct - oldPct) * 0.6 + smoothPct = oldPct + (barPct - oldPct) * 0.6 end data.lastPct = smoothPct smoothPct = math.min(100, math.max(0, smoothPct)) - pct = math.min(100, math.max(0, pct)) - -- Update bar value + -- Update bar value (0-100 range) data.bar:SetValue(smoothPct) - -- Update bar colour (smooth gradient) - local r, g, b = addon.Media.GetThreatColour(pct) + -- Update bar colour (smooth gradient) — driven by barPct so colours + -- top out at aggro red even if rawPct exceeds 100. + local r, g, b = addon.Media.GetThreatColour(barPct) data.bar:SetStatusBarColor(r, g, b, RelationshipsThreatPlatesDB.barAlpha or 1) -- Update statusbar texture vertex colour @@ -948,7 +959,7 @@ function addon.Nameplates:ApplyThreatToPlate(data, pct, situation) end end - -- Update text + -- Update text — uses rawPct so values above 100% are shown as-is. if data.text then local textStr if RelationshipsThreatPlatesDB.tankMode and data.plateName then @@ -958,26 +969,29 @@ function addon.Nameplates:ApplyThreatToPlate(data, pct, situation) textStr = addon.Threat:GetTankDisplayForMob(data.plateName) if not textStr or textStr == "--" then -- Fall back to % when no absolute values are available. - textStr = math.floor(pct + 0.5) .. "%" + textStr = math.floor(rawPct + 0.5) .. "%" end else - textStr = math.floor(pct + 0.5) .. "%" + textStr = math.floor(rawPct + 0.5) .. "%" end data.text:SetText(textStr) - if pct >= 90 then + if rawPct >= 100 then + -- Aggro / over-aggro: bright red text so the top holder + -- stands out even when the bar itself is pegged at full. data.text:SetTextColor(1, 0.2, 0.2, 1) - elseif pct >= 60 then + elseif rawPct >= 60 then data.text:SetTextColor(1, 0.9, 0.8, 1) else data.text:SetTextColor(1, 1, 1, 1) end end - -- Update threat glow + -- Update threat glow (drive by clamped barPct so it caps at max intensity) if data.threatGlow then - if pct >= 80 then - local intensity = (pct - 80) / 20 + if barPct >= 80 then + local intensity = (barPct - 80) / 20 + if intensity > 1 then intensity = 1 end data.threatGlow:SetVertexColor(0.9, 0, 0, 0.6 * intensity) data.threatGlow:Show() else @@ -1213,7 +1227,7 @@ function addon.Nameplates:UpdateAllPlates() -- If a group member is in combat, show a default -- threat bar. The mob is fighting someone. if groupInCombat then - self:ApplyThreatToPlate(data, 100, 3) + self:ApplyThreatToPlate(data, 20, 0) -- was 100,3: showing full aggro for any party-combat mob with no threat data was misleading else data.container:Hide() end @@ -1232,7 +1246,7 @@ function addon.Nameplates:UpdateAllPlates() if pct > 0 then self:ApplyThreatToPlate(data, pct, sit or 0) else - self:ApplyThreatToPlate(data, 100, 3) + self:ApplyThreatToPlate(data, 20, 0) -- was 100,3: showing full aggro for any party-combat mob with no threat data was misleading end else -- Grace period expired and health still at 100%. @@ -1265,7 +1279,7 @@ function addon.Nameplates:UpdateAllPlates() if not data.combatDetectedAt then data.combatDetectedAt = now end - self:ApplyThreatToPlate(data, 100, 3) + self:ApplyThreatToPlate(data, 20, 0) -- was 100,3: showing full aggro for any party-combat mob with no threat data was misleading else -- Can't confirm mob is attacking anyone. -- It might be idle. Don't show bar. @@ -1307,7 +1321,7 @@ function addon.Nameplates:UpdateAllPlates() -- In combat but no threat data — mob is being -- attacked by someone we can't track. if groupInCombat then - self:ApplyThreatToPlate(data, 100, 3) + self:ApplyThreatToPlate(data, 20, 0) -- was 100,3: showing full aggro for any party-combat mob with no threat data was misleading else data.container:Hide() end @@ -1316,7 +1330,7 @@ function addon.Nameplates:UpdateAllPlates() -- In combat (health dropping) but no ThreatEngine data. -- Show default high-threat bar. if groupInCombat then - self:ApplyThreatToPlate(data, 100, 3) + self:ApplyThreatToPlate(data, 20, 0) -- was 100,3: showing full aggro for any party-combat mob with no threat data was misleading else data.container:Hide() end diff --git a/RelationshipsThreatPlates_Threat.lua b/RelationshipsThreatPlates_Threat.lua index 7575bc5..0852d1e 100644 --- a/RelationshipsThreatPlates_Threat.lua +++ b/RelationshipsThreatPlates_Threat.lua @@ -420,61 +420,33 @@ function addon.Threat:GetThreatPercent(unit) -- If the mob is targeting an outsider -> show player's threat -- Otherwise -> show the best (highest) threat from any member + -- The threat bar on a nameplate is always displayed from the + -- PLAYER's point of view: "how close am I to pulling aggro + -- off whoever currently has it?". Showing another group + -- member's percentage would incorrectly make every mob a + -- party member is tanking read as 100% — that is the bug + -- users describe as "snap causes all party-combat enemies + -- to show 100%". So: always return the player's own value + -- here. If the player has no threat on this mob, return 0 + -- so the bar hides. + if mobTargetName == playerName then -- Mob is attacking the player - show player's own threat return playerPct, playerSit elseif mobTargetName and partyNames[mobTargetName] then - -- Mob is attacking a group member - show that member's threat - -- Look up that member's threat from the group scan - local memberUnit = partyNames[mobTargetName] - local memberPct = nil - local memberSit = nil - - if hasUnitDetailedThreatSituation then - local _, status, _, rawPct = SafeCall5(UnitDetailedThreatSituation, memberUnit, unit) - if rawPct then - memberPct = math.min(100, math.max(0, rawPct)) - memberSit = status or 0 - end - end - - if not memberPct and hasGetThreatPercent then - local pct = SafeCall2(GetThreatPercent, memberUnit, unit) - if pct then - memberPct = math.min(100, math.max(0, pct)) - if hasUnitThreatSituation then - memberSit = SafeCall2(UnitThreatSituation, memberUnit, unit) or 0 - else - if memberPct >= 100 then memberSit = 3 - elseif memberPct >= 60 then memberSit = 2 - elseif memberPct >= 30 then memberSit = 1 - else memberSit = 0 end - end - end - end - - if not memberPct and hasUnitThreatSituation then - memberSit = SafeCall2(UnitThreatSituation, memberUnit, unit) or 0 - if memberSit == 0 then memberPct = 0 - elseif memberSit == 1 then memberPct = 25 - elseif memberSit == 2 then memberPct = 60 - elseif memberSit == 3 then memberPct = 100 end - end - - if memberPct then - return memberPct, memberSit or 0 - end - - -- Fallback: can't get specific member data, use best - return bestPct, bestSit - else - -- Mob targets an outsider, or we can't see its target. - -- Show player's threat if they have any, otherwise the - -- best group threat. + -- Mob is attacking a group member. Show the player's + -- own relative threat, not the party member's. if playerPct > 0 then return playerPct, playerSit end - return bestPct, bestSit + return 0, 0 + else + -- Mob targets an outsider, or we can't see its target. + -- Show player's own threat only. + if playerPct > 0 then + return playerPct, playerSit + end + return 0, 0 end end @@ -600,13 +572,18 @@ function addon.Threat:GetThreatPercent(unit) threatPct = 100 situation = 3 elseif partyNames[mobTargetName] then - -- Mob is targeting a PARTY/RAID member. - -- That member has aggro - show high threat. - -- This is the KEY fix: previously showed only 60%/30% - -- for mobs targeting group members. Now shows 100% - -- because that group member IS the one being attacked. - threatPct = 100 - situation = 3 + -- Mob is targeting a PARTY/RAID member. The party member + -- has aggro, NOT the player. Showing 100% here would + -- incorrectly light every mob in party combat up as full + -- aggro on the player. Show a low estimate instead — the + -- player is not in the aggro seat. + if UnitIsUnit("target", unit) then + threatPct = 20 + situation = 0 + else + threatPct = 0 + situation = 0 + end else -- Mob is targeting someone outside the party -- (another player, a pet, etc.) @@ -663,7 +640,13 @@ function addon.Threat:UpdateUnitThreat(unit) and mobTargetName and playerName and mobTargetName == playerName then local ok_pc, pc = pcall(UnitAffectingCombat, "player") if ok_pc and pc then - pct = 100 + -- Player is being targeted, so they have aggro. Force + -- pct to at least 100 without clobbering a real value + -- above 100 (which happens when the player is top and + -- well ahead of the second-highest threat holder). + if not pct or pct < 100 then + pct = 100 + end situation = 3 targetSnapped = true end diff --git a/RelationshipsThreatPlates_ThreatEngine.lua b/RelationshipsThreatPlates_ThreatEngine.lua index 591f836..ef36777 100644 --- a/RelationshipsThreatPlates_ThreatEngine.lua +++ b/RelationshipsThreatPlates_ThreatEngine.lua @@ -1298,8 +1298,31 @@ function addon.ThreatEngine:GetThreatOnTarget(mobName) return 0, 0, 0, 0, false, nil, 0 end - -- Calculate the player's percentage relative to top threat - local pct = (playerThreat / topThreat) * 100 + -- Calculate the player's threat percentage. + -- + -- Two-mode display so the number matches user intuition: + -- * If the player IS the current top-threat holder, show how far + -- ahead they are of the runner-up: pct = playerThreat/second * 100. + -- A tank comfortably tanking a mob will read something like 120% + -- or 200% — anything > 100 means "you are above the next player". + -- * If the player is NOT top, show their share of the top holder's + -- threat: pct = playerThreat/top * 100. This is 0-100 and answers + -- "how close am I to pulling aggro off the current top holder". + -- + -- This lets the plate show > 100% for the actual threat leader + -- instead of clamping everyone with aggro to a flat 100%. + local pct + if topPlayer == playerName then + if secondThreat > 0 then + pct = (playerThreat / secondThreat) * 100 + else + pct = 100 + end + if pct < 100 then pct = 100 end -- safety: top holder never below 100 + else + pct = (playerThreat / topThreat) * 100 + if pct > 100 then pct = 100 end -- non-top can't exceed the leader + end -- Determine threat situation based on the player's threat local sit = 0