local MYTHREAT_VERSION = '1.6.4'; local MYTHREAT_ADJUST_VALUE = -410065408; local options = { type='group', args = { lock = { type = 'toggle', name = 'Lock frame', desc = 'Toggles the locked state of the MyThreat frame.', get = function() return MyThreat_ThreatFrame.lock; end, set = function() MyThreat_ThreatFrame.lock = not MyThreat_ThreatFrame.lock; if (MyThreat_ThreatFrame.lock) then MyThreat_ThreatFrame:EnableMouse(false); MyThreat_ThreatFrame:SetBackdropColor(0,0,0,0); MyThreat_ThreatFrameStatus:Hide(); else MyThreat_ThreatFrame:EnableMouse(true); MyThreat_ThreatFrame:SetBackdropColor(0,0,0,.5); MyThreat_ThreatFrameStatus:Show(); end end, }, red = { type = 'group', name = 'Red alert', desc = 'edit red level settings', args = { absolute = { type = 'range', name = 'Absolute threat value', desc = 'The absolute threat difference for red alert warning.', min = 0, max = 200000, step = 5000, isPercent = false, get = function() return MyThreat.db.profile.red.absolute; end, set = function(_, v) MyThreat.db.profile.red.absolute = v; end, }, relative = { type = 'range', name = 'Relative threat value', desc = 'The relative threat difference for red alert warning.', min = 0, max = 1, step = 0.05, isPercent = true, get = function() return MyThreat.db.profile.red.relative; end, set = function(_, v) MyThreat.db.profile.red.relative = v; end, }, alpha = { type = 'range', name = 'Alpha', desc = 'The alpha level of the MyThreat frame in this threat range.', min = 0, max = 1, step = 0.05, get = function() return MyThreat.db.profile.red.alpha; end, set = function(_, v) MyThreat.db.profile.red.alpha = v; end; }, }, }, orange = { type = 'group', name = 'Orange alert', desc = 'edit orange level settings', args = { absolute = { type = 'range', name = 'Absolute threat value', desc = 'The absolute threat difference for orange alert warning.', min = 0, max = 200000, step = 5000, isPercent = false, get = function() return MyThreat.db.profile.orange.absolute; end, set = function(_, v) MyThreat.db.profile.orange.absolute = v; end, }, relative = { type = 'range', name = 'Relative threat value', desc = 'The relative threat difference for orange alert warning.', min = 0, max = 1, step = 0.05, isPercent = true, get = function() return MyThreat.db.profile.orange.relative; end, set = function(_, v) MyThreat.db.profile.orange.relative = v; end, }, alpha = { type = 'range', name = 'Alpha', desc = 'The alpha level of the MyThreat frame in this threat range.', min = 0, max = 1, step = 0.05, get = function() return MyThreat.db.profile.orange.alpha; end, set = function(_, v) MyThreat.db.profile.orange.alpha = v; end; }, }, }, yellow = { type = 'group', name = 'Yellow alert', desc = 'edit yellow level settings', args = { absolute = { type = 'range', name = 'Absolute threat value', desc = 'The absolute threat difference for yellow alert warning.', min = 0, max = 200000, step = 5000, isPercent = false, get = function() return MyThreat.db.profile.yellow.absolute; end, set = function(_, v) MyThreat.db.profile.yellow.absolute = v; end, }, relative = { type = 'range', name = 'Relative threat value', desc = 'The relative threat difference for yellow alert warning.', min = 0, max = 1, step = 0.05, isPercent = true, get = function() return MyThreat.db.profile.yellow.relative; end, set = function(_, v) MyThreat.db.profile.yellow.relative = v; end, }, alpha = { type = 'range', name = 'Alpha', desc = 'The alpha level of the MyThreat frame in this threat range.', min = 0, max = 1, step = 0.05, get = function() return MyThreat.db.profile.yellow.alpha; end, set = function(_, v) MyThreat.db.profile.yellow.alpha = v; end; }, }, }, test = { type = 'toggle', name = 'Test mode', desc = 'Toggles test mode for the MyThreat frame.', get = function() return MyThreat.test; end, set = function() MyThreat.test = not MyThreat.test; MyThreat:UpdateThreatSourceHandler(); MyThreat:Process(); end, }, fontSize = { type = 'range', name = 'Font size', desc = 'The font size in pixels.', min = 6, max = 120, step = 2, isPercent = false, get = function() return MyThreat.db.profile.fontSize; end, set = function(_, v) MyThreat.db.profile.fontSize = v; MyThreat:UpdateFontSize(); end, }, tankMode = { type = 'toggle', name = 'Tank mode', desc = 'Toggles tank mode for the MyThreat addon.', get = function() return MyThreat.db.profile.tankMode; end, set = function() MyThreat.db.profile.tankMode = not MyThreat.db.profile.tankMode; if (MyThreat.db.profile.tankMode) then MyThreat:Print('Tank mode is now |cFF00FF00ON|r.'); else MyThreat:Print('Tank mode is now |cFFFF0000OFF|r.'); end MyThreat:UpdateThreatSourceHandler(); MyThreat:Process(); end; }, alpha = { type = 'range', name = 'Alpha', desc = 'The alpha level of the MyThreat frame when it is in the safe range.', min = 0, max = 1, step = 0.05, get = function() return MyThreat.db.profile.safeAlpha; end, set = function(_, v) MyThreat.db.profile.safeAlpha = v; end; }, focus = { type = 'group', name = 'Focus', desc = 'threat focus targets', args = { set = { type = 'input', name = 'Set', desc = 'Set the focus list', guiHidden = true, dialogHidden = true, dropdownHidden = true, set = function(_, v) MyThreat:SetFocus(v); MyThreat:Process(); end, get = function() return MyThreat.focusString; end, }, tanks = { type = 'execute', name = 'Tanks', desc = 'Sets the focus list to the Main Tanks as specified in the raid UI', func = function() MyThreat:SetTankFocus(); end, }, clear = { type = 'execute', name = 'Clear', desc = 'Clear the current focus list', func = function() MyThreat:ClearFocus(); end, }, }, }, version = { type = 'execute', name = 'Version', desc = 'Display version information', func = function() MyThreat:Print('Current version: ' .. MYTHREAT_VERSION); end, }, autoAdjust = { type = 'toggle', name = 'Auto-adjust threat', desc = 'Auto-adjusts threat when abilities such as Fade and Mirror Image are active.', get = function() return MyThreat.db.profile.autoAdjust; end, set = function() MyThreat.db.profile.autoAdjust = not MyThreat.db.profile.autoAdjust; MyThreat:Process(); end; } } }; MyThreat = LibStub("AceAddon-3.0"):NewAddon("MyThreat", "AceConsole-3.0", "AceEvent-3.0"); MyThreat.threatSourceHandler = nil; MyThreat.lastUpdate = 0; MyThreat.test = false; MyThreat.focusMode = false; MyThreat.focusTanks = false; MyThreat.focusList = {}; MyThreat.focusListNum = 0; MyThreat.focusString = ''; MyThreat.focusUnits = {}; MyThreat.focusUnitsNum = 0; -------------------------------------------------------------------------- function MyThreat:OnInitialize() MyThreat.console = LibStub("AceConsole-3.0"); MyThreat.db = LibStub("AceDB-3.0"):New("MyThreatDB"); MyThreat.db:RegisterDefaults({ profile = { red = { relative = .95, absolute = 1000, alpha = 1, }, orange = { relative = .90, absolute = 2500, alpha = 1, }, yellow = { relative = .85, absolute = 5000, alpha = 1, }, fontSize = 16, tankMode = false, safeAlpha = 1, autoAdjust = true, } }); options.args.profile = LibStub("AceDBOptions-3.0"):GetOptionsTable(MyThreat.db); MyThreat.configOptions = options; LibStub("AceConfig-3.0"):RegisterOptionsTable("MyThreat", options, {"mythreat", "myt"}); LibStub("AceConfigDialog-3.0"):AddToBlizOptions("MyThreat", "MyThreat"); MyThreat:UpdateThreatSourceHandler(); end -------------------------------------------------------------------------- function MyThreat:OnEnable() MyThreat_ThreatFrame:Show(); MyThreat_ThreatFrame.lock = true; MyThreat_ThreatFrame:EnableMouse(false); MyThreat_ThreatFrame:SetBackdropColor(0,0,0,0); MyThreat_ThreatFrameStatus:Hide(); -- Called when the addon is enabled MyThreat:RegisterEvent("PLAYER_TARGET_CHANGED"); MyThreat:RegisterEvent("PARTY_MEMBERS_CHANGED"); MyThreat:RegisterEvent("UNIT_THREAT_LIST_UPDATE"); MyThreat:UpdateFontSize(); MyThreat:Process(); end -------------------------------------------------------------------------- function MyThreat:OnDisable() MyThreat_ThreatFrame:Hide(); MyThreat:UnregisterEvent("PLAYER_TARGET_CHANGED"); MyThreat:UnregisterEvent("PARTY_MEMBERS_CHANGED"); MyThreat:UnregisterEvent("UNIT_THREAT_LIST_UPDATE"); end -------------------------------------------------------------------------- function MyThreat:Print(t) MyThreat.console:Print('[|cFF00FF00MyThreat|r] ' .. tostring(t)); end -------------------------------------------------------------------------- function MyThreat:UpdateThreatSourceHandler() -- use proper threat handler based on player preferences if (MyThreat.test) then MyThreat.threatSourceHandler = MyThreat.ThreatMode_Test; elseif (MyThreat.focusMode) then MyThreat.threatSourceHandler = MyThreat.ThreatMode_Focus; elseif (MyThreat.db.profile.tankMode) then MyThreat.threatSourceHandler = MyThreat.ThreatMode_Tank; else MyThreat.threatSourceHandler = MyThreat.ThreatMode_Standard; end end -------------------------------------------------------------------------- function MyThreat:UpdateFontSize() if (MyThreat.db.profile.fontSize ~= nil) then -- get font info local fontFace, fontSize, fontFlags = MyThreat_ThreatFrameThreatDiff:GetFont(); -- and set it on the font MyThreat_ThreatFrameThreatDiff:SetFont(fontFace, MyThreat.db.profile.fontSize, fontFlags); MyThreat_ThreatFrameThreatPercDiff:SetFont(fontFace, MyThreat.db.profile.fontSize, fontFlags); MyThreat_ThreatFrameThreatPlayerName:SetFont(fontFace, MyThreat.db.profile.fontSize * .8, fontFlags); end end -------------------------------------------------------------------------- function MyThreat:SetFocus(e) MyThreat.focusString = e; MyThreat.focusList = {}; MyThreat.focusListNum = 0; MyThreat.focusUnits = {}; MyThreat.focusUnitsNum = 0; local tbl = {}; local c = 0; for v in string.gmatch(e, "[^ ,;]+") do c = c + 1; tbl[c] = v; end if (c > 0) then local pName; local nameList = ''; for pName, uid in MyThreat:GetPartyUnitIds() do for j = 1, c, 1 do if (tbl[j] == pName) then MyThreat.focusListNum = MyThreat.focusListNum + 1; MyThreat.focusList[MyThreat.focusListNum] = pName; if (MyThreat.focusListNum > 1) then nameList = nameList .. ', '; end nameList = nameList .. pName; tremove(tbl, j); c = c - 1; end end end for j = 1, c, 1 do MyThreat:Print('Player ' .. (tbl[j]) .. ' not found in group - removed from focus list.'); end -- if no players found in focus list, revert to default threat source handler if (MyThreat.focusListNum > 0) then MyThreat:Print('Focus list set: ' .. nameList); MyThreat:UpdateFocusUnitsTable(); MyThreat.focusMode = true; MyThreat.focusTanks = false; MyThreat:UpdateThreatSourceHandler(); else MyThreat:Print('No players from focus list found in group.'); MyThreat:ClearFocus(); end else MyThreat:ClearFocus(); end end -------------------------------------------------------------------------- function MyThreat:SetTankFocus() MyThreat.focusMode = true; MyThreat.focusTanks = true; MyThreat.focusString = '(Raid tanks)'; MyThreat:Print('Focus list set to raid main tanks.'); MyThreat:UpdateFocusUnitsTable(); MyThreat:UpdateThreatSourceHandler(); end -------------------------------------------------------------------------- function MyThreat:ClearFocus() MyThreat:Print('Clearing focus list.'); MyThreat.focusMode = false; MyThreat.focusTanks = false; MyThreat.focusString = nil; MyThreat.focusList = {}; MyThreat.focusListNum = 0; MyThreat.focusUnits = {}; MyThreat.focusUnitsNum = 0; MyThreat:UpdateThreatSourceHandler(); end -------------------------------------------------------------------------- function MyThreat:UpdateFocusUnitsTable() MyThreat.focusUnits = {}; MyThreat.focusUnitsNum = 0; local found; if (MyThreat.focusTanks) then for i = 1, 40, 1 do local name, _, _, _, _, _, _, _, _, role, _ = GetRaidRosterInfo(i); if (name ~= nil and role == "MAINTANK") then MyThreat.focusUnitsNum = MyThreat.focusUnitsNum + 1; MyThreat.focusUnits[MyThreat.focusUnitsNum] = "raid" .. i; end end else for i = 1, MyThreat.focusListNum, 1 do found = false; for pName, uid in MyThreat:GetPartyUnitIds() do if (strupper(pName) == strupper(MyThreat.focusList[i])) then found = true; MyThreat.focusUnitsNum = MyThreat.focusUnitsNum + 1; MyThreat.focusUnits[MyThreat.focusUnitsNum] = uid; break; end end if (not found) then MyThreat:Print('Player ' .. (MyThreat.focusList[i]) .. ' not found in group - removed from focus list.'); tremove(MyThreat.focusList, i); i = i - 1; end end if (MyThreat.focusUnitsNum == 0) then MyThreat:Print('No players from focus list found in group.'); MyThreat:ClearFocus(); end end end -------------------------------------------------------------------------- do local function ValuesIterator(t, key) if (not t) then return nil; end local n = t.n + 1; local k = t[n]; if (k == nil) then return nil; end t.n = n; return k, t.values[n]; end function MyThreat:GetPartyUnitIds() local t = {}; local v = {}; local c = 1; if (UnitInRaid("player")) then for i = 1, 40, 1 do if (UnitName("raid" .. i) ~= nil) then t[c] = UnitName("raid" .. i); v[c] = "raid" .. i; c = c + 1; end end else t[c] = UnitName("player"); v[c] = "player"; c = c + 1; for i = 1, 4, 1 do if (UnitName("party" .. i) ~= nil) then t[c] = UnitName("party" .. i); v[c] = "party" .. i; c = c + 1; end end end t.values = v; t.n = 0; return ValuesIterator, t, nil; end end -------------------------------------------------------------------------- function MyThreat:GetThreat(pUnit, pTarget) local isTanking, _, _, _, threatValue = UnitDetailedThreatSituation(pUnit, pTarget); local adjusted = false; -- adjust threat for Mirror Image and Fade abilities if (threatValue and threatValue < 0 and pUnit == "player" and MyThreat.db.profile.autoAdjust) then threatValue = threatValue - MYTHREAT_ADJUST_VALUE; adjusted = true; end return threatValue, isTanking, adjusted; end -------------------------------------------------------------------------- function MyThreat:Process() -- use current handler to get threat information local threatDiff, threatDiffPercent, tankName, adjusted = MyThreat.threatSourceHandler(); -- if there is a current tank if (tankName ~= nil and threatDiff ~= nil and threatDiffPercent ~= nil) then -- blizzard threat values are 100 times higher than old Threat-2.0 values. adjust accordingly. threatDiff = threatDiff / 100.0; local threatText, relativeThreatText; local r = 0; local g = 1; local b = 0; local a = MyThreat.db.profile.safeAlpha; -- set the text for absolute threat diff if (threatDiff >= 1000 or threatDiff <= -1000) then threatText = format("%.2f", threatDiff / 1000) .. "k"; else threatText = format("%.0f", threatDiff); end -- set relative text relativeThreatText = format("%.0f", threatDiffPercent * 100) .. "%"; if (adjusted) then r = 0.4; g = 0.8; b = 0.95; a = 1; -- always show threat during adjusted phases threatText = "(" .. threatText .. ")"; relativeThreatText = "(" .. relativeThreatText .. ")"; else -- set the right bar colors if (threatDiff <= MyThreat.db.profile.red.absolute or threatDiffPercent >= MyThreat.db.profile.red.relative) then r = 1; g = 0; a = MyThreat.db.profile.red.alpha; elseif (threatDiff <= MyThreat.db.profile.orange.absolute or threatDiffPercent >= MyThreat.db.profile.orange.relative) then r = 1; g = 0.5; a = MyThreat.db.profile.orange.alpha; elseif (threatDiff <= MyThreat.db.profile.yellow.absolute or threatDiffPercent >= MyThreat.db.profile.yellow.relative) then r = 1; g = 1; a = MyThreat.db.profile.yellow.alpha; end end -- adjust labels MyThreat_ThreatFrameThreatDiff:SetText(threatText); MyThreat_ThreatFrameThreatPercDiff:SetText(relativeThreatText); MyThreat_ThreatFrameThreatDiff:SetTextColor(r,g,b,a); MyThreat_ThreatFrameThreatPercDiff:SetTextColor(r,g,b,a); MyThreat_ThreatFrameThreatPlayerName:SetTextColor(r,g,b,a); MyThreat_ThreatFrameThreatPlayerName:SetText(tankName); -- show labels if hidden if (not MyThreat_ThreatFrameThreatDiff:IsVisible()) then MyThreat_ThreatFrameThreatDiff:Show(); MyThreat_ThreatFrameThreatPercDiff:Show(); MyThreat_ThreatFrameThreatPlayerName:Show(); end else -- hide bars if visible if (MyThreat_ThreatFrameThreatDiff:IsVisible()) then MyThreat_ThreatFrameThreatDiff:Hide(); MyThreat_ThreatFrameThreatPercDiff:Hide(); MyThreat_ThreatFrameThreatPlayerName:Hide(); end end end -------------------------------------------------------------------------- function MyThreat:ThreatMode_Test() -- used for testing purposes - return static values return 905000, .6885, 'Soandso'; end -------------------------------------------------------------------------- function MyThreat:ThreatMode_Standard() -- only process if unit exists, is not a player, is not friendly and is not dead if (not UnitExists("target") or UnitIsPlayer("target") or UnitIsFriend("player", "target") or UnitIsDead("target")) then return nil, nil, nil; end local currentTankName, currentTankThreat, playerThreat, threatDiff, threatDiffPercent, adjusted; adjusted = false; -- first try for targettarget mode before using full scanning if (UnitExists("targettarget")) then local threatValue, isTanking = MyThreat:GetThreat("targettarget", "target"); if (isTanking and not UnitIsUnit("player", "targettarget")) then currentTankName = UnitName("targettarget"); currentTankThreat = threatValue; end end if (currentTankName == nil) then for pName, uid in MyThreat:GetPartyUnitIds() do if (not UnitIsUnit("player", uid)) then local threatValue, isTanking = MyThreat:GetThreat(uid, "target"); if (isTanking) then currentTankName = pName; currentTankThreat = threatValue; break; end end end end if (currentTankName ~= nil) then playerThreat, _, adjusted = MyThreat:GetThreat("player", "target"); if (playerThreat ~= nil) then threatDiff = currentTankThreat - playerThreat; threatDiffPercent = playerThreat / currentTankThreat; end end return threatDiff, threatDiffPercent, currentTankName, adjusted; end -------------------------------------------------------------------------- function MyThreat:ThreatMode_Tank() -- only process if unit exists, is not a player, is not friendly and is not dead if (not UnitExists("target") or UnitIsPlayer("target") or UnitIsFriend("player", "target") or UnitIsDead("target")) then return nil, nil, nil; end local currentTrailing, currentTrailingThreat, playerThreat, threatDiff, threatDiffPercent, adjusted; currentTrailingThreat = 0; adjusted = false; for pName, uid in MyThreat:GetPartyUnitIds() do if (not UnitIsUnit("player", uid)) then local threatValue = MyThreat:GetThreat(uid, "target"); if (threatValue ~= nil and threatValue > currentTrailingThreat) then currentTrailing = pName; currentTrailingThreat = threatValue; end end end if (currentTrailing ~= nil) then playerThreat, _, adjusted = MyThreat:GetThreat("player", "target"); if (playerThreat ~= nil) then threatDiff = 0 - (currentTrailingThreat - playerThreat); threatDiffPercent = currentTrailingThreat / playerThreat; end end return threatDiff, threatDiffPercent, currentTrailing, adjusted; end -------------------------------------------------------------------------- function MyThreat:ThreatMode_Focus() -- only process if unit exists, is not a player, is not friendly and is not dead if (not UnitExists("target") or UnitIsPlayer("target") or UnitIsFriend("player", "target") or UnitIsDead("target")) then return nil, nil, nil; end local currentLowestFocus, currentLowestFocusThreat, playerThreat, threatDiff, threatDiffPercent, adjusted; currentLowestFocusThreat = nil; for i = 1, MyThreat.focusUnitsNum, 1 do local threatValue = MyThreat:GetThreat(MyThreat.focusUnits[i], "target"); if (threatValue == nil) then threatValue = 0; end if (currentLowestFocusThreat == nil or threatValue < currentLowestFocusThreat) then currentLowestFocus = '[F]' .. UnitName(MyThreat.focusUnits[i]); currentLowestFocusThreat = threatValue; end end if (currentLowestFocus ~= nil) then playerThreat, _, adjusted = MyThreat:GetThreat("player", "target"); if (playerThreat ~= nil) then threatDiff = currentLowestFocusThreat - playerThreat; threatDiffPercent = playerThreat / currentLowestFocusThreat; end end return threatDiff, threatDiffPercent, currentLowestFocus, adjusted; end -------------------------------------------------------------------------- function MyThreat:PARTY_MEMBERS_CHANGED() if (MyThreat.focusMode) then MyThreat:UpdateFocusUnitsTable(); MyThreat:Process(); end end -------------------------------------------------------------------------- function MyThreat:PLAYER_TARGET_CHANGED() MyThreat:Process(); end -------------------------------------------------------------------------- function MyThreat:UNIT_THREAT_LIST_UPDATE(_, unitId) local cTime = GetTime(); if ((cTime - MyThreat.lastUpdate) > 0.125) then if (unitId == "target") then MyThreat:Process(); MyThreat.lastUpdate = cTime; end end end