!----------------------------------------------------------------------- ! Priority-Daemons ! Release 1 ! Andrew Plotkin (erkyrath@cmu.edu) ! This file is in the public domain. ! ! This is a module which you can include to make the daemons and timers of ! the Inform libraries behave predictably. They will execute in the order ! defined by the "daemon_priority" property of each object in the ! daemon/timer list. Higher priorities go first; if priorities are equal, ! they execute in the order in which they were started up. The default ! daemon_priority is zero, but you can assign any numerical value to the ! property, including negative ones. However, you cannot use an embedded ! routine which returns a number, as is usual in Inform. This is because it ! would be very bad if the priority changed in mid-stream; priorities are ! only checked when a daemon starts up. For the same reason, you should not ! assign a new value to a priority property. If you must do it, do it while ! the object's daemon/timer is *not* running. ! ! These routines have one restriction that the standard library routines do ! not: It is illegal to call StartDaemon or StartTimer from inside a daemon ! or timer routine, if the daemon you're starting has a priority greater ! than or equal to the calling daemon. It *is* legal to start a ! daemon/timer of lower priority (and that daemon will be called for the ! first time that same turn.) It is also legal to call StopDaemon or ! StopTimer at any time. ! ! To use this thing, include this file after you include the Inform ! "parser" file. Before the "parser" include, drop in these four lines ! (uncommented): ! ! Replace StartDaemon; ! Replace StartTimer; ! Replace StopDaemon; ! Replace StopTimer; !-------------------------------------------------------------------------- Property daemon_priority 0; ! StartDaemonOrTimer inserts the given object in the_timers list, at the ! position defined by its daemon_priority. That is, the object is inserted ! after all objects with a greater-or-equal priority, and before all ! objects with a lesser priority. ! ! This is not complicated, just a little ugly. [ StartDaemonOrTimer obj flag i j; ! check to make sure it's not already running for (i=0:ii==obj) { if (timer_flags->i~=flag) TimerE3(obj); rfalse; } ! find a slot j = (-1); for (i=0:ii ~= 0 && (the_timers-->i).daemon_priority >= obj.daemon_priority) { j = i; } if (the_timers-->i ~= 0 && (the_timers-->i).daemon_priority < obj.daemon_priority) { break; } } ! We must now put obj between j (-1 .. a_t-1) and i (0 .. a_t). Both ! are objects (not empty spaces.) j has >= priority, i has < priority. ! If there's a blank space between them, that's perfect. ! Otherwise we'll have to shift i upwards. j++; while (the_timers-->j ~= 0 && j < i) j++; if (j < i) { ! j is open. Use it. i = j; } else { ! We'll use j, but we have to move i up first. while (i < active_timers && the_timers-->i ~= 0) i++; if (i == active_timers) { if (active_timers*2 >= MAX_TIMERS) { TimerE(); return; } active_timers++; } while (i > j) { the_timers-->i = the_timers-->(i-1); timer_flags->i = timer_flags->(i-1); i--; } } ! i is now set correctly (in both cases.) the_timers-->i=obj; timer_flags->i=flag; ]; [ StartDaemon obj; StartDaemonOrTimer(obj,1); ]; [ StartTimer obj timer; if (obj.&time_left==0) TimerE2(obj); else { StartDaemonOrTimer(obj,2); obj.time_left = timer; } ]; ! StopTimer and StopDaemon are exactly the same as in the standard ! library (from release 8 up to at least release 11.) I include them ! here just in case you're using a really old (or distant future) ! library. [ StopTimer obj i; for (i=0:ii==obj) jump FoundTSlot2; rfalse; .FoundTSlot2; if (obj.&time_left==0) TimerE2(obj); the_timers-->i=0; obj.time_left=0; ]; [ StopDaemon obj i; for (i=0:ii==obj) jump FoundTSlot4; rfalse; .FoundTSlot4; the_timers-->i=0; ]; ! End of file -------------------------------------------------------------