Section 9: Actions and reactions

Contents Back Forward | ### 9. 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 ...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`, `Take` and `Insert`. An action has 0, 1 or 2 objects supplied with it (or, in a few special cases, some numerical information rather than objects). Most actions are triggered off by the game's parser: in fact, the parser's job can be summed up as reducing the player's keyboard commands to actions. Sometimes one action causes another; a really complicated keyboard command ("empty the sack into the umbrella stand'') may fire off quite a sequence of actions. 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. 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 a rich aroma of broth.'' 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 broth'' 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` (i.e., causing a new action and killing the old one) is so useful that it has an abbreviation, putting the action in double angle-brackets. For example,
             <Look>; <<ThrowAt smooth_stone spider>>;
will behave as if the player has asked to look around and to throw the stone at the spider, and will then return `true`. At any given time, just one action is under way (though others may be waiting to resume when the current one has finished). This current action is stored in the three variables
             action      noun      second
`noun` and `second` hold the objects involved, or the special value `nothing` if they aren't involved at all. `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 can see at a glance that an action type is being referred to, but also because the name might be wanted 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 further 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 any game of serious proportion will add some more of its own. This list is initially daunting but many are used only rarely and others are always knocked down into simpler actions (for example, `<Empty rucksack table>`, meaning "empty the contents of the rucksack onto the table'', is broken down into a stream of actions like `<Remove fish rucksack>` and `<PutOn fish table>`). It's useful to know that an object can only enter the player's possession through a `Take` or `Remove` action: block those two and it can never be acquired whatever the player types. The list of actions is traditionally divided into three groups, called Group 1, Group 2 and Group 3. Group 1 contains 'meta' actions for controlling the game, like `Score` and `Save`, which are treated quite differently from other actions and are not worth listing. Of the rest, actions which normally do something form Group 2, while actions which normally only print a polite refusal form Group 3. Group 2 contains:
    Inv, Take, Drop, Remove, PutOn, Insert, Enter, Exit, Go, Look, Examine,
    Unlock, Lock, SwitchOn, SwitchOff, Open, Close, Disrobe, Wear, Eat, Search.
It should be apparent why these do something. However, an action like `Listen` falls into Group 3: the library would normally respond to it by printing "You hear nothing unexpected.'' Only if your program interferes (using a `before` rule) can anything happen. Group 3 contains, in rough order of usefulness:
    Pull, Push, PushDir [push object in direction], Turn,
    Consult, LookUnder [look underneath something], Search,
    Listen, Taste, Drink, Touch, Smell,
    Wait, Sing, Jump [jump on the spot], JumpOver, Attack,
    Swing [something], Blow, Rub, Set, SetTo, Wave [something],
    Burn, Dig, Cut, Tie, Fill, Swim, Climb, Buy, Squeeze,
    Pray, Think, Sleep, Wake, WaveHands [i.e., just "wave"],
    WakeOther [person], Kiss, Answer, Ask, ThrowAt,
    Yes, No, Sorry, Strong [swear word], Mild [swear word]
| Actions involving other people, like `Kiss`, are often best dealt with by a `life` rule, which will be discussed in Section 16.
| A few actions (e.g., `Transfer`, `Empty`, `GetOff`) are omitted from the list above because they're always translated into more familiar ones. For instance, `InvWide` (asking for a "wide--format'' inventory listing) always ends up in an `Inv`.
| The `Search` action (generated by "look inside <container>'' or "search <something>'') only ever prints text, but is in Group 2 rather than Group 3 because it does something substantial. It decides whether something is a container, and if there's enough light to see by, it prints out the contents. Thus, a `before` rule applied to `Search` traps the searching of random scenery, while an `after` can be used to alter the contents-listing rules of containers.
| Most of the group 2 actions -- specifically,
    Take, Drop, Insert, PutOn, Remove, Enter, Exit, Go, Unlock, Lock,
    SwitchOn, SwitchOff, Open, Close, Wear, Disrobe, Eat
