Section 16: Living creatures and conversation

Contents Back Forward | ### 16. Living creatures and conversation
| To know how to live is my trade and my art. ...Michel de Montaigne (1533--1592), Essays Everything that can be said can be said clearly. ...Ludwig Wittgenstein (1889--1951), Tractatus This rummage through special kinds of objects finishes up with the most sophisticated kind: living ones. Note that the finer points of this section, on the arts of conversation, require some knowledge of Chapter V. Animate objects, such as sea monsters, mad aunts or nasty little dwarves, have a property called `life`. This behaves somewhat like a `before` or `after` routine, but only applies to the following actions: `Attack` The player is making hostile advances... `Kiss` ...or excessively friendly ones... `WakeOther` ...or simply trying to rouse the creature from sleep. `ThrowAt` The player asked to throw `noun` at the creature. `Give` The player asked to give `noun` to the creature... `Show` ...or, tantalisingly, just to show it. `Ask` The player asked about something. Just as with a "consult'' topic (see Section 15 passim), the variables `consult_from` and `consult_words` are set up to indicate which words the object might like to think about. (In addition, `second` holds the dictionary value for the first word which isn't `'the'`, but this is much cruder.) `Tell` Likewise, the player is trying to tell the creature about something. The topic is set up just as for `Ask` (that is, `consult_from` and `consult_words` are set, and `second` also holds the first interesting word). `Answer` This can happen in two ways. One is if the player types "answer <some text> to troll" or "say <some text> to troll''; the other is if he gives an order which the parser can't sort out, such as "troll, og south", and which the `orders` property hasn't handled already. Once again, variables are set as if it were a "consult'' topic. (In addition, `noun` is set to the first word, and an attempt to read the text as a number is stored in the variable `special_number`: for instance, "computer, 143" will cause `special_number` to be set to 143.) `Order` This catches any 'orders' which aren't handled by the `orders` property (see below); `action`, `noun` and `second` are set up as usual. If the `life` routine doesn't exist, or returns false, events take their usual course. `life` routines tend to be quite lengthy, even for relatively static characters such as the priest who stands in the 'Ruins' Shrine:
    Object -> priest "mummified priest"
      with name "mummified" "priest",
           description
              "He is desiccated and hangs together only by will-power.  Though
               his first language is presumably local Mayan, you have the curious
               instinct that he will understand your speech.",
           initial "Behind the slab, a mummified priest stands waiting, barely
               alive at best, impossibly venerable.",
           life
           [; Answer: "The priest coughs, and almost falls apart.";
              Ask:     switch(second)
                       {   'dictionary', 'book':
                                if (dictionary has general)
                                    "~The ~bird~ glyph... very funny.~";
                               "~A dictionary? Really?~";
                           'glyph', 'glyphs', 'mayan', 'dialect':
                               "~In our culture, the Priests are ever literate.~";
                           'king', 'tomb', 'shrine', 'temple', 'altar', 'slab':
                               "~The King (life! prosperity! happiness!) is buried
                                deep under this Shrine, where you will never go.~";
                       }
                       "~You must find your own answer.~";
              Tell:   "The priest has no interest in your sordid life.";
              Attack, Kiss:  remove self;
                      "The priest desiccates away into dust until nothing
                       remains, not a breeze nor a bone.";
              ThrowAt: move noun to location; <<Attack self>>;
              Show, Give:
                       if (noun==dictionary && dictionary hasnt general)
                       {   give dictionary general;
                          "The priest reads a little of the book, laughing
                           in a hollow, whispering way.  Unable to restrain
                           his mirth, he scratches in a correction somewhere
                           before returning the book.";
                       }
                      "The priest is not very interested in earthly things.";
           ],
      has  animate;
