| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Contents Back Forward |
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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:
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:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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''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'. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|