Creating a timer [Tutorial] (FAQ)

In this forum you will find and post information regarding the modding of Star Wars Battlefront 2. DO NOT POST MOD IDEAS/REQUESTS.

Moderator: Moderators

Post Reply
YaNkFaN
Field Commander
Field Commander
Posts: 943
Joined: Sat Dec 13, 2008 8:17 am

Creating a timer [Tutorial] (FAQ)

Post by YaNkFaN »

I've looked around for one of these and I couldn't find one so I decided to make one. This tutorial will just teach you how to make a timer in the .lua. Now why would you need a timer exactly? Well basically a timer is kind of self explanatory. It tells the game to do something once a certain amount of time elapses. Here are some basic overviews. A timer goes after this line in your .lua
the lines that should go in the .lua are bolded and the lines with -- are notes and explanations

function ScriptPostLoad()


and usually has to be more than one second in length. If you need something to happen after .5 seconds its usually best not to do a timer.

timername= CreateTimer("timername") --this creates your timer name it's easier if you leave this how it is (of course you can change it but then you have to change the name everywhere)
SetTimerValue(timername, (number)) --this sets the time the timer will run for (you need to put a number here)

StartTimer(timername)--this starts the timer

OnTimerElapse( --this sets up what happens when the timer elapses
function(timer)--this is unimportant

--here is where you put what you want to happen after your timer elapses
DestroyTimer(timer) --self explanatory
end,-- ends the timer function
timername--same as above
) --same as above
there you have it, how to create a timer. A lot of the timers in BF2 have lots of thinks linked to them. This is a simplified version. Timers IMO were the hardest to do because all the examples of stock timers were really full of lots of code lines and hope this helps people

all and all this is what the timer looks like
Hidden/Spoiler:
[code]timername= CreateTimer("timername")
SetTimerValue(timername, (number))
StartTimer(timername)
OnTimerElapse(
function(timer)

DestroyTimer(timer)
end,
timername
) [/code]
SkinnyODST
Lieutenant Colonel
Lieutenant Colonel
Posts: 545
Joined: Mon Jul 04, 2016 10:56 pm
Location: My other account
Contact:

Re: Creating a timer [Tutorial] (FAQ)

Post by SkinnyODST »

How would I make a sound play with this?
Marth8880
Resistance Leader
Posts: 5042
Joined: Tue Feb 09, 2010 8:43 pm
Projects :: DI2 + Psychosis
Games I'm Playing :: Silent Hill 2
xbox live or psn: Marth8880
Location: Edinburgh, UK
Contact:

Re: Creating a timer [Tutorial] (FAQ)

Post by Marth8880 »

SkinnyODST wrote:How would I make a sound play with this?
From soundhooks.txt:

Code: Select all

-------------------------------------------------------------------------------
ScriptCB_SndPlaySound(soundID);

soundID        : ID of sound properties to play
GAMEDOGSHIPMENT
Recruit Womprat Killer
Posts: 13
Joined: Sat Jun 24, 2017 12:27 am
Projects :: No Mod project currently.
Games I'm Playing :: SWBF2
xbox live or psn: No gamertag set

Re: Creating a timer [Tutorial] (FAQ)

Post by GAMEDOGSHIPMENT »

Don't want to bump anything here but here is a cleaner version of this script, thanks for the finding this.

Code: Select all

CreateTimer("timerhere")
SetTimerValue("timerhere", (6))	--in seconds
StartTimer("timerhere")

OnTimerElapse(
		function(timer)

		--Make something here

		DestroyTimer(timer)
                end,
              	"timerhere"
              )
Sporadia
Corporal
Corporal
Posts: 151
Joined: Thu Jan 24, 2019 11:02 pm
Projects :: No Mod project currently
Games I'm Playing :: None
xbox live or psn: No gamertag set

Re: Creating a timer [Tutorial] (FAQ)

Post by Sporadia »

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.
Marth8880
Resistance Leader
Posts: 5042
Joined: Tue Feb 09, 2010 8:43 pm
Projects :: DI2 + Psychosis
Games I'm Playing :: Silent Hill 2
xbox live or psn: Marth8880
Location: Edinburgh, UK
Contact:

Re: Creating a timer [Tutorial] (FAQ)

Post by Marth8880 »

Sporadia wrote: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.
Very interesting and good to know. That would honestly explain quite a lot. Engine must not do any proper end-of-mission garbage collection for timers, so this makes a lot of sense.
Post Reply