Add a bunch of autogenerated index entries.
[ibg.git] / chapters / 12.rst
1 ====================
2 Captain Fate: take 3
3 ====================
4
5 .. epigraph::
6
7    | |CENTER| *W was a watchman, and guarded the door;*
8    | |CENTER| *X was expensive, and so became poor.*
9
10 .. only:: html
11
12    .. image:: /images/picW.png
13       :align: left
14
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.
19
20 Too many toilets
21 ================
22
23 .. index::
24    single: ambiguous objects
25
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?"
34
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.
43
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.
46
47 .. |IFARCHIVE| replace:: http://mirror.ifarchive.org/indexes/if-archive.html
48
49 .. Generated by autoindex
50 .. index::
51    single: IF Archive
52
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.
68
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
77 find a warning:
78
79   This version of pname.h is recommended for use only with version 6/10 of
80   the Inform Library.
81
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.
86
87 .. Generated by autoindex
88 .. index::
89    pair: parse_name; library property
90
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:
98
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
107   routine.
108
109 We'll soon see how it works.  Let's take a look at the installation
110 instructions:
111
112   To incorporate this package into your program, do three things:
113
114   #.  Add four lines near the head of the program (before you include
115       ``Parser.h``).
116
117       .. code-block:: inform
118
119          Replace MakeMatch;
120          Replace Identical;
121          Replace NounDomain;
122          Replace TryGivenObject;
123
124   #.  Include the ``pname.h`` header just after you include ``Parser.h``.
125
126       .. code-block:: inform
127
128          Include "Parser";
129          Include "pname";
130
131   #.  Add ``pname`` properties to those objects which require phrase 
132       recognition.
133
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.
138
139 .. code-block:: inform
140
141   Constant Story "Captain Fate";
142   Constant Headline
143               "^A simple Inform example
144                ^by Roger Firth and Sonja Kesserich.^";
145   Release 3; Serial "040804";     ! for keeping track of public releases
146
147   Constant MANUAL_PRONOUNS;
148
149   Replace MakeMatch;              ! requited by pname.h
150   Replace Identical;
151   Replace NounDomain;
152   Replace TryGivenObject;
153
154   Include "Parser";
155   Include "pname";
156   ...
157
158 Now our source code is ready to benefit from the library package.  How does
159 it work?  We have acquired a new property -- ``pname`` -- which can be
160 added to some of our objects, and which works pretty much like a
161 :prop:`name` property.  In fact, it should be used *instead* of a
162 :prop:`name` property where we have a disambiguation problem.  Let’s change
163 the relevant lines for the toilet door and the toilet key:
164
165 .. code-block:: inform
166
167   Object  toilet_door
168     with  pname '.x' 'red' '.x' 'toilet' 'door',
169           short_name [;
170           ...
171
172   Object  toilet_key "toilet key" benny
173     with  pname '.x' 'toilet' 'key',
174           article "the",
175           ...
176
177 while leaving the ``outside_of_toilet`` unchanged:
178
179 .. code-block:: inform
180
181   Object  outside_of_toilet "toilet" cafe
182     with  name 'toilet' 'bath' 'rest' 'room' 'bathroom' 'restroom',
183           before [;
184           ...
185
186 We are now using a new operator -- ``'.x'`` -- in our ``pname`` word lists.
187 The text file explains
188
189   The first dictionary word to the right of a ``'.x'`` operator is
190   interpreted as optional.
191
192 and this makes the dictionary word ``'toilet'`` of lesser importance for
193 these objects, so that at run-time players could refer to the DOOR or
194 TOILET DOOR or the KEY or TOILET KEY -- but not simply to the TOILET --
195 when referring to either the door or the key.  And, by leaving unchanged
196 the name property of the ``outside_of_toilet`` object – where there is also
197 another ``'toilet'`` entry -- the ``pname`` properties will tell the
198 interpreter to discard the key and the door as possible objects to be
199 considered when players refer just to TOILET.  Looking at it in terms of
200 the English language, we've effectively said that "TOILET" is an adjective
201 in the phrases "TOILET DOOR" and "TOILET KEY", but a noun when used on its
202 own to refer to the room.
203
204 The ``pname.h`` package has additional functionality to deal with more
205 complex phrases, but we don't need it in our example game.  Feel free,
206 however, to read ``pname.txt`` and discover what this fine library
207 extension can do for you: it's an easy answer to many a disambiguation
208 headache.
209
210 Don't shoot! I'm only the barman
211 ================================
212
213 A lot of the action of the game happens around Benny, and his definition
214 needs a little care.  Let's explain what we want to happen.
215
216   So the door is locked and the player, after discovering what the note
217   stuck on the toilet door said, will eventually ask Benny for the key.
218   Sadly, Benny allows use of the toilet only to customers, a remark he'll
219   make looking pointedly at the menu board behind him.  The player will
220   have to ask for a coffee first, thereby qualifying as a customer in
221   Benny's eyes and thus entitled to make use of the toilet.  At last!  Rush
222   inside, change into Captain Fate’s costume and fly away to save the day!
223
224 Except that the player neither paid for the coffee, nor returned the toilet
225 key.  Benny will have to stop the player from leaving the café in these
226 circumstances.  To prevent unnecessary complication, there will be a coin
227 near the lavatory, enough cash to pay for the coffee.  And that about sums
228 it all up; pretty simple to describe -- not so simple to code.  Remember
229 Benny's basic definition from the previous chapter:
230
231 .. code-block:: inform
232
233   Object  benny "Benny" cafe
234     with  name 'benny',
235           description
236               "A deceptively FAT man of uncanny agility, Benny entertains his
237                customers crushing coconuts against his forehead when the mood
238                strikes him.",
239     has   scenery animate male proper transparent;
240
241 We can now add some complexity, beginning with a :prop:`life` property.  In
242 generic form:
243
244 .. code-block:: inform
245
246   life [;
247     Give:             !... code for giving objects to Benny
248     Attack:           !... code to deal with player's aggressive moves
249     Kiss:             !... code about the player getting tender on Benny
250     Ask,Tell,Answer:  !... code to handle conversation
251   ],
252
253 We have seen some of these actions before.  We'll take care of the easier
254 ones:
255
256 .. code-block:: inform
257
258   Attack:
259     if (costume has worn) {
260         deadflag = 4;
261         print "Before the horror-stricken eyes of the surrounding
262                people, you MAGNIFICENTLY jump OVER the counter and
263                attack Benny with REMARKABLE, albeit NOT sufficient,
264                speed. Benny receives you with a TREACHEROUS upper-cut
265                that sends your GRANITE JAW flying through the cafe.^^
266                ~These guys in pyjamas think they can bully innocent
267                folk,~ snorts Benny, as the EERIE hands of DARKNESS
268                engulf your vision and you lose consciousness.";
269     }
270     else
271         "That would be an unlikely act for MEEK John Covarth.";
272
273     Kiss:
274       "This is no time for MINDLESS infatuation.";
275
276     Ask,Tell,Answer:
277       "Benny is too busy for idle chit-chat.";
278
279 Attacking Benny is not wise.  If the player is still dressed as John
280 Covarth, the game displays a message refusing to use violence by reason of
281 staying in character as a worthless wimp.  However, if Captain Fate
282 attempts the action, we'll find that there is more to Benny than meets the
283 eye, and the game is lost.  Kissing and conversation are disallowed by a
284 couple of tailored responses.
285
286 The Give action is a bit more complicated, since Benny reacts to certain
287 objects in a special and significant way.  Bear in mind that Benny's
288 definition needs to keep track of whether the player has asked for a coffee
289 (thereby becoming a customer and thus worthy of the key), whether the
290 coffee has been paid for, and whether the toilet key has been returned.
291 The solution, yet again (this really is a most useful capability), is more
292 local property variables:
293
294 .. code-block:: inform
295
296   Object  benny "Benny" cafe
297     with  name 'benny',
298           description
299               "A deceptively FAT man of uncanny agility, Benny entertains his
300                customers crushing coconuts against his forehead when the mood
301                strikes him.",
302           coffee_asked_for false,          ! has player asked for a coffee?
303           coffee_not_paid  false,          ! is Benny waiting to be paid?
304           key_not_returned false,          ! is Benny waiting for the key?
305           live [;
306           ...
307
308 Now we are ready to tackle the :act:`Give` action of the :prop:`life`
309 property, which deals with commands like GIVE THE KEY TO BENNY (in a
310 moment, we'll come to the :act:`Give` action of the :prop:`orders`
311 property, which deals with commands like BENNY, GIVE ME THE KEY):
312
313 .. code-block:: inform
314
315   Give:
316     switch (noun) {
317       clothes:
318         "You NEED your unpretentious John Covarth clothes.";
319       costume:
320         "You NEED your stupendous ACID-PROTECTIVE suit.";
321       toilet_key:
322         self.key_not_returned = false;
323         move toilet_key to benny;
324         "Benny nods as you ADMIRABLY return his key.";
325       coin:
326         remove coin;
327         self.coffee_not_paid = false;
328         print "With marvellous ILLUSIONIST gestures, you produce the
329                coin from the depths of your ";
330         if (costume has worn) print "BULLET-PROOF costume";
331         else                  print "ordinary street clothes";
332         " as if it had dropped on the counter from Benny's ear!
333          People around you clap politely. Benny takes the coin
334          and gives it a SUSPICIOUS bite. ~Thank you, sir. Come
335          back anytime,~ he says.";
336     }
337
338 The Give action in the :prop:`life` property holds the variable :var:`noun`
339 as the object offered to the NPC.  Remember that we can use the ``switch``
340 statement as shorthand for:
341
342 .. code-block:: inform
343
344   if (noun == costume) { whatever };
345   if (noun == clothes) { whatever };
346   ...
347
348 We won't let players give away their clothes or their costume (yes, an
349 improbable action, but you never know).  The toilet key and the coin are
350 successfully transferred.  The property ``key_not_returned`` will be set to
351 true when we receive the toilet key from Benny (we have not coded that bit
352 yet), and now, when we give it back, it's reset to :const:`false`.  The
353 ``move`` statement is in charge of the actual transfer of the object from
354 the player's inventory to Benny, and we finally display a confirmation
355 message.  With the coin, we find a new statement: ``remove``.  This
356 extracts the object from the object tree, so that it now has no parent.
357 The effect is to make it disappear from the game (though you are not
358 destroying the object permanently -- and indeed you could return it to the
359 object tree using the ``move`` statement); as far as the player is
360 concerned, there isn’t a COIN to be found anywhere.  The
361 ``coffee_not_paid`` property will be set to true when Benny serves us the
362 cup of coffee (again, we’ll see that in a moment); now we reset it to
363 :const:`false`, which liberates the player from debt.  This culminates with
364 the ``"..."`` print-and-return statement, telling the player that the
365 action was successful.  In passing, remember that in :ref:`homely-atmos` we
366 defined the counter such that PUT KEY ON COUNTER is automatically
367 translated into GIVE KEY TO BENNY .
368
369 Why move the key to Benny but remove the coin instead?  Once players
370 qualify as customers by ordering a coffee, they will be able to ask for the
371 key and return it as many times as they like, so it seems sensible to keep
372 the key around.  The coin, however, will be a one-shot.  We won't let
373 players ask for more than one coffee, to prevent their debt from growing ad
374 infinitum -- besides, they came in here to change, not to indulge in
375 caffeine.  Once the coin is paid, it disappears for good, supposedly into
376 Benny's greedy pockets.  No need to worry about it any more.
377
378 .. Generated by autoindex
379 .. index::
380    pair: life; library property
381
382 The benny object needs also an :prop:`orders` property, just to take care
383 of the player's requests for coffee and the key, and to fend off any other
384 demands.  The :act:`Give` action in an :prop:`orders` property deals with
385 inputs like ASK BENNY FOR THE KEY or BENNY, GIVE ME THE KEY.  The syntax is
386 similar to that of the :prop:`life` property:
387
388 .. code-block:: inform
389
390   orders [;   ! handles ASK BENNY FOR X and BENNY, GIVE ME XXX
391     Give:
392       if (second ~= player or nothing) "Benny looks at you strangely.";
393       switch (noun) {
394         toilet_key:
395           if (toilet_key in player) "But you DO have the key already.";
396           if (self.coffee_asked_for == true)
397               if (toilet_key in self) {
398                   move toilet_key to player;
399                   self.key_not_returned = true;
400                   "Benny tosses the key to the rest rooms on the
401                    counter, where you grab it with a dextrous and
402                    precise movement of your HYPER-AGILE hand.";
403               }
404               else
405                   "~Last place I saw that key, it was in YOUR
406                    possession,~ grumbles Benny. ~Be sure to return it
407                    before you leave.~";
408           else
409               "~Toilet is only fer customers,~ he grumbles, looking
410                pointedly at a menu board behind him.";
411         coffee:
412           if (self.coffee_asked_for == true)
413               "One coffee should be enough.";
414           move coffee to counter;
415           self.coffee_asked_for = self.coffee_not_paid = true;
416           "With two gracious steps, Benny places his world-famous
417            Cappuccino in front of you.";
418         food:         
419           "Food will take too much time, and you must change NOW.";
420         menu:
421           "With only the smallest sigh, Benny nods towards the menu
422            on the wall behind him.";
423         default:
424           "~I don't think that's on the menu, sir.~";
425       }
426   ],
427
428 * We test the value of :var:`second` in order to trap over-generous
429   gestures such as BENNY, GIVE COFFEE TO CUSTOMERS.  Then we consider
430   potential requests.
431
432 * **Toilet key:** first, we check whether players already have the key or
433   not, and complain if they do, stopping execution thanks to the implicit
434   ``return true`` of the ``"..."`` statement.  If players don’t have the
435   key, we proceed to check whether they've asked for a coffee yet, by
436   testing the ``coffee_asked_for`` property.  If this is true , we should
437   also check if the key is actually one of Benny’s possessions -- a
438   perverse player could get the key, then drop it somewhere and ask for it
439   again; if this should happen, we indicate that Benny is nobody's fool
440   with the message ``"~Last place I saw that key..."``.  Once all these
441   fitting conditions are :const:`true`, players will get the key, which
442   means that they have to return it -- the ``key_not_returned`` property
443   becomes :const:`true` -- and we display a suitable message.  However, if
444   the player didn't ask for a coffee, Benny refuses to oblige, mentioning
445   for the first time the menu board where players will be able to see a
446   picture of a cup of coffee when they EXAMINE it.  Take care to see how
447   all the ``else`` clauses pair up with the appropriate if statements,
448   triggering responses for each of the conditions that wasn't met.
449
450 * **Coffee:** we check whether players have already asked for a coffee, by
451   testing the ``coffee_asked_for`` property, and refuse to serve another
452   one if :const:`true`.  If :const:`false`, we place the coffee on the
453   counter, and set the properties ``coffee_asked_for`` and
454   ``coffee_not_paid`` to :const:`true`.  The message bit you know about.
455
456 * **Food:** we'll provide an object to deal with all of the delicious
457   comestibles to be found in the café, specifically those (such as
458   "pastries and sandwiches") mentioned in our descriptions.  Although that
459   object is not yet defined, we code ahead to thwart player's gluttony in
460   case they choose to ask Benny for food.
461
462 * **Menu:** our default response -- "I don’t think that’s on the menu, sir"
463   -- isn’t very appropriate if the player asks for a menu, so we provide a
464   better one.
465
466 * **Default:** this takes care of anything else that the player asks Benny
467   for, displaying his curt response.
468
469 And before you know it, Benny's object is out of the way; however, don't
470 celebrate too soon.  There’s still some Benny-related behaviour that,
471 curiously enough, doesn’t happen in Benny's object; we're talking about
472 Benny's reaction if the player tries to leave without paying or returning
473 the key.  We promised you that Benny would stop the player, and indeed he
474 will.  But where?
475
476 We must revisit the café room object:
477
478 .. code-block:: inform
479
480   Room     cafe "Inside Benny's cafe"
481     with   description
482                "Benny's offers the FINEST selection of pastries and sandwiches.
483                 Customers clog the counter, where Benny himself manages to
484                 serve, cook and charge without missing a step. At the north side
485                 of the cafe you can see a red door connecting with the toilet.",
486            before [;
487              Go:   ! The player is about to depart. Is he making for the street?
488                if (noun ~= s_obj) return false;
489                if (benny.coffee_not_paid == true ||
490                    benny.key_not_returned == true) {
491                    print "Just as you are stepping into the street, the big hand
492                           of Benny falls on your shoulder.";
493                    if (benny.coffee_not_paid == true &&
494                        benny.key_not_returned == true)
495                        "^^~Hey! You've got my key and haven't paid for the
496                         coffee. Do I look like a chump?~ You apologise as only a
497                         HERO knows how to do and return inside.";
498                    if (benny.coffee_not_paid == true)
499                        "^^~Just waidda minute here, Mister,~ he says.
500                         ~Sneaking out without paying, are you?~ You quickly 
501                         mumble an excuse and go back into the cafe. Benny
502                         returns to his chores with a mistrusting eye.";
503                    if (benny.key_not_returned == true)
504                        "^^~Just where you think you're going with the toilet
505                         key?~ he says. ~You a thief?~ As Benny forces you back
506                         into the cafe, you quickly assure him that it was only
507                         a STUPEFYING mistake.";
508                }     
509                if (costume has worn) {
510                    deadflag = 5;           ! you win!
511                    "You step onto the sidewalk, where the passing pedestrians
512                     recognise the rainbow EXTRAVAGANZA of Captain FATE's costume
513                     and cry your name in awe as you JUMP with sensational
514                     momentum into the BLUE morning skies!";
515                }
516            ],
517            first_time_out false,           ! Captain Fate's first appearance?
518            after [;
519              Go:   ! The player has just arrived. Did he come from the toilet?
520                if (noun ~= s_obj) return false;
521                if (costume has worn && self.first_time_out == false) {
522                    self.first_time_out = true;
523                    StartDaemon(customers);
524                }
525            ],
526            s_to  street,
527            n_to  toilet_door;
528
529 .. index::
530    pair: LibraryMessages; library object
531
532 .. Generated by autoindex
533 .. index::
534    pair: Go; library action
535    pair: LibraryMessages; library object
536    pair: daemon; library property
537
538 Once again, we find that the solution to a design problem is not
539 necessarily unique.  Remember what we saw when dealing with the player's
540 description: we could have assigned a new value to the
541 ``player.description`` variable, but opted to use the
542 :obj:`LibraryMessages` object instead.  This is a similar case.  The code
543 causing Benny to intercept the forgetful player could have been added,
544 perhaps, to a :prop:`daemon` property in Benny’s definition.  However,
545 since the action to be intercepted is always the same one and happens to be
546 a movement action when the player tries to leave the café room, it is also
547 possible to code it by trapping the :act:`Go` action of the room object.
548 Both would have been right, but this is somewhat simpler.
549
550 .. Generated by autoindex
551 .. index::
552    pair: before; library property
553
554 We have added a :prop:`before` property to the room object (albeit a
555 longish one), just dealing with the :act:`Go` action.  As we mentioned in
556 an earlier chapter, this technique lets you trap the player who is about to
557 exit a room before the movement actually takes place, a good moment to
558 interfere if we want to prevent escape.  The first line:
559
560 .. code-block:: inform
561
562   if (noun ~= s_obj) return false;
563
564 is telling the interpreter that we want to tamper only with southwards
565 movement, allowing the interpreter to apply normal rules for the other
566 available directions.
567
568 From here on, it's only conditions and more conditions.  The player may
569 attempt to leave:
570
571 * without paying for the coffee and without returning the key,
572
573 * having paid for the coffee, but without returning the key,
574
575 * having returned the key, but not paid for the coffee, or
576
577 * free of sin and accountable for nothing in the eyes of all men (well, in
578   the eye of Benny, at least).
579
580 The first three are covered by the test:
581
582 .. code-block:: inform
583
584   if (benny.coffee_not_paid == true || benny.key_not_returned == true) ...
585
586 that is, if either the coffee is not paid for *or* if the key is not
587 returned.  When this condition is :const:`false`, it means that both
588 misdemeanours have been avoided and that the player is free to go.
589 However, when this condition is :const:`true`, the hand of Benny falls on
590 the player's shoulder and then the game displays a different message
591 according to which fault or faults the player has committed.
592
593 If the player is free to go, and is wearing the crime-fighting costume, 
594 the game is won.  We tell you how that's reported in the next chapter, 
595 where we finish off the design.