! zclock.h
! A nearly exact port of the daemons used in Infocom games.
! Ported by Allen Garvin, December 6, 2003
! Use freely!
! Some useful constants. Their names come from the ZIL source for minizork
Constant C_ENABLED 0;
Constant C_TICK 1;
Constant C_RTN 2;
Constant C_INTLEN 6;
! Modify this to increase the number of possible daemons.
! It must be a multiple of C_INTLEN (by default, there can be 30 daemons)
Constant C_TABLELEN 180;
Array C_table --> C_TABLELEN;
Global C_ints = C_TABLELEN;
! Queue is the programmer's interface to the daemons. It takes 2 arguments.
! rtn: The routine to be queued as a timer or daemon
! ticks: If positive, the timer countdown length. After 'ticks' turns
! rtn will be called.
! If negative, then behave like an Inform daemon. 'rtn' will be
! called every turn.
! If zero, the daemon or timer will be disabled.
! A queued event must be explicitly enabled. The return value of the
! function is the interrupt array for the event. If the first byte is
! set to 1, it will be enabled. If 0, it will be disabled.
! Thus:
! Queue(Foo, 5)-->C_ENABLED = true; Call 'Foo' in 5 turns
! Queue(Bar, -1)-->C_ENABLED = true; Call 'Bar' every turn
! Queue(Biff, 20); Place 'Biff' in the queue, but do not start
! the timer yet. The timer will not decrement.
! No real purpose I can see, but Infocom did
! this frequently in their Main routines
! Queue(Foo); or Queue(Foo, 0); Stop the 'Foo' timer
[ Queue rtn ticks cint ;
cint = QueueInterrupt(rtn);
cint-->C_TICK = ticks;
StartDaemon(zork_daemon);
return cint;
];
! QueueInterrupt was rarely called directly in Infocom games
[ QueueInterrupt rtn end c int ;
end = C_table + C_TABLELEN;
c = C_table + C_ints;
while( true ) {
if( c ~= end ) {
if( (c-->C_RTN) == rtn )
return c;
c = c + C_INTLEN;
} else {
C_ints = C_ints - C_INTLEN;
int = C_table + C_ints;
int-->C_RTN = rtn;
return int;
}
}
];
! I decided to implement the queue by using an object with an Inform daemon.
Object zork_daemon "Daemon"
with name 'zdaemon',
daemon [ c end tick flag;
c = C_table + C_ints;
end = C_table + C_TABLELEN;
while( true ) {
if( c == end ) {
return flag;
}
if( c-->C_ENABLED ) {
tick = c-->C_TICK;
if( tick ~= 0 ) {
c-->C_TICK = (tick - 1);
if( tick <= 1 && (c-->C_RTN)() )
flag = 1;
}
}
c = c + C_INTLEN;
}
];
! Notes:
! Once a routine is queued, there is no method to remove it from the
! queue. Thus, there is an absolute number of queues in a game. But
! Requeueing a routine will use the same slot, instead of using a new one.
! If you queue a routine with a negative value, there is a bug. When 32767
! turns have passed, the number will overflow to positive, and the queue
! will then be a countdown instead of a daemon event. Though easy to fix
! I decided to leave it bug-compatible with Infocom games. In Zork I, for
! instance, after 2^15 turns the thief will quit moving around the maze.
! And neither the thief nor troll will defend themselves when attacked.