7 | |CENTER| *W was a watchman, and guarded the door;*
8 | |CENTER| *X was expensive, and so became poor.*
12 .. image:: /images/picW.png
15 |W|\e've given ourselves an interesting challenge by overusing that
16 convenient word "toilet", and here we show you how we resolve the
17 ambiguities that have been introduced. Also, it's time for the eponymous
18 owner of Benny's café to be developed in full.
24 single: ambiguous objects
26 If you check the :prop:`name` properties of the toilet door, the toilet key
27 and the toilet room, you’ll see that the dictionary word ``'toilet'``
28 occurs in all of them. There won't be any problems if players mention the
29 words DOOR or KEY, but we reach a strange impasse should they try to
30 perform some action with just the word TOILET. The interpreter has to
31 think fast: is the player talking about the key? About the door? Or about
32 the toilet? Unable to decide, it asks: "Which do you mean, the door to the
33 toilet, the toilet key or the toilet?"
35 And guess what? Players will never be able to refer to the toilet object
36 (unless they type BATH ROOM or REST ROOM, not an obvious choice since we
37 haven't used those phrases anywhere visible). If the player answers TOILET
38 the parser will still have three objects with that dictionary word as a
39 possible name, so it will ask again, and again -- until we give it some
40 dictionary word which is not ambiguous. A human reader would be able to
41 understand that the word TOILET alone refers to the room, but the
42 interpreter won't -- unless we help it a little.
44 We could work around this problem in more than one way, but we'll take this
45 opportunity of demonstrating the use of a third-party library package.
47 .. |IFARCHIVE| replace:: http://mirror.ifarchive.org/indexes/if-archive.html
49 .. Generated by autoindex
53 When experienced designers find a problem which is not easily solvable,
54 they may come up with a smart solution and then consider that others could
55 benefit from the effort. The product of this generosity takes the form of
56 a library extension: the solution neatly packaged as a file that other
57 designers can incorporate into their source code. These files can be found
58 in the IF Archive: go to |IFARCHIVE| and then select "``.../infocom``",
59 "``.../compilers``", "``.../inform6``", "``.../library``", and
60 "``.../contributions``". All of these files contain Inform code. To use a
61 library extension (also known as a library contribution), you should
62 download it and read the instructions (usually embedded as comments in the
63 file, but occasionally supplied separately) to discover what to do next.
64 Normally, you ``Include`` it (as we have already done with ``Parser``,
65 ``VerbLib`` and ``Grammar``), but often there are rules about where exactly
66 this Include should be placed in your source code. It is not unusual to
67 find other suggestions and warnings.
69 To help us out of the disambiguation problem with the word TOILET, we are
70 going to use Neil Cerutti's extension ``pname.h``, which is designed for
71 situations precisely like this. First, we follow the link to the IF
72 archive and download the compressed file ``pname.zip``, which contains two
73 more files: ``pname.h`` and ``pname.txt``. We place these files in the
74 folder where we are currently developing our game or, if using the
75 environment we proposed in :doc:`02`, in the ``Inform\Lib\Contrib`` folder.
76 The text file offers instructions about installation and usage. Here we
79 This version of pname.h is recommended for use only with version 6/10 of
82 We're actually using a later version, but this doesn't seem to cause a
83 problem. Most extensions aren't so fussy, but ``pname.h`` fiddles with
84 some routines at the heart of the standard library; these may not be
85 identical in other Inform versions.
87 .. Generated by autoindex
89 pair: parse_name; library property
91 The introduction explains what ``pname.h`` does for you; namely, it lets
92 you avoid using complicated :prop:`parse_name` routines to disambiguate the
93 player's input when the same dictionary word refers to more than one item.
94 A :prop:`parse_name` routine would have been the solution to our problem
95 before the existence of this file, and it qualifies as an advanced
96 programming topic, difficult to master on a first approach. Fortunately,
97 we don't need to worry. Neil Cerutti explains:
99 The ``pname.h`` package defines a new object property, ``pname`` (short
100 for phrase name), with a similar look and feel to the standard
101 :prop:`name` property: both contain a list of dictionary words. However,
102 in a ``pname`` property the order of the words is significant, and
103 special operators ``'.p'`` ``'.or'`` and ``'.x'`` enable you to embed
104 some intelligence into the list. In most cases where the standard
105 :prop:`name` property isn't enough, you can now just replace it with a
106 ``pname`` property, rather than write a :prop:`parse_name` property
109 We'll soon see how it works. Let's take a look at the installation
112 To incorporate this package into your program, do three things:
114 #. Add four lines near the head of the program (before you include
117 .. code-block:: inform
122 Replace TryGivenObject;
124 #. Include the ``pname.h`` header just after you include ``Parser.h``.
126 .. code-block:: inform
131 #. Add ``pname`` properties to those objects which require phrase
134 It seems simple enough. So, following steps one and two, we add those
135 ``Replace...`` lines before the inclusion of ``Parser``, and we include
136 ``pname.h`` right after it. ``Replace`` tells the compiler that we're
137 providing replacements for some standard routines.
139 .. include:: /config/typethis.rst
141 .. code-block:: inform
143 Constant Story "Captain Fate";
145 "^A simple Inform example
146 ^by Roger Firth and Sonja Kesserich.^";
147 Release 3; Serial "040804"; ! for keeping track of public releases
149 Constant MANUAL_PRONOUNS;
151 Replace MakeMatch; ! requited by pname.h
154 Replace TryGivenObject;
160 Now our source code is ready to benefit from the library package. How does
161 it work? We have acquired a new property -- ``pname`` -- which can be
162 added to some of our objects, and which works pretty much like a
163 :prop:`name` property. In fact, it should be used *instead* of a
164 :prop:`name` property where we have a disambiguation problem. Let’s change
165 the relevant lines for the toilet door and the toilet key:
167 .. include:: /config/typethis.rst
169 .. code-block:: inform
172 with pname '.x' 'red' '.x' 'toilet' 'door',
176 Object toilet_key "toilet key" benny
177 with pname '.x' 'toilet' 'key',
181 while leaving the ``outside_of_toilet`` unchanged:
183 .. code-block:: inform
185 Object outside_of_toilet "toilet" cafe
186 with name 'toilet' 'bath' 'rest' 'room' 'bathroom' 'restroom',
190 We are now using a new operator -- ``'.x'`` -- in our ``pname`` word lists.
191 The text file explains
193 The first dictionary word to the right of a ``'.x'`` operator is
194 interpreted as optional.
196 and this makes the dictionary word ``'toilet'`` of lesser importance for
197 these objects, so that at run-time players could refer to the DOOR or
198 TOILET DOOR or the KEY or TOILET KEY -- but not simply to the TOILET --
199 when referring to either the door or the key. And, by leaving unchanged
200 the name property of the ``outside_of_toilet`` object – where there is also
201 another ``'toilet'`` entry -- the ``pname`` properties will tell the
202 interpreter to discard the key and the door as possible objects to be
203 considered when players refer just to TOILET. Looking at it in terms of
204 the English language, we've effectively said that "TOILET" is an adjective
205 in the phrases "TOILET DOOR" and "TOILET KEY", but a noun when used on its
206 own to refer to the room.
208 The ``pname.h`` package has additional functionality to deal with more
209 complex phrases, but we don't need it in our example game. Feel free,
210 however, to read ``pname.txt`` and discover what this fine library
211 extension can do for you: it's an easy answer to many a disambiguation
214 Don't shoot! I'm only the barman
215 ================================
217 A lot of the action of the game happens around Benny, and his definition
218 needs a little care. Let's explain what we want to happen.
220 So the door is locked and the player, after discovering what the note
221 stuck on the toilet door said, will eventually ask Benny for the key.
222 Sadly, Benny allows use of the toilet only to customers, a remark he'll
223 make looking pointedly at the menu board behind him. The player will
224 have to ask for a coffee first, thereby qualifying as a customer in
225 Benny's eyes and thus entitled to make use of the toilet. At last! Rush
226 inside, change into Captain Fate’s costume and fly away to save the day!
228 Except that the player neither paid for the coffee, nor returned the toilet
229 key. Benny will have to stop the player from leaving the café in these
230 circumstances. To prevent unnecessary complication, there will be a coin
231 near the lavatory, enough cash to pay for the coffee. And that about sums
232 it all up; pretty simple to describe -- not so simple to code. Remember
233 Benny's basic definition from the previous chapter:
235 .. code-block:: inform
237 Object benny "Benny" cafe
240 "A deceptively FAT man of uncanny agility, Benny entertains his
241 customers crushing coconuts against his forehead when the mood
243 has scenery animate male proper transparent;
245 We can now add some complexity, beginning with a :prop:`life` property. In
248 .. code-block:: inform
251 Give: !... code for giving objects to Benny
252 Attack: !... code to deal with player's aggressive moves
253 Kiss: !... code about the player getting tender on Benny
254 Ask,Tell,Answer: !... code to handle conversation
257 We have seen some of these actions before. We'll take care of the easier
260 .. include:: /config/typethis.rst
262 .. code-block:: inform
265 if (costume has worn) {
267 print "Before the horror-stricken eyes of the surrounding
268 people, you MAGNIFICENTLY jump OVER the counter and
269 attack Benny with REMARKABLE, albeit NOT sufficient,
270 speed. Benny receives you with a TREACHEROUS upper-cut
271 that sends your GRANITE JAW flying through the cafe.^^
272 ~These guys in pyjamas think they can bully innocent
273 folk,~ snorts Benny, as the EERIE hands of DARKNESS
274 engulf your vision and you lose consciousness.";
277 "That would be an unlikely act for MEEK John Covarth.";
280 "This is no time for MINDLESS infatuation.";
283 "Benny is too busy for idle chit-chat.";
285 Attacking Benny is not wise. If the player is still dressed as John
286 Covarth, the game displays a message refusing to use violence by reason of
287 staying in character as a worthless wimp. However, if Captain Fate
288 attempts the action, we'll find that there is more to Benny than meets the
289 eye, and the game is lost. Kissing and conversation are disallowed by a
290 couple of tailored responses.
292 The Give action is a bit more complicated, since Benny reacts to certain
293 objects in a special and significant way. Bear in mind that Benny's
294 definition needs to keep track of whether the player has asked for a coffee
295 (thereby becoming a customer and thus worthy of the key), whether the
296 coffee has been paid for, and whether the toilet key has been returned.
297 The solution, yet again (this really is a most useful capability), is more
298 local property variables:
300 .. include:: /config/typethis.rst
302 .. code-block:: inform
304 Object benny "Benny" cafe
307 "A deceptively FAT man of uncanny agility, Benny entertains his
308 customers crushing coconuts against his forehead when the mood
310 coffee_asked_for false, ! has player asked for a coffee?
311 coffee_not_paid false, ! is Benny waiting to be paid?
312 key_not_returned false, ! is Benny waiting for the key?
316 Now we are ready to tackle the :act:`Give` action of the :prop:`life`
317 property, which deals with commands like GIVE THE KEY TO BENNY (in a
318 moment, we'll come to the :act:`Give` action of the :prop:`orders`
319 property, which deals with commands like BENNY, GIVE ME THE KEY):
321 .. include:: /config/typethis.rst
323 .. code-block:: inform
328 "You NEED your unpretentious John Covarth clothes.";
330 "You NEED your stupendous ACID-PROTECTIVE suit.";
332 self.key_not_returned = false;
333 move toilet_key to benny;
334 "Benny nods as you ADMIRABLY return his key.";
337 self.coffee_not_paid = false;
338 print "With marvellous ILLUSIONIST gestures, you produce the
339 coin from the depths of your ";
340 if (costume has worn) print "BULLET-PROOF costume";
341 else print "ordinary street clothes";
342 " as if it had dropped on the counter from Benny's ear!
343 People around you clap politely. Benny takes the coin
344 and gives it a SUSPICIOUS bite. ~Thank you, sir. Come
345 back anytime,~ he says.";
348 The Give action in the :prop:`life` property holds the variable :var:`noun`
349 as the object offered to the NPC. Remember that we can use the ``switch``
350 statement as shorthand for:
352 .. code-block:: inform
354 if (noun == costume) { whatever };
355 if (noun == clothes) { whatever };
358 We won't let players give away their clothes or their costume (yes, an
359 improbable action, but you never know). The toilet key and the coin are
360 successfully transferred. The property ``key_not_returned`` will be set to
361 true when we receive the toilet key from Benny (we have not coded that bit
362 yet), and now, when we give it back, it's reset to :const:`false`. The
363 ``move`` statement is in charge of the actual transfer of the object from
364 the player's inventory to Benny, and we finally display a confirmation
365 message. With the coin, we find a new statement: ``remove``. This
366 extracts the object from the object tree, so that it now has no parent.
367 The effect is to make it disappear from the game (though you are not
368 destroying the object permanently -- and indeed you could return it to the
369 object tree using the ``move`` statement); as far as the player is
370 concerned, there isn’t a COIN to be found anywhere. The
371 ``coffee_not_paid`` property will be set to true when Benny serves us the
372 cup of coffee (again, we’ll see that in a moment); now we reset it to
373 :const:`false`, which liberates the player from debt. This culminates with
374 the ``"..."`` print-and-return statement, telling the player that the
375 action was successful. In passing, remember that in :ref:`homely-atmos` we
376 defined the counter such that PUT KEY ON COUNTER is automatically
377 translated into GIVE KEY TO BENNY .
379 Why move the key to Benny but remove the coin instead? Once players
380 qualify as customers by ordering a coffee, they will be able to ask for the
381 key and return it as many times as they like, so it seems sensible to keep
382 the key around. The coin, however, will be a one-shot. We won't let
383 players ask for more than one coffee, to prevent their debt from growing ad
384 infinitum -- besides, they came in here to change, not to indulge in
385 caffeine. Once the coin is paid, it disappears for good, supposedly into
386 Benny's greedy pockets. No need to worry about it any more.
388 .. Generated by autoindex
390 pair: life; library property
392 The benny object needs also an :prop:`orders` property, just to take care
393 of the player's requests for coffee and the key, and to fend off any other
394 demands. The :act:`Give` action in an :prop:`orders` property deals with
395 inputs like ASK BENNY FOR THE KEY or BENNY, GIVE ME THE KEY. The syntax is
396 similar to that of the :prop:`life` property:
398 .. include:: /config/typethis.rst
400 .. code-block:: inform
402 orders [; ! handles ASK BENNY FOR X and BENNY, GIVE ME XXX
404 if (second ~= player or nothing) "Benny looks at you strangely.";
407 if (toilet_key in player) "But you DO have the key already.";
408 if (self.coffee_asked_for == true)
409 if (toilet_key in self) {
410 move toilet_key to player;
411 self.key_not_returned = true;
412 "Benny tosses the key to the rest rooms on the
413 counter, where you grab it with a dextrous and
414 precise movement of your HYPER-AGILE hand.";
417 "~Last place I saw that key, it was in YOUR
418 possession,~ grumbles Benny. ~Be sure to return it
421 "~Toilet is only fer customers,~ he grumbles, looking
422 pointedly at a menu board behind him.";
424 if (self.coffee_asked_for == true)
425 "One coffee should be enough.";
426 move coffee to counter;
427 self.coffee_asked_for = self.coffee_not_paid = true;
428 "With two gracious steps, Benny places his world-famous
429 Cappuccino in front of you.";
431 "Food will take too much time, and you must change NOW.";
433 "With only the smallest sigh, Benny nods towards the menu
434 on the wall behind him.";
436 "~I don't think that's on the menu, sir.~";
440 * We test the value of :var:`second` in order to trap over-generous
441 gestures such as BENNY, GIVE COFFEE TO CUSTOMERS. Then we consider
444 * **Toilet key:** first, we check whether players already have the key or
445 not, and complain if they do, stopping execution thanks to the implicit
446 ``return true`` of the ``"..."`` statement. If players don’t have the
447 key, we proceed to check whether they've asked for a coffee yet, by
448 testing the ``coffee_asked_for`` property. If this is true , we should
449 also check if the key is actually one of Benny’s possessions -- a
450 perverse player could get the key, then drop it somewhere and ask for it
451 again; if this should happen, we indicate that Benny is nobody's fool
452 with the message ``"~Last place I saw that key..."``. Once all these
453 fitting conditions are :const:`true`, players will get the key, which
454 means that they have to return it -- the ``key_not_returned`` property
455 becomes :const:`true` -- and we display a suitable message. However, if
456 the player didn't ask for a coffee, Benny refuses to oblige, mentioning
457 for the first time the menu board where players will be able to see a
458 picture of a cup of coffee when they EXAMINE it. Take care to see how
459 all the ``else`` clauses pair up with the appropriate if statements,
460 triggering responses for each of the conditions that wasn't met.
462 * **Coffee:** we check whether players have already asked for a coffee, by
463 testing the ``coffee_asked_for`` property, and refuse to serve another
464 one if :const:`true`. If :const:`false`, we place the coffee on the
465 counter, and set the properties ``coffee_asked_for`` and
466 ``coffee_not_paid`` to :const:`true`. The message bit you know about.
468 * **Food:** we'll provide an object to deal with all of the delicious
469 comestibles to be found in the café, specifically those (such as
470 "pastries and sandwiches") mentioned in our descriptions. Although that
471 object is not yet defined, we code ahead to thwart player's gluttony in
472 case they choose to ask Benny for food.
474 * **Menu:** our default response -- "I don’t think that’s on the menu, sir"
475 -- isn’t very appropriate if the player asks for a menu, so we provide a
478 * **Default:** this takes care of anything else that the player asks Benny
479 for, displaying his curt response.
481 And before you know it, Benny's object is out of the way; however, don't
482 celebrate too soon. There’s still some Benny-related behaviour that,
483 curiously enough, doesn’t happen in Benny's object; we're talking about
484 Benny's reaction if the player tries to leave without paying or returning
485 the key. We promised you that Benny would stop the player, and indeed he
488 We must revisit the café room object:
490 .. include:: /config/typethis.rst
492 .. code-block:: inform
494 Room cafe "Inside Benny's cafe"
496 "Benny's offers the FINEST selection of pastries and sandwiches.
497 Customers clog the counter, where Benny himself manages to
498 serve, cook and charge without missing a step. At the north side
499 of the cafe you can see a red door connecting with the toilet.",
501 Go: ! The player is about to depart. Is he making for the street?
502 if (noun ~= s_obj) return false;
503 if (benny.coffee_not_paid == true ||
504 benny.key_not_returned == true) {
505 print "Just as you are stepping into the street, the big hand
506 of Benny falls on your shoulder.";
507 if (benny.coffee_not_paid == true &&
508 benny.key_not_returned == true)
509 "^^~Hey! You've got my key and haven't paid for the
510 coffee. Do I look like a chump?~ You apologise as only a
511 HERO knows how to do and return inside.";
512 if (benny.coffee_not_paid == true)
513 "^^~Just waidda minute here, Mister,~ he says.
514 ~Sneaking out without paying, are you?~ You quickly
515 mumble an excuse and go back into the cafe. Benny
516 returns to his chores with a mistrusting eye.";
517 if (benny.key_not_returned == true)
518 "^^~Just where you think you're going with the toilet
519 key?~ he says. ~You a thief?~ As Benny forces you back
520 into the cafe, you quickly assure him that it was only
521 a STUPEFYING mistake.";
523 if (costume has worn) {
524 deadflag = 5; ! you win!
525 "You step onto the sidewalk, where the passing pedestrians
526 recognise the rainbow EXTRAVAGANZA of Captain FATE's costume
527 and cry your name in awe as you JUMP with sensational
528 momentum into the BLUE morning skies!";
531 first_time_out false, ! Captain Fate's first appearance?
533 Go: ! The player has just arrived. Did he come from the toilet?
534 if (noun ~= s_obj) return false;
535 if (costume has worn && self.first_time_out == false) {
536 self.first_time_out = true;
537 StartDaemon(customers);
544 pair: LibraryMessages; library object
546 .. Generated by autoindex
548 pair: Go; library action
549 pair: LibraryMessages; library object
550 pair: daemon; library property
552 Once again, we find that the solution to a design problem is not
553 necessarily unique. Remember what we saw when dealing with the player's
554 description: we could have assigned a new value to the
555 ``player.description`` variable, but opted to use the
556 :obj:`LibraryMessages` object instead. This is a similar case. The code
557 causing Benny to intercept the forgetful player could have been added,
558 perhaps, to a :prop:`daemon` property in Benny’s definition. However,
559 since the action to be intercepted is always the same one and happens to be
560 a movement action when the player tries to leave the café room, it is also
561 possible to code it by trapping the :act:`Go` action of the room object.
562 Both would have been right, but this is somewhat simpler.
564 .. Generated by autoindex
566 pair: before; library property
568 We have added a :prop:`before` property to the room object (albeit a
569 longish one), just dealing with the :act:`Go` action. As we mentioned in
570 an earlier chapter, this technique lets you trap the player who is about to
571 exit a room before the movement actually takes place, a good moment to
572 interfere if we want to prevent escape. The first line:
574 .. code-block:: inform
576 if (noun ~= s_obj) return false;
578 is telling the interpreter that we want to tamper only with southwards
579 movement, allowing the interpreter to apply normal rules for the other
580 available directions.
582 From here on, it's only conditions and more conditions. The player may
585 * without paying for the coffee and without returning the key,
587 * having paid for the coffee, but without returning the key,
589 * having returned the key, but not paid for the coffee, or
591 * free of sin and accountable for nothing in the eyes of all men (well, in
592 the eye of Benny, at least).
594 The first three are covered by the test:
596 .. code-block:: inform
598 if (benny.coffee_not_paid == true || benny.key_not_returned == true) ...
600 that is, if either the coffee is not paid for *or* if the key is not
601 returned. When this condition is :const:`false`, it means that both
602 misdemeanours have been avoided and that the player is free to go.
603 However, when this condition is :const:`true`, the hand of Benny falls on
604 the player's shoulder and then the game displays a different message
605 according to which fault or faults the player has committed.
607 If the player is free to go, and is wearing the crime-fighting costume,
608 the game is won. We tell you how that's reported in the next chapter,
609 where we finish off the design.