-
Notifications
You must be signed in to change notification settings - Fork 128
/
Copy pathtimers.lua
253 lines (206 loc) · 6.28 KB
/
timers.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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
TIMERS_VERSION = "1.03"
--[[
-- A timer running every second that starts immediately on the next frame, respects pauses
Timers:CreateTimer(function()
print ("Hello. I'm running immediately and then every second thereafter.")
return 1.0
end
)
-- A timer which calls a function with a table context
Timers:CreateTimer(GameMode.someFunction, GameMode)
-- A timer running every second that starts 5 seconds in the future, respects pauses
Timers:CreateTimer(5, function()
print ("Hello. I'm running 5 seconds after you called me and then every second thereafter.")
return 1.0
end
)
-- 10 second delayed, run once using gametime (respect pauses)
Timers:CreateTimer({
endTime = 10, -- when this timer should first execute, you can omit this if you want it to run first on the next frame
callback = function()
print ("Hello. I'm running 10 seconds after when I was started.")
end
})
-- 10 second delayed, run once regardless of pauses
Timers:CreateTimer({
useGameTime = false,
endTime = 10, -- when this timer should first execute, you can omit this if you want it to run first on the next frame
callback = function()
print ("Hello. I'm running 10 seconds after I was started even if someone paused the game.")
end
})
-- A timer running every second that starts after 2 minutes regardless of pauses
Timers:CreateTimer("uniqueTimerString3", {
useGameTime = false,
endTime = 120,
callback = function()
print ("Hello. I'm running after 2 minutes and then every second thereafter.")
return 1
end
})
-- A timer using the old style to repeat every second starting 5 seconds ahead
Timers:CreateTimer("uniqueTimerString3", {
useOldStyle = true,
endTime = GameRules:GetGameTime() + 5,
callback = function()
print ("Hello. I'm running after 5 seconds and then every second thereafter.")
return GameRules:GetGameTime() + 1
end
})
]]
TIMERS_THINK = 0.01
if Timers == nil then
print ( '[Timers] creating Timers' )
Timers = {}
Timers.__index = Timers
end
function Timers:new( o )
o = o or {}
setmetatable( o, Timers )
return o
end
function Timers:_xpcall (f, ...)
print(f)
print({...})
PrintTable({...})
local result = xpcall (function () return f(unpack(arg)) end,
function (msg)
-- build the error message
return msg..'\n'..debug.traceback()..'\n'
end)
print(result)
PrintTable(result)
if not result[1] then
-- throw an error
end
-- remove status code
table.remove (result, 1)
return unpack (result)
end
function Timers:start()
Timers = self
self.timers = {}
local ent = Entities:CreateByClassname("info_target") -- Entities:FindByClassname(nil, 'CWorld')
ent:SetThink("Think", self, "timers", TIMERS_THINK)
end
function Timers:Think()
if GameRules:State_Get() >= DOTA_GAMERULES_STATE_POST_GAME then
return
end
-- Track game time, since the dt passed in to think is actually wall-clock time not simulation time.
local now = GameRules:GetGameTime()
-- Process timers
for k,v in pairs(Timers.timers) do
local bUseGameTime = true
if v.useGameTime ~= nil and v.useGameTime == false then
bUseGameTime = false
end
local bOldStyle = false
if v.useOldStyle ~= nil and v.useOldStyle == true then
bOldStyle = true
end
local now = GameRules:GetGameTime()
if not bUseGameTime then
now = Time()
end
if v.endTime == nil then
v.endTime = now
end
-- Check if the timer has finished
if now >= v.endTime then
-- Remove from timers list
Timers.timers[k] = nil
-- Run the callback
local status, nextCall
if v.context then
status, nextCall = xpcall(function() return v.callback(v.context, v) end, function (msg)
return msg..'\n'..debug.traceback()..'\n'
end)
else
status, nextCall = xpcall(function() return v.callback(v) end, function (msg)
return msg..'\n'..debug.traceback()..'\n'
end)
end
-- Make sure it worked
if status then
-- Check if it needs to loop
if nextCall then
-- Change its end time
if bOldStyle then
v.endTime = v.endTime + nextCall - now
else
v.endTime = v.endTime + nextCall
end
Timers.timers[k] = v
end
-- Update timer data
--self:UpdateTimerData()
else
-- Nope, handle the error
Timers:HandleEventError('Timer', k, nextCall)
end
end
end
return TIMERS_THINK
end
function Timers:HandleEventError(name, event, err)
print(err)
-- Ensure we have data
name = tostring(name or 'unknown')
event = tostring(event or 'unknown')
err = tostring(err or 'unknown')
-- Tell everyone there was an error
--Say(nil, name .. ' threw an error on event '..event, false)
--Say(nil, err, false)
-- Prevent loop arounds
if not self.errorHandled then
-- Store that we handled an error
self.errorHandled = true
end
end
function Timers:CreateTimer(name, args, context)
if type(name) == "function" then
if args ~= nil then
context = args
end
args = {callback = name}
name = DoUniqueString("timer")
elseif type(name) == "table" then
args = name
name = DoUniqueString("timer")
elseif type(name) == "number" then
args = {endTime = name, callback = args}
name = DoUniqueString("timer")
end
if not args.callback then
print("Invalid timer created: "..name)
return
end
local now = GameRules:GetGameTime()
if args.useGameTime ~= nil and args.useGameTime == false then
now = Time()
end
if args.endTime == nil then
args.endTime = now
elseif args.useOldStyle == nil or args.useOldStyle == false then
args.endTime = now + args.endTime
end
args.context = context
Timers.timers[name] = args
return name
end
function Timers:RemoveTimer(name)
Timers.timers[name] = nil
end
function Timers:RemoveTimers(killAll)
local timers = {}
if not killAll then
for k,v in pairs(Timers.timers) do
if v.persist then
timers[k] = v
end
end
end
Timers.timers = timers
end
if not Timers.timers then Timers:start() end