Timers Tutorial

Timers execute code after a period of time has passed. They can be used to reduce the amount of management code you write by letting MMM take care of the timing details. This tutorial will cover the basic usage of timers, as well as touching on some more advanced uses.

 The Plan

For the tutorial we'll make a timer that destroys the freighters that are surrounding the sensor station one by one.

Creating the Timer

Here we create a new TImer - add the following code to the setup function of your mission. It creates a Timer called explodeTimer. It has an initial 5.0 second tick interval and runs immediately. By default Timers do not repeat, so we need to change that setting.

local newTimer = Timer( "explodeTimer", 5.0, Timer.State.Started )
newTimer.repeats = true

Finding the Ships

We want to blow up the Mandrils, so we need to find them. The following code added to the setup function will do this for us - we store the results so we can use them as arguments to the Timer later.

--Find all of the Mandril class freighters that are placed
--around the sensor station in the middle.
local ents = EntityFinder( function(e)
                            return e:isType(Entity.Type.GameObject)
                                   and e.odf == "fed_mandril.odf"
                            end ):find()

Creating the Hook Function

When the timer ticks we want to blow up one of the ships. The following function will do this - we will use it as the hook function on our timer. The general process is that we blow up the first ship in the list, remove it so we don't blow it up again and then reduce the timing interval slightly to speed up the explosions. We stop when there are no more ships to blow up.

--Timer hook function that blows up a single ship and
--removes it from the list.
function explodeShip( timer, hook )    
    --The table of ships is stored as the argument
    --in the hook - we take the first entry in the list
    local ship = hook.argument[1]
    if ship not nil and ship.valid then
        ship:explode()
    end
    
    --Remove the first ship from the results table
    --so we don't explode it again
    table.remove( hook.argument, 1 )
    
    --Reduce the interval slightly each time to make it
    --more interesting
    timer.interval = timer.interval * 0.75
    
    --If we don't have any more ships to blow up
    --then we stop the timer from repeating.
    if #hook.argument == 0 then
        timer.repeats = false
    end
end

Registering the Hook

Now that we have created our function and the timer, we are ready to register it. This will mean that it will be called every time the timer ticks. For this tutorial, we'll pass ents (the results from the EntityFinder call) as the argument for the hook; this will be stored in the argument field of the hook so that the hook function can access it.

--Passing the entities we found earlier as the argument to the hook
--function
newTimer:hook( "explodeShip", nil, explodeShip, ents )

Final Script

Here is the final script for the mission. Running it you will see freighters exploding with increasing frequency.

class 'TimerTutorial'

function TimerTutorial:__init( )
    
end

function TimerTutorial:setup( )
    
    --Find all of the Mandril class freighters that are placed
    --around the sensor station in the middle.
    local ents = EntityFinder( function(e)
                                return e:isType(Entity.Type.GameObject)
                                       and e.odf == "fed_mandril.odf"
                                end ):find()
    
    --Timer hook function that blows up a single ship and
    --removes it from the list.
    function explodeShip( timer, hook )    
        --The table of ships is stored as the argument
        --in the hook - we take the first entry in the list
        local ship = hook.argument[1]
        if ship ~= nil and ship.valid then
            ship:explode()
        end
       
        --Remove the first ship from the results table
        --so we don't explode it again
        table.remove( hook.argument, 1 )
       
        --Reduce the interval slightly each time to make it
        --more interesting
        timer.interval = timer.interval * 0.75
       
        --If we don't have any more ships to blow up
        --then we stop the timer from repeating.
        if #hook.argument == 0 then
            timer.repeats = false
        end
    end
    
    local newTimer = Timer( "explodeTimer", 5.0, Timer.State.Started )
    newTimer.repeats = true
    
    --Passing the entities we found earlier as the argument to the hook
    --function
    newTimer:hook( "explodeShip", nil, explodeShip, ents )

    Objectives:load( "TimerTutorialObjectives.txt" )
    Objectives.visible = true
    
end

MMM.register( TimerTutorial( ) )