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:
Score
and Save
, which are
treated quite differently from other actions as they do not
happen in the “model world”.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.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_action
‹Action-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:
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.▲
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:
GamePreRoutine
is called, if you have written
one. If it returns true
, nothing else happens and the
action is stopped.orders
property of the player is called
on the same terms. For more details, see §18.react_before
of every object in scope,
which roughly means ‘in the vicinity’. For more details,
see §32.before
of the current room.before
is called on the same terms.▲
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.