-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathtnt.lua
More file actions
410 lines (385 loc) · 15.1 KB
/
Copy pathtnt.lua
File metadata and controls
410 lines (385 loc) · 15.1 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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
-- Pauseable timers and transitions with speed adjustment
-- Author: Lerg
-- Release date: 2012-04-14
-- Version: 1.2
-- License: MIT
-- Web: http://developer.anscamobile.com/code/pausable-timers-and-transitions-speed-adjustment
--
-- USAGE:
-- Import this module with a desired name, for example:
-- tnt = require('tnt')
-- Then you create timers and transitions with the same logic as before:
-- timer1 = tnt:newTimer(1000, function () print('tick') end, 1, {name = 'Tick Timer', userData = 'User data', onEnd = function (event) print(event.name .. ' has completed') end})
-- trans1 = tnt:newTransition(object, {time = 1000, x = 480, name = 'Slide Transition', userData = 'User data', cycle = 10, backAndForth = true, onEnd = function (object, event) print(event.name .. ' has completed') end})
-- Name and userData arguments are optional. userData can be anything.
-- onEnd callback (or object listener) is fired once timer or transition has finished it's job completely, after all ticks or transition cycles.
-- With cycle param you can tell transition to loop. 0 - infinite times. You can also set backAndForth param.
-- Every instance has pause(), resume() and cancel() methods.
-- You can manage all timers and transitions with function like tnt:pauseAllTimers(), tnt:resumeAllTransitions() etc.
-- For speed adjustment first pause all timers and transitions, then modify tnt.speed to say 0.5, which means 2 times faster
-- and lastly resume all paused instances.
--
-- LIMITATIONS:
-- Doesn't work with delta transitions. Easings will start over after each pausing, it can be fixed, but I don't need it at the moment,
-- so didn't implemented. Fix would be to set up custom easings and pass elapsed time to each easing function.
--
-- CONTRIBUTORS:
-- CluelessIdeas (www.cluelessideas.com), TMApps (www.timemachineapps.com/blog)
--
-- CHANGELIST:
-- 1.2:
-- [Feature] Added cycling support for transitions. Both repeating and "back and forth" loops. Infinite and finite.
-- [Feature] Added onEnd listener support - callback to be called when timer or transition elapsed completely (repeative timers and transitions)
-- [Feature] Added table listeners support. Events are timer, timerEnd, transition, transitionEnd
-- [Feature] Added speed constants tnt.NORMAL, tnt.FAST and tnt.SLOW - feel free to use them or add your own.
-- 1.1.2:
-- [Bug] Transitions are wrongfully decided to be already ended.
-- [Feature] Added name and userData params for transtitions just like for timers.
-- [Feature] Added LuaDoc.
-- [Feature] Added default value for the count argument.
-- 1.1.1:
-- [Bug] Quick bugfix on remainingTime calculations.
-- 1.1:
-- [Bug] onComplete function is not getting called for transitions when pausing right before the event.
-- [Bug] Timers are not counting resting time from resuming till next pausing (before next tick).
-- [Feature] Added userData and name to actual timers instances, they are accessible through event callback function argument, like event.userData and event.name.
-- [Feature] Added cleanTimersAndTransitions() function which frees the memory on demand (you can call it every couple of seconds)
--
-- I can be found on the corona IRC channel.
-- Module table
local _M = {}
-- Game speed: 1 - normal, 0.5 - fast, 2 - slow
_M.speed = 1
_M.NORMAL = 1
_M.FAST = 0.5
_M.SLOW = 2
-- Every instance is hold here
local allTimers = {}
local allTransitions = {}
-- Cache
local tInsert = table.insert
local tRemove = table.remove
-- Pausable timers
-- @param duration number Transition duration.
-- @param callback function Function to be called on the each tick.
-- @param count number How many times to tick, 0 - unlimited. Default is 1.
-- @param params table Extra parameters. Optional.
-- name string The name for the timer. Available in the callback.
-- userData table Any user data. Available in the callback.
-- onEnd function A callback to call when timer is elapsed completely (count wise).
function _M:newTimer (duration, callback, count, params)
-- Timer handler
local tH = {}
tH.speed = self.speed
tH.start = system.getTimer()
tH.duration = duration
tH.callback = callback
tH.count = count or 1
tH.counter = 0
tH.isInfinite = (count == 0)
if params then
tH.name = params.name
tH.userData = params.userData
tH.onEnd = params.onEnd
end
tH.shouldRemove = false
tH.paused = false
tH.intervalStartTime = tH.start
tH.remainingTime = duration
-- Internal function which fires up the actual callback function
-- @param event Corona's timer event
local function callbackWrapper (event)
local tH_callback = tH.callback
if tH_callback then
event.userData = tH.userData
event.name = tH.name
if type(tH_callback) == 'function' then
tH_callback(event)
elseif type(tH_callback) == 'table' and type(tH_callback.timerEnd) == 'function' then
tH_callback:timerEnd(event)
end
if not tH.isInfinite then
tH.counter = tH.counter + 1
if tH.counter >= tH.count then
tH:cancel()
local onEnd = tH.onEnd
if type(onEnd) == 'function' then
onEnd(event)
elseif type(onEnd) == 'table' and type(onEnd.timerEnd) == 'function' then
onEnd:timerEnd(event)
end
end
end
tH.remainingTime = tH.duration
tH.intervalStartTime = system.getTimer()
else
tH:cancel()
end
end
tH.t = timer.performWithDelay(tH.duration * self.speed, callbackWrapper, tH.count)
-- Cancels running timer and prepares for the resuming
function tH:pause ()
if self.t then
timer.cancel(self.t)
end
if not self.paused then
self.paused = true
self.pausingTime = system.getTimer()
self.remainingTime = self.remainingTime - ((self.pausingTime - self.intervalStartTime) / _M.speed)
if self.remainingTime < 0 then
self.remainingTime = 0
end
end
end
-- Initiates a fresh timer if paused
function tH:resume ()
if self.paused then
self.paused = false
if not self.isInfinite then
-- Timer elapsed
if self.counter >= self.count then
self:cancel()
else
local function callbackDoubleWrapper (event)
callbackWrapper(event)
local ticksRemains = self.count - self.counter
if ticksRemains > 0 then
self.t = timer.performWithDelay(self.duration * _M.speed, callbackWrapper, ticksRemains)
self.speed = _M.speed
else
self:cancel()
end
end
self.intervalStartTime = system.getTimer()
self.t = timer.performWithDelay(self.remainingTime * _M.speed, callbackDoubleWrapper, 1)
self.speed = _M.speed
end
else
local function callbackDoubleWrapper (event)
callbackWrapper(event)
self.t = timer.performWithDelay(self.duration * _M.speed, callbackWrapper, 0)
end
self.intervalStartTime = system.getTimer()
self.t = timer.performWithDelay(self.remainingTime * _M.speed, callbackDoubleWrapper, 1)
self.speed = _M.speed
end
end
end
-- Cancels actual timer instance and marks this handler to be removed
function tH:cancel ()
if self.t then
timer.cancel(self.t)
end
self.shouldRemove = true
self.callback = nil
end
tInsert(allTimers, tH)
return tH
end
-- Pauses everything in the allTimers table
function _M:pauseAllTimers()
local i
local allTimersCount = #allTimers
if allTimersCount > 0 then
for i = allTimersCount, 1, -1 do
local child = allTimers[i]
if child.shouldRemove then
tRemove(allTimers, i)
else
child:pause()
end
end
end
end
-- Resumes everything in the allTimers table
function _M:resumeAllTimers()
local i
local allTimersCount = #allTimers
if allTimersCount > 0 then
for i = allTimersCount, 1, -1 do
local child = allTimers[i]
if child.shouldRemove then
tRemove(allTimers, i)
else
child:resume()
end
end
end
end
-- Cancels everything in the allTimers table
function _M:cancelAllTimers()
local i
local allTimersCount = #allTimers
if allTimersCount > 0 then
for i = allTimersCount, 1, -1 do
local child = allTimers[i]
child:cancel()
tRemove(allTimers, i)
end
end
end
-- Pausable transitions
-- @param object table An object for which transition is applied.
-- @param params table Transition parameters.
-- name string The name for the transition. Available in the onComplete function. Optional.
-- userData table Any user data. Available in the onComplete function. Optional.
-- cycle number How many times to repeat transition. 0 - infinite. Optional.
-- backAndForth boolean Should it be back and forth cycling? Optional.
-- onEnd function A callback to call when transition is completed completely (count wise). Optional.
function _M:newTransition(object, params)
-- Transition handler
local tH = {name = params.name, userData = params.userData, originalTime = params.time, cycleCount = 0}
local elapsed, elapsedCount, currentCountRemains
local onComplete = params.onComplete
local onEnd = params.onEnd
local cycleTransition = params.cycle or 1
local backAndForthCycling = params.backAndForth or false
local initialValues = {}
for k, v in pairs(params) do
if k ~= 'onComplete' and k ~= 'onEnd' and k ~= 'time' and k ~= 'transition' and k ~= 'delta' and k ~= 'name' and k ~= 'userData' and k ~= 'cycle' and k ~= 'backAndForth' then
initialValues[k] = object[k]
end
end
-- This function is called for each completed transition to mark it's handler for removal
local function callbackWrapper ()
if type(onComplete) == 'function' then
onComplete(object, {userData = tH.userData, name = tH.name})
elseif type(onComplete) == 'table' and type(onComplete.transition) == 'function' then
onComplete:transition(object, {userData = tH.userData, name = tH.name})
end
local doRepeat = false
if cycleTransition > 0 then
tH.cycleCount = tH.cycleCount + 1
if tH.cycleCount >= cycleTransition then
tH:cancel()
if type(onEnd) == 'function' then
onEnd(object, {userData = tH.userData, name = tH.name})
elseif type(onEnd) == 'table' and type(onEnd.transitionEnd) == 'function' then
onEnd:transitionEnd(object, {userData = tH.userData, name = tH.name})
end
else
doRepeat = true
end
elseif cycleTransition == 0 then
doRepeat = true
end
if doRepeat then
transition.cancel(tH.t)
tH.params.time = tH.originalTime
tH.start = system.getTimer()
tH.elapsed = nil
for k, v in pairs(initialValues) do
if not backAndForthCycling then
object[k] = v
else
tH.params[k] = v
initialValues[k] = object[k]
end
end
tH.t = transition.to(object, tH.params)
end
end
tH.params = {}
-- Make a shallow copy of the user's params so they are not messed up in the user's space
for k, v in pairs(params) do tH.params[k] = v end
tH.params.onComplete = callbackWrapper
tH.params.time = tH.originalTime * self.speed
tH.t = transition.to(object, tH.params)
tH.start = system.getTimer()
tH.speed = self.speed
-- Stops current transiton and prepares for the resuming
function tH:pause()
if self.t then
self.elapsed = (system.getTimer() - self.start) / self.speed
transition.cancel(self.t)
else
self:cancel()
end
end
-- Initiates a fresh transition if paused
function tH:resume()
if self.elapsed and not self.shouldRemove then
-- Current speed
local s = _M.speed
self.params.time = (self.originalTime - self.elapsed) * s
self.t = transition.to(object, self.params)
self.start = system.getTimer() - self.elapsed * s
self.speed = s
self.elapsed = nil
end
end
-- Cancels actual transition instance and marks this handler to be removed
function tH:cancel()
if self.t then
transition.cancel(self.t)
end
self.shouldRemove = true
end
tInsert(allTransitions, tH)
return tH
end
-- Pauses everything in the allTransitions table
function _M:pauseAllTransitions()
local i
local allTransitionsCount = #allTransitions
if allTransitionsCount > 0 then
for i = allTransitionsCount, 1, -1 do
local child = allTransitions[i]
if child.shouldRemove then
tRemove(allTransitions, i)
else
child:pause()
end
end
end
end
-- Resumes everything in the allTransitions table
function _M:resumeAllTransitions()
local i
local allTransitionsCount = #allTransitions
if allTransitionsCount > 0 then
for i = allTransitionsCount, 1, -1 do
local child = allTransitions[i]
if child.shouldRemove then
tRemove(allTransitions, i)
else
child:resume()
end
end
end
end
-- Cancels everything in the allTransitions table
function _M:cancelAllTransitions()
local i
local allTransitionsCount = #allTransitions
if allTransitionsCount > 0 then
for i = allTransitionsCount, 1, -1 do
local child = allTransitions[i]
child:cancel()
tRemove(allTransitions, i)
end
end
end
-- Deletes unused instances (frees memory)
function _M:cleanTimersAndTransitions()
local i
local allTimersCount = #allTimers
if allTimersCount > 0 then
for i = allTimersCount, 1, -1 do
local child = allTimers[i]
if child.shouldRemove then
tRemove(allTimers, i)
end
end
end
local allTransitionsCount = #allTransitions
if allTransitionsCount > 0 then
for i = allTransitionsCount, 1, -1 do
local child = allTransitions[i]
if child.shouldRemove then
tRemove(allTransitions, i)
end
end
end
end
return _M