§6   Actions and reactions

Only the actions of the just
Smell sweet and blossom in their dust.
— James Shirley (1594–1666),
The Contention of Ajax and Ulysses
[Greek is] a language obsessed with action, and with the joy of seeing action multiply from action, action marching relentlessly ahead and with yet more actions filing in from either side to fall into neat step at the rear, in a long straight rank of cause and effect, to what will be inevitable, the only possible end.
— Donna Tartt, The Secret History

Inform is a language obsessed with actions. An ‘action’ is an attempt to perform one simple task: for instance,

Inv    Take sword    Insert gold_coin cloth_bag

are all examples. Here the actual actions are Inv (inventory), Take and Insert. An action has none, one or two objects supplied with it (or, in a few special cases, some numerical information rather than objects). It also has an “actor”, the person who is to perform the action, usually the player. Most actions are triggered off by the game's parser, whose job can be summed up as reducing the player's keyboard commands to actions: “take my hat off”, “remove bowler” or “togli il cappello” (if in an Italian game) might all cause the same action. Some keyboard commands, like “drop all”, cause the parser to fire off whole sequences of actions: others, like “empty the sack into the umbrella stand”, cause only a single action but one which may trigger off an avalanche of other actions as it takes place.

An action is only an attempt to do something: it may not succeed. Firstly, a before rule might interfere, as we have seen already. Secondly, the action might not even be very sensible. The parser will happily generate the action Eat iron_girder if the player asked to do so in good English. In this case, even if no before rule interferes, the normal game rules will ensure that the girder is not consumed.

Actions can also be generated by your own code, and this perfectly simulates the effect of a player typing something. For example, generating a Look action makes the game produce a room description as if the player had typed “look”. More subtly, suppose the air in the Pepper Room causes the player to sneeze each turn and drop something at random. This could be programmed directly, with objects being moved onto the floor by explicit move statements. But then suppose the game also contains a toffee apple, which sticks to the player's hands. Suddenly the toffee apple problem has an unintended solution. So rather than moving the objects directly to the floor, the game should generate Drop actions, allowing the game's rules to be applied. The result might read:

You sneeze convulsively, and lose your grip on the toffee apple…
The toffee apple sticks to your hand!

which is at least consistent.

As an example of causing actions, an odorous low_mist will soon settle over ‘Ruins’. It will have the description “The mist carries an aroma reminisicent of tortilla.” The alert player who reads this will immediately type “smell mist”, and we want to provide a better response than the game's stock reply “You smell nothing unexpected.” An economical way of doing this is to somehow deflect the action Smell low_mist into the action Examine low_mist instead, so that the “aroma of tortilla” message is printed in this case too. Here is a suitable before rule to do that:

Smell: <Examine self>; rtrue;

The statement <Examine self> causes the action Examine low_mist to be triggered off immediately, after which whatever was going on at the time resumes. In this case, the action Smell low_mist resumes, but since we immediately return true the action is stopped dead.

Causing an action and then returning true is so useful that it has an abbreviation, putting the action in double angle-brackets. For example, the following could be added to ‘Ruins’ if the designer wanted to make the stone-cut steps more enticing:

before [;
    Search: <<Enter self>>;
],

If a player types “search steps”, the parser will produce the action Search steps and this rule will come into play: it will generate the action Enter steps instead, and return true to stop the original Search action from going any further. The net effect is that one action has been diverted into another.

At any given time, just one action is under way, though others may be waiting to resume when the current one has finished. The current action is always stored in the four variables

actor    action    noun    second

actor, noun and second hold the objects involved, or the special value nothing if they aren't involved at all. (There's always an actor, and for the time being it will always be equal to player.) action holds the kind of action. Its possible values can be referred to in the program using the ## notation: for example

