Add objects to your barren Inform adventure rooms.
Thanks to
[Hack #85]
, we have
two
rooms, but they’re both empty.
Let’s create our first inhabitant: that fierce-eyed
access controller in the Router.
Add this object to your source file. You can put it anywhere between
the Include "VerbLib"; and Include "Grammar"; lines, but it’s tidier to put
it right after the Router object definition.
Object controller "access controller" Router,
with
name 'access' 'controller',
article "an",
description
"The controller guards its network connection with
fierce and watchful intent.",
has scenery;This is the controller object.
It’s similar to the Router and
Scanner objects, but with a couple of differences.
The first line defines an Object called
controller, printed in the game as access controller, but there’s a fourth element,
Router, that indicates where the object is.
It’s not a free-floating thing, as the rooms are. It
begins the game inside the Router (in fact, it
never moves from there).
The has line, once again, introduces a list of
attributes. It’s down at the bottom this time.
(That’s a matter of taste; you can put the
has attributes before or after the
with property declarations.) The only attribute
here is scenery. This fixes the controller in
place; the player can’t pick it up or move it.
Scenery objects must also be mentioned in the
room’s description property. The
standard library won’t mention it. The controller
does appear in the Router’s
description, so this is just what you want.
The controller has three properties.
You’re already familiar with
description. It works a little differently for
objects in a room, though. The description of an
object is what the player sees when he examines it.
The article is simply the indefinite article used
when the library refers to something. By default this is
a, but the
controller’s printable name is
access
controller. You need to
specify an so that it comes out as
an
access
controller.
Why can’t the standard library figure this out
automatically by checking for names that start with a vowel? In fact,
it tries to. However, for muddy technical reasons, not all
interpreters support automatic vowel detection. For even muddier
technical reasons, the library can’t always detect
whether the interpreter can support it. Besides, the vowel rule
isn’t perfect, as you’ll see if you
compare an umbrella to a ukulele. The upshot is that
it’s not reliable. Add the article "an" property to objects that need it.
The other new property is name. The
controller’s name is a list of two words. These are
the various words the player can use to refer to
this object. Notice that these words are delimited by single quotes.
This is a key distinction in Inform. Words are different from
strings. Words are single-quoted, always lowercase, never contain
spaces, and match against player input. Strings are double-quoted,
can contain whitespace and capital letters, and represent printable
game text. The controller’s printed name is the
double-quoted string
access
controller. Its name property
contains the single-quoted words
access and controller.
We’ll delve more deeply into this later. For now,
use single quotes in the name property and double
quotes everywhere else.
Compile this, run it, and watch it go:
INSIDE HACK
An Inform sample adventure.
Release 1 / Serial number 040305 / Inform v6.30 Library 6/11 SD
Router NP-462
This little data interchange is run-down, shabby, and rather sad. You can't
see any traffic -- besides yourself -- and cobwebs of noisy static hang in
the dusty corners. The meat-side that this router serves must be a bare
hallway, almost no hardware.
A broad network connection leads east, and a serial line runs south. The
network connection is barred by a fierce-eyed access controller.
>examine controller
The controller guards its network connection with fierce and watchful intent.
>examine access controller
The controller guards its network connection with fierce and watchful intent.
>examine access
The controller guards its network connection with fierce and watchful intent.
>take controller
That's hardly portable.
>When you type examine
controller, the game recognizes the first word
as an action in the standard library. It checks the following word
against the objects in the room and recognizes
controller as part of the
controller object’s
name. This is a match, so the library performs the
action, executing the controller’s
description property. This is a printable string,
so it prints. That’s the command.
Typing examine
access
controller works just as well, as does examine
access. The parser accepts any combination of
words from the name property. The order
doesn’t matter; examine
controller
access also works.
Finally, you can try take
controller. The game recognizes the command,
but it fails because of the scenery attribute.
A bit of set decoration always fleshes out a room (not that flesh exists in an electronic world). Let’s add those cobwebs of static that should be hanging around.
Object cobwebs "cobwebs" Router,
with
name 'cobweb' 'cobwebs' 'noisy' 'noise' 'of' 'static',
description
"Cobweb traces of line noise glitter and hiss in the
high corners of the router. This place can't have been
degaussed for days.",
has scenery pluralname;Again, this lives in the Router. It has the
scenery attribute, because it’s
mentioned in the room description and you don’t want
it to move. It also has an attribute pluralname.
This tells the library that the cobwebs are a
bunch of things so that library messages can say The cobwebs are... instead of The
cobwebs is....
The name property is crowded. Why include so many
words? You’re trying to anticipate any phrase the
player might use to refer to the cobwebs. The room
description says cobwebs of noisy static, so the
player might very well type examine cobwebs of noisy static. Your word list will match that. (Remember that it
doesn’t care about word order.) So does
examine noise and examine static. Many players will home in on the word
cobwebs and use that, or just
cobweb, if a player thinks she can take just one.
(She can’t, but you should still recognize the
attempt.)
Let’s now consider a transcript from a particularly determined player:
>examine cobwebs of noisy static Cobweb traces of line noise glitter and hiss in the high corners of the router. This place can't have been degaussed for days. >take cobwebs They're hardly portable. >pull cobwebs You are unable to. >taste cobweb You taste nothing unexpected. >feel noisy static You feel nothing unexpected.
Except for the first, these are all standard library messages. They’re bland, deliberately so, because they’re defaults. Inform naturally allows you to override any of these messages, but take care. If you add a different interesting and interactive response for every single player action, it draws the player into interacting with the cobwebs. Depending on the scene, this might be desirable. It isn’t, not in this scene. The cobwebs are solely for atmosphere; they play no part in the game’s events. You want the player to note them and pass on, perhaps to the more important access controller.
How can you smoothly let the player know that the cobwebs probably
aren’t important? One nice trick is to put them out
of reach. That is, override all actions (except
examine) with a message indicating that the player
can’t touch them. They are, after all, in the high
corners of the room.
Change the object definition to this:
Object cobwebs "cobwebs" Router,
with
name 'cobweb' 'cobwebs' 'noisy' 'noise' 'of' 'static',
description
"Cobweb traces of line noise glitter and hiss in the
high corners of the router. This place can't have been
degaussed for days.",
before [;
Examine:
rfalse;
default:
"The cobwebs hang in the corners above you, out of reach.";
],
has scenery pluralname;You’ve added one new property:
before. This critical Inform property defines how
an object reacts to player commands. The engine checks it before
calling the standard library action. The corresponding
after property takes place after the library
default behavior. Most of the time, you’ll want to
override library defaults, so use before.
The previous properties have contained strings, words, and objects.
This before property is new: it contains a
function. The block between the square brackets is an anonymous
function that the library executes before performing any action on
the cobwebs.
The function takes the form of a list of cases, one for each possible action. The code for each case can do anything; that’s the point of code. The value returned by the function determines what happens next. If the code for a given case returns false (zero), the library continues with its default behavior. If the case returns true (nonzero), the action has completed, and the library does nothing else.
The first case in this before routine is
Examine (which covers examine,
look
at, x,
read, and all other synonyms.) This case simply
does rfalse, which is short for return false. So the library continues with its standard behavior,
executing the object’s
description property—exactly what we want.
The default clause handles every other case, the
same as it would in a switch statement in C or
Java. So Take, Pull,
Taste, and every action except
Examine executes a bare double-quoted string as
code. The bare-string statement is another peculiarity of Inform. It
means “Print this text, followed by a line break,
and then return true.” That’s a
baroque construction, but also convenient, because
that’s just what you want to do. For every action
but Examine, the cobwebs will
print that they’re out of reach and then return
true, ending the action. Only Examine carries
through with its default behavior.
We have two scenery objects, which
aren’t very satisfying to play with.
It’s time for something more mobile. How about a
simple object—some name words, that pesky
article, a description, and no
attributes at all:
Object catalog "eye catalog",
with
name 'eye' 'catalog' 'sphere',
article "an",
description
"The eye catalog is a sphere, packed with templates
of compressed retinal data.";What’s the location? Let’s get the
player to carry this sphere at the beginning of the game;
it’s part of his hacking toolkit. You can add that
at the end of the first line, as with the
controller and cobwebs, but,
instead, let’s take this opportunity to try a new
Inform statement. Leave the catalog locationless,
and instead extend your Initialise function:
[ Initialise;
location = Router;
move catalog to player;
];As I’ve said, Initialise executes
before the player’s first command. The
move statement does just what it says, moving the
catalog to reside in the Player
object. That means the player begins the game already carrying his
tool.
Compile this, and spend a few minutes playing with the eye catalog.
You can examine it, of course. You can touch and smell it, although
the results are generic. You can drop it and pick it up. If you drop
it and type look, you’ll see it
mentioned as a distinct item in the room (because it
doesn’t have the scenery
attribute). You can drop it, walk out of the room, walk back in, and
discover it still present in a Piagetian triumph of object
persistence!
The eye catalog plays a central role in this puzzle scene. It is a sort of skeleton key, a file of partial retinal prints that the player can use to fool a retinal scanner. Tune it to different data sets to pick the retinal lock.
Like everything in this game, the catalog is not a physical object; it is software. Its appearance is a mix of metaphor and shortcut. It would be fitting if the catalog itself, the sphere of data, changed as the player tuned and re-tuned it. Let’s build it to change color. Consider the following changes.
Object catalog "eye catalog",
with
name 'eye' 'catalog' 'sphere',
article "an",
color 0,
colorname [;
switch (self.color) {
0: print "bright gold";
1: print "hazy violet";
2: print "electric azure";
3: print "radiant crimson";
4: print "flickering green";
5: print "glittering orange";
}
],
description [;
print "The eye catalog is a ";
self.colorname( );
print " sphere, packed with templates of compressed retinal data.^";
rtrue;
],
before [;
Squeeze:
self.color++;
if (self.color >= 6)
self.color = 0;
print "You give the catalog a squeeze. The patterns of
retinal data swirl into a chaotic rainbow cascade,
and then resolve into ";
self.colorname( );
".";
];The color and colorname
properties are entirely new. They aren’t defined by
the standard library, the way description,
name, and before are.
They’re new, added solely to support the
color-changing idea.
The catalog’s color is a number
between 0 and 5, representing one of six possible colors. In typical
object-oriented style, you can refer to this value as
catalog.color (or self.color,
from within the catalog). Its initial value is zero, meaning gold.
The colorname property contains a function; it is
a method of this object. The purpose of the
function is to print out the color the catalog currently has.
catalog.colorname() contains a
switch statement that checks the value of the
color property and prints an appropriate string.
Note that the switch statement in Inform has no
implicit fall-through; you don’t need a
break at the end of each case.
We use print statements instead of bare-string
statements. Why? It’s because we
don’t care about the return value, but bare strings
put an implicit line break after the strings they print. You want
catalog.colorname() to print a color without a
line break because you want to use colorname from
inside the catalog’s description
property. We’ve changed this from a simple string to
a function; Inform almost always allows that.
The description prints the same text as before,
but it inserts the sphere’s color by calling
self.colorname(). It ends with a line break,
represented by the caret (^) character, and then
returns true. Like before, a
description function must return true to indicate
that the description is complete. If it returns false or no value,
the library prints the default description: You see nothing special about the catalog.. You don’t want
that, particularly after the evocative mention of compressed retinal
data.
You may note that the description routine ends
with a print statement, which ends with a line
break, and then returns true. Can you condense those into a
bare-string statement? Yes, you can. Well spotted.
Notice that the function doesn’t do this:
description [;
print "The eye catalog is a ";
print self.colorname( ); !! wrong!
print " sphere, packed with templates of compressed retinal data.^";
rtrue;
],The colorname routine prints the
color’s name; it does not return a value to be
printed. Actually, all Inform functions implicitly return a value, so
colorname really returns zero. If you code the
description this way, you receive an unpleasant
result:
>examine catalog The eye catalog is a bright gold0 sphere, packed with templates of compressed retinal data.
The unwanted 0 is the return value of
colorname, printed by the unnecessary second
print statement. Avoid that error by leaving out
the print in favor of the function call.
Finally, you need code to allow the player to retune the catalog. She
can do this by typing squeeze
catalog. (Squeeze is
another action in the standard library. You can also create a new
Tune action specifically for this game.)
To do this, you need a before property to
customize the catalog’s Squeeze
action. Again, a before function is a list of
cases (just one case here). All other actions will fail to match the
Squeeze case; they will skip it and hit the end of
the function, which implicitly returns a false value of 0. The
action’s standard behavior will then proceed.
The code is straightforward; for this sort of work, Inform is very
C-like. Increment the number in the color
property, cycling through the values 0 to 5. Then print a message
describing what’s happened, including the new
color’s name. End with a period and a line break,
and then return true so that the action ends there.
How does the player know she has to squeeze the sphere to change its setting? It’s not an obvious action; the player expects a hacking scene, not a wrist workout. If you don’t guide the player to this action, the scene won’t work.
You can give the player a manual to read. You can also change the catalog’s description so that instructions are written on it, but this subtly violates the mood. A spy or commando operative doesn’t carry manuals; he knows his equipment! Another option would be to add a dial or button to the sphere. Turning a dial or pushing a button is pretty obvious, and the player can discover its properties by sheer experimentation. Of course, that complicates the object, adding its own tradeoffs.
To keep this hack simple, let’s take a more subtle path. Earlier in the game, perhaps the first time the player picks up the catalog, she’ll recall how to use it:
You pick up the bright gold sphere and give it a quick test squeeze. The dataset changes to hazy violet; it's working perfectly.
The game prints this message, but it suggests that the player does this action out of habit. Thus, you’ve made the character’s implicit knowledge explicit and passed it on to the player.
If you like, consider this an exercise. You need to modify the
before routine for the Take
action. You also need a new object property, whose value tells
whether it’s the first time the player has taken the
sphere.