Devurandoms Lua Proposal
From WarzoneWiki
Contents |
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]()
2. 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 )


