Thread: Time to die
View Single Post
08-21-21, 08:04 PM   #2
DahkCeles
A Cliff Giant
 
DahkCeles's Avatar
AddOn Author - Click to view addons
Join Date: Jun 2020
Posts: 73
This is a more complicated solution that would weight recent performance more strongly and only consider the last 15 seconds.... which should shield you from awkward boss fights that have intermission phases.
  • Simple average, below five seconds
  • 10% moving average, beyond 15 seconds
  • Hybrid between 5 and 15 seconds

Lua Code:
  1. local healthRecord = {}
  2. local tickers = {}
  3.  
  4. local pattern = "Target will die in %d seconds"
  5.  
  6. local function updateEstimate(unit)
  7.     local record = healthRecord[unit]
  8.     tinsert(record, UnitHealth(unit)/UnitHealthMax(unit))
  9.     local time = #record
  10.     local dps
  11.     if (time > 15) then
  12.         dps = record[time-15] - record[time-14]
  13.         for i=time-14, time-1 do
  14.             dps = 0.9*dps + 0.1*(record[i-1]-record[i])
  15.         end
  16.     elseif (time > 5) then
  17.         dps = (record[1] - record[5]) / 4
  18.         for i=5, time-1 do
  19.             dps = 0.8*dps + 0.2*(record[i-1]-record[i])
  20.         end
  21.     else
  22.         dps = (record[1] - record[time]) / (time-1)
  23.     end
  24.     if (dps > 0.01) then
  25.         local timeToDie = record[time] / dps
  26.         print(pattern:format(timeToDie))
  27.     elseif (dps < -0.01) then
  28.         print("Not dying any time soon!")
  29.     else
  30.         print("Are you doing any damage?")
  31.     end
  32. end
  33.  
  34. local function startTracking(unit)
  35.     if (healthRecord[unit]) then
  36.         wipe(healthRecord[unit])
  37.     else
  38.         healthRecord[unit] = {}
  39.     end
  40.     healthRecord[unit][1] = UnitHealth(unit)/UnitHealthMax(unit)
  41.     tickers[unit] = tickers[unit] or C_Timer.NewTicker(1, function() updateEstimate(unit) end)
  42. end
  43.  
  44. local function stopTracking(unit)
  45.     if tickers[unit] then
  46.         tickers[unit]:Cancel()
  47.         tickers[unit] = nil
  48.     end
  49. end
  50.  
  51. local function stopAllTracking()
  52.     for unit, ticker in pairs(tickers) do
  53.         ticker:Cancel()
  54.         tickers[unit] = nil
  55.     end
  56. end
  57.  
  58. local frame = CreateFrame("Frame")
  59. frame:RegisterUnitEvent("UNIT_TARGET", "player")
  60. frame:RegisterUnitEvent("UNIT_HEALTH", "target")
  61. frame:RegisterEvent("PLAYER_REGEN_ENABLED")
  62. frame:RegisterEvent("PLAYER_REGEN_DISABLED")
  63. frame:SetScript("OnEvent", function(__, event)
  64.     if (event == "UNIT_TARGET" and InCombatLockdown() or event == "PLAYER_REGEN_DISABLED") then
  65.         stopTracking("target")
  66.         if (UnitExists("target") and not UnitIsDeadOrGhost("target")) then
  67.             startTracking("target")
  68.         end
  69.     elseif (event == "UNIT_HEALTH" and UnitIsDeadOrGhost("target")) then
  70.         stopTracking("target")
  71.     elseif (event == "PLAYER_REGEN_ENABLED") then
  72.         stopAllTracking()
  73.     end
  74. end)


PS, if you think this is a bit too complex (you wouldn't be wrong!) then consider a simple-moving-average... ie, just take the last X seconds and do a single division whenever its more CombatStartTime is beyond some threshold.

Last edited by DahkCeles : 08-21-21 at 09:12 PM.
  Reply With Quote