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