-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathcalculation.class.lua
230 lines (197 loc) · 6.61 KB
/
calculation.class.lua
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
--- This is a base class containing the interface that is necessary for communicating with TopFit.
-- Calculation classes that actually calculate sets should inherit from this class to ensure correct functionality
local addonName, ns, _ = ...
local Calculation = ns.class()
ns.Calculation = Calculation
-- create a new, empty calculation object
-- not advised, this class is meant to be inherited from by different calculation methods
function Calculation:construct(set)
self.running = false
self.started = false
self:UseSet(set)
self.elapsed = 0
self.availableItems = {}
self.itemCounts = {}
for slotID, _ in pairs(ns.slotNames) do
self.availableItems[slotID] = {}
end
self:SetOperationsPerFrame(50) -- sensible default that is somewhat easy on the CPU
end
function Calculation:UseSet(set)
self.AssertArgumentType(set, ns.Set)
self.set = set
end
function Calculation:GetUsedSet()
return self.set
end
--- Add an item to be available for the current calculation in the given slot (or all applicable slots if none is provided).
function Calculation:AddItem(item, slotID)
-- accepts item IDs, links and tables
assert(item, "Usage: calculation:AddItem(itemLink or itemID or itemTable[, slotID])")
if type(item) == 'table' then
if item.itemLink then item = item.itemLink
elseif item.itemID then item = item.itemID
else error("Usage: calculation:AddItem(itemLink or itemID or itemTable[, slotID])") end
end
local itemTable = ns:GetCachedItem(item)
if slotID then
assert(self.availableItems[slotID], "calculation:AddItem() - invalid slotID given: "..(slotID or "nil"))
tinsert(self.availableItems[slotID], itemTable)
else
for _, slotID in pairs(itemTable.equipLocationsByType) do
tinsert(self.availableItems[slotID], itemTable)
end
end
end
--- Removes all currently available items from this calculation.
function Calculation:ClearItems()
for _, slotTable in pairs(self.availableItems) do
wipe(slotTable)
end
wipe(self.itemCounts)
end
--- Get available items for a slot.
function Calculation:GetItems(slotID)
if not slotID then
return self.availableItems --TODO: copy tables or use provided table
elseif self.availableItems[slotID] then
--TODO: this assert makes no sense. remove it or the elseif-condition above
assert(self.availableItems[slotID], "calculation:GetItems() - invalid slotID given: "..(slotID or "nil"))
return self.availableItems[slotID] --TODO: copy tables or use provided table
end
end
--- Get a single item for a slot.
function Calculation:GetItem(slotID, itemNum)
assert(slotID and self.availableItems[slotID], "calculation:GetItem(slotID, itemNum) - invalid slotID given: "..(slotID or "nil"))
assert(itemNum and self.availableItems[slotID][itemNum], "calculation:GetItem(slotID, itemNum) - invalid itemNum given: "..(itemNum or "nil"))
return self.availableItems[slotID][itemNum]
end
function Calculation:SetItemCount(itemLink, count)
self.itemCounts[itemLink] = count
end
function Calculation:GetItemCount(itemLink)
return self.itemCounts[itemLink] or 1
end
--- Set a callback to be called when the calculation completes.
function Calculation:SetCallback(callback)
if callback then
self.AssertArgumentType(callback, "function")
end
self.callback = callback
end
--- Check whether the calculation is currently running.
-- Even if it is not running, it might be in progress but paused.
-- @see Calculation:IsStarted
function Calculation:IsRunning()
return self.running
end
--- Check whether the calculation has been started and is not finished yet.
-- It might be paused, however
-- @see Calculation:IsRunning
function Calculation:IsStarted()
return self.started
end
--- Start or resume this calculation.
function Calculation:Start()
if not self:IsRunning() then
if not self:IsStarted() then
-- init and run
self.elapsed = 0 -- for performance testing
self:Initialize()
end
Calculation._RunCalculation(self)
self.running = true
self.started = true
end
end
Calculation.Resume = Calculation.Start
--- Pause the current calculation so it can be resumed later.
function Calculation:Pause()
if self:IsRunning() then
self.running = false
end
end
--- Abort the current calculation completely.
function Calculation:Abort()
if self:IsStarted() then
self.running = false
self.started = false
end
end
Calculation.Stop = Calculation.Abort
Calculation.Cancel = Calculation.Abort
--- Set the number of combinations to check each frame.
-- @param ops
function Calculation:SetOperationsPerFrame(ops)
self.AssertArgumentType(ops, 'number')
self.operationsPerFrame = ops
end
--- Get the number of combinations being checked each frame.
function Calculation:GetOperationsPerFrame()
return self.operationsPerFrame
end
--- Run steps needed for initializing the calculation process.
function Calculation:Initialize()
-- empty and intended to be overridden
end
--- Run a single step of this calculation.
function Calculation:Step()
-- empty and intended to be overridden
self:Done()
end
--- Run final operations for this calculation and get ready to return a result.
function Calculation:Finalize()
-- empty and intended to be overridden
end
--- Mark the current calculation as finished.
function Calculation:Done()
self.running = false
self.started = false
self:Finalize()
if type(self.OnComplete) == 'function' then
self:OnComplete()
end
if type(self.callback) == 'function' then
self.callback() --TODO: also send results
end
end
-- run steps consecutively
function Calculation:RunSteps()
local operation = 0
while self.running and operation < self.operationsPerFrame do
self:Step()
operation = operation + 1
end
if type(self.OnUpdate) == 'function' then
self:OnUpdate() --TODO: we should also provide the current combination and/or progress (configurable?)
end
end
-- returns a value between 0 and 1 indicating how far along the calculation is
function Calculation:GetCurrentProgress()
return 0 -- this should be overridden
end
local calculationsFrame = CreateFrame("Frame")
function Calculation._RunCalculation(calculation)
if not ns.activeCalculations then
ns.activeCalculations = {}
end
tinsert(ns.activeCalculations, calculation)
calculationsFrame:SetScript("OnUpdate", Calculation._ContinueActiveCalculations)
end
function Calculation._ContinueActiveCalculations(frame, elapsed)
ns:Debug("Continue")
if #(ns.activeCalculations) < 1 then
calculationsFrame:SetScript("OnUpdate", nil)
else
for i = #(ns.activeCalculations), 1, -1 do
ns:Debug("Continue "..i)
local calculation = ns.activeCalculations[i]
if not calculation:IsRunning() then
tremove(ns.activeCalculations, i)
else
calculation.elapsed = calculation.elapsed + elapsed
calculation:RunSteps()
end
end
end
end