Thread Tools Display Modes
08-21-21, 11:24 AM   #1
Billtopia
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Apr 2009
Posts: 110
Time to die

So I am doing something like the following to figure out how long it will take a mob to die...is there a better way or a "smoother" or more accurate way (it is a time to die driven status bar being you vs your target)it does the calculations for you and your target and sets the value of the status bar off of that along with showing both times to die as text under the bar (RemainingTimeToKill is capped also to prevent infinite errors)

the HPs / MaxHPs are actually updated by unit event handlers but this is the current formula I am using

lua Code:
  1. -- CombatStartTime is set on event elsewhere
  2. local FullHP = UnitHealtMax("target")
  3. local CurrentHP = UnitHealth("target")
  4. local LostHP = FullHP - CurrentHP
  5. local TimeSpent = (Time - CombatStartTime > 1) and (Time - CombatStartTime) or 1
  6. local DPS = LostHP / TimeSpent > 0.1 and LostHP / TimeSpent or 0.1
  7. local FullTimeToKill = FullHP / DPS
  8. local RemainingTimeToKill = math.floor((FullTimeToKill - TimeSpent) + 0.5)
  Reply With Quote
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
08-22-21, 08:22 AM   #3
Billtopia
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Apr 2009
Posts: 110
Thanks... I will have to figure out how to work this in to the addon... It should solve the jumpiness nicely. (All the GUID/units my addon tracks.. (They get added from "NAME_PLATE_UNIT_ADDED" initially. Well .5 seconds after as when I tried when the event fired I wasn't getting all the info available) and combat checked when added... I will have to tweak my GUID cache table (already contains names, health data, and more) and put the tables to average in them along with the ticker so I can purge old records but destroy the ticker first... because I don't want to leave a ton of them around. (Well I guess I could make them self delete easily enough which would be even better than purging the records on "PLAYER_REGEN_ENABLED") So many new ideas... Thank You
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Time to die

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off