Section 22: Describing objects and rooms

Contents Back Forward | ### 22. Describing objects and rooms
| And we were angry and poor and happy, And proud of seeing our names in print. ...G. K. Chesterton (1874--1936), A Song of Defeat Talking to the player about the state of the world is much easier than listening to his intentions for it. Despite this, the business of description takes up a fair part of Chapter V since the designer of a really complex game will eventually need to know almost every rule involved. (Whereas nobody would want to know everything about the parser.) To begin, the simplest description is the "short name'' given to a single object. For instance
        print (a) brass_lamp;
may result in "an old brass lamp'' being printed. There are four such forms of `print`:
`print (the) obj` | Print the object with its definite article
`print (The) obj` | The same, but capitalised
`print (a) obj` | Print the object with indefinite article
`print (name) obj` | Print the object's short name alone
and these can be freely mixed into lists of things to `print` or `print_ret`, as for example:
        print_ret "The ogre declines to eat ", (the) noun, ".";
| EXERCISE 49: (link to the answer)
| (By Gareth Rees.) When referring to `animate` objects, one usually needs to use pronouns such as "his''. Define new printing routines so that, say, `print "You throw the book at ", (PronounAcc) obj, "!";` will insert the right accusative pronoun.
| There is also a special syntax `print object` for printing object names, but do not use it without good reason: it doesn't understand some of the features below and is not protected against crashing if you mistakenly try to print the name for an out of range object number.
| Inform tries to work out the right indefinite article for any object automatically. In English-language games, it uses 'an' when the short name starts with a vowel and 'a' when it does not (unless the name is plural, when 'some' is used in either case). You can override this by setting `article` yourself. Here are some possibilities: a / platinum bar, an / orange balloon, your / Aunt Jemima, some bundles of / reeds, far too many / marbles, The / London Planetarium If the object is given the attribute `proper` then its name is treated as a proper noun with no indefinite article, so the value of `article` is ignored.
| The `article` property can also hold a routine to print one.
| Definite articles are always "the'' (except for `proper` nouns). Thus the platinum bar, Benjamin Franklin, Elbereth are all printed by `print (the) ...`; the latter two objects being `proper`.
| There's usually no need to worry about definite and indefinite articles for room objects, as Inform never has cause to print them.
| A single object whose name is plural, such as "grapes'' or "marble pillars'', should be given the attribute `pluralname`. As a result the library might say, e.g., "You can't open those" instead of "You can't open that". It also affects the pronoun "them'' and makes the usual indefinite article "some''.
| You can give `animate` objects the attributes `male`, `female` or `neuter` to help the parser understand pronouns properly. `animate` objects are assumed to be male if you set neither alternative.
| The short name of an object is normally the text given in double-quotes at the head of its definition. This is very inconvenient to change during play when, for example, "blue liquid'' becomes "purple liquid'' as a result of a chemical reaction. A more flexible way to specify an object's short name is with the `short_name` property. To print the name of such an object, Inform does the following: 1. -- If the `short_name` is a string, it's printed and that's all. 2. -- If it is a routine, then it is called. If it returns true, that's all. 3. -- The text given in the header of the object definition is printed. For example, the dye might be given:
    short_name
    [;   switch(self.colour)
         {   1: print "blue ";
             2: print "purple ";
             3: print "horrid sludge"; rtrue;
         }
    ],
with `"liquid"` as the short name in its header. According to whether its `colour` property is 1, 2 or 3, the printed result is "blue liquid'', "purple liquid'' or "horrid sludge''.
| Alternatively, define the dye with `short_name "blue liquid"` and then simply execute `dye.short_name = "purple liquid";` when the time comes.
| Rooms can also be given a `short_name` routine, which is useful to code, say, a grid of four hundred similar locations called "Area 1" up to "Area 400". (They can be represented by just one object in the program.)
| For many objects the indefinite article and short name will most often be seen in inventory lists, such as
    >i
    You are carrying:
      a leaf of mint
      a peculiar book
      your satchel (which is open)
        a green cube
Some objects, though, ought to have fuller entries in an inventory: a wine bottle should say how much wine is left, for instance. The `invent` property is designed for this. The simplest way to use `invent` is as a string. For instance, declaring a peculiar book with
        invent "that harmless old book of Geoffrey's",
will make this the inventory line for the book. In the light of events, it could later be changed to
        geoffreys_book.invent = "that lethal old book of Geoffrey's";
| Note that this string becomes the whole inventory entry: if the object were an open container, its contents wouldn't be listed, which might be unfortunate. In such circumstances it's better to write an `invent` routine, and that's also the way to append text like "(half-empty)''.
| Each line of an inventory is produced in two stages. First, the basic line: 1a. -- The global variable `inventory_stage` is set to 1. 1b. -- The `invent` routine is called (if there is one). If it returns true, stop here. 1c. -- The object's indefinite article and short-name are printed.
| Second, little informative messages like "(which is open)" are printed, and inventories are given for the contents of open containers: 2a. -- The global variable `inventory_stage` is set to 2. 2b. -- The `invent` routine is called (if there is one). If it returns true, stop here. 2c. -- A message such as "(closed, empty and providing light)'' is printed, as appropriate. 2d. -- If it is an `open container`, its contents are inventoried.
| After each line is printed, linking text such as a new-line or a comma is printed, according to the current "list style''.
| For example, here is the `invent` routine used by the matchbook in 'Toyshop':
    invent
    [ i; if (inventory_stage==2)
         {   i=self.number;
             if (i==0) print " (empty)";
             if (i==1) print " (1 match left)";
             if (i>1)  print " (",i," matches left)";
         }
    ],
