Section 8: Introducing messages and classes

Contents Back Forward | ### 8. Introducing messages and classes
| On a round ball A workman that hath copies by, can lay An Europe, Afrique and an Asia, And quickly make that, which was nothing, All. ...John Donne (1571?--1631), Valediction: Of Weeping In fact, messages have already appeared in Section 7. Recall from Section 3 that a message called `messagename` can be sent to an object called `objectname` with various supplied details (called `info1` and `info2` here, though there can be any number from none to 6) as follows:
           objectname.messagename(info1, info2);
And the given object sends back a reply value (which is just a single quantity). This is what is really happening when the player tries to eat the mushroom: first the library sends the mushroom a `before` message to warn it that something will happen; it might reply `true`, in which case the library gives up; otherwise the eating takes place, and the library sends an `after` message to inform the mushroom of its demise. Properties like `before`, then, are really rules to deal with incoming messages. The same applies to most of the properties in Section 3. For example, the message
           mushroom.description();
is sent when the player tries to examine the mushroom: if the reply is `false` then the game prints "You see nothing special about the speckled mushroom.'' Now the mushroom was set up with
           description
              "The mushroom is capped with blotches, and you aren't at all sure
               it's not a toadstool.",
which doesn't look like a rule for receiving a message, but it is one all the same: it means "print this text out, print a new-line and reply `true`''. A more complicated rule could have been given instead, as in the following elaboration of the stone-cut steps in 'Ruins':
           description
           [;  print "The cracked and worn steps descend into a dim chamber.
                      Yours might ";
               if (Square_Chamber has visited)
                    print "be the first feet to tread";
               else print "have been the first feet to have trodden";
               " them for five hundred years.  On the top step is inscribed
                the glyph Q1.";
           ],
`visited` is an attribute which is currently held only by rooms which the player has been to. (The glyphs will be explained later on, as will the `SquareChamber` room, which is where the steps will lead down into.) The library, i.e., the standard game rules, can send out about 40 different kinds of message, `before` and `description` being two of these. The more interesting an object is, the more ingeniously it will respond to these messages: an object which ignores all incoming messages will be lifeless and inert in play, like a small stone.
| In fact there are subtle differences between how the library uses properties, and message-sending: the `name` property, for example, is not really a message-receiver but is just what it appears to be -- a list of useful data. Also, the library is careful not to send (for instance) a `description` message to an object which doesn't provide a rule for what to do with one. But the idea is right.
| So the library is sending out messages to your objects all the time during play. Your objects can also send each other messages, including "new'' ones that the library would never send. It's sometimes convenient to use these to trigger off happenings in the game. For example, suppose the 'Ruins' are home to a parrot which squawks from time to time, for a variety of reasons:
    Object -> parrot "red-tailed parrot"
      with name "red" "tailed" "red-tailed" "parrot" "bird",
           description
              "Beautiful plumage.",
           squawk
           [ utterance;
               if (self in location)
                   print "The parrot squawks, ~", (string) utterance,
                       "! ", (string) utterance, "!~^";
           ],
      has  animate;
We might then, for instance, change the `after` rule for dropping the mushroom to read:
               Drop: parrot.squawk("Drop the mushroom");
                     "The mushroom drops to the ground, battered slightly.";
so that the wretched creature would squawk "Drop the mushroom! Drop the mushroom!'' each time this was done. Likewise, `squawk` messages could be sent for any number of other reasons connected with other objects. But at present it would be an error to send a `squawk` message to any object other than the parrot, since only the parrot has been given a rule telling it what to do if it receives one.
| EXERCISE 2: (link to the answer)
| Make a medicine bottle, which can be opened in a variety of ways in the game, so that the opening--code only occurs once in the bottle definition. In most games there are groups of objects with certain rules in common, which it would be tiresome to have to write out many times. For making such a group, a class definition is the better technique. These closely resemble object definitions, but since they define prototypes rather than actual things, they have no initial location. (An individual tree may be somewhere, but the concept of being a tree has no particular place.) So the 'header' part of the definition is simpler. For example, the scoring system in 'Ruins' works as follows: the player, an archaeologist of the old school, gets a certain number of points for each 'treasure' (i.e., cultural artifact) he can filch and put away into his packing case. This is implemented with the following class:
     Class Treasure
      with cultural_value 10,
           after
           [; Insert:
                    if (second==packing_case)
                        score = score + self.cultural_value;
                    "Safely packed away.";
           ],
           before
           [; Take, Remove:
                    if (self in packing_case)
                       "Unpacking such a priceless artifact had best wait
                        until the Metropolitan Museum can do it.";
           ];
Note that `self` is a variable, which always means "whatever object I am''. If we used it in the definition of the mushroom it would mean the mushroom: used here, it means whatever treasure happens to be being dealt with. (Explanations about `Insert` and `Remove` will come later, but hopefully the idea is clear enough.) An object of the class `Treasure` inherits the properties and attributes it defines: in this case, an object of class `Treasure` picks up the given score and rules automatically. So
    Treasure statuette "pygmy statuette"
      with description
              "A menacing, almost cartoon-like statuette of a pygmy spirit
               with a snake around its neck.",
           initial "A precious Mayan statuette rests here!",
           name "snake" "mayan" "pygmy" "spirit" "statue" "statuette";
inherits the `cultural_value` score of 10 and the rules about taking and dropping. If the statuette had itself set `cultural_value` to 15, say, then the value would be 15, because the object's actual definition always takes priority over anything the class might have specified. A more unusual artifact:
    Treasure honeycomb "ancient honeycomb"
      with article "an",
           name "ancient" "old" "honey" "honeycomb",
           description "Perhaps some kind of funerary votive offering.",
           initial "An exquisitely preserved, ancient honeycomb rests here!",
           after
           [;  Eat: "Perhaps the most expensive meal of your life.  The honey
                   tastes odd, perhaps because it was used to store the entrails
                   of the king buried here, but still like honey.";
           ],
      has  edible;
The honeycomb now has two `after` rules: a new one of its own, plus the existing one that all treasures have. Both apply, but the new one happens first.
| So comparing `cultural_value` and `after`, there seems to be an inconsistency. In the first case, an object's own given value wiped out the value from the class, but in the second, the two values were joined up into a list. Why? The reason is that some of the library's properties are special (again) in being what's called "additive'', so that their values accumulate into a list when class inheritance takes place. The three useful examples are `before`, `after` and `name`.
| Non-library properties you invent (like `squawk` or `cultural_value`) will never be additive, unless you write a directive like:
    Property additive squawk;
(before `squawk` is otherwise mentioned) to say so.
| Finally, note that an object can inherit from several classes at once (see Section 3 for how to give such a definition). Moreover, a class can itself inherit from other classes, so it's easy to make a class for "like Treasure but with `cultural_value` normally 8 instead of 10''.
| REFERENCES: See 'Balances' for an extensive use of message-sending. 'Advent' has a treasure-class similar to this one, and uses class definitions for the many similar maze and dead-end rooms (and the sides of the fissure). That class definitions can be worthwhile even when only two objects use them, can be seen from the kittens-class in 'Alice Through The Looking-Glass'. 'Balances' defines many complicated classes: see especially the white cube, spell and scroll classes. 'Toyshop' contains one easy one (the wax candles) and one unusually hard one (the building blocks).

Contents / Back / Forward
Chapter I / Chapter II / Chapter III / Chapter IV / Chapter V / Chapter VI / Appendix

Mechanically translated to HTML from third edition as revised 16 May 1997. Copyright © Graham Nelson 1993, 1994, 1995, 1996, 1997: all rights reserved.