9895c8c1ca
Add option to lock Omen from being moved/resized. Update profile change function to properly resize and reanchor bars. Fix weird bar animation effect which occurs only when resizing Omen. Fix GrowUp option to take effect immediately.
1124 lines
34 KiB
Lua
1124 lines
34 KiB
Lua
-----------------------------------------------------------------------------
|
|
-- Force used libraries to load. This is necessary because load on demand
|
|
-- addons (in our case, disembedded libs) will no longer load even when
|
|
-- listed in OptionalDeps and can only be loaded via LoadAddOn(). This must
|
|
-- be done before a LibStub("AceAddon-3.0"):NewAddon() call due to
|
|
-- ADDON_LOADED firing, as AceAddon-3.0 will call :OnInit() on any addon's
|
|
-- ADDON_LOADED event due to needing to support submodules which may not be
|
|
-- its their own addons.
|
|
LoadAddOn("Ace3")
|
|
LoadAddOn("LibSharedMedia-3.0")
|
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Addon declaration
|
|
local Omen = LibStub("AceAddon-3.0"):NewAddon("Omen", "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0")
|
|
local L = LibStub("AceLocale-3.0"):GetLocale("Omen", false)
|
|
local LSM = LibStub("LibSharedMedia-3.0")
|
|
_G["Omen"] = Omen
|
|
|
|
|
|
-- TODO: Add TPS calculations
|
|
-- TODO: Add visible/audible warnings
|
|
-- TODO: Add Sink/SharedMedia stuff
|
|
-- TODO: Code optimizations
|
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Register some media
|
|
LSM:Register("sound", "Rubber Ducky", [[Sound\Doodad\Goblin_Lottery_Open01.wav]])
|
|
LSM:Register("sound", "Cartoon FX", [[Sound\Doodad\Goblin_Lottery_Open03.wav]])
|
|
LSM:Register("sound", "Explosion", [[Sound\Doodad\Hellfire_Raid_FX_Explosion05.wav]])
|
|
LSM:Register("sound", "Shing!", [[Sound\Doodad\PortcullisActive_Closed.wav]])
|
|
LSM:Register("sound", "Wham!", [[Sound\Doodad\PVP_Lordaeron_Door_Open.wav]])
|
|
LSM:Register("sound", "Simon Chime", [[Sound\Doodad\SimonGame_LargeBlueTree.wav]])
|
|
LSM:Register("sound", "War Drums", [[Sound\Event Sounds\Event_wardrum_ogre.wav]])
|
|
LSM:Register("sound", "Cheer", [[Sound\Event Sounds\OgreEventCheerUnique.wav]])
|
|
LSM:Register("sound", "Humm", [[Sound\Spells\SimonGame_Visual_GameStart.wav]])
|
|
LSM:Register("sound", "Short Circuit", [[Sound\Spells\SimonGame_Visual_BadPress.wav]])
|
|
LSM:Register("sound", "Fel Portal", [[Sound\Spells\Sunwell_Fel_PortalStand.wav]])
|
|
LSM:Register("sound", "Fel Nova", [[Sound\Spells\SeepingGaseous_Fel_Nova.wav]])
|
|
LSM:Register("sound", "You Will Die!", [[Sound\Creature\CThun\CThunYouWillDIe.wav]])
|
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Localize some global functions
|
|
local floor, format = floor, format
|
|
local sort = sort
|
|
local tinsert, tremove = tinsert, tremove
|
|
local UnitDetailedThreatSituation = UnitDetailedThreatSituation
|
|
local UnitExists, UnitGUID, UnitName, UnitClass = UnitExists, UnitGUID, UnitName, UnitClass
|
|
local UnitIsPlayer, UnitPlayerControlled, UnitCanAttack = UnitIsPlayer, UnitPlayerControlled, UnitCanAttack
|
|
local GetNumRaidMembers, GetNumPartyMembers = GetNumRaidMembers, GetNumPartyMembers
|
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Local variables used
|
|
local db
|
|
local defaults = {
|
|
profile = {
|
|
Alpha = 1,
|
|
Scale = 1,
|
|
GrowUp = false,
|
|
Autocollapse = false,
|
|
NumBars = 10,
|
|
CollapseHide = false,
|
|
Locked = false,
|
|
Bar = {
|
|
Texture = "Blizzard",
|
|
Height = 16,
|
|
Spacing = 0,
|
|
AnimateBars = true,
|
|
ShortNumbers = true,
|
|
},
|
|
ShowWith = {
|
|
Pet = true,
|
|
Alone = false,
|
|
Party = true,
|
|
Raid = true,
|
|
Resting = false,
|
|
PVP = false,
|
|
Dungeon = true
|
|
},
|
|
Classes = {
|
|
["DEATHKNIGHT"] = true,
|
|
["DRUID"] = true,
|
|
["HUNTER"] = true,
|
|
["MAGE"] = true,
|
|
["PALADIN"] = true,
|
|
["PET"] = true,
|
|
["PRIEST"] = true,
|
|
["ROGUE"] = true,
|
|
["SHAMAN"] = true,
|
|
["WARLOCK"] = true,
|
|
["WARRIOR"] = true
|
|
},
|
|
},
|
|
}
|
|
local guidNameLookup = {} -- Format: guidNameLookup[guid] = "Unit Name"
|
|
local guidClassLookup = {} -- Format: guidClassLookup[guid] = "CLASS"
|
|
local timers = {} -- Format: timers.timerName = timer returned from AceTimer-3.0
|
|
local bars = {} -- Format: bars[i] = frame containing the i-th bar from the top of Omen
|
|
local inRaid, inParty -- boolean variables indicating if the player is in a raid and/or party
|
|
|
|
Omen.GuidNameLookup = guidNameLookup
|
|
Omen.GuidClassLookup = guidClassLookup
|
|
Omen.Timers = timers
|
|
Omen.Bars = bars
|
|
|
|
local default_color = { -- Default bar color for units not in the player's raid/party
|
|
r = 1,
|
|
g = 0,
|
|
b = 0,
|
|
a = 0.6
|
|
}
|
|
local pet_color = { -- Default pet color for all pets in the player's raid/party
|
|
r = 0.83, -- This is a light green-ish color, or sort of a washed out
|
|
g = 0.98, -- version of the Hunter color
|
|
b = 0.74,
|
|
a = 0.6
|
|
}
|
|
|
|
-- For speedups. Rather than concantenating every time we need a unitID, we just look
|
|
-- it up instead. That is rID[i] is much faster than format("raid%d", i) or "raid"..i
|
|
local pID = {}
|
|
local ptID = {}
|
|
local ppID = {}
|
|
local pptID = {}
|
|
local rID = {}
|
|
local rtID = {}
|
|
local rpID = {}
|
|
local rptID = {}
|
|
for i = 1, 4 do
|
|
pID[i] = format("party%d", i)
|
|
ptID[i] = format("party%dtarget", i)
|
|
ppID[i] = format("partypet%d", i)
|
|
pptID[i] = format("partypet%dtarget", i)
|
|
end
|
|
for i = 1, 40 do
|
|
rID[i] = format("raid%d", i)
|
|
rtID[i] = format("raid%dtarget", i)
|
|
rpID[i] = format("raidpet%d", i)
|
|
rptID[i] = format("raidpet%dtarget", i)
|
|
end
|
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Omen initialization and frame functions
|
|
|
|
local function startmoving()
|
|
if not db.Locked then
|
|
Omen.Anchor.IsMovingOrSizing = 1
|
|
Omen.Anchor:StartMoving()
|
|
end
|
|
end
|
|
local function stopmoving()
|
|
if Omen.Anchor.IsMovingOrSizing then
|
|
Omen.Anchor:StopMovingOrSizing()
|
|
Omen:SetAnchors()
|
|
Omen:UpdateBars()
|
|
Omen.Anchor.IsMovingOrSizing = nil
|
|
end
|
|
end
|
|
local function sizing()
|
|
Omen:ResizeBars()
|
|
Omen:UpdateBars()
|
|
end
|
|
|
|
function Omen:CreateFrames()
|
|
-- Create anchor
|
|
self.Anchor = CreateFrame("Frame", "OmenAnchor", UIParent)
|
|
self.Anchor:SetResizable(true)
|
|
self.Anchor:SetMinResize(90, 16)
|
|
self.Anchor:SetMovable(true)
|
|
self.Anchor:SetPoint("CENTER", UIParent, "CENTER")
|
|
self.Anchor:SetWidth(225)
|
|
self.Anchor:SetHeight(150)
|
|
self.Anchor:SetClampedToScreen(true)
|
|
self.Anchor:SetScript("OnHide", stopmoving)
|
|
|
|
-- Create Title
|
|
self.Title = CreateFrame("Frame", "OmenTitle", self.Anchor)
|
|
self.Title:SetPoint("TOPLEFT", self.Anchor, "TOPLEFT")
|
|
self.Title:SetPoint("TOPRIGHT", self.Anchor, "TOPRIGHT")
|
|
self.Title:SetHeight(16)
|
|
self.Title:EnableMouse(true)
|
|
self.Title:SetScript("OnMouseDown", startmoving)
|
|
self.Title:SetScript("OnMouseUp", stopmoving)
|
|
self.Title.Texture = self.Title:CreateTexture()
|
|
self.Title.Texture:SetAllPoints()
|
|
self.Title.Texture:SetTexture(0, 0, 0, 0.8)
|
|
|
|
-- Create Title text
|
|
self.TitleText = self.Title:CreateFontString(nil, nil, "GameFontNormal")
|
|
self.TitleText:SetPoint("LEFT", self.Title, "LEFT", 10, 0)
|
|
self.TitleText:SetJustifyH("LEFT")
|
|
self.TitleText:SetTextColor(1, 1, 1, 0.95)
|
|
self.defaultTitle = "Omen|cffffcc003|r"
|
|
self.TitleText:SetText(self.defaultTitle)
|
|
|
|
-- Create Bar List
|
|
self.BarList = CreateFrame("Frame", "OmenBarList", self.Anchor)
|
|
self.BarList:SetResizable(true)
|
|
self.BarList:EnableMouse(true)
|
|
self.BarList:SetPoint("TOPLEFT", self.Title, "BOTTOMLEFT")
|
|
self.BarList:SetPoint("TOPRIGHT", self.Title, "BOTTOMRIGHT")
|
|
self.BarList:SetPoint("BOTTOMLEFT", self.Anchor, "BOTTOMLEFT")
|
|
self.BarList:SetPoint("BOTTOMRIGHT", self.Anchor, "BOTTOMRIGHT")
|
|
self.BarList.Texture = self.BarList:CreateTexture()
|
|
self.BarList.Texture:SetAllPoints()
|
|
self.BarList.Texture:SetTexture(0, 0, 0, 0.8)
|
|
self.BarList:SetScript("OnMouseDown", startmoving)
|
|
self.BarList:SetScript("OnMouseUp", stopmoving)
|
|
|
|
-- Create resizing corner grip
|
|
self.Grip = CreateFrame("Button", "OmenResizeGrip", self.BarList)
|
|
self.Grip:SetNormalTexture("Interface\\AddOns\\Omen\\ResizeGrip")
|
|
self.Grip:SetHighlightTexture("Interface\\AddOns\\Omen\\ResizeGrip")
|
|
self.Grip:SetWidth(16)
|
|
self.Grip:SetHeight(16)
|
|
self.Grip:SetPoint("BOTTOMRIGHT", self.BarList, "BOTTOMRIGHT", 0, 1)
|
|
if db.Locked then
|
|
self.Grip:Hide()
|
|
end
|
|
self.Grip:SetScript("OnMouseDown", function()
|
|
if not db.Locked then
|
|
self.Anchor.IsMovingOrSizing = 2
|
|
self.Anchor:SetScript("OnSizeChanged", sizing)
|
|
self.Anchor:StartSizing()
|
|
end
|
|
end)
|
|
self.Grip:SetScript("OnMouseUp", function()
|
|
if self.Anchor.IsMovingOrSizing then
|
|
self.Anchor:SetScript("OnSizeChanged", nil)
|
|
self.Anchor:StopMovingOrSizing()
|
|
sizing()
|
|
self:SetAnchors()
|
|
self.Anchor.IsMovingOrSizing = nil
|
|
end
|
|
end)
|
|
self.Grip:SetScript("OnHide", self.Grip:GetScript("OnMouseUp"))
|
|
end
|
|
|
|
function Omen:OnInitialize()
|
|
-- Create savedvariables
|
|
self.db = LibStub("AceDB-3.0"):New("Omen3DB", defaults)
|
|
self.db.RegisterCallback(self, "OnProfileChanged", "OnProfileChanged")
|
|
self.db.RegisterCallback(self, "OnProfileCopied", "OnProfileChanged")
|
|
self.db.RegisterCallback(self, "OnProfileReset", "OnProfileChanged")
|
|
db = self.db.profile
|
|
|
|
self:CreateFrames()
|
|
self:SetupOptions()
|
|
|
|
-- Register events that are "always on". The rest are in :OnEnable()
|
|
self:RegisterEvent("PLAYER_LOGIN")
|
|
self:RegisterEvent("PLAYER_ENTERING_WORLD")
|
|
end
|
|
|
|
function Omen:PLAYER_LOGIN()
|
|
-- We set up anchors here because we only want to do it once on
|
|
-- PLAYER_LOGIN, hence we don't do it in OnEnable() which triggers on
|
|
-- the same event as well as on every subsequent Enable()/Disable() calls.
|
|
-- It cannot be earlier than PLAYER_LOGIN because layout-cache.txt
|
|
-- is loaded just before this event fires.
|
|
self:SetAnchors(true)
|
|
self:UnregisterEvent("PLAYER_LOGIN")
|
|
|
|
-- Optional launcher support for LDB-1.1 if present, this code is placed here so
|
|
-- that it runs after all other addons have loaded since we don't embed LDB-1.1
|
|
local DataBroker = LibStub("LibDataBroker-1.1", true)
|
|
if DataBroker then
|
|
local launcher = DataBroker:NewDataObject("Omen", {
|
|
type = "launcher",
|
|
icon = "Interface\\AddOns\\Omen\\icon",
|
|
OnClick = function(clickedframe, button)
|
|
if button == "RightButton" then Omen:ShowConfig() else Omen:Toggle() end
|
|
end,
|
|
})
|
|
end
|
|
end
|
|
|
|
function Omen:OnEnable()
|
|
self:RegisterEvent("UNIT_THREAT_LIST_UPDATE")
|
|
self:RegisterEvent("UNIT_THREAT_SITUATION_UPDATE")
|
|
self:RegisterEvent("PLAYER_TARGET_CHANGED")
|
|
self:RegisterEvent("UNIT_PET")
|
|
self:RegisterEvent("PARTY_MEMBERS_CHANGED")
|
|
|
|
self:PARTY_MEMBERS_CHANGED()
|
|
self:PLAYER_TARGET_CHANGED()
|
|
end
|
|
|
|
function Omen:Disable()
|
|
self:UnregisterEvent("UNIT_THREAT_LIST_UPDATE")
|
|
self:UnregisterEvent("UNIT_THREAT_SITUATION_UPDATE")
|
|
self:UnregisterEvent("PLAYER_TARGET_CHANGED")
|
|
self:UnregisterEvent("UNIT_PET")
|
|
self:UnregisterEvent("PARTY_MEMBERS_CHANGED")
|
|
|
|
-- Cancel all timers (well at least nil them all
|
|
-- out in timers[], since AceTimer-3.0 cancels
|
|
-- them all OnDisable anyway).
|
|
for k, v in pairs(timers) do
|
|
self:CancelTimer(v, true)
|
|
timers[k] = nil
|
|
end
|
|
|
|
self:Toggle(false)
|
|
end
|
|
|
|
function Omen:OnProfileChanged(db, name)
|
|
db = self.db.profile
|
|
self:SetAnchors(true)
|
|
self:ResizeBars()
|
|
self:ReAnchorBars()
|
|
self:UpdateVisible()
|
|
self:UpdateBars()
|
|
end
|
|
|
|
function Omen:SetAnchors(useDB)
|
|
local x, y, w, h
|
|
|
|
-- Set the scale, since the scaling affects the position
|
|
self.Anchor:SetScale(db.Scale)
|
|
|
|
-- Get position
|
|
if useDB then
|
|
x, y = db.PositionX, db.PositionY
|
|
if not x or not y then
|
|
self.Anchor:ClearAllPoints()
|
|
self.Anchor:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
|
|
x, y = self.Anchor:GetLeft(), db.GrowUp and self.Anchor:GetBottom() or self.Anchor:GetTop()
|
|
end
|
|
else
|
|
x, y = self.Anchor:GetLeft(), db.GrowUp and self.Anchor:GetBottom() or self.Anchor:GetTop()
|
|
end
|
|
|
|
-- Get width/height
|
|
w = useDB and db.PositionW or self.Anchor:GetWidth()
|
|
h = useDB and db.PositionH or self.Anchor:GetHeight()
|
|
|
|
-- Set the anchors and size
|
|
self.Anchor:ClearAllPoints()
|
|
self.Anchor:SetPoint(db.GrowUp and "BOTTOMLEFT" or "TOPLEFT", UIParent, "BOTTOMLEFT", x, y)
|
|
self.Anchor:SetWidth(w)
|
|
self.Anchor:SetHeight(h)
|
|
self.Anchor:SetUserPlaced(nil)
|
|
|
|
-- Save the data
|
|
db.PositionX, db.PositionY = x, y
|
|
db.PositionW, db.PositionH = w, h
|
|
end
|
|
|
|
function Omen:Toggle(setting)
|
|
if setting == nil then
|
|
setting = not self.Anchor:IsShown()
|
|
end
|
|
if setting then
|
|
self.Anchor:Show()
|
|
else
|
|
self.Anchor:Hide()
|
|
end
|
|
end
|
|
|
|
function Omen:UpdateVisible()
|
|
local inInstance, instanceType = IsInInstance()
|
|
local show
|
|
if not db.ShowWith.Resting and IsResting() then
|
|
show = false
|
|
end
|
|
if not db.ShowWith.PVP and inInstance and (instanceType == "pvp" or instanceType == "arena") then
|
|
show = false
|
|
end
|
|
if not db.ShowWith.Dungeon and inInstance and (instanceType == "party" or instanceType == "raid") then
|
|
show = false
|
|
end
|
|
if show == nil then
|
|
show = (db.ShowWith.Pet and UnitExists("pet")) or
|
|
(db.ShowWith.Alone and GetNumPartyMembers() + GetNumRaidMembers() == 0 and not UnitExists("pet")) or
|
|
(db.ShowWith.Party and GetNumPartyMembers() > 0) or
|
|
(db.ShowWith.Raid and GetNumRaidMembers() > 0)
|
|
end
|
|
self:Toggle(show)
|
|
end
|
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Omen bar stuff
|
|
|
|
do
|
|
local function animate(self, v)
|
|
self.animationCursor = self.animationCursor + v
|
|
if self.animationCursor > self.animationTime then
|
|
self.texture:SetWidth(self.animations[1])
|
|
tremove(self.animations, 1)
|
|
tremove(self.animations, 1)
|
|
tremove(self.animations, 1)
|
|
self.animationCursor = self.animationCursor - self.animationTime
|
|
if #self.animations == 0 then
|
|
self:SetScript("OnUpdate", nil)
|
|
end
|
|
else
|
|
self.texture:SetWidth(self.animations[2] + (self.animations[3] * (self.animationCursor / self.animationTime)))
|
|
end
|
|
end
|
|
|
|
local function AnimateTo(self, val)
|
|
if val == 1/0 or val == -1/0 then return end
|
|
if val == 0 then val = 1 end
|
|
if #self.animations > 0 and self.animations[#self.animations-2] == val then
|
|
return
|
|
end
|
|
local currentWidth = self.texture:GetWidth()
|
|
local diff = (val - currentWidth)
|
|
tinsert(self.animations, val)
|
|
tinsert(self.animations, currentWidth)
|
|
tinsert(self.animations, diff)
|
|
|
|
if #self.animations > 0 then
|
|
self:SetScript("OnUpdate", self.animate)
|
|
end
|
|
end
|
|
|
|
setmetatable(bars, {__index = function(self, barID)
|
|
local bar = CreateFrame("Frame", nil, Omen.BarList)
|
|
self[barID] = bar
|
|
|
|
bar:SetWidth(Omen.BarList:GetWidth())
|
|
bar:SetHeight(db.Bar.Height)
|
|
bar:SetPoint("TOPLEFT", Omen.BarList, "TOPLEFT", 0, (barID-1) * -(db.Bar.Height + db.Bar.Spacing))
|
|
|
|
bar.Text1 = bar:CreateFontString(nil, nil, "GameFontNormalSmall")
|
|
bar.Text1:SetPoint("LEFT", bar, "LEFT", 10, 0)
|
|
bar.Text1:SetJustifyH("LEFT")
|
|
bar.Text1:SetTextColor(1, 1, 1, 1)
|
|
bar.Text1:SetText("abc")
|
|
bar.Text2 = bar:CreateFontString(nil, nil, "GameFontNormalSmall")
|
|
bar.Text2:SetPoint("RIGHT", bar, "RIGHT", -10, 0)
|
|
bar.Text2:SetJustifyH("RIGHT")
|
|
bar.Text2:SetTextColor(1, 1, 1, 1)
|
|
bar.Text2:SetText("def")
|
|
|
|
bar.texture = bar:CreateTexture()
|
|
--bar.texture:SetTexture(1, 1, 1, 0.6)
|
|
bar.texture:SetTexture(LSM:Fetch("statusbar", db.Bar.Texture))
|
|
bar.texture:SetPoint("TOPLEFT", bar, "TOPLEFT")
|
|
bar.texture:SetPoint("BOTTOMLEFT", bar, "BOTTOMLEFT")
|
|
|
|
bar.animations = {}
|
|
bar.animationCursor = 0
|
|
bar.animationTime = 0.25
|
|
bar.animate = animate
|
|
bar.AnimateTo = AnimateTo
|
|
|
|
return bar
|
|
end})
|
|
end
|
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Omen event functions
|
|
|
|
-- Fired when a mob has its threat list updated. The mob that
|
|
-- had its list updated is the first parameter of the event.
|
|
function Omen:UNIT_THREAT_LIST_UPDATE(event, unitID)
|
|
if unitID == "target" then
|
|
self:UpdateBars()
|
|
end
|
|
end
|
|
|
|
-- Fired when a unit's threat situation changes. The unit that
|
|
-- had a change in threat situation is the first parameter of
|
|
-- the event. Note that this only triggers when major state
|
|
-- changes, not when the raw threat values change.
|
|
function Omen:UNIT_THREAT_SITUATION_UPDATE(...)
|
|
self:UpdateBars()
|
|
end
|
|
|
|
function Omen:PLAYER_TARGET_CHANGED()
|
|
-- Stop our unit update timer for updating threat on "targettarget"
|
|
if timers.UpdateBars then
|
|
self:CancelTimer(timers.UpdateBars, true)
|
|
timers.UpdateBars = nil
|
|
end
|
|
|
|
if UnitExists("target") then
|
|
self:UpdateBars()
|
|
else
|
|
self:ClearAll()
|
|
end
|
|
end
|
|
|
|
function Omen:UNIT_PET()
|
|
self:PARTY_MEMBERS_CHANGED()
|
|
end
|
|
|
|
function Omen:PLAYER_ENTERING_WORLD()
|
|
if UnitGUID("player") then
|
|
guidNameLookup[UnitGUID("player")] = UnitName("player")
|
|
end
|
|
self:UpdateVisible()
|
|
end
|
|
|
|
local lastPartyUpdateTime = GetTime()
|
|
|
|
function Omen:PARTY_MEMBERS_CHANGED()
|
|
inRaid = GetNumRaidMembers() > 0
|
|
inParty = GetNumPartyMembers() > 0
|
|
self:UpdateVisible()
|
|
|
|
-- Run the update if the last call is more than 0.5 seconds ago else
|
|
-- schedule an update 0.5 seconds later if one isn't already scheduled
|
|
if GetTime() - lastPartyUpdateTime > 0.5 then
|
|
self:UpdatePartyGUIDs()
|
|
elseif not timers.UpdatePartyGUIDs then
|
|
timers.UpdatePartyGUIDs = self:ScheduleTimer("UpdatePartyGUIDs", 0.5)
|
|
end
|
|
end
|
|
|
|
-- This function updates the name and class guid lookup tables of the raid
|
|
function Omen:UpdatePartyGUIDs()
|
|
lastPartyUpdateTime = GetTime()
|
|
if timers.UpdatePartyGUIDs then
|
|
self:CancelTimer(timers.UpdatePartyGUIDs, true)
|
|
timers.UpdatePartyGUIDs = nil
|
|
end
|
|
|
|
local _
|
|
for k, v in pairs(guidClassLookup) do
|
|
guidClassLookup[k] = nil
|
|
end
|
|
_, guidClassLookup[UnitGUID("player")] = UnitClass("player")
|
|
if UnitExists("pet") then
|
|
guidClassLookup[UnitGUID("pet")] = "PET"
|
|
guidNameLookup[UnitGUID("pet")] = UnitName("pet")--.." ["..UnitName("player").."]"
|
|
end
|
|
|
|
if inParty or inRaid then
|
|
local playerFmt = inRaid and rID or pID
|
|
local petFmt = inRaid and rpID or ppID
|
|
local currentPartySize = inRaid and GetNumRaidMembers() or GetNumPartyMembers()
|
|
|
|
for i = 1, currentPartySize do
|
|
local unitID = playerFmt[i]
|
|
local pGUID = UnitGUID(unitID)
|
|
|
|
if pGUID then
|
|
guidNameLookup[pGUID] = UnitName(unitID)
|
|
_, guidClassLookup[pGUID] = UnitClass(unitID)
|
|
|
|
-- lookup pet (if existing)
|
|
local petID = petFmt[i]
|
|
local petGUID = UnitGUID(petID)
|
|
if petGUID then
|
|
guidNameLookup[petGUID] = UnitName(petID)--.." ["..UnitName(unitID).."]"
|
|
guidClassLookup[petGUID] = "PET"
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Omen update functions
|
|
|
|
--[[
|
|
First, some definitions:
|
|
* mob - enemy creature
|
|
* threat list - a mob's list of possible targets, along with each possible target's current threat value
|
|
* threat situation - the situation that a unit is currently in (either globally, or with respect to a certain mob)
|
|
* scaled percentage - a threat percentage, where 100% means you will pull aggro (become the primary target of the mob), and thus this % cannot be higher than 100% under normal circumstances
|
|
* raw threat percentage - the percentage of the units threat when divided by the threat of the mob's current primary target, this % CAN be over 100%
|
|
---------
|
|
state = UnitThreatSituation(unit, mob)
|
|
|
|
Returns the unit's threat situation with respect to the given mob. The state can be one of the following values:
|
|
nil = the unit is not on the mob's threat list
|
|
0 = 0-99% raw threat percentage (no indicator shown)
|
|
1 = 100% or more raw threat percentage (yellow warning indicator shown)
|
|
2 = tanking, other has 100% or more raw threat percentage (orange indicator shown)
|
|
3 = tanking, all others have less than 100% raw percentage threat (red indicator shown)
|
|
---------
|
|
state = UnitThreatSituation(unit)
|
|
|
|
Returns the unit's maximum threat state on any mob's threat list.
|
|
---------
|
|
isTanking, state, scaledPercent, rawPercent, threatValue = UnitDetailedThreatSituation(unit, mob)
|
|
|
|
Returns detailed information about the unit's state on the mob's threat list.
|
|
isTanking is true if the unit the primary target of the mob (and by definition has 100% threat)
|
|
state is the unit's threat situation, as listed above.
|
|
scaledPercent is the current percent threat of the unit, scaled in the 0-100% range based on distance from target.
|
|
rawPercent is the current percent threat of the unit relative to the primary target of the mob.
|
|
threatValue is the amount of threat that the unit has on the mob's threat list. This is roughly approximate to the amount of damage and healing the unit has done.
|
|
---------
|
|
r, g, b = GetThreatStatusColor(state)
|
|
|
|
Returns the colors used in the UI to represent each major threat state.
|
|
]]
|
|
|
|
local threatTable = {} -- Format: threatTable[guid] = threatValue
|
|
local sortTable = {} -- Format: threatTable[i] = guid -- used for sorting by sortfunction()
|
|
local tankGUID -- Used to store which unit is tanking and hence has 100% threat by definition
|
|
|
|
local function sortfunction(a, b)
|
|
return threatTable[a] > threatTable[b]
|
|
end
|
|
|
|
local function updatethreat(unitid, mobunitid)
|
|
local guid = UnitGUID(unitid)
|
|
if guid and not threatTable[guid] then
|
|
local isTanking, state, scaledPercent, rawPercent, threatValue = UnitDetailedThreatSituation(unitid, mobunitid)
|
|
threatTable[guid] = threatValue or -1
|
|
if isTanking then tankGUID = guid end
|
|
end
|
|
end
|
|
|
|
function Omen:UpdateBars()
|
|
if not self.Anchor:IsShown() and not db.CollapseHide then return end
|
|
|
|
-- TODO: Put a update throtle on this function
|
|
|
|
-- Figure out which mob to show threat on, either "target" or "targettarget"
|
|
-- It has to be attackable and not human controlled, otherwise return
|
|
local mob = "target"
|
|
if UnitIsPlayer(mob) or UnitPlayerControlled(mob) or not UnitCanAttack("player", mob) then
|
|
mob = "targettarget"
|
|
end
|
|
if not UnitExists(mob) or UnitIsPlayer(mob) or UnitPlayerControlled(mob) or not UnitCanAttack("player", mob) then
|
|
self:ClearAll()
|
|
return
|
|
end
|
|
local mobGUID = UnitGUID(mob)
|
|
guidNameLookup[mobGUID] = UnitName(mob)
|
|
self.TitleText:SetText(guidNameLookup[mobGUID])
|
|
|
|
-- Schedule a repeating timer for updating threat on "targettarget"
|
|
-- since we get no events on a targettarget change.
|
|
if mob == "targettarget" and not timers.UpdateBars then
|
|
timers.UpdateBars = self:ScheduleRepeatingTimer("UpdateBars", 0.5)
|
|
end
|
|
|
|
-- We want the mob's target just in case the tank isn't
|
|
-- in our raid (say an NPC or some other player)
|
|
local mobTarget = mob.."target"
|
|
local mobTargetGUID = UnitGUID(mobTarget)
|
|
if mobTargetGUID and not guidNameLookup[mobTargetGUID] then
|
|
guidNameLookup[mobTargetGUID] = UnitName(mobTarget)
|
|
end
|
|
|
|
-- Clear the threat table
|
|
for k, v in pairs(threatTable) do
|
|
threatTable[k] = nil
|
|
end
|
|
threatTable[mobGUID] = -1
|
|
tankGUID = nil
|
|
|
|
-- Get data for threat on mob by scanning the whole raid
|
|
if inParty or inRaid then
|
|
if inRaid then
|
|
for i = 1, GetNumRaidMembers() do
|
|
updatethreat(rID[i], mob)
|
|
updatethreat(rpID[i], mob)
|
|
updatethreat(rtID[i], mob)
|
|
updatethreat(rptID[i], mob)
|
|
end
|
|
else
|
|
for i = 1, GetNumPartyMembers() do
|
|
updatethreat(pID[i], mob)
|
|
updatethreat(ppID[i], mob)
|
|
updatethreat(ptID[i], mob)
|
|
updatethreat(pptID[i], mob)
|
|
end
|
|
end
|
|
|
|
end
|
|
if not inRaid then
|
|
updatethreat("player", mob)
|
|
updatethreat("pet", mob)
|
|
updatethreat("target", mob)
|
|
updatethreat("pettarget", mob)
|
|
end
|
|
updatethreat("focus", mob)
|
|
updatethreat("focustarget", mob)
|
|
updatethreat(mobTarget, mob)
|
|
updatethreat("mouseover", mob)
|
|
updatethreat("mouseovertarget", mob)
|
|
|
|
-- Sort the threatTable
|
|
local i = 1
|
|
for k, v in pairs(threatTable) do
|
|
if v >= 0 then
|
|
sortTable[i] = k
|
|
i = i + 1
|
|
end
|
|
end
|
|
for j = i, #sortTable do
|
|
sortTable[j] = nil
|
|
end
|
|
if #sortTable == 0 then
|
|
self:ClearAll()
|
|
self.TitleText:SetText(guidNameLookup[mobGUID])
|
|
return
|
|
end
|
|
sort(sortTable, sortfunction)
|
|
|
|
-- Now update the bars on screen
|
|
local w = self.BarList:GetWidth()
|
|
local h = self.BarList:GetHeight()
|
|
local topthreat = threatTable[sortTable[1]]
|
|
if topthreat == 0 then topthreat = 1 end
|
|
local tankThreat = threatTable[tankGUID or mobTargetGUID or sortTable[1]]
|
|
i = 1
|
|
for j = 1, #sortTable do
|
|
local guid = sortTable[j]
|
|
local class = guidClassLookup[guid]
|
|
if class == nil or db.Classes[class] then -- class == nil implies its not in your raid
|
|
if db.Autocollapse then
|
|
if i > db.NumBars then break end
|
|
else
|
|
if i*db.Bar.Height + (i-1)*db.Bar.Spacing > h then break end
|
|
end
|
|
local bar = bars[j]
|
|
local threat = threatTable[guid]
|
|
bar.Text1:SetText(guidNameLookup[guid] or L["Unknown"])
|
|
if db.Bar.ShortNumbers and threat > 100000 then
|
|
bar.Text2:SetFormattedText("%2.1fk [%d%%]", threat / 100000, tankThreat == 0 and 0 or threat / tankThreat * 100)
|
|
else
|
|
bar.Text2:SetFormattedText("%d [%d%%]", threat / 100, tankThreat == 0 and 0 or threat / tankThreat * 100)
|
|
end
|
|
local c = (class == "PET" and pet_color) or RAID_CLASS_COLORS[class] or default_color
|
|
bar.texture:SetVertexColor(c.r, c.g, c.b)
|
|
if db.Bar.AnimateBars and self.Anchor.IsMovingOrSizing ~= 2 then
|
|
bar:AnimateTo(w * threat / topthreat)
|
|
else
|
|
bar.texture:SetWidth(w * threat / topthreat)
|
|
end
|
|
bar:Show()
|
|
i = i + 1
|
|
end
|
|
end
|
|
-- And hide the rest
|
|
for j = i, #bars do
|
|
bars[j]:Hide()
|
|
end
|
|
if db.Autocollapse then
|
|
self.Anchor:SetHeight((i-1)*db.Bar.Height + (i-2)*db.Bar.Spacing + 16)
|
|
end
|
|
self.Anchor:Show()
|
|
end
|
|
|
|
function Omen:ClearAll()
|
|
for i = 1, #bars do
|
|
bars[i]:Hide()
|
|
end
|
|
self.TitleText:SetText(self.defaultTitle)
|
|
if db.Autocollapse then
|
|
self.Anchor:SetHeight(16)
|
|
if db.CollapseHide and not self.Anchor.IsMovingOrSizing then
|
|
self.Anchor:Hide()
|
|
end
|
|
end
|
|
end
|
|
|
|
function Omen:ResizeBars()
|
|
local w = Omen.BarList:GetWidth()
|
|
for i = 1, #bars do
|
|
bars[i]:SetWidth(w)
|
|
bars[i]:SetHeight(db.Bar.Height)
|
|
end
|
|
end
|
|
|
|
function Omen:ReAnchorBars()
|
|
for i = 1, #bars do
|
|
bars[i]:SetPoint("TOPLEFT", self.BarList, "TOPLEFT", 0, (i-1) * -(db.Bar.Height + db.Bar.Spacing))
|
|
end
|
|
end
|
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Omen config stuff
|
|
|
|
local function GetLSMIndex(t, value)
|
|
for k, v in pairs(LSM:List(t)) do
|
|
if v == value then
|
|
return k
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local options = {
|
|
type = "group",
|
|
name = "Omen",
|
|
get = function(info) return Omen.db.profile[ info[#info] ] end,
|
|
set = function(info, value) Omen.db.profile[ info[#info] ] = value end,
|
|
args = {
|
|
general = {
|
|
order = 1,
|
|
type = "group",
|
|
name = L["General Settings"],
|
|
args = {
|
|
intro = {
|
|
order = 1,
|
|
type = "description",
|
|
name = L["OMEN_DESC"],
|
|
},
|
|
Alpha = {
|
|
order = 3,
|
|
name = L["Alpha"],
|
|
desc = L["Controls the transparency of the main Omen window."],
|
|
type = "range",
|
|
min = 0, max = 1, step = 0.01,
|
|
isPercent = true,
|
|
set = function(info, value)
|
|
db.Alpha = value
|
|
Omen.Anchor:SetAlpha(value)
|
|
end,
|
|
},
|
|
Scale = {
|
|
order = 5,
|
|
name = L["Scale"],
|
|
desc = L["Controls the scaling of the main Omen window."],
|
|
type = "range",
|
|
min = 0.50, max = 1.50, step = 0.01,
|
|
isPercent = true,
|
|
set = function(info, value)
|
|
db.Scale = value
|
|
Omen:SetAnchors()
|
|
end,
|
|
},
|
|
Locked = {
|
|
type = "toggle",
|
|
name = L["Lock Omen"],
|
|
desc = L["Locks Omen in place and prevents it from being dragged or resized."],
|
|
order = 7,
|
|
set = function(info, value)
|
|
db.Locked = value
|
|
if value then Omen.Grip:Hide() else Omen.Grip:Show() end
|
|
end,
|
|
},
|
|
linebreak = {
|
|
order = 9,
|
|
type = "description",
|
|
name = "",
|
|
},
|
|
Autocollapse = {
|
|
type = "toggle",
|
|
name = L["Autocollapse"],
|
|
desc = L["Collapse to show a minimum number of bars"],
|
|
order = 10,
|
|
set = function(info, value)
|
|
db.Autocollapse = value
|
|
Omen:UpdateVisible()
|
|
Omen:UpdateBars()
|
|
end,
|
|
},
|
|
AutocollapseGroup = {
|
|
type = "group",
|
|
name = L["Autocollapse Options"],
|
|
guiInline = true,
|
|
order = 11,
|
|
disabled = function() return not Omen.db.profile.Autocollapse end,
|
|
set = function(info, value)
|
|
Omen.db.profile[ info[#info] ] = value
|
|
Omen:UpdateVisible()
|
|
Omen:UpdateBars()
|
|
end,
|
|
args = {
|
|
GrowUp = {
|
|
order = 1,
|
|
type = "toggle",
|
|
name = L["Grow bars upwards"],
|
|
desc = L["Grow bars upwards"],
|
|
set = function(info, value)
|
|
db.GrowUp = value
|
|
Omen:SetAnchors()
|
|
end,
|
|
},
|
|
CollapseHide = {
|
|
order = 2,
|
|
type = "toggle",
|
|
name = L["Hide Omen on 0 bars"],
|
|
desc = L["Hide Omen entirely if it collapses to show 0 bars"],
|
|
},
|
|
NumBars = {
|
|
order = 3,
|
|
name = L["Max bars to show"],
|
|
desc = L["Max number of bars to show"],
|
|
type = "range",
|
|
min = 1, max = 40, step = 1,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
ShowWhen = {
|
|
type = "group",
|
|
name = L["Show When..."],
|
|
desc = L["Show Omen when..."],
|
|
get = function(info) return Omen.db.profile.ShowWith[ info[#info] ] end,
|
|
set = function(info, value)
|
|
Omen.db.profile.ShowWith[ info[#info] ] = value
|
|
Omen:UpdateVisible()
|
|
Omen:UpdateBars()
|
|
end,
|
|
args = {
|
|
intro = {
|
|
order = 1,
|
|
type = "description",
|
|
name = L["Show Omen when any of the following are true"],
|
|
},
|
|
Pet = {
|
|
type = "toggle",
|
|
name = L["You have a pet"],
|
|
desc = L["Show Omen when you have a pet out"],
|
|
},
|
|
Alone = {
|
|
type = "toggle",
|
|
name = L["You are alone"],
|
|
desc = L["Show Omen when you are alone"],
|
|
},
|
|
Party = {
|
|
type = "toggle",
|
|
name = L["You are in a party"],
|
|
desc = L["Show Omen when you are in a 5-man party"],
|
|
},
|
|
Raid = {
|
|
type = "toggle",
|
|
name = L["You are in a raid"],
|
|
desc = L["Show Omen when you are in a raid"],
|
|
},
|
|
Resting = {
|
|
type = "toggle",
|
|
name = L["You are resting"],
|
|
desc = L["Show Omen when you are resting"],
|
|
},
|
|
PVP = {
|
|
type = "toggle",
|
|
name = L["You are in a battleground"],
|
|
desc = L["Show Omen when you are in a battleground or arena"],
|
|
},
|
|
Dungeon = {
|
|
type = "toggle",
|
|
name = L["You are in a dungeon"],
|
|
desc = L["Show Omen when you are in a dungeon (5 man and raid)"],
|
|
},
|
|
},
|
|
},
|
|
Bars = {
|
|
type = "group",
|
|
name = L["Bar Settings"],
|
|
desc = L["Bar Settings"],
|
|
get = function(info) return Omen.db.profile.Bar[ info[#info] ] end,
|
|
set = function(info, value)
|
|
Omen.db.profile.Bar[ info[#info] ] = value
|
|
Omen:UpdateBars()
|
|
end,
|
|
args = {
|
|
intro = {
|
|
order = 1,
|
|
type = "description",
|
|
name = L["Configure bar settings."],
|
|
},
|
|
AnimateBars = {
|
|
type = "toggle",
|
|
name = L["Animate Bars"],
|
|
desc = L["Smoothly animate bar changes"],
|
|
},
|
|
ShortNumbers = {
|
|
type = "toggle",
|
|
name = L["Short Numbers"],
|
|
desc = L["Display large numbers in Ks"],
|
|
},
|
|
Texture = {
|
|
type = "select",
|
|
name = L["Bar Texture"],
|
|
desc = L["The texture that the bar will use"],
|
|
values = function() return LSM:List("statusbar") end,
|
|
get = function(info) return GetLSMIndex("statusbar", db.Bar.Texture) end,
|
|
set = function(info, v)
|
|
db.Bar.Texture = LSM:List("statusbar")[v]
|
|
local texturepath = LSM:Fetch("statusbar", db.Bar.Texture)
|
|
for i = 1, #bars do
|
|
bars[i].texture:SetTexture(texturepath)
|
|
end
|
|
end
|
|
},
|
|
Height = {
|
|
type = "range",
|
|
name = L["Bar Height"],
|
|
desc = L["Height of each bar"],
|
|
min = 5, max = 50, step = 1, bigStep = 1,
|
|
set = function(info, value)
|
|
db.Bar.Height = value
|
|
Omen:ReAnchorBars()
|
|
Omen:ResizeBars()
|
|
Omen:UpdateBars()
|
|
end,
|
|
},
|
|
Spacing = {
|
|
type = "range",
|
|
name = L["Bar Spacing"],
|
|
desc = L["Spacing between each bar"],
|
|
min = 0, max = 20, step = 1, bigStep = 1,
|
|
set = function(info, value)
|
|
db.Bar.Spacing = value
|
|
Omen:ReAnchorBars()
|
|
Omen:UpdateBars()
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
Help = {
|
|
type = "group",
|
|
name = L["Help File"],
|
|
desc = L["A collection of help pages"],
|
|
childGroups = "select",
|
|
args = {
|
|
FAQ = {
|
|
type = "group",
|
|
name = "FAQ",
|
|
args = {
|
|
header = {
|
|
type = "header",
|
|
name = L["Frequently Asked Questions"],
|
|
order = 0,
|
|
},
|
|
text = {
|
|
order = 1,
|
|
type = "description",
|
|
name = L["GENERAL_FAQ"],
|
|
},
|
|
},
|
|
},
|
|
WARRIOR = {
|
|
type = "group",
|
|
name = "Warrior",
|
|
args = {
|
|
header = {
|
|
type = "header",
|
|
name = L["Warrior"],
|
|
order = 0,
|
|
},
|
|
text = {
|
|
order = 1,
|
|
type = "description",
|
|
name = L["WARRIOR_FAQ"],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
local optionsSlash = {
|
|
type = "group",
|
|
name = L["Slash Command"],
|
|
args = {
|
|
intro = {
|
|
order = 1,
|
|
type = "description",
|
|
name = L["OMEN_SLASH_DESC"],
|
|
cmdHidden = true,
|
|
},
|
|
toggle = {
|
|
type = "execute",
|
|
name = L["Toggle Omen"],
|
|
desc = L["Toggle Omen"].." ( /omen toggle )",
|
|
func = function()
|
|
Omen:Toggle()
|
|
end,
|
|
},
|
|
center = {
|
|
type = "execute",
|
|
name = L["Center Omen"],
|
|
desc = L["Center Omen"].." ( /omen center )",
|
|
func = function()
|
|
Omen.Anchor:ClearAllPoints()
|
|
Omen.Anchor:SetPoint("CENTER", UIParent, "CENTER")
|
|
Omen:SetAnchors()
|
|
end,
|
|
},
|
|
config = {
|
|
type = "execute",
|
|
name = L["Configure"],
|
|
desc = L["Open the configuration dialog"].." ( /omen config )",
|
|
func = function()
|
|
Omen:ShowConfig()
|
|
end,
|
|
guiHidden = true,
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
function Omen:SetupOptions()
|
|
self.optionsFrames = {}
|
|
|
|
-- setup options table
|
|
LibStub("AceConfigRegistry-3.0"):RegisterOptionsTable("Omen", options)
|
|
LibStub("AceConfig-3.0"):RegisterOptionsTable("OmenSlashCommand", optionsSlash, "omen")
|
|
self.optionsFrames.Omen = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("Omen", nil, nil, "general")
|
|
|
|
self:RegisterModuleOptions("Profiles", LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db), L["Profiles"])
|
|
self:RegisterModuleOptions("OmenSlashCommand", optionsSlash, L["Slash Command"])
|
|
self.optionsFrames["Help"] = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("Omen", L["Help File"], "Omen", "Help")
|
|
self.optionsFrames["ShowWhen"] = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("Omen", L["Show When..."], "Omen", "ShowWhen")
|
|
self.optionsFrames["Bars"] = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("Omen", L["Bar Settings"], "Omen", "Bars")
|
|
end
|
|
|
|
function Omen:RegisterModuleOptions(name, optionTbl, displayName)
|
|
options.args[name] = (type(optionTbl) == "function") and optionTbl() or optionTbl
|
|
self.optionsFrames[name] = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("Omen", displayName, "Omen", name)
|
|
end
|
|
|
|
function Omen:ShowConfig()
|
|
-- Open the profiles tab before, so the menu expands
|
|
InterfaceOptionsFrame_OpenToCategory(self.optionsFrames.Profiles)
|
|
InterfaceOptionsFrame_OpenToCategory(self.optionsFrames.Omen)
|
|
end
|