(Some of the `Ask` topics are omitted for brevity.) Of course an `animate` object still has `before` and `after` routines like any other, so you can trap many other kinds of behaviour. Animate creatures can also `react_before` and `react_after`, and it's here that these properties really come into their own:
           react_before
           [; Drop: if (noun==satellite_gadget)
                 print "~I wouldn't do that, Mr Bond,~ says Blofeld.^^";
              Shoot: remove beretta;
                "As you draw, Blofeld snaps his fingers and a giant
                 magnet snatches the gun from your hand.  It hits the
                 ceiling with a clang.  Blofeld silkily strokes his cat.";
           ];
If Blofeld moves from place to place, these rules move with him.
| EXERCISE 18: (link to the answer)
| Arrange for a bearded psychiatrist to place the player under observation, occasionally mumbling insights such as "Subject puts green cone on table. Interesting.'' Another example is the coiled snake from 'Balances', which shows that even the tiniest `life` routine can be adequate for an animal:
    Object -> snake "hissing snake"
      with name "hissing" "snake",
           initial "Tightly coiled at the edge of the chasm is a hissing snake.",
           life [; "The snake hisses angrily!"; ],
      has  animate;
| When writing general code to deal with `animate` creatures, it's sometimes convenient to have a system worked out for printing pronouns such as "her'' and "He''. See Section 22 for one way to do this.
| Sometimes creatures should be `transparent`, sometimes not. Consider these two cases of `animate` characters, for instance: -- an urchin with something bulging inside his jacket pocket; -- a hacker who has a bunch of keys hanging off his belt. The hacker is `transparent`, the urchin not. That way the parser prevents the player from referring to whatever the urchin is hiding, even if the player has played the game before, and knows what is in there and what it's called. But the player can look at and be tantalised by the hacker's keys. When the player types in something like "pilot, fly south'', the result is called an 'order': this is the corresponding idea to an 'action'. (Indeed, if the player types "me, go south'' an ordinary `Go s_obj` action is produced.) The order is sent to the pilot's `orders` property, which may if it wishes obey or react in some other way. Otherwise, the standard game rules will simply print something like "The pilot has better things to do.'' The above priest is especially unhelpful:
           orders
           [;  Go: "~I must not leave the Shrine.~";
               NotUnderstood: "~You speak in riddles.~";
               default: "~It is not your orders I serve.~";
           ];
