ff082ec038d2eac4c421874217cb41953510ca7d
[ibg.git] / chapters / 11.rst
1 ====================
2 Captain Fate: take 2
3 ====================
4
5 .. epigraph::
6
7    | |CENTER| *U was a usurer, a miserable elf;*
8    | |CENTER| *V was a vintner, who drank all himself.*
9
10 .. only:: html
11
12    .. image:: /images/picV.png
13       :align: left
14
15 |V|\iewed from the inside, Benny's café is warm and welcoming, and packed
16 with lunchtime customers.  We'll try to conjure up some appropriate images,
17 but the main focus of the room isn't the decor: it's the door leading to
18 the toilet -- and, perhaps, privacy?
19
20 .. _homely-atmos:
21
22 A homely atmosphere
23 ===================
24
25 Benny's café is populated with customers enjoying their lunch, so it won't
26 be a good place to change identities.  However, the toilet to the north
27 looks promising, though Benny has strict rules about its use and the door
28 seems to be locked.
29
30 .. admonition:: Cultural Note
31    :class: admonition note
32
33    Not for the first time, this guide betrays its origins.  In European
34    countries the word "toilet" often refers not only to the white porcelain
35    artefact, but also to the room in which it can be found (also, a
36    "bathroom" is for taking a bath, a "restroom" for taking a rest).  Bear
37    with us on this; the dual usage becomes important a little later on.
38
39 We define the café room in simple form:
40
41 .. code-block:: inform
42
43   Room    cafe "Inside Benny's cafe"
44     with  description
45           "Benny's offers the FINEST selection of pastries and
46            sandwiches. Customers clog the counter, where Benny himself
47            manages to serve, cook and charge without missing a step. At
48            the north side of the cafe you can see a red door connecting
49            with the toilet.",
50           s_to street,
51           n_to toilet_door;
52
53 We'll elaborate on the last line (``n_to toilet_door``) later, when we
54 define the door object which lies between the café and the toilet.
55
56 We've mentioned a counter:
57
58 .. code-block:: inform
59
60   Appliance counter "counter" cafe
61     with name 'counter' 'bar',
62          article "the",
63          description
64              "The counter is made of an astonishing ALLOY of metals,
65               STAIN-PROOF, SPILL-RESISTANT and VERY EASY to clean. Customers
66               enjoy their snacks with UTTER tranquillity, safe in the notion
67               that the counter can take it all.",
68          before [;
69            Receive:
70              <<Give noun benny>>;
71          ],
72     has  supporter;
73
74 That :prop:`before` property, superficially normal, actually conceals a
75 little surprise.  By now you should be entirely comfortable with using an
76 object's :prop:`before` property to intercept an action directed at that
77 object; for example, if the player types HIT COUNTER then the counter's
78 :prop:`before` property is potentially able to intercept the resulting
79 :act:`Attack` action.  However, the command PUT KEY ON COUNTER generates
80 *two* actions.  First, a :act:`PutOn` action is offered to the key
81 (effectively saying, do you want to be placed on top of the counter?);
82 that’s the normal bit.  And then the surprise: a :act:`Receive` action is
83 offered to the counter (effectively saying, are you happy to have the key
84 placed on you?)  Both actions have the same opportunity of returning
85 :const:`false` to let the action continue, :const:`true` to prevent it.
86
87 .. Generated by autoindex
88 .. index::
89    pair: LetGo; library action
90    pair: Receive; library action
91
92 The :act:`Receive` action is generated by the library in the ``PutOnSub``
93 action handler, and also in ``InsertSub`` (so a command like PUT BIRD IN
94 NEST sends a Receive to the nest object).  There’s a matching :act:`LetGo`,
95 generated by the library from commands like TAKE KEY OFF COUNTER and REMOVE
96 BIRD FROM NEST.  :act:`Receive` and :act:`LetGo` are examples of what’s
97 called a :term:`fake action`.
98
99 .. note::
100
101   In "William Tell" we defined the ``quiver``, way back in
102   :ref:`possessions`, as an ``open container``.  As things stand, the
103   player can put *any* held object, however inappropriate, into it.  We
104   could have trapped the :act:`Receive` action to ensure that arrows are
105   the only acceptable contents (recollect that ``~~``, to be read as "not",
106   turns true into false and vice versa):
107
108   .. code-block:: inform
109
110     before [;
111       Drop,Give:
112         print_ret "But it was a present from Hedwig, your wife.";
113       Receive:
114         if (~~(noun ofclass Arrow))
115             print_ret "Only arrows -- clean arrows -- go in your quiver.";
116     ],
117
118 Here, we intercept any attempt to place an item on the counter, and
119 translate it into an attempt to give that item to Benny.  Part of the
120 game's plot depends on the player returning the toilet key to Benny, and
121 also paying him for his delicious cup of world-famous Cappuccino.  Putting
122 the key and the money on the counter is a reasonable alternative way for
123 the player to accomplish this.
124
125 We've also mentioned some customers.  These are treated as NPCs, reacting
126 to our hero’s performance.
127
128 .. code-block:: inform
129
130   Object  customers "customers" cafe
131     with  name 'customers' 'people' 'customer' 'men' 'women',
132           description [;
133               if (costume has worn)
134                   "Most seem to be concentrating on their food, but some do
135                    look at you quite blatantly. Must be the MIND-BEFUDDLING
136                    colours of your costume.";
137               else
138                   "A group of HELPLESS and UNSUSPECTING mortals, the kind
139                    Captain FATE swore to DEFEND the day his parents choked on a
140                    DEVIOUS slice of RASPBERRY PIE.";
141           ],
142           life [;
143             Ask,Tell,Answer:
144               if (costume has worn)
145                   "People seem to MISTRUST the look of your FABULOUS costume.";
146               else
147                   "As John Covarth, you attract LESS interest than Benny's
148                    food.";
149             Kiss:
150               "There's no telling what sorts of MUTANT bacteria these
151                STRANGERS may be carrying around.";
152             Attack:
153               "Mindless massacre of civilians is the qualification for
154                VILLAINS. You are SUPPOSED to protect the likes of these
155                people.";
156           ],
157           orders [;
158               "These people don't appear to be of the cooperative sort.";
159           ],
160           number_of_comments 0,          ! for counting the customer comments
161           daemon [;
162               if (location ~= cafe) return;
163               if (self.number_of_comments == 0) {
164                   self.number_of_comments = 1;
165                   print "^Nearby customers glance at your costume with open
166                       curiosity.^";
167               }
168               if (random(2) == 1) {       ! do this 50% of the time
169                   self.number_of_comments = self.number_of_comments + 1;
170                   switch (self.number_of_comments) {
171                    2: "^~Didn't know there was a circus in town,~ comments one
172                         customer to another. ~Seems like the clowns have the
173                         day off.~";
174                    3: "^~These fashion designers don't know what to do to show
175                         off,~ snorts a fat gentleman, looking your way. Those
176                         within earshot try to conceal their smiles.";
177                    4: "^~Must be carnival again,~ says a man to his wife, who
178                         giggles, stealing a peek at you. ~Time sure flies.~";
179                    5: "^~Bad thing about big towns~, comments someone to his
180                         table companion, ~is you get the damnedest bugs coming
181                         out from toilets.~";
182                    6: "^~I sure WISH I could go to work in my pyjamas,~ says a
183                         girl in an office suit to some colleagues. ~It looks SO
184                         comfortable.~";
185                    default: StopDaemon(self);
186                   }
187               }      
188           ],      
189     has   scenery animate pluralname;
190
191 Let's go step by step.  Our hero enters the café dressed as John Covarth,
192 but will eventually manage to change clothes in the toilet, and he'll have
193 to cross back through the café to reach the street and win the game.  The
194 customers' :prop:`description` takes into consideration which outfit the
195 player character is wearing.
196
197 .. Generated by autoindex
198 .. index::
199    pair: Answer; library action
200    pair: Ask; library action
201    pair: Attack; library action
202    pair: Kiss; library action
203    pair: Tell; library action
204    pair: life; library property
205
206 In "William Tell" we’ve seen a brief manifestation of the :prop:`life`
207 property, but here we'll extend it a little.  As we explained, :prop:`life`
208 lets you intercept those actions particular to animate objects.  Here we
209 trap :act:`Attack` and :act:`Kiss` to offer some customised messages for
210 these actions when applied to the customers.  Also, we avoid conversation
211 by intercepting :act:`Ask`, :act:`Tell` and :act:`Answer` in order just to
212 produce a message which depends on the player character's attire.
213
214 .. Generated by autoindex
215 .. index::
216    pair: animate; library attribute
217    pair: orders; library property
218
219 One other feature of :attr:`animate` objects is the possibility of giving
220 them orders: BILL, SHAKE THE SPEAR or ANNIE, GET YOUR GUN .  These actions
221 are dealt with in the :prop:`orders` property and, as with the :prop:`life`
222 property, the embedded routine can become quite complex if you want your
223 NPCs to behave in an interesting way.  In this case, we don't need the
224 customers to perform tasks for us, so instead we provide a simple rejection
225 message, just in case the player tries to order people around.
226
227 .. Generated by autoindex
228 .. index::
229    pair: daemon; library property
230
231 Which leaves us with the :prop:`daemon` bit.  A daemon is a property
232 normally used to perform some timed or repetitive action without the need
233 of the player’s direct interaction; for example, machines which work by
234 themselves, animals that move on their own, or people going about their
235 business.  More powerfully, a daemon may take notice of the player’s
236 decisions at a particular moment, allowing for some interactive behaviour;
237 this is, however, an advanced feature that we won't use in this example.  A
238 daemon gets a chance of doing something at the end of every turn, typically
239 to (or with) the object to which it’s associated.  In our example, the
240 daemon triggers some sneers and nasty comments from the customers once our
241 hero comes out of the toilet dressed in Captain Fate’s costume.
242
243 To code a daemon, you need to do three things:
244
245 #. First, define a daemon property in the object’s body; the value of the
246    property is always an embedded routine.
247
248 #. However, daemons do nothing until you activate them.  This is easily
249    achieved with the call :samp:`StartDaemon({obj_id})`, which may happen
250    anywhere (if you want some object's daemon to be active from the
251    beginning of the game,you can make the call in your Initialise routine).
252
253 #. Once the daemon has finished its mission (if ever) you may stop it with
254    the call :samp:`StopDaemon({obj_id})`.
255
256 How does our particular daemon work?  The appearance of our hero in full
257 crime-fighting wear will make the customers stare at him and make snarky
258 remarks.  This must happen in the café room – the place where the customers
259 are -- so we need to make certain that the daemon does something
260 interesting only while the player stays in the right place (and hasn’t
261 wandered, say, back into the toilet):
262
263 .. code-block:: inform
264
265   if (location ~= cafe) return;
266
267 So if the location is not the café room (remember ~= means "not equal to"),
268 return without doing anything else; on this turn, there’s nothing for the
269 daemon to do.  We use a plain ``return`` statement because the value
270 returned from a daemon doesn’t matter.
271
272 We have defined a customised local property, ``number_of_comments``, to
273 control the sequence of customers' remarks.  When the Captain enters the
274 café room from the toilet for the first time, the value of the property
275 should be zero, so the statement block under the test:
276
277 .. code-block:: inform
278
279   if (self.number_of_comments == 0) {
280       self.number_of_comments = 1;
281       print "^Nearby customers glance at your costume with open
282           curiosity.^";
283   }
284
285 will happen only this once.  What we intend is to output the text "Nearby
286 customers..."  right after the startling entrance of our hero, setting up
287 the scene for the comments which are about to happen.  Since we assign a
288 value of 1 to the property, the message will not be printed again.  Notice
289 how we use an explicit ``print`` statement; the execution of the daemon
290 will continue normally to the next line.
291
292 We want the customers to indulge in witticisms once they see the costumed
293 Captain, but not on a completely predictable basis.
294
295 .. code-block:: inform
296
297   if (random(2) == 1) ...
298
299 ``random`` is an Inform routine used to generate random numbers or to
300 choose randomly between given choices; in the form
301 :samp:`random({expression})` it returns a random number between 1 and
302 ``expression`` inclusive.  So our condition is actually stating: if a
303 random choice between 1 and 2 happens to be 1 then perform some action.
304 Remember that a daemon is run once at the end of every turn, so the
305 condition is trying to squeeze a comment from a customer roughly once every
306 other turn.
307
308 Next, we proceed as we have already seen in "William Tell", with a switch
309 statement to order the comments in a controlled sequence by cunning use of
310 our tailored local property, ``number_of_comments``.  We have written just
311 five messages (could have been one or a hundred) and then we reach the
312 default case, which is a good place to stop the daemon, since we have no
313 more customers’ remarks to display.
314
315 .. Generated by autoindex
316 .. index::
317    pair: after; library property
318
319 Ah, but when does the daemon *start* functioning?  Well, as soon as our
320 protagonist comes out of the toilet dressed in his multicoloured super-hero
321 pyjamas.  Since we want to minimise the possible game states, we’ll make
322 some general rules to avoid trouble: (a) players will be able to change
323 only in the toilet; (b) we won’t let players change back into street
324 clothes; and (c) once players manage to step into the street thus dressed,
325 the game is won.  So, we can safely assume that if players enter the café
326 in their Captain’s outfit, they’ll be coming from the toilet.  As a
327 consequence of all this, we add an :prop:`after` property to the café room
328 object:
329
330 .. code-block:: inform
331
332   Room   cafe "Inside Benny's cafe"
333          ...
334          first_time_out false,           ! Captain Fate's first appearance?
335          after [;
336            Go:   ! The player has just arrived. Did he come from the toilet?
337              if (noun ~= s_obj) return false;
338              if (costume has worn && self.first_time_out == false) {
339                  self.first_time_out = true;
340                  StartDaemon(customers);
341              }
342          ],
343          s_to  street,
344          n_to  toilet_door
345
346 There are two useful techniques to detect when the player is entering or
347 leaving a room.  We'll later see in detail how to deal with a player trying
348 to go away and how to avoid it if need be.  For now, let’s just mention
349 that, in both cases, you have to intercept the :act:`Go` action in a room
350 object; if you trap it in a :prop:`before` property, you’re checking for
351 departure from the room; if you trap it in an :prop:`after` property,
352 you’re checking for arrivals into the room.  Right now we wish to know if
353 the player just came from the toilet, so we use an :prop:`after` property.
354
355 The first line:
356
357 .. code-block:: inform
358
359   if (noun ~= s_obj) return false;
360
361 is telling the interpreter that we want to do something if the player
362 entered the room by typing a GO SOUTH command (this would normally mean
363 "coming from the north", but remember that nothing stops you from
364 connecting rooms without cardinal logic); the interpreter will apply normal
365 rules for the other available directions.
366
367 .. Generated by autoindex
368 .. index::
369    pair: daemon; library property
370    pair: true; library constant
371
372 Then we check whether the player character is wearing the costume, in which
373 case it starts the :prop:`daemon` of the ``customers`` object.  The use of
374 the local ``first_time_out`` property ensures that the condition is
375 :const:`true` only once, so the statement block attached to it runs also
376 once.
377
378 We've finished with the customers in the café.  Now, we have the toilet to
379 the north which, for reasons of gameplay *and* decency, is protected by a
380 door.
381
382 A door to adore
383 ===============
384
385 Door objects require some specific properties and attributes.  Let's first
386 code a simple door:
387
388 .. code-block:: inform
389
390   Object  toilet_door "toilet door" cafe
391     name name 'red' 'toilet' 'door',
392          description
393              "A red door with the unequivocal black man-woman
394               silhouettes marking the entrance to hygienic facilities.
395               There is a scribbled note stuck on its surface.",
396          door_dir n_to,
397          door_to toilet,
398          with_key toilet_key,
399     has  scenery door openable lockable locked;
400
401 We find this door in the café.  We must specify the direction in which the
402 door leads and, as we have mentioned in the café's description, that would
403 be to the north.  That’s what the :prop:`door_dir` property is for, and in
404 this case it takes the value of the north direction property :prop:`n_to`.
405 Then we must tell Inform the identity of the room to be found behind the
406 door, hence the :prop:`door_to` property, which takes the value of the
407 toilet room -- to be defined later.  Remember the café's connection to the
408 north, ``n_to toilet_door``?  Thanks to it, Inform will know that the door
409 is in the way, and thanks to the :prop:`door_to` property, what lies
410 beyond.
411
412 .. Generated by autoindex
413 .. index::
414    pair: door; library attribute
415    pair: lockable; library attribute
416    pair: locked; library attribute
417    pair: open; library attribute
418    pair: openable; library attribute
419    pair: with_key; library property
420
421 Doors *must* have the attribute :attr:`door`, but beyond that we have a
422 stock of options to help us define exactly what kind of door we are dealing
423 with.  As for containers, doors can be :attr:`openable` (which activates
424 the verbs OPEN and CLOSE so that they can be applied to this object) and,
425 since by default they are closed, you can give them the attribute
426 :attr:`open` if you wish otherwise.  Additionally, doors can be
427 :attr:`lockable` (which sets up the LOCK/UNLOCK verbs) and you can make
428 them :attr:`locked` to override their default unlocked status.  The verbs
429 LOCK and UNLOCK are expecting some kind of key object to operate the door.
430 This must be defined using the :prop:`with_key` property, whose value
431 should be the internal ID of the key; in our example, the
432 soon-to-be-defined ``toilet_key`` .  If you don't supply this property,
433 players won't be able to lock or unlock the door.
434
435 This simple door definition has one problem, namely, that it exists only in
436 the café room.  If you wish the door to be present also from the toilet
437 side, you can either (a) define another door to be found in the ``toilet
438 room``, or (b) make this one a two-sided door.
439
440 Solution (a) seems superficially straightforward, but then you have the
441 problem of keeping the states of the two doors – open/closed,
442 locked/unlocked -- in synch.  In this scenario, where you can access the
443 toilet only through this door, that wouldn't be too complicated, since you
444 could leave the door object in the café room opened all the time,
445 regardless of what players do with the door object in the toilet room and
446 vice versa -- they are never going to see them at the same time.  In
447 general terms, though, such inconsistencies lead to problems; solution
448 (a) is best ignored for most purposes.
449
450 Solution (b) is better, since you have only one door object to deal with
451 and its possible states affect both sides.  However, the coding gets a
452 little bit complicated and you''ll have to define routines for most
453 properties:
454
455 .. code-block:: inform
456
457   Object  toilet_door "toilet door"
458     with  name 'red' 'toilet' 'door',
459           description [;
460               if (location == cafe)
461                    "A red door with the unequivocal black man-woman silhouettes
462                     marking the entrance to hygienic facilities. There is a
463                     scribbled note stuck on its surface.";
464               else
465                     "A red door with no OUTSTANDING features.";
466           ],
467           found_in cafe toilet,
468           door_dir [;
469               if (location == cafe) return n_to;
470               else                  return s_to;
471           ],
472           door_to [;
473               if (location == cafe) return toilet;
474               else                  return cafe;
475           ],
476           with_key toilet_key,
477     has   scenery door openable lockable locked;
478
479 First of all, the door now needs a :prop:`found_in` property, since it's
480 going to be located both in the café and the toilet.  The
481 :prop:`description` checks which side of the door we are looking at –
482 testing the current value of the variable :var:`location`, which holds the
483 room the player is in -- because we have a scribbled note stuck on one
484 side, but not on the other.  And the :prop:`door_dir` and :prop:`door_to`
485 properties must use the same trick, because we travel north from the café
486 into the toilet, but south from the toilet into the café.
487
488 Right now, the game will display "the toilet door" every time it needs to
489 refer to this object.  It would be nice if we could somehow get the game to
490 distinguish between "the door to the toilet" and "the door to the cafe",
491 depending on the side we are facing.  For this, a ``short_name property``
492 is the thing.  We have already talked about the external name defined as
493 part of an object's header information:
494
495 .. code-block:: inform
496
497   Object  toilet_door "toilet door"
498
499 That ``toilet door`` will be the name displayed by the game at run-time to
500 refer to the door.  With identical effect, this could also have been coded
501 thus:
502
503 .. code-block:: inform
504
505   Object  toilet_door
506     with  short_name "toilet door",
507
508 :prop:`short_name` is a property that supplies the external name of an
509 object, either as a string or an embedded routine.  Normally, objects
510 retain the same external name throughout the game -- and the header
511 information method is perfect in that case -- but if it needs to change,
512 it's easy to write a routine as the value of :prop:`short_name`:
513
514 .. code-block:: inform
515
516   Object  toilet_door
517     with  name 'red' 'toilet' 'door'
518           short_name [;
519               if (location == cafe) print "door to the toilet";
520               else                  print "door to the cafe";
521               return true;
522           ],
523           description
524               ...
525
526 Notice the ``return true`` at the end of the routine.  You''ll recall that
527 the standard rule says "return false to carry on, true to take over and
528 stop normal execution".  In the case of :prop:`short_name`, "carry on"
529 means "and now display the external name from the header information",
530 which is sometimes handy; for instance, you could write a
531 :prop:`short_name` routine to prefix an object's external name with one of
532 a range of adjectives -- perhaps a shining/flickering/fading/useless
533 lantern.
534
535 .. note::
536
537   What's displayed if there isn't an external name in an object's header?
538   If you've read the section :ref:`compile-as-you-go`, you'll recall that
539   the interpreter simply uses the internal identifier within parentheses;
540   that is, with no external name and no :prop:`short_name` property, we
541   might see::
542
543     You open the (toilet_door).
544
545   And the same principle applies if we were mistakenly to ``return false``
546   from this short_name routine: we would get, first, the result of our
547   ``print`` statement, and then the standard rules would display the
548   internal ID::
549
550     You open the door to the toilet(toilet_door).
551
552 Doors can get more complicated than this (no, please, don't throw our guide
553 out of the window).  Here comes some optional deluxe coding to make the
554 door object a bit friendlier in game play, so you can skip it if you
555 foresee headaches.
556
557 Our door now behaves nicely at run-time.  It can be locked and unlocked if
558 the player character has the right key; it can be opened and closed.  A
559 sequence of commands to go into the toilet and lock the door behind you
560 would be: UNLOCK DOOR WITH KEY, OPEN DOOR, GO NORTH, CLOSE DOOR, LOCK DOOR
561 WITH KEY.  After we are finished, let's go back to the café: UNLOCK DOOR
562 WITH KEY, OPEN DOOR, SOUTH.  If the player is of the fastidious kind: CLOSE
563 DOOR, LOCK DOOR WITH KEY.  This game features only one door, but if it had
564 three or four of them, players would grow restless (at the very least) if
565 they needed to type so many commands just to go through a door.  This is
566 the kind of thing reportedly considered as poor design, because the game is
567 suddenly slowed down to get over a simple action which involves no secrets
568 or surprises.  How exciting can the crossing of an ordinary door be, after
569 all?
570
571 .. Generated by autoindex
572 .. index::
573    pair: after; library property
574    pair: before; library property
575
576 If a few lines of code can make the life of the player easier, it's worth a
577 shot.  Let's provide a few improvements to our toilet door in
578 :prop:`before` and :prop:`after` properties:
579
580 .. code-block:: inform
581
582   before [ ks;
583     Open:
584       if (self hasnt locked || toilet_key notin player)
585           return false;
586       ks = keep_silent; keep_silent = true;
587       <Unlock self toilet_key>; keep_silent = ks;
588       return true;
589     Lock:
590       if (self hasnt open) return false;
591       print "(first closing ", (the) self, ")^";
592       ks = keep_silent; keep_silent = true;
593       <Close self>; keep_silent = ks;
594       return false;
595     ],
596     after [ ks;
597       Unlock:
598         if (self has locked) return false;
599         print "You unlock ", (the) self, " and open it.^";
600         ks = keep_silent; keep_silent = true;
601         <Open self>; keep_silent = ks;
602         return true;
603     ],
604
605 The basic idea here is to let the player who holds the key perform just one
606 action to both unlock *and* open the door (and, conversely, to close *and*
607 lock it).  The relevant actions are :act:`Unlock` and :act:`Open`, and
608 :act:`Lock` (:act:`Close` is not necessary; if players just close the door
609 we shouldn’t assume that they want to lock it as well).
610
611 * **Open**: if the door isn't locked or the player doesn't hold the key,
612   keep going with the default :act:`Open` action defined by the library.
613   That leaves a locked door and a player holding the key, so we redirect
614   processing to the :act:`Unlock` action, giving as arguments the door
615   (self) and the toilet key.  Since we are using single angle-brackets
616   ``<...>``, the action resumes after the unlocking is done (note that the
617   :act:`Unlock` action also takes care of opening the door).  Finally, we
618   ``return true`` to stop the library from trying to open the door by
619   itself.
620
621 * **Lock**: if the door is already closed, keep going with the standard
622   library :act:`Lock` action.  If not, tell players that we are closing the
623   door for them, redirect the action briefly to actually close it, and then
624   ``return false`` to let the :act:`Lock` action proceed as before.
625
626 .. Generated by autoindex
627 .. index::
628    pair: true; library constant
629
630 * **Unlock**: we place this action in the after property, so (let's hope)
631   the :act:`Unlock` action has already happened.  If the door is still
632   locked, something went wrong, so we ``return false`` to display the
633   standard message for an unsuccessful unlocking.  Otherwise, the door is
634   now unlocked, so we inform the player that we are opening the door and
635   redirect the action to actually open it, returning :const:`true` to
636   suppress the standard message.
637
638 .. Generated by autoindex
639 .. index::
640    pair: false; library constant
641    pair: keep_silent; library variable
642
643 In all processes there is a library variable called :var:`keep_silent`,
644 which can be either :const:`false` (the normal state) or :const:`true`;
645 when :const:`true`, the interpreter does not display the associated message
646 of an action in progress, so we can avoid things like:
647
648 .. code-block:: transcript
649
650   >OPEN DOOR
651   You open the door to the toilet.
652   You unlock the door to the toilet and open it.
653
654 Although we want to set :var:`keep_silent` to :const:`true` for the
655 duration of our extra processing, we need to reset it afterwards.  In a
656 case like this, good design practice is to preserve its initial value
657 (which was probably :const:`false`, but you should avoid risky
658 assumptions); we use a local variable ``ks`` to remember that initial
659 setting so that we can safely restore it afterwards.  You’ll remember that
660 a local variable in a standalone routine is declared between the routine’s
661 name and the semicolon:
662
663 .. code-block:: inform
664
665   [ BeenToBefore this_room;
666
667 In exactly the same way, a local variable in an embedded routine is
668 declared between the ``[`` starting marker of the routine and the
669 semicolon:
670
671 .. code-block:: inform
672
673   before [ ks;
674
675 You can declare up to fifteen variables this way -- just separated by
676 spaces -- which are usable only within the embedded routine.  When we
677 assign it thus:
678
679 .. code-block:: inform
680
681   ks = keep_silent;
682
683 we are actually making ``ks`` equal to whatever value :var:`keep_silent`
684 has (either :const:`true` or :const:`false`; we actually don't care).  We
685 then set :var:`keep_silent` to :const:`true`, make the desired silent
686 actions, and we assign:
687
688 .. code-block:: inform
689
690   keep_silent = ks;
691
692 which restores the value originally stored in ``ks`` to :var:`keep_silent`.
693 The effect is that we manage to leave it as it was before we tampered with
694 it.
695
696 Well, that's about everything about doors.  Everything?  Well, no, not
697 really; any object can grow as complex as your imagination allows, but
698 we’ll drop the subject here.  If you care to see more sophisticated doors,
699 check Exercises :dm4:`3 and 4 <s6.html#ex3>` in the |DM4|, where an
700 obliging door opens and unlocks by itself if the player simply walks in its
701 direction.
702
703 So far, we have the player in front of a locked door leading to the toilet.
704 A dead end?  No, the description mentions a scribbled note on its surface.
705 This one should offer no problem:
706
707 .. code-block:: inform
708
709   Object  "scribbled note" cafe
710     with  name 'scribbled' 'note',
711           description [;
712               if (self.read_once == false) {
713                   self.read_once = true;
714                   "You apply your ENHANCED ULTRAFREQUENCY vision to the note
715                    and squint in concentration, giving up only when you see the
716                    borders of the note begin to blacken under the incredible
717                    intensity of your burning stare. You reflect once more how
718                    helpful it would've been if you'd ever learnt to read.
719                    ^^A kind old lady passes by and explains:
720                    ~You have to ask Benny for the key, at the counter.~^^
721                    You turn quickly and begin, ~Oh, I KNOW that, but...~^^
722                    ~My pleasure, son,~ says the lady, as she exits the cafe.";
723               }
724               else
725                   "The scorched undecipherable note holds no SECRETS from
726                    you NOW! Ha!";
727           ],
728           read_once false,                ! has the player read the note once?
729           before [;
730             Take:
731               "No reason to start collecting UNDECIPHERABLE notes.";
732           ],
733     has   scenery;
734
735 Just notice how we change the description after the first time the player
736 examines the note, using the local property ``read_once`` created just for
737 this purpose.  We don’t want the player to walk off with the note, so we
738 intercept the :act:`Take` action and display something more in character
739 than the default message for scenery objects: "That's hardly portable".
740
741 We've talked a lot about the toilet key; it seems about time to code it.
742 Originally, the key is in Benny's possession, and the player will have to
743 ask for it, just as the note explains.  Although we'll define Benny in
744 detail throughout the next chapter, here we present a basic definition,
745 largely so that the key has a parent object.
746
747 .. code-block:: inform
748
749   Object  benny "Benny"  cafe
750     with  name 'benny',
751           description
752               "A deceptively FAT man of uncanny agility, Benny entertains his
753                customers crushing coconuts against his forehead when the mood
754                strikes him.",
755     has   scenery animate male proper transparent;
756
757   Object  toilet_key "toilet key" benny
758     with  name 'toilet' 'key',
759           article "the",
760           invent [;
761               if (clothes has worn) print "the CRUCIAL key";
762               else                  print "the used and IRRELEVANT key";
763               return true;
764           ],
765           description
766               "Your SUPRA PERCEPTIVE senses detect nothing of consequence
767                about the toilet key.",
768           before [;
769               if (self in benny)
770                   "You SCAN your surroundings with ENHANCED AWARENESS,
771                    but fail to detect any key.";
772             Drop:
773               "Benny is trusting you to look after that key.";
774           ];
775
776 While Benny has the key, there's logically no way to examine it (or perform
777 any other action involving it), but we want to prevent the interpreter from
778 objecting that ``You can't see any such thing``.  We've made the
779 ``toilet_key`` a child of the ``benny`` object, and you can see that
780 Benny's got a :attr:`transparent` attribute; this means that the key is in
781 scope, and enables the player to refer to it without the interpreter
782 complaining.  Because Benny also has an :attr:`animate` attribute, the
783 interpreter would normally intercept a TAKE KEY action with "That seems to
784 belong to Benny"; however, the same wouldn't apply to other commands like
785 TOUCH KEY and TASTE KEY.  So, to prevent any interaction with the key while
786 it’s in Benny’s pockets, we define a :prop:`before` property.
787
788 .. code-block:: inform
789
790   before [;
791       if (self in benny)
792           "You SCAN your surroundings with ENHANCED AWARENESS,
793            but fail to detect any key.";
794     Drop:
795       "Benny is trusting you to look after that key.";
796   ];
797
798 All of the :prop:`before` properties that we've so far created have
799 contained one or more labels specifying the actions which they are to
800 intercept; you'll remember that in "William Tell" we introduced the
801 ``default`` action (see :ref:`props-class`) to mean "any value not already
802 catered for".  There's one of those labels here, for the Drop action, but
803 that's preceded by a piece of code that will be executed at the start of
804 *every* action directed at the key.  If it's still in Benny’s possession,
805 we display a polite refusal; if the player has it then we prevent careless
806 disposal; otherwise, the action continues unhindered.
807
808 (In fact, the hat-on-a-pole ``Prop`` introduced in :ref:`south-side` had
809 this all-exclusive :prop:`before` property:
810
811 .. code-block:: inform
812
813   before [;
814     default:
815       print_ret "You're too far away at the moment.";
816   ],
817
818 It would have behaved exactly the same if we'd omitted the ``default``
819 label, as we do here for Benny's key.)
820
821 .. Generated by autoindex
822 .. index::
823    pair: article; library property
824
825 Another small innovation here: the :prop:`invent` library property (we
826 didn’t make it up) which enables you to control how objects appear in
827 inventory listings, overriding the default.  Left to itself, the
828 interpreter simply displays the object's external name, preceded either by
829 a standard article like "a" or "some", or one specifically defined in the
830 object's :prop:`article` property.  Here we replace "the toilet key" with
831 one of two more helpful descriptions, making it a most valuable object in
832 the eyes of John Covarth, and something to be despised haughtily by Captain
833 Fate once it's of no further use to him.
834
835 When we had players in the street, we faced the problem that they might
836 choose to examine the café from the outside.  While it's unlikely that
837 they'll try to examine the toilet room from the outside, it takes very
838 little effort to offer a sensible output just in case:
839
840 .. code-block:: inform
841
842   Object  outside_of_toilet "toilet" cafe
843     with  name 'toilet' 'bath' 'rest' 'room' 'bathroom' 'restroom',
844           before [;
845             Enter:
846               if (toilet_door has open) {
847                   PlayerTo(toilet);
848                   return true;
849               }
850               else
851                   "Your SUPERB deductive mind detects that the DOOR is
852                    CLOSED.";
853             Examine:
854               if (toilet_door has open)
855                    "A brilliant thought flashes through your SUPERLATIVE
856                     brain: detailed examination of the toilet would be
857                     EXTREMELY facilitated if you entered it.";
858               else
859                    "With a TREMENDOUS effort of will, you summon your
860                     unfathomable ASTRAL VISION and project it FORWARD
861                     towards the closed door... until you remember that it's
862                     Dr Mystere who's the one with mystic powers.";
863             Open:
864               <<Open   toilet_door>>;
865             Close:
866               <<Close  toilet_door>>;
867             Take,Push,Pull:
868               "That would be PART of the building.";
869           ],  
870     has   scenery openable enterable;
871
872 As with the ``outside_of_cafe`` object, we intercept an :act:`Enter`
873 action, to teleport players into the toilet room if they type ENTER TOILET
874 (or to display a refusal if the toilet door is closed).  Players may try to
875 EXAMINE TOILET; they'll get a different message if the door is open -- we
876 invite them to enter it -- or if it's closed.  OPEN TOILET and CLOSE TOILET
877 inputs are redirected to :act:`Open` and :act:`Close` actions for the
878 toilet door; remember that the double angle-brackets imply a ``return
879 true``, so that the action stops there and the interpreter does not attempt
880 to :act:`Open` or :act:`Close` the ``outside_of_toilet`` object itself
881 after it has dealt with the door.
882
883 You're right: the toilet looms large in this game (we blame it on early
884 maternal influences).  We’ve introduced an ambiguity problem with the
885 ``outside_of_toilet`` object, and we'll need some help in fixing it.