TIMERS_VERSION = "1.05" --[[ -- 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 ) -- The same timer as above with a shorthand call Timers(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 = {} setmetatable(Timers, { __call = function(t, ...) return t:CreateTimer(...) end }) --Timers.__index = Timers end function Timers:start() Timers = self self.timers = {} --local ent = Entities:CreateByClassname("info_target") -- Entities:FindByClassname(nil, 'CWorld') local ent = SpawnEntityFromTableSynchronous("info_target", {targetname="timers_lua_thinker"}) 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 Timers.runningTimer = k Timers.removeSelf = false -- 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 Timers.runningTimer = nil -- Make sure it worked if status then -- Check if it needs to loop if nextCall and not Timers.removeSelf 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 if Timers.runningTimer == name then Timers.removeSelf = true end end function Timers:RemoveTimers(killAll) local timers = {} Timers.removeSelf = true 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 GameRules.Timers = Timers