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