(The `NotUnderstood` clause is run when the parser couldn't understand what the player typed: e.g., "priest, go onrth''.)
| Something to bear in mind is that because the library regards the words "yes'' and "no'' as being verbs in Inform, it understands "delores, yes'' as being a `Yes` order. (This can be a slight nuisance, as "say yes to delores'' is treated differently: it gets routed through the `life` routine as an `Answer`.)
| If the `orders` property returns false (or if there wasn't an `orders` property in the first place), the order is sent either to the `Order:` part of the `life` property (if it's understood) or to the `Answer:` part (if it isn't). (This is how all orders used to be processed, and it's retained to avoid making reams of old Inform code go wrong.) If these also return false, a message like "X has better things to do'' or "There is no reply'' is finally printed.
| To clarify the various kinds of conversation:
Command | rule | `action` | `noun` | `second` | consult
"orc, take axe'' | `order` | `Take` | `axe` | `0` |
"orc, yes'' | `order` | `Yes` | `0` | `0` |
"ask orc for the shield'' | `order` | `Give` | `shield` | `player` |
"orc, troll'' | `order` | `NotU...` | `'troll'` | `orc` | 3`` 1
"say troll to orc'' | `life` | `Answer` | `'troll'` | `orc` | 2`` 1
"answer troll to orc'' | `life` | `Answer` | `'troll'` | `orc` | 2`` 1
"orc, tell me about coins'' | `life` | `Ask` | `orc` | `'coins'` | 6`` 1
"ask orc about the big troll'' | `life` | `Ask` | `orc` | `'big'` | 4`` 3
"ask orc about wyvern'' | `life` | `Ask` | `orc` | `0` | 4`` 1
"tell orc about lost troll'' | `life` | `Tell` | `orc` | `'lost'` | 4`` 2
where "wyvern'' is a word not mentioned anywhere in the program, which is why its value is `0`.
| EXERCISE 19: (link to the answer)
| In some ways, `Answer` and `Tell` are just too much trouble. How can you make attempts to use these produce a message saying "To talk to someone, try 'someone, something'.''? Some objects are not alive as such, but can be spoken to: microphones, tape recorders, voice-activated computers and so on. It would be a nuisance to implement these as `animate`, since they have none of the other characteristics of life: instead, they can be given just the attribute `talkable` and `orders` and `life` properties to deal with the resulting conversation.
| EXERCISE 20: (link to the answer)
| (Cf. 'Starcross'.) Construct a computer responding to "computer, theta is 180''.
| The rest of this section starts to overlap much more with Chapter V, and assumes a little familiarity with the parser.
| The `NotUnderstood` clause of `orders` is run when the parser has got stuck parsing an order like "pilot, fly somersaults''. The variable `etype` holds the parser error that would have been printed out, had it been a command by the player himself. See Section 29: for instance, `CANTSEE_PE` would mean "the pilot can't see any such object''.
| When the player issues requests to an `animate` or `talkable` object, they're normally parsed exactly as if they were commands by the player himself (except that the `actor` is now the person being spoken to). But sometimes one would rather they were parsed by an entirely different grammar. For instance, consider Zen, the flight computer of an alien spacecraft. It's inappropriate to tell Zen to (say) pick up a teleport bracelet and the crew tend to give commands more like: "Zen, set course for Centauro'' "Zen, speed standard by six'' "Zen, scan 360 orbital'' "Zen, raise the force wall'' "Zen, clear the neutron blasters for firing'' This could mostly be implemented by adding verbs like "raise'' to the usual game grammar (see the 'Starcross' computer exercise above), or by carefully trapping the `Answer` rule. But this is a nuisance, especially if about half the commands you want are recognised as orders in the usual grammar but the other half aren't.
| An `animate` or `talkable` object can therefore provide a `grammar` routine (if it likes). This is called at a time when the parser has worked out the object that is being addressed and has set the variables `verb_num` and `verb_word` (to the number of the 'verb' and its dictionary entry, respectively: for example, in "orac, operate the teleport'' `verb_num` would be 3 (because the comma counts as a word on its own) and `verb_word` would be `'operate'`). The `grammar` routine can reply by returning: 0. -- The parser carries on as usual. 1. -- The `grammar` routine is saying it has done all the parsing necessary itself, by hand (i.e., using `NextWord`, `TryNumber`, `NounDomain` and the like): the variables `action`, `noun` and `second` must be set up to contain the resulting order. `'verb'` -- The parser ignores the usual grammar and instead works through the grammar lines for the given verb (see below). `-'verb'` -- Ditto, except that if none of those grammar lines work then the parser goes back and tries the usual grammar as well. In addition, the `grammar` routine is free to do some partial parsing of the early words provided it moves on `verb_num` accordingly to show how much it's got through.
| EXERCISE 21: (link to the answer)
| Implement Charlotte, a little girl who's playing Simon Says (a game in which she only follows your instructions if you remember to say "Simon says'' in front of them: so she'll disobey "charlotte, wave'' but obey "charlotte, simon says wave'').
| EXERCISE 22: (link to the answer)
| Another of Charlotte's rules is that if you say a number, she has to clap that many times. Can you play?
| EXERCISE 23: (link to the answer)
| Regrettably, Dyslexic Dan has always mixed up the words "take'' and "drop''. Implement him anyway.
| It's useful to know that if the player types a comma or a full stop, then the parser cuts these out as separate words. Because of this, a dictionary word containing up to 7 letters and then a comma or a full stop can never be matched by what the player types. Such a word is called an "untypeable verb'', and it's useful to help a `grammar` routine to shunt parsing into a piece of game grammar which the player can never use. For instance, here's a way to implement the 'Starcross' computer which doesn't involve creating foolish new actions. We create grammar:
    [ Control;
      switch(NextWord())
      {   'theta': parsed_number=1; return 1;
          'phi':   parsed_number=2; return 1;
          'range': parsed_number=3; return 1;
          default: return -1;
      }
    ];
    Verb "comp," * Control "is" number -> SetTo;
