I’m bumping this tutorial because I’ve been working with timers recently and have a bunch to add, specifically on how to work with respawn timers.
If you look in the documentation that comes with BF2_ModTools then there’s a doc called Battlefront2_scripting_system. This doc is all about lua events and it lists all the functions that you should ever need relating to a timer.
Now to add to what’s already in this tutorial:
Important point 1 (Using timers in other events):
OnTimerElapse will only be recognised if that timer’s CreateTimer has been run by the lua. So, putting CreateTimer in an event like OnCharacterDeath and then putting OnTimerElapse outside of that event does not work. The engine would read through the ScriptPostLoad before any characters die, make the OnCharacterDeath but not run it, then reach OnTimerElapse, see that the timer it refers to hasn’t been created and ignore it. Then when a character finally dies, the engine will create the timer correctly and start it correctly but you will find that the OnTimerElapse does not exist. You either need both CreateTimer and OnTimerElapse to be outside of the event, or you need them both to be inside, (or you could even have CreateTimer outside of the event and OnTimerElapse inside). But you cannot put CreateTimer inside an event and OnTimerElapse outside of the same event because the code outside events is always run first.
Important point 2 (Preventing a weird crash):
If you don’t destroy timers, then your map may crash when it’s run in an instant action playlist. This is something of an invisible bug because the diagnostic tools don’t let you run instant action playlists (to check for the problem) and this crash never effects the first map in a playlist (from what I’ve seen). So, it’s important that DestroyTimer is included in an OnTimerElapse to ensure that all timers are destroyed when they have run to 0. However, it’s also important that you never use a destroyed timer. For single use timers the solution here is easy, you put CreateTimer at the start of ScriptPostLoad and OnTimerElapse at the end of it. Provided you can guarantee that the timer will only ever elapse once from the way you’ve coded its other settings then you’ll be fine.
Multiple use timers
For multiple use timers like a respawn timer, the solution is more complicated. Since you’re destroying the timer every time it runs out, you need to ensure that you create the timer again every time you need it. This is where you would want to put both CreateTimer and OnTimerElapse inside of OnCharacterDeath. Putting OnTimerElapse inside OnCharacterDeath does
not create an event inside an event like you might expect to happen. This will
not create a situation where a character has to die at the same time the timer runs out for OnTimerElapse to run. What actually happens is the game will need OnCharacterDeath to run in order for it to see that OnTimerElapse code is there, then when it does it will treat OnTimerElapse as its own event, the same as if the code for it was outside of OnCharacterDeath.
So, putting CreateTimer and OnTimerElapse together inside an event will ensure that a new timer is made every time the event happens, then that new timer is destroyed every time it runs out. To stop an event making multiple OnTimerElapse sections for one timer, you need some code to ensure that the OnTimerElapse is only seen the first time the event is called. Define a local variable such as k or something before the event. Set k = 0 before the event. Then put the OnTimerElapse inside of an ‘if k == 0 then’ condition and set k = 1.
Multiple use hero respawn timer example:
Code: Select all
local k = 0 -- need this for OnTimerElapse to only be created once
-- for this example, we’re respawning the team 1 hero
OnCharacterDeath(
function(player, killer)
-- code to check if the team 1 hero died
local team = GetCharacterTeam(player)
if team == 1 then
-- assume the mission has 6 units and a hero on team 1
-- GetCharacterClass of 0 – 5 is the 6 units
-- GetCharacterClass of 6 is the hero
if GetCharacterClass(player) == 6 then
-- now it’s been determined that the team 1 hero died
CreateTimer(“HeroRespawnTimer”)
-- print(“HeroRespawnTimer Created”) -- for debugging
SetTimerValue(“HeroRespawnTimer”, 4)
StartTimer(“HeroRespawnTimer”)
-- print(“HeroRespawnTimer Started”) -- for debugging
-- k = 0 would set k to 0
-- k == 0 checks if k is currently equal to 0 (and returns 1 if it is)
if k == 0 then
OnTimerElapse(
function(timer)
-- print(“HeroRespawnTimer elapsed”) -- for debugging
-- assume SpawnHero is a function I’ve already made for this example
-- which spawns a hero for the team you tell it to
SpawnHero(1)
DestroyTimer(timer)
end,
“HeroRespawnTimer”
)
k = 1
end -- end of ‘if k == 0 then’
end
end
end -- end of function(player, killer)
)
-- OnCharacterDeathTeam and OnCharacterDeathClass have not been used
-- because I don’t think those filters apply to the player
-- and can never get them to work with OnCharacterDeath
It’s a good idea to just put OnTimerElapse immediately after its CreateTimer and a good idea to have a different name for every timer.
If you forget to put DestroyTimer in an OnTimerElapse, you might see a new timer being made every time CreateTimer is called, all of which have the same name and are effected by the same SetTimerValue, StartTimer and StopTimer commands etc, and all of which hit 0 at the same time, calling OnTimerElapse multiple times when you’d expect it to only run once. Also, if you end up with code that’s creating timers faster than it’s destroying them, you might see similar weird behaviour. Respawn timers are easiest for classes that there is only one of on the battlefield at any given time.