if (action == ##Look) ...

tests to see if the current action is a Look.

Why have ## at all, why not just write Look? Partly because this way the reader of the source code can see at a glance that an action type is being referred to, but also because the name might be used for something else. For instance there's a variable called score (holding the current game score), quite different from the action type ##Score.

▲▲ For a few actions, the ‘noun’ (or the ‘second noun’) is actually a number (for instance, “set timer to 20” would probably end up with noun being timer and second being 20). Occasionally one needs to be sure of the difference, e.g., to tell if second is holding a number or an object. It's then useful to know that there are two more primitive variables, inp1 and inp2, parallel to noun and second and usually equal to them – but equal to 1 to indicate “some numerical value, not an object”.

The library supports about 120 different actions and most large games will add some more of their own. The full list, given in Table 6, is initially daunting, but for any given object most of the actions are irrelevant. For instance, if you only want to prevent an object from entering the player's possession, you need only block the Take action, unless the object is initially in something or on something, in which case you need to block Remove as well. In the author's game ‘Curses’, one exceptional object (Austin, the cat) contains rules concerning 15 different actions, but the average is more like two or three action-rules per object.

The list of actions is divided into three groups, called Group 1, Group 2 and Group 3:

  1. Group 1 contains ‘meta’ actions for controlling the game, like Score and Save, which are treated quite differently from other actions as they do not happen in the “model world”.
  2. Actions in group 2 normally do something to change the state of the model world, or else to print important information about it. Take (“pick up”) and Inv (“inventory”) are examples of each. Such actions will affect any object which doesn't block them with a before rule.
  3. Finally, group 3 actions are the ones which normally do nothing but print a polite refusal, like Pull (“it is fixed in place”), or a bland response, like Listen (“you hear nothing unexpected”). Such actions will never affect any object which doesn't positively react with a before rule.

Some of the group 2 actions can be ignored by the programmer because they are really only keyboard shorthands for the player. For example, <Empty rucksack table> means “empty the contents of the rucksack onto the table” and is automatically broken down into a stream of actions like <Remove fish rucksack> and <PutOn fish table>. You needn't write rules concerning Empty, only Remove and PutOn.

▲▲ Most of the library's group 2 actions are able to “run silently”. This means that if the variable keep_silent is set to true, then the actions print nothing in the event of success. The group 2 actions which can't run silently are exactly those ones whose successful operation does nothing but print: Wait, Inv, Look, Examine, Search.

EXERCISE 3
“The door-handle of my room… was different from all other door-handles in the world, inasmuch as it seemed to open of its own accord and without my having to turn it, so unconscious had its manipulation become…” (Marcel Proust). Use silent-running actions to make an unconsciously manipulated door: if the player tries to pass through when it's closed, print “(first opening the door)” and do so. (You need to know some of §13, the section on doors, to answer this.)

▲▲ EXERCISE 4
Now add “(first unlocking the door with…)”, automatically trying to unlock it using either a key already known to work, or failing that, any key carried by the player which hasn't been tried in the lock before.

Some actions happen even though they don't arise directly from anything the player has typed. For instance, an action called ThrownAt is listed under group 3 in Table 6. It's a side-effect of the ordinary ThrowAt action: if the player types “throw rock at dalek”, the parser generates the action ThrowAt rock dalek. As usual the rock is sent a before message asking if it objects to being thrown at a Dalek. Since the Dalek may also have an opinion on the matter, another before message is sent to the Dalek, but this time with the action ThrownAt. A dartboard can thus distinguish between being thrown, and having things thrown at it:

before [;
    ThrowAt: "Haven't you got that the wrong way round?";
    ThrownAt:
        if (noun==dart) {
            move dart to self;
            if (random(31)==1)
                print (string) random("Outer bull", "Bullseye");
            else {
                print (string) random("Single", "Double", "Triple");
                print " ", (number) random(20);
            }
            "!";
        }
        move noun to location;
        print_ret (The) noun, " bounces back off the board.";
],

Such an imaginary action – usually, as in this case, a perfectly sensible action seen from the point of view of the second object involved, rather than the first – is sometimes called a “fake action”. Two things about it are fake: there's no grammar that produces ThrownAt, and there's no routine called ThrownAtSub. The important fake actions are ThrownAt, Receive and LetGo, the latter two being used for containers: see §12.

If you really need to, you can declare a new fake action with the directive Fake_actionAction-name;. You can then cause this action with < and > as usual.

▲▲ EXERCISE 5
ThrownAt would be unnecessary if Inform had an idea of before and after routines which an object could provide if it were the second noun of an action. How might this be implemented?

▲▲ Very occasionally, in the darker recesses of §18 for instance, you want “fake fake actions”, actions which are only halfway faked in that they still have action routines. Actually, these are perfectly genuine actions, but with the parser's grammar jinxed so that they can never be produced whatever the player types.

The standard stock of actions is easily added to. Two things are necessary to create a new action: first one must provide a routine to make it happen. For instance:

[ BlorpleSub;
  "You speak the magic word ~Blorple~. Nothing happens.";
];

Every action has to have a “subroutine” like this, the name of which is always the name of the action with Sub appended. Secondly, one must add grammar so that Blorple can actually be called for. Far more about grammar in Chapter IV: for now we add the simplest of all grammar lines, a directive

Verb 'blorple' * -> Blorple;

placed after the inclusion of the Grammar file. The word “blorple” can now be used as a verb. It can't take any nouns, so the parser will complain if the player types “blorple daisy”.

Blorple is now a typical Group 3 action. before rules can be written for it, and it can be triggered off by a statement like

<Blorple>;

The unusual action in ‘Ruins’, Photograph, needs to be a Group 2 action, since it actually does something, and objects need to be able to react with after rules. (Indeed, the definition of the Treasure class in the previous section contains just such an after rule.) A photographer needs a camera:

Object -> -> camera "wet-plate camera"
  with name 'wet-plate' 'plate' 'wet' 'camera',
       description
          "A cumbersome, sturdy, stubborn wooden-framed wet plate
          model: like all archaeologists, you have a love-hate
          relationship with your camera.";

(This is going to be inside a packing case which is inside the Forest, hence the two arrows ->.) And now the action subroutine. The sodium lamp referred to will be constructed in §14.

[ PhotographSub;
  if (camera notin player) "Not without the use of your camera.";
  if (noun == player) "Best not. You haven't shaved since Mexico.";
  if (children(player) > 1)
     "Photography is a cumbersome business, needing the use of both
      hands. You'll have to put everything else down.";
  if (location == Forest) "In this rain-soaked forest, best not.";
  if (location == thedark) "It is far too dark.";
  if (AfterRoutines()) return;
 "You set up the elephantine, large-format, wet-plate camera, adjust
  the sodium lamp and make a patient exposure of ", (the) noun, ".";
];

What makes this a Group 2 action is that, if the action successfully takes place, then the library routine AfterRoutines is called. This routine takes care of all the standard rules to do with after (see below), and returns true if any object involved has dealt with the action and printed something already. (Failing that, the message “You set up…” will be printed.) Finally, some grammar for the parser:

Verb 'photograph' * noun -> Photograph;

This matches input like “photograph statuette”, because the grammar token noun tells the parser to expect the name of a visible object. See §30 and §31 for much more on grammar.

To make a Group 1 action, define the verb as meta (see §30).

Actions are processed in a simple way, but one which involves many little stages. There are three main stages:

  1. ‘Before’, for group 2 and 3 actions. An opportunity for your code to interfere with or block altogether what might soon happen.
  2. ‘During’, for all actions. The library takes control and decides if the action makes sense according to its normal world model: for example, only an edible object may be eaten; only an object in the player's possession can be thrown at somebody, and so on. If the action is impossible, a complaint is printed and that's all. Otherwise the action is now carried out.
  3. ‘After’, for group 2 actions. An opportunity for your code to react to what has happened, after it has happened but before any text announcing it has been printed. If it chooses, your code can print and cause an entirely different outcome. If your code doesn't interfere, the library reports back to the player (with such choice phrases as “Dropped.”).

Group 1 actions, like Score, have no ‘Before’ or ‘After’ stages: you can't (easily) stop them from taking place. They aren't happening in the game's world, but in the player's.

The ‘Before’ stage consults your code in five ways, and occasionally it's useful to know in what order:

The library processes the ‘During’ stage by calling the action's subroutine: for instance, by calling TakeSub.

The ‘After’ stage only applies to Group 2 actions, as all Group 3 actions have been wound up with a complaint or a bland response at the ‘During’ stage. During ‘After’ the sequence is as follows: (3a) react_after rules for every object in scope (including the player object); (3b) the room's after; (3c) the first noun's after and (3d) finally GamePostRoutine.

▲▲ To some extent you can even meddle with the ‘During’ stage, and thus even interfere with Group 1 actions, by unscrupulous use of the LibraryMessages system. See §25.

As mentioned above, the parser can generate decidedly odd actions, such as Insert camel eye_of_needle. The parser's policy is to allow any action which the player has clearly asked for at the keyboard, and it never uses knowledge about the current game position except to resolve ambiguities. For instance, “take house” in the presence of the Sydney Opera House and also a souvenir model of the same will be resolved in favour of the model. But if there is no model to cloud the issue, the parser will cheerfully generate Take Sydney_Opera_House.

Actions are only checked for sensibleness after the before stage. In many ways this is a good thing, because in adventure games the very unlikely is sometimes correct. But sometimes it needs to be remembered when writing before rules. Suppose a before rule intercepts the action of putting the mushroom in the crate, and exciting things happen as a result. Now even if the mushroom is, say, sealed up inside a glass jar, the parser will still generate the action Insert mushroom crate, and the before rule will still cut in, because the impossibility of the action hasn't yet been realised.

The upshot of this is that the exciting happening should be written not as a before but as an after rule, when it's known that the attempt to put the mushroom in the crate has already succeeded.

That's fine if it's a Group 2 action you're working with. But consider the following scenario: a siren has a cord which needs to be pulled to sound the alarm. But the siren can be behind glass, and is on the other side of a barred cage in which the player is imprisoned. You need to write a rule for Pull cord, but you can't place this among the cord's after rules because Pull is a group 3 action and there isn't any “after”: so it has to be a before rule. Probably it's best to write your own code by hand to check that the cord is reachable. But an alternative is to call the library's routine:

ObjectIsUntouchable(item, silent_flag, take_flag)

This determines whether or not the player can touch item, returning true if there is some obstruction. If silent_flag is true, or if there's no obstruction anyway, nothing will be printed. Otherwise a suitable message will be printed up, such as “The barred cage isn't open.” So a safe way to write the cord's before rule would be:

before [;
    Pull: if (ObjectIsUntouchable(self)) rtrue;
          "~Vwoorp! Vwoorp!~";
],

ObjectIsUntouchable can also be a convenience when writing action subroutines for new actions of your own.

▲▲ If you set take_flag, then a further restriction will be imposed: the item must not belong to something or someone already: specifically, it must not be in the possession of an animate or a transparent object that isn't a container or supporter. For instance, the off button on a television set can certainly be touched, but if take_flag is true, then ObjectIsUntouchable will print up “That seems to be a part of the television set.” and return true to report an obstacle.

REFERENCES
In a game compiled with the -D for “Debugging” switch set, the “actions” verb will result in trace information being printed each time any action is generated. Try putting many things into a rucksack and asking to “empty” it for an extravagant list.   Diverted actions (using << and >>) are commonplace. They're used in about 20 places in ‘Advent’: a good example is the way “take water” is translated into a Fill bottle action.   L. Ross Raszewski's library extension "yesno.h" makes an interesting use of react_before to handle semi-rhetorical questions. For instance, suppose the player types “eat whale”, an absurd command to which the game replies “You can fit a blue whale in your mouth?” Should the player take this as a literal question and type “yes”, the designer might want to be able to reply “Oh. I should never have let you go through all those doors.” How might this be done? The trick is that, when the game's first reply is made, an invisible object is moved into play which does nothing except to react to a Yes action by making the second reply.