Extended Timeout

Some time ago, while work­ing on a game, I had to use a mul­ti­tude of #time­out objects (over 250) with dif­fer­ent dura­tions. In the course of the work it was get­ting more and more dif­fi­cult for me to man­age them. Unfor­tu­nate­ly, I ful­ly real­ized that I was going in the wrong direc­tion just when I had to imple­ment the pause game/resume game func­tion­al­i­ty. At that moment I thought that it would have been bet­ter if the #time­out object had have meth­ods as pause(), resume() and restart(). The script below adds this func­tion­al­i­ty. Its usage is a lit­tle bit dif­fer­ent from the stan­dard. 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 def­i­nite­ly requires more lines of code, but in return we can do things as:

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

Time­out script inher­its from Event­Dis­patcher ( see Event­Dis­patcher ), so we can add or remove lis­ten­ers at any time:

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

When the work is done, we call dis­pose(), to remove the ref­er­ences to the lis­ten­ers hold by myTime­out:

myTimeout.dispose()
myTimeout = VOID

Here fol­lows Time­out script, as well as a small demon­stra­tion:

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

Extend­ed Time­out demo

To visu­al­ize the time past, we use:

on enterFrame( me )
  p = myTimeout.percentDone()
  sprite( "PROGRESS" ).setProgress( p )
end enterFrame
Extend­ed Time­out (7288 down­loads)
 
Comments

This is great and use­ful code. Thanks for post­ing!