Last modified 4 years ago Last modified on 08/04/08 21:42:53

Events

Prerequisites

You should know how tables work in Lua and what userdata is.

Knowing how SDL timers work is also helpful.

Data structures

Only one needed: A big table, I'll call it event_table.

Timer-like events index with number ("timestamp") into it, like this: event_table[12]. These will be the events that need to be executed at the 12th game-tick (absolute value).

Callback-style events use an Event-userdata as index, I'll write it like this: event_table[CALL_DROIDKILLED].

Under a given index in table, there is another, unordered table: event_table[idx] = {}. That is so you can assign more than one eventhandler to an event. For simplicities sake I will ignore this fact below!

Internal functions

A ticker function, which will do 2 things:

  1. Iterate through the indexes from lastTick to currentTick and execute the eventhandler associated with the time i, if any.

for (i = lastTick ; i < currentTick ; i++ )

event_table[i]()

  1. Walk through the event stack (since the last frame), executing any associated eventhandler, if any:

while( event = event_stack.pop() )

event_table[event]()

Alternatively to (2), you can of course immediately execute an event at the time it happens. For example where delaying is not possible, eg. for "died" events, where the droid might already be deleted when we come to the eventhandling.
Note: Currently (svn/trunk) droid deletion is delayed for one frame to prevent such situations.

Actually, step (1) has to look a bit more complicated, so that the return-value of the function is used to reschedule it (relative time):

for (i = lastTick ; i < currentTick ; i++ )

event_table[ now + event_table[i]() ] = event_table[i]

External functions

event.link(event, handler)

Links the handler to be executed when the specified event happens. (Remember: If event is a number, it specifies a relative time, counted in ticks from now)

Implemented for example like this:

function link(event, handler)

if isnumber event then

event_table[now + event] = handler

else

event_table[event] = handler

end

end

Alternative proposal

id = event.link(event, handler) -- parameters like above

function link(event, handler)

id = find_free_id() -- Get a free id

if isnumber event then

event += now -- Times are relative to 'now'

end

event_table[id] = { event, handler } -- Store it using the id as an index

return id -- return the id, so it eventhandler can be unlinked

end

The associated internal function would have to look like this (only necessary for time-based events, since the others are called immediately):

for id, blob in pairs(event_table) do

if isnumber blob[1] and blob[1] <= now then -- check whether 'event' is a time and lies in the past

nextTime = blob[2]() if nextTime != nil then -- Reschedule if we got a new time

event_table[id] = { now + nextTime, handler, condition }

else

event_table[id] = nil -- Don't schedule it again if it returned nil

end

end

end

Use the returned id to unlink events again:

event.unlink(id)

function unlink(id)

make_free_id(id) -- Put the id back into the pool

event_table[id] = nil -- Remove it from the table

end

Example:

-- Destroy nexus after 1000 ticks: id = event.link( 10, function() if now > 1000 then nexus.destroy() end end ) event.unlink(id)

Examples

Assuming now contains the current time/tick.

event.link( 10, function() print "hello" end ) -- Will print "hello" 10 ticks from now, just once.

event.link( 10, function()

print "hello again" return now + 10

end ) -- Will print "hello again" 10 ticks from now and continue doing that every 10 ticks

event.link( event.callback.droidkilled, function() print "ouch" end ) -- Will print "ouch" whenever a droid gets killed

event.link( event.callback.droidkilled, function(player)

if player == game.player.green then

print "dont hurt me"

end

end ) -- Will print "dont hurt me" whenever a droid of player "green" gets killed

event.link( event.init, function() print "hello world" end ) -- Print hello world at the beginning of the game.

Convenience functions, WZS style

handler is always a function!

every(time, handler)

A wrapper, which returns 2 values which shall be passed to event.link(). 1st value is the time of first execution, 2nd value is a function to repeatedly schedule the execution

function every(time, handler) -- every 'time' ticks call 'handler'

return time, function() -- return 2 values, initial execution time + eventhandler

handler() -- execute the eventhandler return time -- fire again in 'time' ticks

end

end

wait(time, handler)

A very simple wrapper to schedule handler to execute in time ticks:

function wait(time, handler)

return time, handler

Convenience functions, Gerard-style

handler is always a function!

repeatedEvent(time, handler)

Will register handler to execute every time ticks. Could look like this:

function repeatedEvent(time, handler)

event.link( every( time, handler ) )

booleanEvent(condition, handler)

Run handler, when condition evaluates to not nil. condition is a function! Could look like this:

function booleanEvent(condition, handler)

event.link( every( 1, function()

if condition() then

handler()

end

end )

Category:Proposals Category:Scripting ?