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
68 are going to use Neil Cerutti's extension ``pname.h``, which is designed
69 for situations precisely like this. First, we follow the link to the IF
70 archive and download the compressed file ``pname.zip``, which contains
71 two more files: ``pname.h`` and ``pname.txt``. We place these files in
72 the folder where we are currently developing our game or, if using the
73 environment we proposed in "Tools of the trade" on page 17, in the
74 ``Inform\Lib\Contrib`` folder. The text file offers instructions about
75 installation and usage. Here we find a warning:
77 This version of pname.h is recommended for use only with version 6/10
78 of the Inform Library.
80 We're actually using a later version, but this doesn't seem to cause a
81 problem. Most extensions aren't so fussy, but ``pname.h`` fiddles with
82 some routines at the heart of the standard library; these may not be
83 identical in other Inform versions.
85 The introduction explains what ``pname.h`` does for you; namely, it lets
86 you avoid using complicated ``parse_name`` routines to disambiguate the
87 player's input when the same dictionary word refers to more than one
88 item. A ``parse_name`` routine would have been the solution to our
89 problem before the existence of this file, and it qualifies as an
90 advanced programming topic, difficult to master on a first approach.
91 Fortunately, we don't need to worry. Neil Cerutti explains:
93 The ``pname.h`` package defines a new object property, ``pname``
94 (short for phrase name), with a similar look and feel to the standard
95 ``name`` property: both contain a list of dictionary words. However,
96 in a ``pname`` property the order of the words is significant, and
97 special operators ``'.p'`` ``'.or'`` and ``'.x'`` enable you to embed
98 some intelligence into the list. In most cases where the standard
99 ``name`` property isn't enough, you can now just replace it with a
100 ``pname`` property, rather than write a ``parse_name`` property
103 We'll soon see how it works. Let's take a look at the installation
106 To incorporate this package into your program, do three things:
108 #. Add four lines near the head of the program (before you include
111 ``Replace MakeMatch;``
112 ``Replace Identical;``
113 ``Replace NounDomain;``
114 ``Replace TryGivenObject;``
116 #. Include the ``pname.h`` header just after you include ``Parser.h``.
117 ``Include "Parser";``
120 #. Add ``pname`` properties to those objects which require phrase
123 It seems simple enough. So, following steps one and two, we add those
124 ``Replace...`` lines before the inclusion of ``Parser``, and we include
125 ``pname.h`` right after it. ``Replace`` tells the compiler that we're
126 providing replacements for some standard routines.
128 .. code-block:: inform6
130 Constant Story "Captain Fate";
132 "^A simple Inform example
133 ^by Roger Firth and Sonja Kesserich.^";
134 Release 3; Serial "040804"; ! for keeping track of public releases
136 Constant MANUAL_PRONOUNS;
138 Replace MakeMatch; ! requited by pname.h
141 Replace TryGivenObject;
147 Now our source code is ready to benefit from the library package. How
148 does it work? We have acquired a new property -- ``pname`` -- which can
149 be added to some of our objects, and which works pretty much like a
150 ``name`` property. In fact, it should be used *instead* of a ``name``
151 property where we have a disambiguation problem. Let’s change the
152 relevant lines for the toilet door and the toilet key:
156 Maybe specially highlight the lines using pname?
158 .. code-block:: inform6
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:: inform6
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:: inform6
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:: inform6
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:: inform6
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:: inform6
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:: inform6
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:: inform6
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
346 to true when we receive the toilet key from Benny (we have not coded
347 that bit yet), and now, when we give it back, it's reset to ``false``.
348 The ``move`` statement is in charge of the actual transfer of the object
349 from the player's inventory to Benny, and we finally display a
350 confirmation message. With the coin, we find a new statement:
351 ``remove``. This extracts the object from the object tree, so that it
352 now has no parent. The effect is to make it disappear from the game
353 (though you are not destroying the object permanently -- and indeed you
354 could return it to the object tree using the ``move`` statement); as far
355 as the player is concerned, there isn’t a COIN to be found anywhere. The
356 ``coffee_not_paid`` property will be set to true when Benny serves us
357 the cup of coffee (again, we’ll see that in a moment); now we reset it
358 to ``false``, which liberates the player from debt. This culminates with
359 the ``"..."`` print-and-return statement, telling the player that the
360 action was successful. In passing, remember that in "A homely
361 atmosphere" on page 131 we defined the counter such that PUT KEY ON
362 COUNTER is automatically translated into GIVE KEY TO BENNY .
364 Why move the key to Benny but remove the coin instead? Once players
365 qualify as customers by ordering a coffee, they will be able to ask for
366 the key and return it as many times as they like, so it seems sensible
367 to keep the key around. The coin, however, will be a one-shot. We won't
368 let players ask for more than one coffee, to prevent their debt from
369 growing ad infinitum -- besides, they came in here to change, not to
370 indulge in caffeine. Once the coin is paid, it disappears for good,
371 supposedly into Benny's greedy pockets. No need to worry about it any
374 The benny object needs also an ``orders`` property, just to take care of
375 the player's requests for coffee and the key, and to fend off any other
376 demands. The ``Give`` action in an ``orders`` property deals with inputs
377 like ASK BENNY FOR THE KEY or BENNY, GIVE ME THE KEY. The syntax is
378 similar to that of the ``life`` property:
380 .. code-block:: inform6
382 orders [; ! handles ASK BENNY FOR X and BENNY, GIVE ME XXX
384 if (second ~= player or nothing) "Benny looks at you strangely.";
387 if (toilet_key in player) "But you DO have the key already.";
388 if (self.coffee_asked_for == true)
389 if (toilet_key in self) {
390 move toilet_key to player;
391 self.key_not_returned = true;
392 "Benny tosses the key to the rest rooms on the
393 counter, where you grab it with a dextrous and
394 precise movement of your HYPER-AGILE hand.";
397 "~Last place I saw that key, it was in YOUR
398 possession,~ grumbles Benny. ~Be sure to return it
401 "~Toilet is only fer customers,~ he grumbles, looking
402 pointedly at a menu board behind him.";
404 if (self.coffee_asked_for == true)
405 "One coffee should be enough.";
406 move coffee to counter;
407 self.coffee_asked_for = self.coffee_not_paid = true;
408 "With two gracious steps, Benny places his world-famous
409 Cappuccino in front of you.";
411 "Food will take too much time, and you must change NOW.";
413 "With only the smallest sigh, Benny nods towards the menu
414 on the wall behind him.";
416 "~I don't think that's on the menu, sir.~";
420 * We test the value of ``second`` in order to trap over-generous
421 gestures such as BENNY, GIVE COFFEE TO CUSTOMERS . Then we consider
424 * **Toilet key:** first, we check whether players already have the key
425 or not, and complain if they do, stopping execution thanks to the
426 implicit ``return true`` of the ``"..."`` statement. If players don’t
427 have the key, we proceed to check whether they've asked for a coffee
428 yet, by testing the ``coffee_asked_for`` property. If this is true ,
429 we should also check if the key is actually one of Benny’s
430 possessions -- a perverse player could get the key, then drop it
431 somewhere and ask for it again; if this should happen, we indicate
432 that Benny is nobody's fool with the message ``"~Last place I saw
433 that key..."``. Once all these fitting conditions are ``true``,
434 players will get the key, which means that they have to return it --
435 the ``key_not_returned`` property becomes ``true`` -- and we display
436 a suitable message. However, if the player didn't ask for a coffee,
437 Benny refuses to oblige, mentioning for the first time the menu board
438 where players will be able to see a picture of a cup of coffee when
439 they EXAMINE it. Take care to see how all the ``else`` clauses pair
440 up with the appropriate if statements, triggering responses for each
441 of the conditions that wasn't met.
443 * **Coffee:** we check whether players have already asked for a coffee,
444 by testing the ``coffee_asked_for`` property, and refuse to serve
445 another one if ``true``. If ``false``, we place the coffee on the
446 counter, and set the properties ``coffee_asked_for`` and
447 ``coffee_not_paid`` to ``true``. The message bit you know about.
449 * **Food:** we'll provide an object to deal with all of the delicious
450 comestibles to be found in the café, specifically those (such as
451 "pastries and sandwiches") mentioned in our descriptions. Although
452 that object is not yet defined, we code ahead to thwart player's
453 gluttony in case they choose to ask Benny for food.
455 * **Menu:** our default response -- "I don’t think that’s on the menu,
456 sir" -- isn’t very appropriate if the player asks for a menu, so we
457 provide a better one.
459 * **Default:** this takes care of anything else that the player asks
460 Benny for, displaying his curt response.
462 And before you know it, Benny's object is out of the way; however, don't
463 celebrate too soon. There’s still some Benny-related behaviour that,
464 curiously enough, doesn’t happen in Benny's object; we're talking about
465 Benny's reaction if the player tries to leave without paying or
466 returning the key. We promised you that Benny would stop the player, and
467 indeed he will. But where?
469 We must revisit the café room object:
471 .. code-block:: inform6
473 Room cafe "Inside Benny's cafe"
475 "Benny's offers the FINEST selection of pastries and sandwiches.
476 Customers clog the counter, where Benny himself manages to
477 serve, cook and charge without missing a step. At the north side
478 of the cafe you can see a red door connecting with the toilet.",
480 Go: ! The player is about to depart. Is he making for the street?
481 if (noun ~= s_obj) return false;
482 if (benny.coffee_not_paid == true ||
483 benny.key_not_returned == true) {
484 print "Just as you are stepping into the street, the big hand
485 of Benny falls on your shoulder.";
486 if (benny.coffee_not_paid == true &&
487 benny.key_not_returned == true)
488 "^^~Hey! You've got my key and haven't paid for the
489 coffee. Do I look like a chump?~ You apologise as only a
490 HERO knows how to do and return inside.";
491 if (benny.coffee_not_paid == true)
492 "^^~Just waidda minute here, Mister,~ he says.
493 ~Sneaking out without paying, are you?~ You quickly
494 mumble an excuse and go back into the cafe. Benny
495 returns to his chores with a mistrusting eye.";
496 if (benny.key_not_returned == true)
497 "^^~Just where you think you're going with the toilet
498 key?~ he says. ~You a thief?~ As Benny forces you back
499 into the cafe, you quickly assure him that it was only
500 a STUPEFYING mistake.";
502 if (costume has worn) {
503 deadflag = 5; ! you win!
504 "You step onto the sidewalk, where the passing pedestrians
505 recognise the rainbow EXTRAVAGANZA of Captain FATE's costume
506 and cry your name in awe as you JUMP with sensational
507 momentum into the BLUE morning skies!";
510 first_time_out false, ! Captain Fate's first appearance?
512 Go: ! The player has just arrived. Did he come from the toilet?
513 if (noun ~= s_obj) return false;
514 if (costume has worn && self.first_time_out == false) {
515 self.first_time_out = true;
516 StartDaemon(customers);
522 Once again, we find that the solution to a design problem is not
523 necessarily unique. Remember what we saw when dealing with the player's
524 description: we could have assigned a new value to the
525 ``player.description`` variable, but opted to use the
526 ``LibraryMessages`` object instead. This is a similar case. The code
527 causing Benny to intercept the forgetful player could have been added,
528 perhaps, to a ``daemon`` property in Benny’s definition. However, since
529 the action to be intercepted is always the same one and happens to be a
530 movement action when the player tries to leave the café room, it is also
531 possible to code it by trapping the ``Go`` action of the room object.
532 Both would have been right, but this is somewhat simpler.
534 We have added a ``before`` property to the room object (albeit a longish
535 one), just dealing with the ``Go`` action. As we mentioned in an earlier
536 chapter, this technique lets you trap the player who is about to exit a
537 room before the movement actually takes place, a good moment to
538 interfere if we want to prevent escape. The first line:
540 .. code-block:: inform6
542 if (noun ~= s_obj) return false;
544 is telling the interpreter that we want to tamper only with southwards
545 movement, allowing the interpreter to apply normal rules for the other
546 available directions.
548 From here on, it's only conditions and more conditions. The player may
551 * without paying for the coffee and without returning the key,
553 * having paid for the coffee, but without returning the key,
555 * having returned the key, but not paid for the coffee, or
557 * free of sin and accountable for nothing in the eyes of all men (well,
558 in the eye of Benny, at least).
560 The first three are covered by the test:
562 .. code-block:: inform6
564 if (benny.coffee_not_paid == true || benny.key_not_returned == true) ...
566 that is, if either the coffee is not paid for *or* if the key is not
567 returned. When this condition is ``false``, it means that both
568 misdemeanours have been avoided and that the player is free to go.
569 However, when this condition is ``true``, the hand of Benny falls on the
570 player's shoulder and then the game displays a different message
571 according to which fault or faults the player has committed.
573 If the player is free to go, and is wearing the crime-fighting costume,
574 the game is won. We tell you how that's reported in the next chapter,
575 where we finish off the design.