Life's but a walking shadow, a poor player
That struts and frets his hour upon the stage
And then is heard no more; it is a tale
Told by an idiot, full of sound and fury,
Signifying nothing.
— William Shakespeare (1564–1616), Macbeth V v
To recap on §4, an “entry point
routine” is one provided by your own source code which the
library may call from time to time. There are about twenty of these,
listed in §A5, and all of them are
optional but one: Initialise
. This routine is called
before any text of the game is printed, and it can do many things:
start timers and daemons, set the time of day, equip the player with
possessions, make any random settings needed and so on. It usually
prints out some welcoming text, though not the name and author of
the game, because that appears soon after when the “game
banner” is printed. The only thing it must do is to set the
location
variable to where the player begins.
This is usually a room, possibly in darkness, but
might instead be an enterable
object inside a room,
such as a chair or a bed. Like medieval romance epics, interactive
fiction games often start by waking the player from sleep, sometimes by
way of a dream sequence. If your game begins with verbose instructions
before the first opportunity for a player to type a command, you may
want to offer the chance to restore a saved game at once:
print "Would you like to restore a game? >"; if (YesOrNo()) <Restore>;
To equip the player with possessions, simply move
the relevant objects to player
.
The return value from Initialise
is
ordinarily ignored, whether true
or false
,
and the library goes on to print the game banner. If, however, you
return 2, the game banner is suppressed for now. This feature is
provided for games like ‘Sorcerer’ and ‘Seastalker’
which play out a short prelude first. If you do suppress the banner
from Initialise
, you should print it no more than a
few turns later on by calling the library routine Banner
.
The banner is familiar to players, reassuringly traditional and
useful when testing, because it identifies which version of a game
is giving trouble. Like an imprint page with an ISBN, it is invaluable
to bibliographers and collectors of story files.
· · · · ·
‘Ruins’ opens in classical fashion:
[ Initialise; TitlePage(); location = Forest; move map to player; move sodium_lamp to player; move dictionary to player; thedark.description = "The darkness of ages presses in on you, and you feel claustrophobic."; "^^^Days of searching, days of thirsty hacking through the briars of the forest, but at last your patience was rewarded. A discovery!^"; ];
For the source code of the ‘Ruins’
TitlePage
routine, see the exercises in
§42.
· · · · ·
The question “where is the player?”
can be answered in three different ways. Looking at parent(player)
tells you the object immediately containing the player, which can
be a location but might instead be a chair or vehicle. So a condition
such as:
if (player in Bridleway) ...
would be false
if the player were riding
a horse through the Bridleway. The safer alternative is:
if (location == Bridleway) ...
but even this would be false
if the
Bridleway were in darkness, because then location
would
be the special object thedark
(see §19).
The definitive location value is stored in real_location
,
so that:
if (real_location == Bridleway) ...
works in all cases. The condition for “is the player in a dark Bridleway?” is:
if (location == thedark && real_location == Bridleway) ...
Except for the one time in Initialise
,
you should not attempt to change either of these variables, nor to move
the player-object by hand. One safe way to move the player in your
own source code is to cause actions like
<Go n_obj>;
but for moments of teleportation it's easier to
use the library routine PlayerTo
. Calling PlayerTo(somewhere)
makes the parent-object of the player somewhere
and
adjusts the location
variables accordingly: it also runs
through a fair number of standard game rules, for instance checking
the light level and performing a Look
action to print
out the new room description. The value somewhere
can
be a room, or an enterable
object such as a cage or
a traction-engine, provided that the cardinal rule is always observed:
The parent of the player
object must at all times be “location-like”. An object
is “location-like” if either it is a location, or it has
enterable
and its parent is location-like.
In other words, you can't put the player in an
enterable
cardboard box if that box is itself shut up in
a free-standing safe which isn't enterable
. And you
can't PlayerTo(nothing)
or PlayerTo(thedark)
because nothing
is not an object and thedark
is not location-like.
▲
Calling PlayerTo(somewhere,1)
moves the player without
printing any room description. All other standard game rules are applied.
▲
Calling PlayerTo(somewhere,2)
is just like PlayerTo(somewhere)
except that the room description is in the form the player would expect
from typing “go east” rather than from typing “look”.
The only difference is that in the former case the room is (normally)
given an abbreviated description if it has been visited before, whereas
in the latter case the description is always given in full.
· · · · ·
▲▲
It's perhaps worth taking a moment to say what the standard rules upon
changing location are. The following rules are applied whenever a Look
action or a call to PlayerTo
take place.
PlayerTo
has been called then the parent
of the player, location
and real_location
are set.found_in
is checked. If it
claims to be found_in
the location, it is moved to that
location. If not, or if it has absent
, it is removed
from the object tree. (See §8.)location
is set to thedark
if necessary.location
,
i.e., either thedark
or the room object.thedark
or (say) a box, skip
this rule. Otherwise: if the VC has changed since the
previous time that rule (3) produced a VC which wasn't
an enterable
object, then:
location.initial()
is sent, if
the location provides an initial
rule. If the library finds that the
player has been moved in the course of running initial
, it
goes back to rule (3).NewRoom
is
called, if it provides one.PlayerTo(somewhere,1)
. For exactly
what happens in printing a room description, see §26.location
doesn't have visited
,
give it this attribute and award the player ROOM_SCORE
points if the location
has scored
.
(See §22.) Note that this rule looks
at location
, not real_location
, so no points
unless the room is visible.· · · · ·
In the course of this chapter, rules to interfere
with actions have been attached to items, rooms and people, but not
yet to the player. In §18 it was set out
that an order like “austin, eat tuna” would result in the
action Eat tuna
being passed to austin.orders
,
and heavy hints were dropped that orders and actions are more or
less the same thing. This is indeed so, and the player's own object
has an orders
routine. This normally does nothing and
always returns false
to mean “carry on as usual”,
but you can install a rule of your own instead:
player.orders = MyNewRule;
where MyNewRule
is a new orders
rule. This rule is applied to every action or order issued by the player.
The variable actor holds the person asked to do something, usually
but not always player
, and the variables action
,
noun
and second
are set up as usual. For
instance:
Example command | actor |
action |
noun |
second |
“put tuna in dish” | player |
Insert |
tuna |
dish |
“austin, eat tuna” | Austin |
Eat |
tuna |
nothing |
For instance, if a cannon goes off right next to the player, a period of partial deafness might ensue:
[ MyNewRule; if (actor ~= player) rfalse; Listen: "Your hearing is still weak from all that cannon-fire."; default: rfalse; ];
The if
statement needs to be there
to prevent commands like “helena, listen” from being
ruled out – after all, the player can still speak.
•▲
EXERCISE 51
Why not achieve the same effect by giving the player a react_before
rule instead?
•
EXERCISE 52
(Cf. ‘Curses’.) Write an orders
routine for
the player so that wearing a gas mask will prevent speech.
· · · · ·
The player
object can not only be
altered but switched altogether, allowing the player to play from
the perspective of someone or something else at any point in the
game. The player who tampers with Dr Frankenstein's brain transference
machine may suddenly become the Monster strapped to the table. A
player who drinks too much wine could become a drunk player object
to whom many different rules apply. The “snavig” spell
of ‘Spellbreaker’, which transforms the player to an
animal like the one cast upon, could be implemented thus. Similarly
the protagonist of ‘Suspended’, who telepathically runs
a weather-control station by acting through six sensory robots,
Iris, Waldo, Sensa, Auda, Poet and Whiz. In a less original setting,
a player might have a team of four adventurers exploring a labyrinth,
and be able to switch the one being controlled by typing the name.
In this case, an AfterLife
routine (see below) may be
needed to switch the focus back to a still-living member of the team
after one has met a sticky end.
The library routine ChangePlayer(obj)
transforms
the player to obj
. Any object can be used for this. There's
no need to give it any name, as the parser always understands pronouns
like “me” and “myself” to refer to the current
player-object. You may want to set its description, as this is the text
printed if the player types “examine myself”, or its
capacity
, the maximum number of items which this form
of the player can carry. Finally, this player-object can have its
own orders
property and thus its own rules about what
it can and can't do.
As ChangePlayer
prints nothing, you
may want to follow the call with a <<Look>>;
action.
▲
You can call ChangePlayer
as part of a game's Initialise
routine, but if so then you should do this before setting location
.
▲
Calling ChangePlayer(obj,1);
does the same except that
it makes the game print “(as Whoever)” during subsequent
room descriptions.
▲
The body dispossessed remains where it was, in play, unless you move
it away or otherwise dispose of it. The player-object which the player
begins with is a library-defined object called selfobj
, and is
described in room descriptions as “your former self”.
•
EXERCISE 53
In Central American legend, a sorceror can transform himself into a
nagual, a familiar such as a spider-monkey; indeed, each individual
has an animal self or wayhel, living in a volcanic land over
which the king, as a jaguar, rules. Turn the player into wayhel
form.
•
EXERCISE 54
Alter the Wormcast of ‘Ruins’ (previously defined in
§9) so that when in wayhel form,
the player can pass through into a hidden burial shaft.
•
EXERCISE 55
To complete the design of this sequence from ‘Ruins’,
place a visible iron cage above the hidden burial shaft. The cage
contains skeletons and a warning written in glyphs, but the player
who enters it despite these (and they all will) passes into wayhel
life. (The transformed body is unable to move the sodium lamp, but
has nocturnal vision, so doesn't need to.) Unfortunately the player
is now outside a cage which has closed around the human self which
must be returned to, while the wayhel lacks the dexterity
to open the cage. The solution is to use the Wormcast to reach the
Burial Chamber, then bring its earthen roof down, opening a connection
between the chamber below and the cage above. Recovering human form,
the player can take the grave goods, climb up into the cage, open it
from the inside and escape. Lara Croft would be proud.
· · · · ·
▲▲
The situation becomes a little complicated if the same orders
routine has to do service in two situations: once while its owner is a
character met in the course of play, and then a second time when the
player has changed into it. This could be done simply by changing the
value of orders
when the transformation takes place,
but an alternative is to arrange code for a single orders
routine like so:
orders [; if (player == self) { if (actor == self) { ! I give myself an action } else { ! I give someone else an order } } else { ! Someone else gives me an order } ],
•▲▲
EXERCISE 56
Write an orders
routine for a Giant with a conscience,
who will refuse to attack even a mouse, but so that a player who
becomes the Giant can be wantonly cruel.
· · · · ·
“There are only three events in a man's life;
birth, life and death; he is not conscious of being born, he dies in
pain and he forgets to live.” (Jean de la Bruyère again.)
Death is indeed the usual conclusion of an adventure game, and occurs
when the source code sets the library variable deadflag
to true
: in normal play deadflag
is always
false
. The “standard Inform rules” never
lead to the player's death, so this is something the designer must
explicitly do.
Unlike life, however, interactive fiction offers
another way out: the player can win. This happens if and when the
variable deadflag
is set to 2.
Any higher values of deadflag
are
considered to be more exotic ways the game can end, requiring text
beyond “You have died” or “You have won”.
The Inform library doesn't know what this text should be, so it
calls the DeathMessage
entry point routine, which
is expected to look at deadflag
and can then print
something suitable. For instance, ‘Ruins’ has a chasm
subject to the following before
rule:
before [; Enter: deadflag = 3; "You plummet through the silent void of darkness, cracking your skull against an outcrop of rock. Amid the pain and redness, you dimly make out the God with the Owl-Headdress..."; JumpOver: "It is far too wide."; ],
and this means that it needs a DeathMessage
routine like so:
[ DeathMessage; if (deadflag == 3) print "You have been captured"; ];
Capture was about the worst fate that could befall you in the unspeakably inhumane world of Maya strife.
‘Ruins’ doesn't, but many games allow
reincarnation or, as David M. Baggett points out, in fact resurrection.
You too can allow this, by providing an AfterLife
entry
point routine. This gets the chance to do as it pleases before
any “death message” is printed, and it can even reset
deadflag
to false
, causing the game to
resume as though nothing had happened. Such AfterLife
routines can be tricky to write, though, because the game often has
to be altered to reflect what has happened.
•
REFERENCES
The magic words “xyzzy” and “plugh” in
‘Advent’ employ PlayerTo
.
•‘Advent’ has an
amusing AfterLife
routine: for instance, try collapsing
the bridge by leading the bear across, then returning to the scene
after resurrection. ‘Balances’ has one which only
slightly penalises death.