SpoilerShow
I really do recommend downloading the .zip file with documentation. For convenience, here is the business end of the code:
Code: Select all
synchInterval = 0.1
--[[All delays should be divisible by synchInterval.
e.g. if synchInterval is 0.1 then acceptable
delays are 0.1 , 0.2 , ... , 1.0 , 1.1 , ...
Lower interval -> more precise timing & more cycles
consumed synching. Adjust to your taste.
--]]
newSequence = function( copyFromSequence )
initSynch()
local s = {
copy = newSequence,
--[[I kind of hate the syntax below. At first glance,
it looks as if it's doing nothing, but it's absolutely
necessary for the way this table works.
--]]
addEvent = addEvent,
execute = execute,
abort = abort,
setInterruptMode = setInterruptMode,
}
if copyFromSequence then
--[[There is a danger here: upvalues.
Grimrock does not currently (US 3/9/2013) support
upvalues, but I remain hopeful that it will eventually.
If we simply copy the function as below,
then we are actually referencing the same closure.
I don't see any way around this. I'm afraid that
this behavior will be an unpleasant surprise for
some users.
--]]
s.eventCount = copyFromSequence.eventCount
for i , v in ipairs(copyFromSequence) do
s[i] = v
end
s.interruptMode = copyFromSequence.interruptMode
else
s.eventCount = 0
end
return s
end
addEvent = function( sequence , newEvent )
local i = sequence.eventCount + 1
sequence.eventCount = i
sequence[i] = newEvent
end
execute = function( sequence , extraArgument )
if ( not sequence.currentEvent ) or ( sequence.interruptMode == "restart" ) then
sequence.extra = extraArgument
sequence.currentEvent = 1
sequence.tickTarget = nil
sequence.timerId = nil
executeEvent( sequence )
elseif sequence.interruptMode == "parallel" then
sequence:copy():execute( extraArgument )
end
end
abort = function( sequence )
sequence.currentEvent = nil
sequence.tickTarget = nil
sequence.timerId = nil
end
setInterruptMode = function( sequence , mode )
--mode should be one of "ignore" , "restart" , or "parallel"
--default is "ignore"
sequence.interruptMode = mode
end
------------------------------------------
--[[Functions below this line are intended
for internal use only.
--]]
initSynch = function()
if synchInitialized then
return
end
synchTimers()
synchInitialized = true
end
timers = {}
--Keys are timer id's. Values are sequences.
synchTimers = function( synch )
--ensure that all timers are on the same level as the party
if ( not synch ) or
( not ( synch.level == party.level ) ) then
if synch then
synch:destroy()
end
synch = spawn( "timer" , party.level , 0 , 0 , 0 )
synch:setTimerInterval( synchInterval )
synch:addConnector( "activate" , "sequenceScript" , "synchTimers" )
synch:activate()
local newTimers = {}
for k , v in pairs( timers ) do
local timer = findEntity( k )
timer:destroy()
if v.tickTarget then
local newTimer = spawn( "timer" , party.level, 0 , 0 , 0 )
v.timerId = newTimer.id
newTimer:setTimerInterval( synchInterval )
newTimer:addConnector( "activate" , "sequenceScript" , v.tickTarget )
newTimer:activate()
newTimers[newTimer.id] = v
else
v.timerId = nil
end
end
timers = newTimers
end
end
executeEvent = function( sequence )
--this function is called to begin an event
if not sequence.currentEvent then
--aborted
return
end
local event = sequence[sequence.currentEvent]
if not event then
--reached the end of the sequence
sequence.currentEvent = nil
return
end
if event.delayBefore then
sequence.tickTarget = "delayBeforeTick"
sequence.beforeTicks = nil
local timer = spawn( "timer" , party.level , 0 , 0 , 0 )
sequence.timerId = timer.id
timer:setTimerInterval( synchInterval )
timer:addConnector( "activate" , "sequenceScript" , sequence.tickTarget )
timers[timer.id] = sequence
timer:activate()
else
runEventFunction( sequence )
end
end
delayBeforeTick = function( timer )
local sequence = timers[timer.id]
if not ( sequence.timerId == timer.id ) then
--aborted or restarted
timers[timer.id] = nil
timer:destroy()
return
end
sequence.beforeTicks = ( sequence.beforeTicks or 0 ) + 1
if ( sequence.beforeTicks * synchInterval ) >= sequence[sequence.currentEvent].delayBefore then
sequence.tickTarget = nil
sequence.beforeTicks = nil
sequence.timerId = nil
timers[timer.id] = nil
timer:destroy()
runEventFunction( sequence )
end
end
delayAfterTick = function( timer )
local sequence = timers[timer.id]
if not ( sequence.timerId == timer.id ) then
--aborted or restarted
timers[timer.id] = nil
timer:destroy()
return
end
sequence.afterTicks = ( sequence.afterTicks or 0 ) + 1
if ( sequence.afterTicks * synchInterval ) >= sequence[sequence.currentEvent].delayAfter then
sequence.tickTarget = nil
sequence.afterTicks = nil
sequence.timerId = nil
timers[timer.id] = nil
timer:destroy()
sequence.currentEvent = sequence.currentEvent + 1
executeEvent( sequence )
end
end
runEventFunction = function( sequence )
--this function is called to run the actual event code
--and determine what to do immediately thereafter
if not sequence.currentEvent then
--aborted
return
end
local e = sequence[sequence.currentEvent]
e.event( sequence.extra )
if e.delayAfter then
sequence.tickTarget = "delayAfterTick"
sequence.afterTicks = nil
local timer = spawn( "timer" , party.level , 0 , 0 , 0 )
sequence.timerId = timer.id
timer:setTimerInterval( synchInterval )
timer:addConnector( "activate" , "sequenceScript" , sequence.tickTarget )
timers[timer.id] = sequence
timer:activate()
else
sequence.currentEvent = sequence.currentEvent + 1
executeEvent( sequence )
end
end
Code: Select all
function onButton()
mySequence:execute()
end
mySequence = newSequence()
mySequence:addEvent{
event = function()
hudPrint("The air behind the gate appears to be congealing")
--maybe add some fx here?
end,
delayAfter = 3,
}
mySequence:addEvent{
event = function()
spawn( "snail" , 1 , 15 , 15 , 0 )
end,
delayAfter = 1,
}
mySequence:addEvent{
event = function()
hudPrint("Where did that snail come from?")
end,
delayAfter = 0.5,
}
mySequence:addEvent{
event = function()
gate_1:open()
end,
}