4 Commits

Author SHA1 Message Date
Relationship 16705f72ac Add files via upload 2026-07-08 18:24:21 +01:00
Relationship d0a77fe622 Update RelationshipsThreatPlates.toc 2026-07-04 03:37:43 +01:00
Relationship b7e0084a27 Update RelationshipsThreatPlates_Core.lua 2026-07-04 02:26:24 +01:00
Relationship 4bb8243357 Update README.md 2026-07-03 07:59:14 +01:00
7 changed files with 195 additions and 27 deletions
-10
View File
@@ -80,13 +80,3 @@ ThreatPlates uses a three-tier threat detection system:
* Move the RelationshipsThreatPlates folder to your Interface/AddOns/ directory * Move the RelationshipsThreatPlates folder to your Interface/AddOns/ directory
*Restart WoW or reload the UI (/console reloadui) *Restart WoW or reload the UI (/console reloadui)
*Type /tp to configure *Type /tp to configure
== Version History ==
* v1.1.0
* Fixed Lua 5.0/1.12 parser issues (no varargs, no string.match)
* Fixed bare format/floor globals (use string.format/math.floor)
* Fixed CheckButton SetBackdrop (use Frame + textures instead)
* Added pcall() safety for nameplate child scanning
* Cached GetChildren/GetRegions calls for performance
* Three-tier threat detection with fallback heuristics
+1 -1
View File
@@ -2,7 +2,7 @@
## Title: RelationshipsThreatPlates - OctoWoW ## Title: RelationshipsThreatPlates - OctoWoW
## Notes: OctoWoW-compatible RelationshipsThreatPlates ## Notes: OctoWoW-compatible RelationshipsThreatPlates
## Author: Packaged By Relationship ## Author: Packaged By Relationship
## Version: 1.0 ## Version: 1.2
## SavedVariables: RelationshipsThreatPlatesDB ## SavedVariables: RelationshipsThreatPlatesDB
## X-Website: https://github.com/Relationship-OctoWoW/RelationshipsThreatPlates ## X-Website: https://github.com/Relationship-OctoWoW/RelationshipsThreatPlates
## X-GitHub: https://github.com/Relationship-OctoWoW/RelationshipsThreatPlates ## X-GitHub: https://github.com/Relationship-OctoWoW/RelationshipsThreatPlates
+4 -2
View File
@@ -12,7 +12,7 @@ addon.MAJOR = 1
addon.MINOR = 1 addon.MINOR = 1
-- Version string -- Version string
TP_VERSION = "2.3.1" TP_VERSION = "2.4.0"
TP_ADDON_NAME = "RelationshipsThreatPlates" TP_ADDON_NAME = "RelationshipsThreatPlates"
-- ================================================================== -- ==================================================================
@@ -21,9 +21,11 @@ TP_ADDON_NAME = "RelationshipsThreatPlates"
local DEFAULT_CONFIG = { local DEFAULT_CONFIG = {
enabled = true, enabled = true,
showText = true, showText = true,
tankMode = false,
targetSnap100 = true,
barHeight = 6, barHeight = 6,
barAlpha = 0.5, barAlpha = 0.5,
barOffsetY = 1, barOffsetY = 24,
barOffsetX = -17.8, barOffsetX = -17.8,
barWidthScale = 0.8, barWidthScale = 0.8,
fontSize = 9, fontSize = 9,
+45 -3
View File
@@ -341,7 +341,7 @@ function addon.GUI:BuildSettings(parent)
local slider = CreateFrame("Slider", "RelationshipsThreatPlatesSlider_" .. configKey, parent, "OptionsSliderTemplate") local slider = CreateFrame("Slider", "RelationshipsThreatPlatesSlider_" .. configKey, parent, "OptionsSliderTemplate")
slider:SetWidth(W - 60) slider:SetWidth(W - 60)
slider:SetHeight(16) slider:SetHeight(10)
slider:SetPoint("TOPLEFT", parent, "TOPLEFT", MARGIN, -y) slider:SetPoint("TOPLEFT", parent, "TOPLEFT", MARGIN, -y)
slider:SetOrientation("HORIZONTAL") slider:SetOrientation("HORIZONTAL")
slider:SetMinMaxValues(minVal, maxVal) slider:SetMinMaxValues(minVal, maxVal)
@@ -364,6 +364,8 @@ function addon.GUI:BuildSettings(parent)
if thumbTexture then if thumbTexture then
thumbTexture:SetTexture(C.SOLID) thumbTexture:SetTexture(C.SOLID)
thumbTexture:SetVertexColor(unpack(C.ACCENT)) thumbTexture:SetVertexColor(unpack(C.ACCENT))
thumbTexture:SetWidth(8)
thumbTexture:SetHeight(14)
end end
local lowText = getglobal(sliderName .. "Low") local lowText = getglobal(sliderName .. "Low")
@@ -403,7 +405,9 @@ function addon.GUI:BuildSettings(parent)
-- ============================================================== -- ==============================================================
SectionLabel("General") SectionLabel("General")
CheckBox("Enable Threat Plates", "enabled", "Show threat bars above nameplates") CheckBox("Enable Threat Plates", "enabled", "Show threat bars above nameplates")
CheckBox("Show Threat Text", "showText", "Display percentage in the bar") CheckBox("Show Threat Text", "showText", "Display value in the bar")
CheckBox("Tank Mode", "tankMode", "Show raw threat lead over 2nd place instead of %")
CheckBox("Snap to 100% when Targeted", "targetSnap100", "Force 100% while a mob is focused on you")
CheckBox("Smooth Colours", "smoothColors", "Gradient colour transition") CheckBox("Smooth Colours", "smoothColors", "Gradient colour transition")
-- ============================================================== -- ==============================================================
@@ -412,7 +416,7 @@ function addon.GUI:BuildSettings(parent)
SectionLabel("Appearance") SectionLabel("Appearance")
Slider("Bar Height", "barHeight", 2, 20, 1, "px") Slider("Bar Height", "barHeight", 2, 20, 1, "px")
Slider("Bar Width Scale", "barWidthScale", 0.3, 1.0, 0.05, "") Slider("Bar Width Scale", "barWidthScale", 0.3, 1.0, 0.05, "")
Slider("Bar Offset Y", "barOffsetY", 0, 20, 1, "px") Slider("Bar Offset Y", "barOffsetY", 0, 50, 1, "px")
Slider("Bar Offset X", "barOffsetX", -50, 50, 0.1, "px") Slider("Bar Offset X", "barOffsetX", -50, 50, 0.1, "px")
Slider("Bar Alpha", "barAlpha", 0.1, 1.0, 0.1, "") Slider("Bar Alpha", "barAlpha", 0.1, 1.0, 0.1, "")
Slider("Font Size", "fontSize", 6, 16, 1, "pt") Slider("Font Size", "fontSize", 6, 16, 1, "pt")
@@ -437,6 +441,44 @@ function addon.GUI:BuildSettings(parent)
CheckBox("Show on Neutral", "showOnNeutral", "Show threat bars on neutral units") CheckBox("Show on Neutral", "showOnNeutral", "Show threat bars on neutral units")
CheckBox("Show on Friendly", "showOnFriendly", "Show threat bars on friendly units") CheckBox("Show on Friendly", "showOnFriendly", "Show threat bars on friendly units")
-- ==============================================================
-- Reset button at bottom
-- ==============================================================
y = y + 10
local resetBtn = CreateFrame("Button", nil, parent)
resetBtn:SetWidth(W - MARGIN * 2)
resetBtn:SetHeight(22)
resetBtn:SetPoint("TOPLEFT", parent, "TOPLEFT", MARGIN, -y)
SafeSetBackdrop(resetBtn, {
bgFile = C.SOLID,
edgeFile = C.SOLID,
tile = false, tileSize = 0,
edgeSize = 1,
})
resetBtn:SetBackdropColor(unpack(C.BUTTON_BG))
resetBtn:SetBackdropBorderColor(unpack(C.BORDER))
local resetText = resetBtn:CreateFontString(nil, "OVERLAY")
resetText:SetFont(FONT_NORMAL, FONT_SIZE, "OUTLINE")
resetText:SetPoint("CENTER", resetBtn, "CENTER", 0, 0)
resetText:SetTextColor(unpack(C.BUTTON_TEXT))
resetText:SetText("Reset to Defaults")
resetBtn:SetFontString(resetText)
resetBtn:SetScript("OnEnter", function()
this:SetBackdropColor(unpack(C.BUTTON_HOVER))
end)
resetBtn:SetScript("OnLeave", function()
this:SetBackdropColor(unpack(C.BUTTON_BG))
end)
resetBtn:SetScript("OnClick", function()
SlashCmdList["THREATPLATES"]("reset")
if addon.Nameplates and addon.Nameplates.RecreateAll then
addon.Nameplates:RecreateAll()
end
end)
y = y + 22 + 8
-- Return total content height for scroll setup -- Return total content height for scroll setup
return y return y
end end
+19 -5
View File
@@ -897,10 +897,12 @@ function addon.Nameplates:ApplyThreatToPlate(data, pct, situation)
-- Smooth the percentage -- Smooth the percentage
local oldPct = data.lastPct or 0 local oldPct = data.lastPct or 0
local smoothPct local smoothPct
if pct == 0 then -- Snap instantly when jumping to full aggro (100%) or dropping to 0
smoothPct = 0 -- (mob switched target). Smooth intermediate transitions moderately.
if pct == 0 or pct >= 99.5 or math.abs(pct - oldPct) > 40 then
smoothPct = pct
else else
smoothPct = oldPct + (pct - oldPct) * 0.3 smoothPct = oldPct + (pct - oldPct) * 0.6
end end
data.lastPct = smoothPct data.lastPct = smoothPct
@@ -948,8 +950,20 @@ function addon.Nameplates:ApplyThreatToPlate(data, pct, situation)
-- Update text -- Update text
if data.text then if data.text then
local displayPct = math.floor(pct + 0.5) local textStr
data.text:SetText(displayPct .. "%") if RelationshipsThreatPlatesDB.tankMode and data.plateName then
-- Tank mode: show raw threat lead over the 2nd-highest player
-- (positive = you have that much lead as top-threat holder;
-- negative = you're that much behind the current top).
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) .. "%"
end
else
textStr = math.floor(pct + 0.5) .. "%"
end
data.text:SetText(textStr)
if pct >= 90 then if pct >= 90 then
data.text:SetTextColor(1, 0.2, 0.2, 1) data.text:SetTextColor(1, 0.2, 0.2, 1)
+117 -1
View File
@@ -57,7 +57,11 @@ local partyNames = {}
local partyUnits = {} local partyUnits = {}
-- Update throttle (seconds) -- Update throttle (seconds)
local THROTTLE = 0.2 local THROTTLE = 0.1
-- Cache of per-mob tank-mode details, keyed by mob UID (name in vanilla).
-- Populated by UpdateUnitThreat via ThreatEngine, and read by the Nameplates
-- module when rendering the tank-mode text (raw threat lead).
local tankDetailsCache = {}
-- ================================================================== -- ==================================================================
-- Safe API call helpers -- Safe API call helpers
@@ -647,12 +651,54 @@ function addon.Threat:UpdateUnitThreat(unit)
mobTargetName = mt mobTargetName = mt
end end
-- ------------------------------------------------------------
-- TARGET-SNAP override: if the mob is currently targeting the
-- player, force display to 100% (aggro). Some mobs pick focused
-- / random targets regardless of threat, so the plate should
-- immediately snap to 100% while targeted, and fall back to the
-- real threat % when the mob switches targets.
-- ------------------------------------------------------------
local targetSnapped = false
if RelationshipsThreatPlatesDB and RelationshipsThreatPlatesDB.targetSnap100
and mobTargetName and playerName and mobTargetName == playerName then
local ok_pc, pc = pcall(UnitAffectingCombat, "player")
if ok_pc and pc then
pct = 100
situation = 3
targetSnapped = true
end
end
-- ------------------------------------------------------------
-- Compute raw tank-mode details (player threat vs 2nd highest).
-- Uses ThreatEngine's combat-log-parsed absolute values when
-- available. This is what the nameplate renders in Tank Mode.
-- ------------------------------------------------------------
local ok_mn, mobName = pcall(UnitName, unit)
if ok_mn and mobName then
local ePct, eSit, ePlayerThreat, eTopThreat, eHasGroup, eTopPlayer, eSecondThreat =
addon.ThreatEngine:GetThreatOnTarget(mobName)
tankDetailsCache[uid] = {
playerThreat = ePlayerThreat or 0,
topThreat = eTopThreat or 0,
secondThreat = eSecondThreat or 0,
topPlayer = eTopPlayer,
hasAggro = (eTopPlayer == playerName) or targetSnapped,
hasGroupData = eHasGroup or false,
targetSnapped = targetSnapped,
mobTargetName = mobTargetName,
lastUpdate = GetTime(),
}
end
threatCache[uid] = { threatCache[uid] = {
pct = pct, pct = pct,
situation = situation, situation = situation,
unit = unit, unit = unit,
lastUpdate = GetTime(), lastUpdate = GetTime(),
mobTargetName = mobTargetName, mobTargetName = mobTargetName,
targetSnapped = targetSnapped,
} }
-- Also store in group threat cache for cross-reference -- Also store in group threat cache for cross-reference
@@ -681,6 +727,76 @@ function addon.Threat:GetCachedThreat(uid)
return 0, 0 return 0, 0
end end
-- ==================================================================
-- Tank-mode details for a mob (by UID / name in vanilla).
-- Returns a table (or nil) with:
-- playerThreat, topThreat, secondThreat, topPlayer,
-- hasAggro, hasGroupData, targetSnapped, mobTargetName
-- ==================================================================
function addon.Threat:GetTankDetails(uid)
if not uid then return nil end
local d = tankDetailsCache[uid]
if not d then return nil end
if GetTime() - d.lastUpdate > 5 then return nil end
return d
end
-- ==================================================================
-- Format a raw threat value as a compact signed string.
-- Examples: 12345 -> "+12.3K", -845 -> "-845", 0 -> "0"
-- ==================================================================
function addon.Threat:FormatThreatLead(v)
if not v then return "--" end
local a = math.abs(v)
local sign = ""
if v > 0 then sign = "+" elseif v < 0 then sign = "-" end
if a >= 100000 then
return sign .. string.format("%.0fK", a / 1000)
elseif a >= 10000 then
return sign .. string.format("%.1fK", a / 1000)
elseif a >= 1000 then
return sign .. string.format("%.2fK", a / 1000)
else
return sign .. string.format("%d", a)
end
end
-- ==================================================================
-- Return the display string for tank mode given a plate's mob name.
-- Uses the ThreatEngine directly (works even without a unit token,
-- e.g. for unique-name plates).
-- Returns: string like "+1.20K" or "-450", or "--" if unknown.
-- ==================================================================
function addon.Threat:GetTankDisplayForMob(mobName)
if not mobName then return "--" end
local d = tankDetailsCache[mobName]
if not d or (GetTime() - d.lastUpdate > 5) then
-- Query ThreatEngine on demand (no cache hit).
local _, _, playerThreat, topThreat, _, topPlayer, secondThreat =
addon.ThreatEngine:GetThreatOnTarget(mobName)
if not playerThreat or playerThreat <= 0 then
return "--"
end
local lead
if topPlayer == playerName then
lead = playerThreat - (secondThreat or 0)
else
lead = playerThreat - (topThreat or 0)
end
return self:FormatThreatLead(lead)
end
if d.playerThreat <= 0 then return "--" end
local lead
if d.hasAggro then
lead = d.playerThreat - (d.secondThreat or 0)
else
lead = d.playerThreat - (d.topThreat or 0)
end
return self:FormatThreatLead(lead)
end
-- ================================================================== -- ==================================================================
-- Get threat percentage by mob NAME (no unit token required) -- Get threat percentage by mob NAME (no unit token required)
-- This is the fallback for when FindUnitByName couldn't resolve -- This is the fallback for when FindUnitByName couldn't resolve
+9 -5
View File
@@ -1268,30 +1268,34 @@ end
-- ============================================================ -- ============================================================
function addon.ThreatEngine:GetThreatOnTarget(mobName) function addon.ThreatEngine:GetThreatOnTarget(mobName)
if not mobName then return 0, 0, 0, 0, false, nil end if not mobName then return 0, 0, 0, 0, false, nil, 0 end
-- Check if we have threat data for this mob -- Check if we have threat data for this mob
local mobThreat = threatData[mobName] local mobThreat = threatData[mobName]
if not mobThreat then if not mobThreat then
return 0, 0, 0, 0, false, nil return 0, 0, 0, 0, false, nil, 0
end end
-- Find player's threat and top threat -- Find player's threat, top threat and second-highest threat
local playerThreat = mobThreat[playerName] or 0 local playerThreat = mobThreat[playerName] or 0
local topThreat = 0 local topThreat = 0
local topPlayer = nil local topPlayer = nil
local secondThreat = 0
local sourceCount = 0 local sourceCount = 0
for source, value in pairs(mobThreat) do for source, value in pairs(mobThreat) do
sourceCount = sourceCount + 1 sourceCount = sourceCount + 1
if value > topThreat then if value > topThreat then
secondThreat = topThreat
topThreat = value topThreat = value
topPlayer = source topPlayer = source
elseif value > secondThreat then
secondThreat = value
end end
end end
if topThreat <= 0 then if topThreat <= 0 then
return 0, 0, 0, 0, false, nil return 0, 0, 0, 0, false, nil, 0
end end
-- Calculate the player's percentage relative to top threat -- Calculate the player's percentage relative to top threat
@@ -1314,7 +1318,7 @@ function addon.ThreatEngine:GetThreatOnTarget(mobName)
-- (via addon communication or party combat log parsing). -- (via addon communication or party combat log parsing).
local hasGroupData = (sourceCount > 1) local hasGroupData = (sourceCount > 1)
return pct, sit, playerThreat, topThreat, hasGroupData, topPlayer return pct, sit, playerThreat, topThreat, hasGroupData, topPlayer, secondThreat
end end
-- ============================================================ -- ============================================================