Repository navigation
Expand file tree
/
Copy pathTimeToDie.lua
More file actions
executable file
·243 lines (201 loc) · 6.22 KB
/
Copy pathTimeToDie.lua
File metadata and controls
executable file
·243 lines (201 loc) · 6.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
local LubStub = LibStub
local TimeToDie = LibStub('AceAddon-3.0'):NewAddon('TimeToDie', 'AceEvent-3.0', 'AceConsole-3.0')
_G.TimeToDie = TimeToDie
local SML = LibStub("LibSharedMedia-3.0")
------- Initialization -------
local UnitHealth = UnitHealth
local GetTime = GetTime
local format = format
local ceil = ceil
local samplingFrequency = samplingFrequency
local updateFrequency = updateFrequency
local interpolationMaxPoints = interpolationMaxPoints
local interpolationMinPoints = interpolationMinPoints
local defaults = {
profile = {
timeFormat = '%d:%02d',
frame = true,
locked = false,
samplingFrequency = 0.2,
updateFrequency = 1,
interpolationMaxPoints = 50,
interpolationMinPoints = 2,
font = defaultFont,
size = 24,
outline = '',
r = 1,
g = .82,
b = 0,
strata = 'LOW',
justify = 'CENTER',
p1 = 'CENTER',
p2 = 'CENTER',
x = 0,
y = 0,
}
}
function TimeToDie:OnInitialize()
local db = LibStub('AceDB-3.0'):New('TimeToDieDB', defaults, 'Default')
self.db = db
local RegisterCallback = db.RegisterCallback
RegisterCallback(self, 'OnProfileChanged', 'OnEnable')
RegisterCallback(self, 'OnProfileCopied', 'OnEnable')
RegisterCallback(self, 'OnProfileReset', 'OnEnable')
self:RegisterOptions()
end
function TimeToDie:OnEnable()
local profile = self.db.profile
self:ApplySettings(profile)
if profile.frame then
self:UpdateFrame(profile)
end
end
------- Event Functions --------
local eventFrame
local dataobj
local timeFormat
local interpolating = false
local interpolationHealthPoints = {}
local interpolationTimePoints = {}
local interpolationSavedPoints = 0
local interpolationIndex = 0
local healthSum = 0
local timeSum = 0
local healthTimeSum = 0
local timeSquaredSum = 0
function TimeToDie:ProjectTime(currentHealth, currentTime, needsUpdate)
interpolating = true
interpolationIndex = (interpolationIndex % interpolationMaxPoints) + 1
if interpolationSavedPoints < interpolationMaxPoints then
interpolationSavedPoints = interpolationSavedPoints + 1
else
local oldestHealth = interpolationHealthPoints[interpolationIndex]
local oldestTime = interpolationTimePoints[interpolationIndex]
healthSum = healthSum - oldestHealth
timeSum = timeSum - oldestTime
healthTimeSum = healthTimeSum - oldestHealth * oldestTime
timeSquaredSum = timeSquaredSum - oldestTime * oldestTime
end
interpolationHealthPoints[interpolationIndex] = currentHealth
interpolationTimePoints[interpolationIndex] = currentTime
healthSum = healthSum + currentHealth
timeSum = timeSum + currentTime
healthTimeSum = healthTimeSum + currentHealth * currentTime
timeSquaredSum = timeSquaredSum + currentTime * currentTime
if interpolationSavedPoints < interpolationMinPoints then
return
end
slope = (interpolationSavedPoints * healthTimeSum - healthSum * timeSum) / (interpolationSavedPoints * timeSquaredSum - timeSum * timeSum)
if slope >= 0 then
TimeToDie:ResetInterpolation()
return
end
local projectedTime = currentHealth / slope * -1
if projectedTime > 86400 then
TimeToDie:ResetInterpolation()
return
end
if needsUpdate then
if projectedTime < 60 or timeFormat == 'seconds' then
dataobj.text = ceil(projectedTime)
else
dataobj.text = format(timeFormat, 1/60 * projectedTime, projectedTime % 60)
end
end
end
function TimeToDie:PLAYER_TARGET_CHANGED(self, event, unit)
TimeToDie:ResetInterpolation()
end
function TimeToDie:ResetInterpolation()
if interpolating then
dataobj.text = nil
interpolationIndex = 0
interpolationSavedPoints = 0
healthSum = 0
timeSum = 0
healthTimeSum = 0
timeSquaredSum = 0
interpolating = false
end
end
local totalElapsedSinceLastSampling = 0
local totalElapsedSinceLastUpdate = 0
local function OnUpdate(self, elapsed)
totalElapsedSinceLastSampling = totalElapsedSinceLastSampling + elapsed
totalElapsedSinceLastUpdate = totalElapsedSinceLastUpdate + elapsed
if totalElapsedSinceLastSampling >= samplingFrequency then
totalElapsedSinceLastSampling = 0
local currentHealth = UnitHealth('target')
if currentHealth <= 0 then
TimeToDie:ResetInterpolation()
return
end
local currentTime = GetTime()
local needsUpdate = false
if totalElapsedSinceLastUpdate >= updateFrequency then
needsUpdate = true
totalElapsedSinceLastUpdate = 0
end
TimeToDie:ProjectTime(currentHealth, currentTime, needsUpdate)
end
end
function TimeToDie:EnableEventFrame()
if not eventFrame then
eventFrame = CreateFrame('Frame')
self.eventFrame = eventFrame
end
self:RegisterEvent('PLAYER_TARGET_CHANGED', PLAYER_TARGET_CHANGED)
eventFrame:SetScript('OnUpdate', OnUpdate)
eventFrame:Show()
end
function TimeToDie:DisableEventFrame()
if eventFrame then
eventFrame:Hide()
eventFrame:UnregisterAllEvents()
end
end
function TimeToDie:ApplySettings(profile)
self:DisableEventFrame()
timeFormat = profile.timeFormat
self:EnableEventFrame()
end
function TimeToDie:UpdateFrame(profile)
local frame = self.frame
if not frame then
frame = CreateFrame('Frame', nil, UIParent, 'TimeToDieFrameTemplate')
self.frame = frame
end
local text = frame.text
frame:ClearAllPoints()
frame:SetPoint(profile.p1, UIParent, profile.p2, profile.x, profile.y)
if profile.frame then
frame:Show()
else
frame:Hide()
profile.locked = true
end
if profile.locked then
frame.bg:Hide()
frame:EnableMouse(false)
dataobj.text = nil
else
frame.bg:Show()
frame:EnableMouse(true)
dataobj.text = 'TimeToDie'
end
text:SetFont(SML:Fetch('font', profile.font), profile.size, profile.outline)
text:SetTextColor(profile.r, profile.g, profile.b)
frame:SetFrameStrata(profile.strata)
text:SetJustifyH(profile.justify)
samplingFrequency = profile.samplingFrequency
updateFrequency = profile.updateFrequency
interpolationMaxPoints = profile.interpolationMaxPoints
interpolationMinPoints = profile.interpolationMinPoints
end
dataobj = LibStub:GetLibrary('LibDataBroker-1.1'):NewDataObject('TimeToDie', {
type = 'data source',
text = 'TTD',
label = 'TTD',
icon = 'Interface\\Icons\\Spell_Holy_BorrowedTime',
})
TimeToDie.dataobj = dataobj