Extended Timeout

Some time ago, while working on a game, I had to use a multitude of #timeout objects (over 250) with different durations. In the course of the work it was getting more and more difficult for me to manage them. Unfortunately, I fully realized that I was going in the wrong direction just when I had to implement the pause game/resume game functionality. At that moment I thought that it would have been better if the #timeout object had have methods as pause(), resume() and restart(). The script below adds this functionality. Its usage is a little bit different from the standard. Instead of

myTimeout = timeout().new( "myTimeout", 1000, #yourHandlerName, me )

We use:

myTimeout = script( "Timeout" ).new( 1000, -1 )
myTimeout.addEventListener( #TIMER, #yourHandlerName, me )
myTimeout.start()

It definitely requires more lines of code, but in return we can do things as:

myTimeout.pause()
trace( myTimeout.percentDone())
myTimeout.resume()

Timeout script inherits from EventDispatcher ( see EventDispatcher ), so we can add or remove listeners at any time:

myTimeout.addEventListener( #TIMER, #displayTimeCode, _movie )
myTimeout.addEventListener( #TIMER_COMPLETE, #displayEndTitles, _movie )

When the work is done, we call dispose(), to remove the references to the listeners hold by myTimeout:

myTimeout.dispose()
myTimeout = VOID

Here follows Timeout script, as well as a small demonstration:

Show code

————————————————-
— "Timeout" parent script
————————————————-
property ancestor
property _timeout — #timeout; The timeout object, which this script really uses.
property _name — #string; The timeout name.
property _period — #integer; The timeout period.
property _remMs — #integer; Remaining milliseconds until the next excution.
property _afterResume — #boolean; Indicates the cycle after "resuming" the timeout.
property _percentDone — #float; Stores the percent done in the moment of pausing.
————————————————-
— PUBLIC PROPERTIES
————————————————-
on ___________________________PUBLIC_PROPERTIES
end

property _state
on state( me )
return _state
end state

property _count
on currentCount( me )
return _count
end currentCount

property _number
on repeatCount( me, avalue )
if voidP( avalue ) then return _number
_number = max( avalue, _count )
end repeatCount

on period( me, avalue )
if( voidP( avalue ) ) then return me._getPeriod()
_timeout.period = avalue
end period

on remainingTime( me )
if( ilk( _timeout ) = #timeout ) then
return( _timeout.time – _system.milliseconds )
else
return 0.000001
end if
end remainingTime

on time( me )
if( ilk( _timeout ) = #timeout ) then return VOID
return _timeout.time
end time

on percentDone( me )
if( _state = #stopped ) then
return 0.0
else if( _state = #runned ) then
if( _afterResume ) then
return 1 – ( me.remainingTime() / me.period() ) * _percentDone
else
return 1 – ( me.remainingTime() / me.period() )
end if
else if( _state = #paused ) then
return 1 – ( _remMs / float( _period ) )
end if
end percentDone

————————————————-
— CONSTRUCTOR
————————————————-
on ___________________________CONSTRUCTOR
end

on new( me, aPeriod, aNumber )
if( not ilk( aPeriod ) = #integer ) then me._exception( #new, #period )
if( not ilk( aNumber ) = #integer ) then me._exception( #new, #cycles )
ancestor = script( "EventDispatcher" ).rawNew()
callAncestor( #new, me )
_name = string( me )
_period = aPeriod
_number = aNumber
_count = 0
_remMs = 0
_state = #stopped
_afterResume = false
_percentDone = 0
return me
end new

————————————————-
— PUBLIC METHODS
————————————————-
on ___________________________PUBLIC_METHODS
end

on start( me )
if( _number = 0 ) then return
_timeout = timeout().new( _name, _period, #_execute, me )
_state = #runned
end start

on stop( me )
if ( ilk( _timeout ) = #timeout ) then
_timeout.forget()
_timeout = VOID
_remMs = 0
_afterResume = false
_percentDone = 0.0
end if
_state = #stopped
end stop

on pause( me )
if( _state = #runned ) then return
_remMs = me.remainingTime()
_percentDone = ( _remMs / float( _period ) )
if ( ilk( _timeout ) = #timeout ) then
_timeout.forget()
_timeout = VOID
end if
_state = #paused
end pause

on resume( me )
if( _state = #stopped ) then return
_timeout = timeout().new( _name, _remMs, #_execute, me )
_state = #runned
_afterResume = true
nothing
end resume

on scale( me, avalue )
if( ilk( _timeout ) = #timeout ) then return
p = integer( _timeout.period * avalue )
_timeout.period = p
end scale

on dispose( me )
if not voidP( ancestor ) then callAncestor( #dispose, me )
if ilk( _timeout ) = #timeout then
_timeout.forget()
end if
_timeout = VOID
_name = VOID
_period = VOID
_number = VOID
_count = VOID
_remMs = VOID
_state = VOID
_afterResume = VOID
_percentDone = VOID
end dispose

————————————————-
— PRIVATE METHODS
————————————————-
on ___________________________PRIVATE_METHODS
end

on _getPeriod( me )
if( ilk( _timeout ) = #timeout ) then
return float( _timeout.period )
else
return 0.000001
end if
end _getPeriod

on _execute( me )
_count = _count + 1
me.dispatchEvent( #TIMER, me, [ #currentCount:_count, #repeatCount:_number, #totalPercentDone:max( 0, _count / float( _number ) ) ] )
if( _afterResume ) then
_timeout.period = _period
_afterResume = false
end if
if ( _number = -1 ) then
nothing
else
if ( _count >= _number ) then
me.stop()
me.dispatchEvent( #TIMER_COMPLETE, me, [ #repeatCount:_number, #totalPercentDone:max( 0,_count / float( _number ) ) ] )
end if
end if
end _execute

on _exception( me, ahandler, amessage, avalue )
exl = propList()
exl.addProp( #period, "Bad parameter – Timeout period (milliseconds): #integer" )
exl.addProp( #cycles, "Bad parameter – Number of cycles: #integer" )
_player.alert( string( exl[ amessage ] ) & RETURN & \
"script:" && me.script & RETURN & \
"handler:" && "#" & ahandler )
_movie.halt()
end _exception

Extended Timeout demo

To visualize the time past, we use:

on enterFrame( me )
p = myTimeout.percentDone()
sprite( "PROGRESS" ).setProgress( p )
end enterFrame

Extended Timeout (7590 downloads)
 
Comments

This is great and useful code. Thanks for posting!

Leave a Reply