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