! 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; ! Here's the code: ! Define the property. Property daemon_priority 0; ! StartDaemon 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. [ StartDaemon obj i j; ! check to make sure it's not already running for (i=0:ii==obj) { if (timer_flags->i==1) 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=2; ]; ! StartTimer is exactly parallel to StartDaemon; see above for comments. [ StartTimer obj timer i j; for (i=0:ii==obj) { if (timer_flags->i==2) TimerE3(obj); rfalse; } 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; } } j++; while (the_timers-->j ~= 0 && j < i) j++; if (j < i) { i = j; } else { 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--; } } if (obj.&time_left==0) TimerE2(obj); the_timers-->i=obj; timer_flags->i=1; 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; ];