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.