Section 18: Daemons and the passing of time

Contents Back Forward | ### 18. Daemons and the passing of time
| Some, such as Sleep and Love, were never human. From this class an individual daemon is allotted to each human being as his 'witness and guardian' through life. ...C. S. Lewis (1898--1963), The Discarded Image A great Daemon... Through him subsist all divination, and the science of sacred things as it relates to sacrifices, and expiations, and disenchantments, and prophecy, and magic... he who is wise in the science of this intercourse is supremely happy...\ ...Plato (c.427--347 BC), The Symposium, translated by Percy Bysshe Shelley (1792--1822) In medieval neo-Platonist philosophy, daemons are the intermediaries of God, hovering invisibly over the world and interfering with it. They may be guardian spirits of places or people. So, here, a daemon is a meddling spirit, associated with a particular game object, which gets a chance to interfere once per turn while it is 'active'. The classic example is of the dwarves of 'Advent', who appear in the cave from time to time: a daemon routine attached to the dwarf object moves it about, throws knives at the player and so on. Each object can have a `daemon` routine of its own. This is set going, and stopped again, by calling the (library) routines
    StartDaemon(object);
    StopDaemon(object);
Once active, the `daemon` property of the object is called as a routine each turn. Daemons are often started by a game's `Initialise` routine and sometimes remain active throughout. For instance, a lamp-battery daemon might do something every turn, while others may hide for many turns before pouncing: such as the daemon in 'Advent' which waits until the player has found all the treasures.
| In particular, a daemon doesn't stop running just because the player has moved on to somewhere else. (Indeed, the library never stops a daemon unless told to.) Actually this is very useful, as it means daemons can be used for 'tidying-up operations', or for the consequences of the player's actions to catch up with him.
| EXERCISE 34: (link to the answer)
| Many games contain 'wandering monsters', characters who walk around the map. Use a daemon to implement one who wanders as freely as the player, like the gentleman thief in 'Zork'.
| EXERCISE 35: (link to the answer)
| Use a background daemon to implement a system of weights, so that the player can only carry a certain weight before her strength gives out and she is obliged to drop something. It should allow for feathers to be lighter than lawn-mowers. A 'timer' (these are traditionally called 'fuses') can also be attached to an object. A timer is started with
    StartTimer(object, time);
in which case it will 'go off', alarm clock-style, in the given number of turns. This means that its `time_out` property will be called, once and once only, when the time comes. The timer can be deactivated (so that it will never go off) by calling
    StopTimer(object);
A timer is required to provide a `time_left` property, to hold the amount of time left. (If it doesn't, an error message is printed at run-time.) You can alter `time_left` yourself: a value of 0 means 'will go off at the end of this turn', so setting `time_left` to 0 triggers immediate activation.
| Normally, you can only have 32 timers or daemons active at the same time as each other (plus any number of inactive ones). But this limit is easily raised: just define the constant `MAX_TIMERS` to some larger value, putting the definition in your code before the `Parser` file is included.
| There is yet a third form of timed event. If a room provides an `each_turn` routine, then this will be called at the end of each turn while the player is there; if an object provides `each_turn`, this is called while the object is nearby. For instance, a radio might blare out music whenever it is nearby; a sword might glow whenever monsters are nearby; or a stream running through several forest locations might occasionally float objects by. 'Each turn' is especially useful to run creatures which stay in one room and are only active when the player is nearby. An ogre with limited patience can therefore have an `each_turn` routine which worries the player ("The ogre stamps his feet angrily!'', etc.) while also having a timer set to go off when his patience runs out.
| 'Nearby' actually means 'in scope', a term which will be properly explained later. The idea is based on line of sight, which works well in most cases.
| But it does mean that the radio will be inaudible when shut up inside most containers -- which is arguably fair enough -- yet audible when shut up inside transparent, say glass, ones. You can always change the scope rules using an `InScope` routine to get around this. In case you want to tell whether scope is being worked out for ordinary parsing reasons or instead for `each_turn` processing, look at the `scope_reason` variable (see Section 28). Powerful effects are available this way -- you could put the radio in scope within all nearby rooms so as to allow sound to travel. Or you could make a thief audible throughout the maze he is wandering around in, as in 'Zork I'.
| EXERCISE 36: (link to the answer)
| (Why the 'Ruins' are claustrophobic.) Make "the sound of scuttling claws'' approach the player in darkness and, after 4 consecutive turns in darkness, kill him.
| EXERCISE 37: (link to the answer)
| A little harder: implement the scuttling claws in a single object definition, with no associated code anywhere else in the program (not even a line in `Initialise`) and without running its daemon all the time. The library also has the (limited) ability to keep track of time of day as the game goes on. The current time is held in the variable `the_time` and runs on a 24-hour clock: this variable holds minutes since midnight, so it has values between 0 and 1439. The time can be set by `SetTime(` 60$\times$<hours>+<minutes>`,` <rate> `);` The `rate` controls how rapidly time is moving: a `rate` of 0 means it is standing still (that is, that the library doesn't change it: your routines still can). A positive `rate` means that that many minutes pass between each turn, while a negative `rate` means that many turns pass between each minute. (It's usual for a timed game to start off the clock by calling `SetTime` in its `Initialise` routine.) The time only (usually) appears on the game's status line if you set
    Statusline time;
as a directive somewhere in your code.
| EXERCISE 38: (link to the answer)
| How could you make your game take notice of the time passing midnight, so that the day of the week could be nudged on?
| EXERCISE 39: (link to the answer)
| (Cf. Sam Hulick's vampire game, 'Knight of Ages'.) Make the lighting throughout the game change at sunrise and sunset.
| Exactly what happens at the end of each turn is: 1. -- The turns counter is incremented. 2. -- The 24-clock is moved on. 3. -- Daemons and timers are run (in no guaranteed order). 4. -- `each_turn` takes place for the current room, and then for everything nearby (that is, in scope). 5. -- The game's global `TimePasses` routine is called. 6. -- Light is re-considered (it may have changed as a result of events since this time last turn). \PAR The sequence is abandoned if at any stage the player dies or wins.
| EXERCISE 40: (link to the answer)
| Suppose the player is magically suspended in mid-air, but that anything let go of will fall out of sight. The natural way to code this is to use a daemon which gets rid of anything it finds on the floor (this is better than just trapping `Drop` actions because objects might end up on the floor in many different ways). Why is using `each_turn` better?
| EXERCISE 41: (link to the answer)
| How would a game work if it involved a month-long archaeological dig, where anything from days to minutes pass between successive game turns?
| REFERENCES: Daemons abound in most games. 'Advent' uses them to run down the lamp batteries, make the bear follow you, animate the dwarves and the pirate and watch for the treasure all being found. See also the flying tortoise from 'Balances' and the chiggers from 'Adventureland'. For more ingenious uses of `daemon`, see the helium balloon, the matchbook and (particularly cunning) the pair of white gloves in 'Toyshop'. Classic timers include the burning match and the hand grenade from 'Toyshop', the endgame timer from 'Advent' and the 'Balances' cyclops (also employing `each_turn`). 'Adventureland' makes much use of `each_turn`: see the golden fish, the mud, the dragon and the bees. The library extension 'timewait.h' by Andrew Clover thoroughly implements time of day, allowing the player to "wait until quarter past three''.

Contents / Back / Forward
Chapter I / Chapter II / Chapter III / Chapter IV / Chapter V / Chapter VI / Appendix

Mechanically translated to HTML from third edition as revised 16 May 1997. Copyright © Graham Nelson 1993, 1994, 1995, 1996, 1997: all rights reserved.