From: David Griffith Date: Fri, 15 Apr 2016 05:00:24 +0000 (-0700) Subject: Added Chapter 11. X-Git-Url: https://jxself.org/git/?p=ibg.git;a=commitdiff_plain;h=6f29703a02de1ec9cac82267ff0ea24a01679de6;hp=eb27272c6bc482b00105a9edbd7bad3dd4cab141 Added Chapter 11. --- diff --git a/chapters/11.rst b/chapters/11.rst new file mode 100644 index 0000000..ec95f79 --- /dev/null +++ b/chapters/11.rst @@ -0,0 +1,843 @@ +==================== +Captain Fate: take 2 +==================== + +.. epigraph:: + + | *U was a usurer, a miserable elf;* + | *V was a vintner, who drank all himself.* + +.. only:: html + + .. image:: /images/picV.png + :align: left + +.. raw:: latex + + \dropcap{v} + +iewed from the inside, Benny's café is warm and welcoming, and packed +with lunchtime customers. We'll try to conjure up some appropriate +images, but the main focus of the room isn't the decor: it's the door +leading to the toilet -- and, perhaps, privacy? + +A homely atmosphere +=================== + +Benny's café is populated with customers enjoying their lunch, so it +won't be a good place to change identities. However, the toilet to the +north looks promising, though Benny has strict rules about its use and +the door seems to be locked. + +.. admonition:: Cultural Note + :class: admonition note + + not for the first time, this guide betrays its origins. In + European countries the word "toilet" often refers not only to the + white porcelain artefact, but also to the room in which it can be + found (also, a "bathroom" is for taking a bath, a "restroom" for + taking a rest). Bear with us on this; the dual usage becomes + important a little later on. + +We define the café room in simple form: + +.. code-block:: inform6 + + Room cafe "Inside Benny's cafe" + with description + "Benny's offers the FINEST selection of pastries and + sandwiches. Customers clog the counter, where Benny himself + manages to serve, cook and charge without missing a step. At + the north side of the cafe you can see a red door connecting + with the toilet.", + s_to street, + n_to toilet_door; + +We'll elaborate on the last line (``n_to toilet_door``) later, when we +define the door object which lies between the café and the toilet. + +We've mentioned a counter: + +.. code-block:: inform6 + + Appliance counter "counter" cafe + with name 'counter' 'bar', + article "the", + description + "The counter is made of an astonishing ALLOY of metals, + STAIN-PROOF, SPILL-RESISTANT and VERY EASY to clean. Customers + enjoy their snacks with UTTER tranquillity, safe in the notion + that the counter can take it all.", + before [; + Receive: + <>; + ], + has supporter; + +That ``before property``, superficially normal, actually conceals a +little surprise. By now you should be entirely comfortable with using an +object's ``before`` property to intercept an action directed at that +object; for example, if the player types HIT COUNTER then the counter's +``before`` property is potentially able to intercept the resulting +Attack action. However, the command PUT KEY ON COUNTER generates *two* +actions. First, a PutOn action is offered to the key (effectively +saying, do you want to be placed on top of the counter?); that’s the +normal bit. And then the surprise: a Receive action is offered to the +counter (effectively saying, are you happy to have the key placed on +you?) Both actions have the same opportunity of returning ``false`` to +let the action continue, ``true`` to prevent it. + +.. todo:: + + There are a lot of actions here that are rendered in a typewriter font + and others that are not. Should these ones that are not be promoted + to having a typewriter font? + +The Receive action is generated by the library in the PutOnSub action +handler, and also in InsertSub (so a command like PUT BIRD IN NEST sends +a Receive to the nest object). There’s a matching LetGo, generated by +the library from commands like TAKE KEY OFF COUNTER and REMOVE BIRD FROM +NEST. Receive and LetGo are examples of what’s called a **fake action**. + +.. note:: + + in "William Tell" we defined the ``quiver``, way back in "The + player's possessions" on page 83, as an ``open container``. As things + stand, the player can put *any* held object, however inappropriate, + into it. We could have trapped the Receive action to ensure that + arrows are the only acceptable contents (recollect that ``~~``, to be + read as "not", turns true into false and vice versa): + + .. code-block:: inform6 + + before [; + Drop,Give: + print_ret "But it was a present from Hedwig, your wife."; + Receive: + if (~~(noun ofclass Arrow)) + print_ret "Only arrows -- clean arrows -- go in your quiver."; + ], + +Here, we intercept any attempt to place an item on the counter, and +translate it into an attempt to give that item to Benny. Part of the +game's plot depends on the player returning the toilet key to Benny, and +also paying him for his delicious cup of world-famous Cappuccino. +Putting the key and the money on the counter is a reasonable alternative +way for the player to accomplish this. + +We've also mentioned some customers. These are treated as NPCs, reacting +to our hero’s performance. + +.. code-block:: inform6 + + Object customers "customers" cafe + with name 'customers' 'people' 'customer' 'men' 'women', + description [; + if (costume has worn) + "Most seem to be concentrating on their food, but some do + look at you quite blatantly. Must be the MIND-BEFUDDLING + colours of your costume."; + else + "A group of HELPLESS and UNSUSPECTING mortals, the kind + Captain FATE swore to DEFEND the day his parents choked on a + DEVIOUS slice of RASPBERRY PIE."; + ], + life [; + Ask,Tell,Answer: + if (costume has worn) + "People seem to MISTRUST the look of your FABULOUS costume."; + else + "As John Covarth, you attract LESS interest than Benny's + food."; + Kiss: + "There's no telling what sorts of MUTANT bacteria these + STRANGERS may be carrying around."; + Attack: + "Mindless massacre of civilians is the qualification for + VILLAINS. You are SUPPOSED to protect the likes of these + people."; + ], + orders [; + "These people don't appear to be of the cooperative sort."; + ], + number_of_comments 0, ! for counting the customer comments + daemon [; + if (location ~= cafe) return; + if (self.number_of_comments == 0) { + self.number_of_comments = 1; + print "^Nearby customers glance at your costume with open + curiosity.^"; + } + if (random(2) == 1) { ! do this 50% of the time + self.number_of_comments = self.number_of_comments + 1; + switch (self.number_of_comments) { + 2: "^~Didn't know there was a circus in town,~ comments one + customer to another. ~Seems like the clowns have the + day off.~"; + 3: "^~These fashion designers don't know what to do to show + off,~ snorts a fat gentleman, looking your way. Those + within earshot try to conceal their smiles."; + 4: "^~Must be carnival again,~ says a man to his wife, who + giggles, stealing a peek at you. ~Time sure flies.~"; + 5: "^~Bad thing about big towns~, comments someone to his + table companion, ~is you get the damnedest bugs coming + out from toilets.~"; + 6: "^~I sure WISH I could go to work in my pyjamas,~ says a + girl in an office suit to some colleagues. ~It looks SO + comfortable.~"; + default: StopDaemon(self); + } + } + ], + has scenery animate pluralname; + +Let's go step by step. Our hero enters the café dressed as John Covarth, +but will eventually manage to change clothes in the toilet, and he'll +have to cross back through the café to reach the street and win the +game. The customers' ``description`` takes into consideration which +outfit the player character is wearing. + +In "William Tell" we’ve seen a brief manifestation of the ``life`` +property, but here we'll extend it a little. As we explained, ``life`` +lets you intercept those actions particular to animate objects. Here we +trap ``Attack`` and ``Kiss`` to offer some customised messages for these +actions when applied to the customers. Also, we avoid conversation by +intercepting ``Ask``, ``Tell`` and ``Answer`` in order just to produce a +message which depends on the player character's attire. + +One other feature of ``animate`` objects is the possibility of giving +them orders: BILL, SHAKE THE SPEAR or ANNIE, GET YOUR GUN . These +actions are dealt with in the ``orders`` property and, as with the +``life`` property, the embedded routine can become quite complex if you +want your NPCs to behave in an interesting way. In this case, we don't +need the customers to perform tasks for us, so instead we provide a +simple rejection message, just in case the player tries to order people +around. + +Which leaves us with the ``daemon`` bit. A daemon is a property normally +used to perform some timed or repetitive action without the need of the +player’s direct interaction; for example, machines which work by +themselves, animals that move on their own, or people going about their +business. More powerfully, a daemon may take notice of the player’s +decisions at a particular moment, allowing for some interactive +behaviour; this is, however, an advanced feature that we won't use in +this example. A daemon gets a chance of doing something at the end of +every turn, typically to (or with) the object to which it’s associated. +In our example, the daemon triggers some sneers and nasty comments from +the customers once our hero comes out of the toilet dressed in Captain +Fate’s costume. + +To code a daemon, you need to do three things: + +#. First, define a daemon property in the object’s body; the value of + the property is always an embedded routine. + +#. However, daemons do nothing until you activate them. This is easily + achieved with the call ``StartDaemon(obj_id)``, which may happen + anywhere (if you want some object's daemon to be active from the + beginning of the game,you can make the call in your Initialise + routine). + +#. Once the daemon has finished its mission (if ever) you may stop it + with the call ``StopDaemon(obj_id)``. + +How does our particular daemon work? The appearance of our hero in full +crime-fighting wear will make the customers stare at him and make snarky +remarks. This must happen in the café room – the place where the +customers are -- so we need to make certain that the daemon does +something interesting only while the player stays in the right place +(and hasn’t wandered, say, back into the toilet): + +.. code-block:: inform6 + + if (location ~= cafe) return; + +So if the location is not the café room (remember ~= means "not equal +to"), return without doing anything else; on this turn, there’s nothing +for the daemon to do. We use a plain ``return`` statement because the +value returned from a daemon doesn’t matter. + +We have defined a customised local property, ``number_of_comments``, to +control the sequence of customers' remarks. When the Captain enters the +café room from the toilet for the first time, the value of the property +should be zero, so the statement block under the test: + +.. code-block:: inform6 + + if (self.number_of_comments == 0) { + self.number_of_comments = 1; + print "^Nearby customers glance at your costume with open + curiosity.^"; + } + +will happen only this once. What we intend is to output the text "Nearby +customers..." right after the startling entrance of our hero, setting up +the scene for the comments which are about to happen. Since we assign a +value of 1 to the property, the message will not be printed again. +Notice how we use an explicit ``print`` statement; the execution of the +daemon will continue normally to the next line. + +We want the customers to indulge in witticisms once they see the +costumed Captain, but not on a completely predictable basis. + +.. code-block:: inform6 + + if (random(2) == 1) ... + +.. todo:: + + "expression" in "random(expression)" should be typewriter and italic + +``random`` is an Inform routine used to generate random numbers or to +choose randomly between given choices; in the form +``random(expression)`` it returns a random number between 1 and +``expression`` inclusive. So our condition is actually stating: if a +random choice between 1 and 2 happens to be 1 then perform some action. +Remember that a daemon is run once at the end of every turn, so the +condition is trying to squeeze a comment from a customer roughly once +every other turn. + +Next, we proceed as we have already seen in "William Tell", with a +switch statement to order the comments in a controlled sequence by +cunning use of our tailored local property, ``number_of_comments``. We +have written just five messages (could have been one or a hundred) and +then we reach the default case, which is a good place to stop the +daemon, since we have no more customers’ remarks to display. + +Ah, but when does the daemon *start* functioning? Well, as soon as our +protagonist comes out of the toilet dressed in his multicoloured +super-hero pyjamas. Since we want to minimise the possible game states, +we’ll make some general rules to avoid trouble: (a) players will be able +to change only in the toilet; (b) we won’t let players change back into +street clothes; and (c) once players manage to step into the street thus +dressed, the game is won. So, we can safely assume that if players enter +the café in their Captain’s outfit, they’ll be coming from the toilet. +As a consequence of all this, we add an ``after`` property to the café +room object: + +.. code-block:: inform6 + + Room cafe "Inside Benny's cafe" + ... + first_time_out false, ! Captain Fate's first appearance? + after [; + Go: ! The player has just arrived. Did he come from the toilet? + if (noun ~= s_obj) return false; + if (costume has worn && self.first_time_out == false) { + self.first_time_out = true; + StartDaemon(customers); + } + ], + s_to street, + n_to toilet_door + +There are two useful techniques to detect when the player is entering or +leaving a room. We'll later see in detail how to deal with a player +trying to go away and how to avoid it if need be. For now, let’s just +mention that, in both cases, you have to intercept the ``Go`` action in +a room object; if you trap it in a ``before`` property, you’re checking +for departure from the room; if you trap it in an ``after`` property, +you’re checking for arrivals into the room. Right now we wish to know if +the player just came from the toilet, so we use an ``after`` property. + +The first line: + +.. code-block:: inform6 + + if (noun ~= s_obj) return false; + +is telling the interpreter that we want to do something if the player +entered the room by typing a GO SOUTH command (this would normally mean +"coming from the north", but remember that nothing stops you from +connecting rooms without cardinal logic); the interpreter will apply +normal rules for the other available directions. + +Then we check whether the player character is wearing the costume, in +which case it starts the ``daemon`` of the ``customers`` object. The use +of the local first_time_out property ensures that the condition is +``true`` only once, so the statement block attached to it runs also +once. + +We've finished with the customers in the café. Now, we have the toilet +to the north which, for reasons of gameplay *and* decency, is protected +by a door. + +A door to adore +=============== + +Door objects require some specific properties and attributes. Let's +first code a simple door: + +.. code-block:: inform6 + + Object toilet_door "toilet door" cafe + name name 'red' 'toilet' 'door', + description + "A red door with the unequivocal black man-woman + silhouettes marking the entrance to hygienic facilities. + There is a scribbled note stuck on its surface.", + door_dir n_to, + door_to toilet, + with_key toilet_key, + has scenery door openable lockable locked; + +We find this door in the café. We must specify the direction in which +the door leads and, as we have mentioned in the café's description, that +would be to the north. That’s what the ``door_dir`` property is for, and +in this case it takes the value of the north direction property +``n_to``. Then we must tell Inform the identity of the room to be found +behind the door, hence the ``door_to`` property, which takes the value +of the toilet room -- to be defined later. Remember the café's +connection to the north, ``n_to toilet_door``? Thanks to it, Inform will +know that the door is in the way, and thanks to the ``door_to`` +property, what lies beyond. + +Doors *must* have the attribute ``door``, but beyond that we have a +stock of options to help us define exactly what kind of door we are +dealing with. As for containers, doors can be ``openable`` (which +activates the verbs OPEN and CLOSE so that they can be applied to this +object) and, since by default they are closed, you can give them the +attribute ``open`` if you wish otherwise. Additionally, doors can be +``lockable`` (which sets up the LOCK/UNLOCK verbs) and you can make them +``locked`` to override their default unlocked status. The verbs LOCK +and UNLOCK are expecting some kind of key object to operate the door. +This must be defined using the ``with_key`` property, whose value should +be the internal ID of the key; in our example, the soon-to-be-defined +``toilet_key`` . If you don't supply this property, players won't be +able to lock or unlock the door. + +This simple door definition has one problem, namely, that it exists only +in the café room. If you wish the door to be present also from the +toilet side, you can either (a) define another door to be found in the +``toilet room``, or (b) make this one a two-sided door. + +Solution (a) seems superficially straightforward, but then you have the +problem of keeping the states of the two doors – open/closed, +locked/unlocked -- in synch. In this scenario, where you can access the +toilet only through this door, that wouldn't be too complicated, since +you could leave the door object in the café room opened all the time, +regardless of what players do with the door object in the toilet room +and vice versa -- they are never going to see them at the same time. In +general terms, though, such inconsistencies lead to problems; solution +(a) is best ignored for most purposes. + +Solution (b) is better, since you have only one door object to deal with +and its possible states affect both sides. However, the coding gets a +little bit complicated and you''ll have to define routines for most +properties: + +.. code-block:: inform6 + + Object toilet_door "toilet door" + with name 'red' 'toilet' 'door', + description [; + if (location == cafe) + "A red door with the unequivocal black man-woman silhouettes + marking the entrance to hygienic facilities. There is a + scribbled note stuck on its surface."; + else + "A red door with no OUTSTANDING features."; + ], + found_in cafe toilet, + door_dir [; + if (location == cafe) return n_to; + else return s_to; + ], + door_to [; + if (location == cafe) return toilet; + else return cafe; + ], + with_key toilet_key, + has scenery door openable lockable locked; + +First of all, the door now needs a ``found_in`` property, since it's +going to be located both in the café and the toilet. The ``description`` +checks which side of the door we are looking at – testing the current +value of the variable ``location``, which holds the room the player is +in -- because we have a scribbled note stuck on one side, but not on the +other. And the ``door_dir`` and ``door_to`` properties must use the same +trick, because we travel north from the café into the toilet, but south +from the toilet into the café. + +Right now, the game will display "the toilet door" every time it needs +to refer to this object. It would be nice if we could somehow get the +game to distinguish between "the door to the toilet" and "the door to +the cafe", depending on the side we are facing. For this, a ``short_name +property`` is the thing. We have already talked about the external name +defined as part of an object's header information: + +.. code-block:: inform6 + + Object toilet_door "toilet door" + +That ``toilet door`` will be the name displayed by the game at run-time +to refer to the door. With identical effect, this could also have been +coded thus: + +.. code-block:: inform6 + + Object toilet_door + with short_name "toilet door", + +``short_name`` is a property that supplies the external name of an +object, either as a string or an embedded routine. Normally, objects +retain the same external name throughout the game -- and the header +information method is perfect in that case -- but if it needs to change, +it's easy to write a routine as the value of ``short_name``: + +.. code-block:: inform6 + + Object toilet_door + with name 'red' 'toilet' 'door' + short_name [; + if (location == cafe) print "door to the toilet"; + else print "door to the cafe"; + ], + description + ... + +Notice the ``return true`` at the end of the routine. You''ll recall +that the standard rule says "return false to carry on, true to take over +and stop normal execution”. In the case of ``short_name``, "carry on" +means "and now display the external name from the header information", +which is sometimes handy; for instance, you could write a ``short_name`` +routine to prefix an object's external name with one of a range of +adjectives -- perhaps a shining/flickering/fading/useless lantern. + +.. note:: + + what's displayed if there isn't an external name in an object's + header? If you've read the section "Compile-as-you-go" on page 233, + you'll recall that the interpreter simply uses the internal + identifier within parentheses; that is, with no external name and no + ``short_name`` property, we might see: + + .. code-block:: inform6 + + You open the (toilet_door). + + And the same principle applies if we were mistakenly to ``return + false`` from this short_name routine: we would get, first, the result + of our ``print`` statement, and then the standard rules would display + the internal ID: + + .. code-block:: inform6 + + You open the door to the toilet(toilet_door). + +Doors can get more complicated than this (no, please, don't throw our +guide out of the window). Here comes some optional deluxe coding to make +the door object a bit friendlier in game play, so you can skip it if you +foresee headaches. + +Our door now behaves nicely at run-time. It can be locked and unlocked +if the player character has the right key; it can be opened and closed. +A sequence of commands to go into the toilet and lock the door behind +you would be: UNLOCK DOOR WITH KEY, OPEN DOOR, GO NORTH, CLOSE DOOR, +LOCK DOOR WITH KEY. After we are finished, let's go back to the café: +UNLOCK DOOR WITH KEY, OPEN DOOR, SOUTH. If the player is of the +fastidious kind: CLOSE DOOR, LOCK DOOR WITH KEY. This game features only +one door, but if it had three or four of them, players would grow +restless (at the very least) if they needed to type so many commands +just to go through a door. This is the kind of thing reportedly +considered as poor design, because the game is suddenly slowed down to +get over a simple action which involves no secrets or surprises. How +exciting can the crossing of an ordinary door be, after all? + +If a few lines of code can make the life of the player easier, it's +worth a shot. Let's provide a few improvements to our toilet door in +``before`` and ``after`` properties: + +.. code-block:: inform6 + + before [ ks; + Open: + if (self hasnt locked || toilet_key notin player) + return false; + ks = keep_silent; keep_silent = true; + ; keep_silent = ks; + return true; + Lock: + if (self hasnt open) return false; + print "(first closing ", (the) self, ")^"; + ks = keep_silent; keep_silent = true; + ; keep_silent = ks; + return false; + ], + after [ ks; + Unlock: + if (self has locked) return false; + print "You unlock ", (the) self, " and open it.^"; + ks = keep_silent; keep_silent = true; + ; keep_silent = ks; + return true; + ], + +The basic idea here is to let the player who holds the key perform just +one action to both unlock *and* open the door (and, conversely, to close +*and* lock it). The relevant actions are ``Unlock`` and ``Open``, and +``Lock`` ( ``Close`` is not necessary; if players just close the door we +shouldn’t assume that they want to lock it as well). + +* **Open**: if the door isn't locked or the player doesn't hold the key, + keep going with the default ``Open`` action defined by the library. + That leaves a locked door and a player holding the key, so we + redirect processing to the ``Unlock`` action, giving as arguments the + door (self) and the toilet key. Since we are using single + angle-brackets ``<...>``, the action resumes after the unlocking is + done (note that the ``Unlock`` action also takes care of opening the + door). Finally, we ``return true`` to stop the library from trying to + open the door by itself. + +* **Lock**: if the door is already closed, keep going with the standard + library ``Lock`` action. If not, tell players that we are closing the + door for them, redirect the action briefly to actually close it, and + then ``return false`` to let the ``Lock`` action proceed as before. + +* **Unlock**: we place this action in the after property, so (let's + hope) the ``Unlock`` action has already happened. If the door is still + locked, something went wrong, so we ``return false`` to display the + standard message for an unsuccessful unlocking. Otherwise, the door is + now unlocked, so we inform the player that we are opening the door and + redirect the action to actually open it, returning ``true`` to + suppress the standard message. + +In all processes there is a library variable called ``keep_silent``, +which can be either ``false`` (the normal state) or ``true``; when +``true``, the interpreter does not display the associated message of an +action in progress, so we can avoid things like:: + + >OPEN DOOR + You open the door to the toilet. + You unlock the door to the toilet and open it. + +Although we want to set ``keep_silent`` to ``true`` for the duration of +our extra processing, we need to reset it afterwards. In a case like +this, good design practice is to preserve its initial value (which was +probably ``false``, but you should avoid risky assumptions); we use a +local variable ``ks`` to remember that initial setting so that we can +safely restore it afterwards. You’ll remember that a local variable in a +standalone routine is declared between the routine’s name and the +semicolon: + +.. code-block:: inform6 + + [ BeenToBefore this_room; + +In exactly the same way, a local variable in an embedded routine is +declared between the ``[`` starting marker of the routine and the +semicolon: + +.. code-block:: inform6 + + before [ ks; + +You can declare up to fifteen variables this way -- just separated by +spaces -- which are usable only within the embedded routine. When we +assign it thus: + +.. code-block:: inform6 + + ks = keep_silent; + +we are actually making ``ks`` equal to whatever value ``keep_silent`` +has (either ``true`` or ``false``; we actually don't care). We then set +``keep_silent`` to ``true``, make the desired silent actions, and we +assign: + +.. code-block:: inform6 + + keep_silent = ks; + +which restores the value originally stored in ``ks`` to ``keep_silent``. +The effect is that we manage to leave it as it was before we tampered +with it. + +Well, that's about everything about doors. Everything? Well, no, not +really; any object can grow as complex as your imagination allows, but +we’ll drop the subject here. If you care to see more sophisticated +doors, check Exercises 3 and 4 in the *Inform Designer's Manual*, where +an obliging door opens and unlocks by itself if the player simply walks +in its direction. + +So far, we have the player in front of a locked door leading to the +toilet. A dead end? No, the description mentions a scribbled note on its +surface. This one should offer no problem: + +.. code-block:: inform6 + + Object "scribbled note" cafe + with name 'scribbled' 'note', + description [; + if (self.read_once == false) { + self.read_once = true; + "You apply your ENHANCED ULTRAFREQUENCY vision to the note + and squint in concentration, giving up only when you see the + borders of the note begin to blacken under the incredible + intensity of your burning stare. You reflect once more how + helpful it would've been if you'd ever learnt to read. + ^^A kind old lady passes by and explains: + ~You have to ask Benny for the key, at the counter.~^^ + You turn quickly and begin, ~Oh, I KNOW that, but...~^^ + ~My pleasure, son,~ says the lady, as she exits the cafe."; + } + else + "The scorched undecipherable note holds no SECRETS from + you NOW! Ha!"; + ], + read_once false, ! has the player read the note once? + before [; + Take: + "No reason to start collecting UNDECIPHERABLE notes."; + ], + has scenery; + +Just notice how we change the description after the first time the +player examines the note, using the local property ``read_once`` created +just for this purpose. We don’t want the player to walk off with the +note, so we intercept the ``Take`` action and display something more in +character than the default message for scenery objects: "That's hardly +portable". + +We've talked a lot about the toilet key; it seems about time to code it. +Originally, the key is in Benny's possession, and the player will have +to ask for it, just as the note explains. Although we'll define Benny in +detail throughout the next chapter, here we present a basic definition, +largely so that the key has a parent object. + +.. code-block:: inform6 + + Object benny "Benny" cafe + with name 'benny', + description + "A deceptively FAT man of uncanny agility, Benny entertains his + customers crushing coconuts against his forehead when the mood + strikes him.", + has scenery animate male proper transparent; + + Object toilet_key "toilet key" benny + with name 'toilet' 'key', + article "the", + invent [; + if (clothes has worn) print "the CRUCIAL key"; + else print "the used and IRRELEVANT key"; + return true; + ], + description + "Your SUPRA PERCEPTIVE senses detect nothing of consequence + about the toilet key.", + before [; + if (self in benny) + "You SCAN your surroundings with ENHANCED AWARENESS, + but fail to detect any key."; + Drop: + "Benny is trusting you to look after that key."; + ]; + +While Benny has the key, there's logically no way to examine it (or +perform any other action involving it), but we want to prevent the +interpreter from objecting that ``You can't see any such thing``. We've +made the ``toilet_key`` a child of the ``benny`` object, and you can see +that Benny's got a ``transparent`` attribute; this means that the key is +in scope, and enables the player to refer to it without the interpreter +complaining. Because Benny also has an ``animate`` attribute, the +interpreter would normally intercept a TAKE KEY action with "That seems +to belong to Benny"; however, the same wouldn't apply to other commands +like TOUCH KEY and TASTE KEY . So, to prevent any interaction with the +key while it’s in Benny’s pockets, we define a ``before`` property. + +.. code-block:: inform6 + + before [; + if (self in benny) + "You SCAN your surroundings with ENHANCED AWARENESS, + but fail to detect any key."; + Drop: + "Benny is trusting you to look after that key."; + ]; + +All of the ``before`` properties that we've so far created have +contained one or more labels specifying the actions which they are to +intercept; you'll remember that in "William Tell" we introduced the +``default`` action (see "A class for props" on page 74) to mean "any +value not already catered for". There's one of those labels here, for +the Drop action, but that's preceded by a piece of code that will be +executed at the start of *every* action directed at the key. If it’s +still in Benny’s possession, we display a polite refusal; if the player +has it then we prevent careless disposal; otherwise, the action +continues unhindered. + +(In fact, the hat-on-a-pole ``Prop`` introduced on page 91 had this +all-exclusive ``before`` property: + +.. code-block:: inform6 + + before [; + default: + print_ret "You're too far away at the moment."; + ], + +It would have behaved exactly the same if we'd omitted the ``default`` +label, as we do here for Benny's key.) + +Another small innovation here: the ``invent`` library property (we +didn’t make it up) which enables you to control how objects appear in +inventory listings, overriding the default. Left to itself, the +interpreter simply displays the object’s external name, preceded either +by a standard article like "a" or "some", or one specifically defined in +the object's ``article`` property. Here we replace "the toilet key" with +one of two more helpful descriptions, making it a most valuable object +in the eyes of John Covarth, and something to be despised haughtily by +Captain Fate once it's of no further use to him. + +When we had players in the street, we faced the problem that they might +choose to examine the café from the outside. While it's unlikely that +they'll try to examine the toilet room from the outside, it takes very +little effort to offer a sensible output just in case: + +.. code-block:: inform6 + + Object outside_of_toilet "toilet" cafe + with name 'toilet' 'bath' 'rest' 'room' 'bathroom' 'restroom', + before [; + Enter: + if (toilet_door has open) { + PlayerTo(toilet); + return true; + } + else + "Your SUPERB deductive mind detects that the DOOR is + CLOSED."; + Examine: + if (toilet_door has open) + "A brilliant thought flashes through your SUPERLATIVE + brain: detailed examination of the toilet would be + EXTREMELY facilitated if you entered it."; + else + "With a TREMENDOUS effort of will, you summon your + unfathomable ASTRAL VISION and project it FORWARD + towards the closed door... until you remember that it's + Dr Mystere who's the one with mystic powers."; + Open: + <>; + Close: + <>; + Take,Push,Pull: + "That would be PART of the building."; + ], + has scenery openable enterable; + +As with the ``outside_of_cafe`` object, we intercept an ``Enter`` +action, to teleport players into the toilet room if they type ENTER +TOILET (or to display a refusal if the toilet door is closed). Players +may try to EXAMINE TOILET; they'll get a different message if the door +is open -- we invite them to enter it -- or if it's closed. OPEN TOILET +and CLOSE TOILET inputs are redirected to ``Open`` and ``Close`` actions +for the toilet door; remember that the double angle-brackets imply a +``return true``, so that the action stops there and the interpreter does +not attempt to ``Open`` or ``Close`` the ``outside_of_toilet`` object +itself after it has dealt with the door. + +You're right: the toilet looms large in this game (we blame it on early +maternal influences). We’ve introduced an ambiguity problem with the +``outside_of_toilet`` object, and we'll need some help in fixing it. diff --git a/images/picV.png b/images/picV.png new file mode 100644 index 0000000..0472d98 Binary files /dev/null and b/images/picV.png differ