| EXERCISE 50: (link to the answer)
| Suppose you want to change the whole inventory line for an ornate box but you can't use an `invent` string, or return true from stage 1, because you still want stage 2d to happen properly (so that its contents will be listed). How can you achieve this?
| The largest and most complicated messages Inform ever prints on its own initiative are room descriptions, printed when the `Look` action is carried out (for instance, when the statement `<Look>;` triggers a room description). What happens is: the room's short name is printed (usually in bold-face) on a line of its own, then its `description`, followed by a list of the objects residing there which aren't `concealed` or `scenery`. Chapter IV mentioned many different properties -- `initial`, `when_on`, `when_off` and so on -- giving descriptions of what an object looks like when in the same room as the player; some apply to doors, others to switchable objects and so on. All of them can be routines to print text, instead of being strings to print. The precise rules are given below. But the whole system can be bypassed using the `describe` property. If an object gives a `describe` routine then this takes priority over everything: if it returns true, the library assumes that the object has already been described, and prints nothing further. For example,
        describe
        [;  "^The platinum pyramid catches the light beautifully.";
        ];
means that even when the pyramid has been `moved` (i.e. held by the player at some stage) it will always have its own line of room description.
| Note the initial `^` (new-line) character. The library doesn't print a skipped line itself before calling `describe` because it doesn't know yet whether the routine will want to say anything. A `describe` routine which prints nothing and returns true makes an object invisible, as if it were `concealed`.
| The `Look` action does three things: it 'notes arrival', prints the room description then 'scores arrival'. Only the printing rules are given here (see Section 20 for the others), but they're given in full. In what follows, the word 'location' means the room object if there's light to see by, and the special "Darkness'' object otherwise. First the top line: 1a. -- A new-line is printed. The location's short name is printed (in bold-face, if possible). 1b. -- If the player is on a `supporter`, then " (on <something>)'' is printed; if inside anything else, then " (in <something>)''. 1c. -- " (as <something>)'' is printed if this was requested by the game's most recent call to `ChangePlayer` (for instance, " (as a werewolf)''). 1d. -- A new-line is printed.
| Now the 'long description'. This step is skipped if the player has just moved of his own will into a location already visited, unless the game is in "verbose" mode. 2. -- If the location has a `describe` property, then run it. If not, look at the location's `description` property: if it's a string, print it; if it's a routine, run it.
| All rooms must provide either a `describe` property or a `description` of themselves. Now for items nearby: 3a. -- List any objects on the floor. 3b. -- If the player is in or on something, list the other objects in that.
| The library has now finished, but your game gets a chance to add a postscript: 4. -- Call the entry point `LookRoutine`.
| The `visited` attribute is only given to a room after its description has been printed for the first time (it happens during 'scoring arrival'). This is convenient for making the description different after the first time.
| 'Listing objects' (as in 3a and 3b) is a complicated business. Some objects are given a line or paragraph to themselves, others are lumped together in a list at the end. The following objects are not mentioned at all: the player, what the player is in or on (if anything) and anything which is `scenery` or `concealed`. The remaining objects are looked through (eldest first) as follows: 1. -- If the object has a `describe` routine, run it. If it returns true, stop here and don't mention the object at all. 2. -- Work out the "description property'' for the object: -- a. -- For a `container`, this is `when_open` or `when_closed`; -- b. -- Otherwise, for a `switchable` object this is `when_on` or `when_off`; -- c. -- Otherwise, for a `door` this is `when_open` or `when_closed`; -- d. -- Otherwise, it's `initial`. 3. -- If either the object doesn't have this property or the object has been held by the player before (i.e., has `moved`) and the property isn't `when_off` or `when_closed` then then the object will be listed at the end. 4. -- Otherwise a new-line is printed and the property is printed (if it's a string) or run (if it's a routine).
| A `supporter` which is `scenery` won't be mentioned, but anything on top of it which is not `concealed` will be.
| Objects which have just been pushed into a new room are not listed in that room's description on the turn in question. This is not because of any rule about room descriptions, but because the pushed object is moved into the new room only after the room description is made. This means that when a wheelbarrow is pushed for a long distance, the player does not have to keep reading "You can see a wheelbarrow here.'' every move, as though that were a surprise.
| You can use a library routine called `Locale` to perform 'object listing'. See Appendix A7 for details: suffice to say here that the process above is equivalent to executing
        if (Locale(location, "You can see", "You can also see"))
            print " here.^";
`Locale` is useful for describing areas of a room which are sub-divided off, such as the stage of a theatre.
| EXERCISE 51: (link to the answer)
| The library implements "superbrief'' and "verbose'' modes for room description (one always omits long room descriptions, the other never does). How can verbose mode automatically print room descriptions every turn? (Some of the later Infocom games did this.)
| REFERENCES: 'Balances' often uses `short_name`, especially for the white cubes (whose names change) and lottery tickets (whose numbers are chosen by the player). 'Adventureland' uses `short_name` in simpler ways: see the bear and the bottle, for instance. The scroll class of 'Balances' uses `invent`. See the `ScottRoom` class of 'Adventureland' for a radically different way to describe rooms (in pidgin English, like telegraphese).

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.