zclock.h 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. ! zclock.h
  2. ! A nearly exact port of the daemons used in Infocom games.
  3. ! Ported by Allen Garvin, December 6, 2003
  4. ! Use freely!
  5. ! Some useful constants. Their names come from the ZIL source for minizork
  6. Constant C_ENABLED 0;
  7. Constant C_TICK 1;
  8. Constant C_RTN 2;
  9. Constant C_INTLEN 6;
  10. ! Modify this to increase the number of possible daemons.
  11. ! It must be a multiple of C_INTLEN (by default, there can be 30 daemons)
  12. Constant C_TABLELEN 180;
  13. Array C_table --> C_TABLELEN;
  14. Global C_ints = C_TABLELEN;
  15. ! Queue is the programmer's interface to the daemons. It takes 2 arguments.
  16. ! rtn: The routine to be queued as a timer or daemon
  17. ! ticks: If positive, the timer countdown length. After 'ticks' turns
  18. ! rtn will be called.
  19. ! If negative, then behave like an Inform daemon. 'rtn' will be
  20. ! called every turn.
  21. ! If zero, the daemon or timer will be disabled.
  22. ! A queued event must be explicitly enabled. The return value of the
  23. ! function is the interrupt array for the event. If the first byte is
  24. ! set to 1, it will be enabled. If 0, it will be disabled.
  25. ! Thus:
  26. ! Queue(Foo, 5)-->C_ENABLED = true; Call 'Foo' in 5 turns
  27. ! Queue(Bar, -1)-->C_ENABLED = true; Call 'Bar' every turn
  28. ! Queue(Biff, 20); Place 'Biff' in the queue, but do not start
  29. ! the timer yet. The timer will not decrement.
  30. ! No real purpose I can see, but Infocom did
  31. ! this frequently in their Main routines
  32. ! Queue(Foo); or Queue(Foo, 0); Stop the 'Foo' timer
  33. [ Queue rtn ticks cint ;
  34. cint = QueueInterrupt(rtn);
  35. cint-->C_TICK = ticks;
  36. StartDaemon(zork_daemon);
  37. return cint;
  38. ];
  39. ! QueueInterrupt was rarely called directly in Infocom games
  40. [ QueueInterrupt rtn end c int ;
  41. end = C_table + C_TABLELEN;
  42. c = C_table + C_ints;
  43. while( true ) {
  44. if( c ~= end ) {
  45. if( (c-->C_RTN) == rtn )
  46. return c;
  47. c = c + C_INTLEN;
  48. } else {
  49. C_ints = C_ints - C_INTLEN;
  50. int = C_table + C_ints;
  51. int-->C_RTN = rtn;
  52. return int;
  53. }
  54. }
  55. ];
  56. ! I decided to implement the queue by using an object with an Inform daemon.
  57. Object zork_daemon "Daemon"
  58. with name 'zdaemon',
  59. daemon [ c end tick flag;
  60. c = C_table + C_ints;
  61. end = C_table + C_TABLELEN;
  62. while( true ) {
  63. if( c == end ) {
  64. return flag;
  65. }
  66. if( c-->C_ENABLED ) {
  67. tick = c-->C_TICK;
  68. if( tick ~= 0 ) {
  69. c-->C_TICK = (tick - 1);
  70. if( tick <= 1 && (c-->C_RTN)() )
  71. flag = 1;
  72. }
  73. }
  74. c = c + C_INTLEN;
  75. }
  76. ];
  77. ! Notes:
  78. ! Once a routine is queued, there is no method to remove it from the
  79. ! queue. Thus, there is an absolute number of queues in a game. But
  80. ! Requeueing a routine will use the same slot, instead of using a new one.
  81. ! If you queue a routine with a negative value, there is a bug. When 32767
  82. ! turns have passed, the number will overflow to positive, and the queue
  83. ! will then be a countdown instead of a daemon event. Though easy to fix
  84. ! I decided to leave it bug-compatible with Infocom games. In Zork I, for
  85. ! instance, after 2^15 turns the thief will quit moving around the maze.
  86. ! And neither the thief nor troll will defend themselves when attacked.