!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!  oldaemon                     The Objectloop Daemon
!  Version 1.0                  By L. Ross Raszewski
!  12.2.2002                    ([email protected])
!  For Z-code and Glulx
!  No external requirements
!
! It is a well known fact that one of the major causes of game sluggishness
! is use of the general-form objectloop.
! The general form objectloop is any objectloop construct other than
! (x in y), (x from y), or (x near y).
!
! If a game uses the general-form objectloop within a daemon or each_turn
! construct, the latency between turn cycles is substantially increased.
! Inform's standard library already performs one such objectloop
! (In 'MoveFloatingObjects')
!
! The purpose of the Objectloop Daemon is to reduce the number of times
! that an objectloop must be executed by combining a number of tasks into
! a single loop.
!
! To use the objectloop daemon, simply insert the line:
! StartDaemon(loop_daemon);
! In your Initialise function. This will activate the daemon.
! Now, instead of performing an objectloop in the context of a daemon
! or each_turn, place an object inside the loop_daemon object to
! perform the task.  objectloop daemon events have the following structure:
!
! Object loop_task
! with
!   exec [ o;
!        ! Required. When the daemon is run, the exec function will be
!        !   executed for every object in the game
!        !   The exec method corresponds to the body of the original
!        !   objectloop
!       ],
!   init [;
!       ! Optional. If provided, this method is called before looping begins
!       ],
!   fini [;
!       ! Optional. If provided, this method is called after looping ends
!       ],
!   pred [ o;
!       ! Optional. If provided, this is called for every object immediately
!       ! before exec. It should return false to exclude the object, true to
!       ! include it. The pred method corresponds to the loop condition.
!       ! If no pred is included, the loop is equivalent to "loop over
!       ! all objects"
!       ];
!
! Here is an example of a loop task:
!  This code shows a daemon which sets the 'general' attribute on every
!  object of class 'Foo' once per turn. If several such daemons exist,
!  they might produce a substantial lag. The objectloop daemon version
!  will perform only one objectloop, regardless of how many tasks are
!  assigned.
!  ...
!  daemon [o;
!            objectloop(o ofclass Foo)
!               give o general;
!       ],
! ...
!
! And now the objectloop daemon version:
!
! object flag_set loop_daemon
!  with pred [o; return (o ofclass Foo); ],
!       exec [o; give o general; ];
!
! Placing a task inside the loop_daemon enables it -- the task will be run
! the next time daemons are executed. Removing a task disables it.
!
! Caveats:
! 1. Removing a task
!    It is safe to remove a task from the objectloop daemon at any time.
!    The task will stop immediately. Of course, disabling a task does not
!    undo work already done. For reference, here is the effect of removing
!    a task at various times:
!    When the daemon is not currently executing:
!                         The task will not be executed when the daemon starts
!    Within the task's 'init':
!                         The task will not be executed on this turn
!    Within the task's 'fini':
!                         The task has already been done for this turn, but
!                         will not be done on subsequent turns
!    Within an 'exec':  The task will not be applied to any more objects.
!                       This is roughly equivalent to performing a 'break'
!                       within a normal objectloop.
!                       To allow a task to break in the middle, but to make it
!                       continue to run on the next turn, it is
!                       necessary to re-insert it (via a daemon of its own).
!                       'fini' will not be run for this task.
!   Within a 'pred': If the pred function returns true, the task will
!                       *still be run* on the current object, but will not
!                       be run on any other objects.
!
! 2. Interlacing. In a traditional demon-based objectloop, the body of each
!                 loop is executed once for every object consecutively.
!                 If there are two such loops, the first loop is executed for
!                 every object, then the second loop is executed for every
!                 object. Within the objectloop daemon, the order is changed
!                 slightly. Tasks are interlaced. That is, every task is run
!                 for the first object, then every task is run for the second
!                 object. This may be important if the body of the loop makes
!                 use of global variables.
! 3. Scope. Since 'exec' is excecuted fresh for each object, you cannot use
!           local variables to collect information over the course of an
!           objectloop. The recommended solution to this is to attach
!           properties to the task which are used in place fo local variables.
!           You must then also use the .init() routine to reset these
!           properties each time
! 4. Context. The loop_daemon is run in the context of a daemon. Consult the
!        inform designer's manual for a discussion of where exactly daemons
!        are executed during the course of a turn cycle.  Individual tasks are
!        executed in lineage order. Using 'move x to loop_daemon' will make
!        x the first task to be executed. For finer control, use pmove and
!        rmove (in Utility.h)
! 5. Speed. The loop daemon imposes some overhead over a standard objectloop.
!           It is not sensible to use it to replace a single objectloop.
!           However, it will perform at most one objectloop per turn, regardless
!           of how many tasks are active. Therefore, if there is more than one
!           such loop, the daemon will save time. The amount of time it saves
!           is proportional to the total number of objects in the game.
!           If no tasks are active, the daemon does not perform the objectloop.
!
! The author would appreciate any and all commentary. This library extention
! is free for use in all non-commercial and competition games.
! Permission is required (and will almost certainly be forthcoming) for
! commercial releases.
!
ifndef OLDAEMON_LIBRARY;
Constant OLDAEMON_LIBRARY 32;

Object loop_daemon "Objectloop Daemon"
with
init 0, fini 0, exec 0, pred OLDAEMON_LIBRARY,
daemon
[x o y;
      for(x=child(self):x:x=y)
      {
       y=sibling(x);
       if (x provides init)
        x.init();
      }
      if (~~child(self)) return;
      objectloop(o)
      if (~~child(self)) return; else
      for(x=child(self):x:x=y)
      {
       y=sibling(x);
       if (~~(x provides pred) || x.pred(o))
       x.exec(o);
      }
      for(x=child(self):x:x=y)
      {
       y=sibling(x);
       if (x provides fini)
       x.fini();
      }
   ];

endif;