can happen "silently''. If the variable `keep_silent` is set to 1, then these actions print nothing in the event of success. (E.g., if the door was unlocked as requested.) They print up objections as usual if anything goes wrong (e.g., if the suggested key doesn't fit). This is useful to implement implicit actions: for instance, to code a door which will be automatically unlocked by a player asking to go through it, who is holding the right key.
| 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 V: for now we add the simplest of all grammar lines, a directive
    Verb "blorple" *                            -> Blorple;
placed after the inclusion of the `Grammar` file. (The spacing around the `*` is just a matter of convention.) 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>;
| To make it a Group 1 action, define the verb as `meta` (see Section 26).
| To make it a Group 2 action, rewrite the subroutine in the following form:
    [ WhateverSub;
      ... do whatever the action is supposed to do,
          printing a suitable message and returning
          if it turns out not to be a sensible thing to do...
      if (AfterRoutines()==1) rtrue;
      ... print a suitable message saying that it has been done ...
    ];
(`AfterRoutines` is a library routine which sends suitable `after` messages to see if the objects want to prevent the usual message being printed.)
| A few of the library's actions fall into none of Groups 1, 2 or 3, though these aren't proper actions at all, but are used only to signal goings-on. For instance, when 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 as if the action were something called `ThrownAt`. For example, here is a dartboard's response to a dart:
    before
    [;  ThrownAt: if (noun==dart)
                  {   move dart to self; "Triple 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 called a "fake action''. The important ones are `ThrownAt`, `Receive` and `LetGo` (the latter two being used for containers: see Section 11).
| If you really need to, you can declare a new fake action with the directive `Fake_action` <Action-name>`;`.
| EXERCISE 3: (link to the answer)
| `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?
| Actions are processed in a simple way, but one which involves many little stages. There are three main stages: (a) -- 'Before'. An opportunity for your code to interfere with or block altogether what might soon happen. (b) -- 'During'. 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. (c) -- 'After'. 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:
| i. -- The `GamePreRoutine` is called, if you have written one. If it returns 'true', nothing else happens and the action is stopped. ii. -- The `orders` property of the player is called on the same terms. For more details, see Section 16. iii. -- And the `react_before` of every object in scope (which roughly means 'in the vicinity'). iv. -- And the `before` of the current room. v. -- If the action has a first noun, its `before` is called on the same terms.
| The library processes the 'During' stage by calling the action's subroutine. (Subroutines like `TakeSub` make up a large part of the library.)
| The 'After' stage only applies to Group 2 actions, as all Group 3 actions have been packed up at the 'During' stage if not 'Before'. During 'After' the sequence is as follows: `react_after` rules for every object in scope (including the player object); the room's `after`; the first noun's `after` and finally `GamePostRoutine`.
| Two things are fake about "fake actions'' (see above): they don't have subroutines, and they never occur in the grammar of any verb (so they're never directly generated by the parser).
| As mentioned above, the parser can generate very peculiar actions, and this sometimes needs to be remembered when writing `before` rules. Suppose a `before` rule intercepts the action of putting the mushroom in the crate, and makes something exciting happen as a result. Now even if the mushroom is, say, sealed up inside a glass jar, the parser might still generate this action: the impossibility won't be realised until 'During' time. So the exciting happening should be written as an `after` rule, when the attempt to put the mushroom in the crate has already succeeded.
| EXERCISE 4: (link to the answer)
| This kind of snag could be avoided altogether if Inform had a 'validation stage' in action processing, to check whether an action is sensible before allowing it to get as far as `before` rules. How could this be added to Inform?
| To some extent you can even meddle with the 'During' stage (and with the final messages produced), and thus even interfere with Group 1 actions if you are unscrupulous enough, by cunning use of the `LibraryMessages` system. See Section 21.
| REFERENCES: In a game compiled with the `-D` switch set, typing in 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. Sometimes you want "fake fake actions'' which are fully--fledged actions (with action routines and so on) but are still never generated by the parser (see Section 16).

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.