diff --git a/Localization/enUS.lua b/Localization/enUS.lua index 27fb078..f10f0ad 100644 --- a/Localization/enUS.lua +++ b/Localization/enUS.lua @@ -9,6 +9,9 @@ L["Enter Test Mode"] = true L["Exit Test Mode"] = true L["Unlock Omen"] = true L["Open Config"] = true +L["Name"] = true +L["Threat [%]"] = true +L["TPS"] = true -- Warnings L["|cffff0000Error:|r Omen cannot use shake warning if you have turned on nameplates at least once since logging in."] = true diff --git a/Omen.lua b/Omen.lua index 972dfa0..9a9f041 100644 --- a/Omen.lua +++ b/Omen.lua @@ -71,7 +71,7 @@ local defaults = { Locked = false, PositionW = 200, PositionH = 82, - VGrip1 = 120, + VGrip1 = 115, Background = { Texture = "Blizzard Parchment", BorderTexture = "Blizzard Dialog", @@ -189,6 +189,48 @@ for i = 1, 40 do end +----------------------------------------------------------------------------- +-- Table Pool for recycling tables +local tablePool = {} +setmetatable(tablePool, {__mode = "kv"}) -- Weak table + +-- Get a new table +local function newTable() + local t = next(tablePool) or {} + tablePool[t] = nil + return t +end + +-- Delete table and return to pool -- Recursive!! -- Use with care!! +local function delTable(t) + if type(t) == "table" then + for k, v in pairs(t) do + if type(v) == "table" then + delTable(v) -- child tables get put into the pool + end + t[k] = nil + end + t[true] = true -- resize table to 1 item + t[true] = nil + setmetatable(t, nil) + tablePool[t] = true + end + return nil -- return nil to assign input reference +end + +-- Empties a table of everything -- Non-recursive +local function clearTable(t) + if type(t) == "table" then + for k, v in pairs(t) do + t[k] = nil + end + t[true] = true + t[true] = nil + setmetatable(t, nil) + end +end + + ----------------------------------------------------------------------------- -- Omen initialization and frame functions @@ -747,6 +789,16 @@ do bar.Text2:SetHeight(db.Bar.FontSize) bar.Text2:SetNonSpaceWrap(false) + bar.Text3 = bar:CreateFontString(nil, nil, "GameFontNormalSmall") + bar.Text3:SetPoint("RIGHT", bar, "RIGHT", -5, 1) + bar.Text3:SetJustifyH("RIGHT") + bar.Text3:SetFont(LSM:Fetch("font", db.Bar.Font), db.Bar.FontSize, db.Bar.FontOutline) + bar.Text3:SetTextColor(color.r, color.g, color.b, color.a) + bar.Text3:SetWidth(Omen.BarList:GetWidth() - db.VGrip1 - 5) + bar.Text3:SetHeight(db.Bar.FontSize) + bar.Text3:SetNonSpaceWrap(false) + bar.Text3:Hide() + bar.texture = bar:CreateTexture() bar.texture:SetTexture(LSM:Fetch("statusbar", db.Bar.Texture)) bar.texture:SetPoint("TOPLEFT", bar, "TOPLEFT") @@ -757,6 +809,17 @@ do bar.animationTime = 0.25 bar.AnimateTo = AnimateTo + if barID == 0 then + bar.Text1:SetText(L["Name"]) + bar.Text2:SetText(L["Threat [%]"]) + bar.Text3:SetText(L["TPS"]) + elseif barID == 1 then + -- Parent our TPS update frame to the first bar, so that TPS updates + -- updates happen when at least 1 bar (the first bar) is shown. + Omen.TPSUpdateFrame = CreateFrame("Frame", bar) + Omen.TPSUpdateFrame:SetScript("OnUpdate", function(self, elapsed) Omen:UpdateTPS() end) + end + return bar end}) end @@ -936,12 +999,14 @@ 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 lastWarn = { -- Used to store information for threat warnings +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 lastWarn = { -- Used to store information for threat warnings threatpercent = 0, } +local threatStore = {} -- Format: threatStore[i] = threatTable[guid] -- used for storing past threatTables +local threatStoreTime = {} -- Format: threatStoreTime[i] = GetTime() local function sortfunction(a, b) return threatTable[a] > threatTable[b] @@ -964,9 +1029,7 @@ function Omen:UpdateBars() local mobGUID, mobTargetGUID if testMode then - for k, v in pairs(threatTable) do - threatTable[k] = nil - end + threatTable = newTable() for i = 1, 25 do threatTable[i] = i*5000 end @@ -1002,10 +1065,7 @@ function Omen:UpdateBars() guidNameLookup[mobTargetGUID] = UnitName(mobTarget) end - -- Clear the threat table - for k, v in pairs(threatTable) do - threatTable[k] = nil - end + threatTable = newTable() threatTable[mobGUID] = -1 tankGUID = nil @@ -1094,6 +1154,7 @@ function Omen:UpdateBars() else bar.texture:SetWidth(width) end + bar.guid = guid bar:Show() i = i + 1 end @@ -1109,7 +1170,9 @@ function Omen:UpdateBars() self.BarList:Show() -- Threat warnings - if not testMode then + if testMode then + threatTable = delTable(threatTable) + else local pGUID = UnitGUID("player") local pClass = guidClassLookup[pGUID] local myThreatPercent = threatTable[pGUID] / tankThreat * 100 @@ -1122,8 +1185,18 @@ function Omen:UpdateBars() self:Warn(t.Sound, t.Flash, t.Shake, t.Message and L["Passed %s%% of %s's threat!"]:format(t.Threshold, guidNameLookup[tankGUID or mobTargetGUID or sortTable[1]])) end end + -- Remove TPS data if the last scanned mob is different + if lastWarn.mobGUID ~= mobGUID then + delTable(threatStore) + threatStore = newTable() + clearTable(threatStoreTime) + end + tinsert(threatStore, threatTable) + tinsert(threatStoreTime, GetTime()) + -- Store last scanned mob GUID lastWarn.mobGUID = mobGUID lastWarn.threatpercent = myThreatPercent + threatTable = nil end end @@ -1139,8 +1212,57 @@ function Omen:ClearAll() self.Anchor:Hide() end end + -- Store last scanned mob GUID lastWarn.mobGUID = nil lastWarn.threatpercent = 0 + -- Remove TPS data + delTable(threatStore) + threatStore = newTable() + clearTable(threatStoreTime) + threatTable = nil +end + +function Omen:UpdateTPS() + -- Remove data that is too old + local startTime = GetTime() - 10 + while threatStoreTime[2] and startTime > threatStoreTime[2] do + delTable(tremove(threatStore, 1)) + tremove(threatStoreTime, 1) + end + -- Now check that we still have enough data + local dataSize = #threatStoreTime + if dataSize == 0 or startTime <= threatStoreTime[1] then + -- We do not have enough data, 10 seconds has not passed + for i = 1, #bars do + bars[i].Text3:SetText("??") + end + return + end + -- Check for special case with just 1 data point past 10 seconds + if dataSize == 1 then + -- Threat generated is 0 + for i = 1, #bars do + bars[i].Text3:SetText("0") + end + return + end + -- We have at least 2 data points + for i = 1, #bars do + if not bars[i]:IsShown() then return end + local guid = bars[i].guid + local baseThreat = threatStore[1][guid] + local secondThreat = threatStore[2][guid] + local finalThreat = threatStore[dataSize][guid] + if baseThreat and secondThreat and finalThreat then + -- Calculate TPS + local ratio = (startTime - threatStoreTime[1]) / (threatStoreTime[2] - threatStoreTime[1]) + local startThreat = (secondThreat - baseThreat) * ratio + baseThreat + bars[i].Text3:SetFormattedText("%d", (finalThreat - startThreat) / 1000) + else + -- We don't have enough data for this unit + bars[i].Text3:SetText("??") + end + end end