And the computer itself needs properties
           grammar [; return 'comp,'; ],
           orders
           [;  SetTo:
                   switch(noun)
                   {   1: print "~Theta"; 2: print "~Phi"; 3: print "~Range"; }
                   print_ret " set to ", second, ".~";
               default: "~Does not compute!~";
           ];
This may not look easier, but it's much more flexible, as the exercises below will hopefully demonstrate.
| Another use for untypeable verbs is to create what might be called 'fake fake actions'. Recall that a fake action is one which is never generated by the parser, and has no action routine. Sometimes (very rarely) you want a proper action but which still can't be generated by the parser: the following example creates three.
    Verb "actions." * -> Prepare * -> Simmer * -> Cook;
The parser never uses "actions.'' in its ordinary grammar, so this definition has the sole effect of creating three new actions: `Prepare`, `Simmer` and `Cook`.
| EXERCISE 24: (link to the answer)
| How can you make a grammar extension to an ordinary verb that will apply only to Dan?
| EXERCISE 25: (link to the answer)
| Make an alarm clock responding to "alarm, off'', "alarm, on'' and "alarm, half past seven'' (the latter to set its alarm time).
| EXERCISE 26: (link to the answer)
| Implement a tricorder (from Star Trek) which analyses nearby objects on a request like "tricorder, the quartz stratum''.
| EXERCISE 27: (link to the answer)
| And, for good measure, a replicator responding to commands like "replicator, tea earl grey'' and "replicator, aldebaran brandy''.
| EXERCISE 28: (link to the answer)
| And a communications badge in contact with the ship's computer, which answers questions like "computer, where is Admiral Lebling''.
| EXERCISE 29: (link to the answer)
| Finally, construct the formidable flight computer Zen.
| The next two exercises really belong to Section 28, but are too useful (for the "someone on the other end of a phone'' situation) to bury far away. Note that an alternative to these scope-hacking tricks, if you just want to implement something like "michael, tell me about the crystals'' (when Michael is at the other end of the line), is to make the phone a `talkable` object and make the word `'michael'` refer to the phone (using a `parse_name` routine). For more on scope hacking, see Section 28. Note that the variable `scope_reason` is always set to the constant value `TALKING_REASON` when the game is trying to work out who you wish to talk to: so it's quite easy to make the scope different for conversational purposes.
| EXERCISE 30: (link to the answer)
| Via the main screen of the Starship Enterprise, Captain Picard wants to see and talk to Noslen Maharg, the notorious tyrant, who is down on the planet Mrofni. Make it so.
| EXERCISE 31: (link to the answer)
| Put the player in telepathic contact with Martha, who is in a sealed room some distance away, but who has a talent for telekinesis. Martha should respond well to "martha, look'', "ask martha about...'', "say yes to martha'', "ask martha for red ball'', "martha, give me the red ball'' and the like.
| REFERENCES: A much fuller example of a 'non-player character' is given in the example game 'The Thief', by Gareth Rees (though it's really an implementation of the gentleman in 'Zork', himself an imitation of the pirate in 'Advent'). The thief is capable of walking around, being followed, stealing things, picking locks, opening doors and so on. Other good definitions of `animate` objects to look at are Christopher in 'Toyshop', who will stack up building blocks on request; the kittens in 'Alice Through The Looking-Glass'; the barker in 'Balances', and the cast of 'Advent': the little bird, the snake, bear and dragon, the pirate and of course the threatening little dwarves. Following people means being able to refer to them after they've left the room: see 'Follow my leader', also by Mr Rees, or the library extension "follower.h'' by Andrew Clover. See the Inform home page for a way round the `Yes` awkwardness. For parsing topics of conversation in advanced ways, see the example game 'Encyclopaedia Frobozzica' by Gareth Rees. To see how much a good set of characters can do for a game, try playing the prologue of 'Christminster'.

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.