14. Some last lousy points¶

inally our three example games are written; we’ve shown you as much of the Inform language as we’ve needed to, and made a lot of observations about how and why something should be done. Despite all that, there’s much that we’ve left unsaid, or touched on only lightly. In this chapter we’ll revisit key topics and review some of the more significant omissions, to give you a better feel for what’s important, and what can be ignored for the time being; when you become an accomplished designer, you will decide what matters and what can be left on the shelf.
We’ll also talk, in Reading other people’s code, about a few ways of doing things that we’ve chosen not to tell you about, but which you’re quite likely to encounter if you look at Inform code written by other designers.
The tone here is perhaps a little dry, but trust us: in walking this dusty ground we touch on just about everything that is fundamental in your overall understanding of Inform. And as always, the Inform Designer’s Manual provides rounder and more comprehensive coverage.
Expressions¶
In this guide we’ve used the placeholder expression
a few times;
here’s roughly what we mean.
- An
expression
is a singlevalue
, or severalvalues
combined usingoperators
and sometimes parentheses(...)
. - Possible
values
include:- a literal number (-32768 to 32767)
- something that’s represented as a number (a character
'a'
, a dictionary word'aardvark'
, a string"aardvark's adventure"
or an action##Look
) - the internal identifier of a constant, an object, a class or a routine
- (only in a run-time statement, not in a compile-time directive) the contents of a variable, or the return value from a routine.
- Possible
operators
include:- an arithmetic operator:
+ - * / % ++
- a bitwise logical operator:
& | ~
- a numeric comparison operator:
== ~= > < >= <=
- an object conditional operator:
ofclass in notin provides has hasnt
- a boolean combinational operator:
&& || ~~
- an arithmetic operator:
Internal IDs¶
Many of the items which you define in your source file – objects,
variables, routines, etc. – need to be given a name so that other items
can refer to them. We call this name an item’s internal identifier
(because it’s used only within the source file and isn’t visible to the
player), and we use the placeholders obj_id
, var_id
,
routine_id
, etc. to represent where it’s used. An internal ID
- can be up to thirty-two characters long
- must start with a letter or underscore, and then continue with letters
A-Z
, underscore_
and digits0-9
(where upper-case and lower-case letters are treated as indistinguishable) - should generally be unique across all files: your source file, the standard library files, and any library contributions which you’ve used (except that a routine’s local variables are not visible outside that routine).
Statements¶
A statement is an instruction intended for the interpreter, telling it what to do at run-time. It must be given in lower-case, and always ends with a semicolon.
Some statements, like if
, control one or more other statements. We
use the placeholder statement_block
to represent either a single
statement
, or any number of statements
enclosed in braces:
statement;
statement; statement; ... statement;
Statements that we’ve met¶
Our games have used these statements, about half of the Inform possibilities:
give obj_id attribute;
give obj_id attribute attribute ... attribute;
if (expression) statement_block
if (expression) statement_block else statement_block
move obj_id to parent_obj_id;
objectloop (var_id) statement_block
print value;
print value, value, ... value;
print_ret value;
print_ret value, value, ... value;
remove obj_id;
return false;
return true;
style underline; print...; style roman;
switch (expression) {
value: statement; statement; ... statement;
...
default: statement; statement; ... statement;
}
"string";
"string", value, ... value;
<action>;
<action noun>;
<action noun second>;
<<action>>;
<<action noun>>;
<<action noun second>>;
Statements that we’ve not met¶
Although our example games haven’t needed to use them, these looping statements are sometimes useful:
break;
continue;
do statement_block until (expression)
for (set_var : loop_while_expression : update_var) statement_block
while (expression) statement_block
On the other hand, we suggest that you put the following statements on hold for now; they’re not immediately relevant to everyday code and have mostly to do with printing and formatting:
box
font
jump
new_line
spaces
string
In particular, avoid using the deprecated jump statement if you possibly can.
Print rules¶
In print
and print_ret
statements, each value
can be:
a numeric
expression
, displayed as a signed decimal number,a
"string"
, displayed literally, ora print rule. You can create your own, or use a standard one, including:
(a) obj_id
the object’s name, preceded by “a”, “an” or “some” (A) obj_id
as (a)
but using “A”, “An” or “Some”(the) obj_id
the object’s name, preceded by “the” (The) obj_id
as (the)
but using “The”(number) expression
the numeric expression’s value in words
Directives¶
A directive is an instruction intended for the compiler, telling it what to do at compile-time, while the source file is being translated into Z-code. By convention it’s given an initial capital letter (though the compiler doesn’t enforce this) and always ends with a semicolon.
Directives that we’ve met¶
We’ve used all of these directives; note that for Class
, Extend
,
Object
and Verb
the full supported syntax is more sophisticated
than the basic form presented here:
Class class_id
with property value,
property value,
...
property value,
has attribute attribute ... attribute;
Constant const_id:
Constant const_id = expression;
Constant const_id expression;
Extend 'verb'
* token token ... token -> action
* token token ... token -> action
...
* token token ... token -> action
Include "filename";
Object obj_id "external_name" parent_obj_id
with property value,
property value,
...
property value,
has attribute attribute ... attribute;
Release expression;
Replace routine_id;
Serial "yymmdd";
Verb 'verb'
* token token ... token -> action
* token token ... token -> action
...
* token token ... token -> action;
! comment text which the compiler ignores
[ routine_id; statement; statement; ... statement; ];
#Ifdef any_id; ... #Endif;
Directives that we’ve not met¶
There’s only a handful of useful directives which we haven’t needed to use:
Attribute attribute;
Global var_id;
Global var_id = expression;
Property property;
Statusline score;
Statusline time;
but there’s a whole load which are of fairly low importance for now:
Abbreviate
Array
Default
End
Ifndef
Ifnot
Iftrue
Iffalse
Import
Link
Lowstring
Message
Switches
System_file
Zcharacter
Objects¶
An object is really just a collection of variables which together represent the capabilities and current status of some specific component of the model world. Full variables are called properties; simpler two-state variables are attributes.
Properties¶
The library defines around forty-eight standard property variables (such
as before
or name
), but you can readily create further ones just
by using them within an object definition.
You can create and initialise a property in an object’s with
segment:
property, ! set to zero / false
property value, ! set to a single value
property value value ... value, ! set to a list of values
In each case, the value
is either a compile-time expression
, or
an embedded routine:
property expression,
property [; statement; statement; ... statement; ],
You can refer to the value of a property:
self.property ! only within that same object
obj_id.property ! everywhere
and you can test whether an object definition includes a given property:
(obj_id provides property) ! is true or false
Routines¶
Inform provides standalone routines and embedded routines.
Standalone routines¶
Standalone routines are defined like this:
[ routine_id; statement; statement; ... statement; ];
and called like this:
routine_id()
Embedded routines¶
These are embedded as the value of an object’s property:
property [; statement; statement; ... statement; ],
and are usually called automatically by the library, or manually by:
self.property() ! only within that same object
obj_id.property() ! everywhere
Arguments and local variables¶
Both types of routine support up to fifteen local variables – variables which can be used only by the statements within the routine, and which are automatically initialised to zero every time that the routine is called:
[ routine_id var_id var_id ... var_id; statement; statement; ... statement; ];
property [ var_id var_id ... var_id; statement; statement; ... statement; ],
You can pass up to seven arguments to a routine, by listing those arguments within the parentheses when you call the routine. The effect is simply to initialise the matching local variables to the argument values rather than to zero:
routine_id(expression, expression, ... expression)
Although it works, this technique is rarely used with embedded routines, because there is no mechanism for the library to supply argument values when calling the routine.
Return values¶
Every routine returns a single value, which is supplied either explicitly by some form of return statement:
[ routine_id; statement; statement; ... return expr; ]; ! returns expr
property [; statement; statement; ... return expr; ], ! returns expr
or implicitly when the routine runs out of statements. If none of these
statements
is one – return
, print_ret
, "..."
or
<<...>>
– that causes an explicit return, then:
[ routine_id; statement; statement; ... statement; ];
returns true
and
property [; statement; statement; ... statement; ]
return false
.
This difference is important. Remember it by the letter pairs STEF: left to themselves, Standalone routines return True, Embedded routines return False.
Here’s an example standalone routine which returns the larger of its two argument values:
[ Max a b; if (a > b) return a; else return b; ];
and here are some examples of its use (note that the first example, though legal, does nothing useful whatsoever):
Max(x,y);
x = Max(2,3);
if (Max(x,7) == 7) ...
switch (Max(3,y)) { ...
Library routines versus entry points¶
A library routine is a standard routine, included within the library files, which you can optionally call from your source file if you require the functionality which the routine provides. We’ve mentioned these library routines:
IndirectlyContains(parent_obj_id, obj_id)
PlaceInScope(obj_id)
PlayerTo(obj_id, flag)
StartDaemon(obj_id)
StopDaemon(obj_id)
By contrast, an entry point routine is a routine which you can provide in your source file, in which case the library calls it at an appropriate time. We’ve mentioned these optional entry point routines:
DeathMessage()
InScope(actor_obj_id)
And this, the only mandatory one:
Initialise()
There are full lists in Library routines and Optional entry points.
Reading other people’s code¶
Right at the start of this guide, we warned you that we weren’t setting out to be comprehensive; we’ve concentrated on presenting the most important aspects of Inform, as clearly as we can. However, when you read the Inform Designer’s Manual, and more especially when you look at complete games or library extensions which other designers have produced, you’ll come across other ways of doing things – and it might be that you, like other authors, prefer them over our methods. Just try to find a style that suits you and, this is the important bit, be consistent about its use. In this section, we highlight some of the more obvious differences which you may encounter.
Code layout¶
Every designer has his or her own style for laying out their source code, and they’re all worse than the one you adopt. Inform’s flexibility makes it easy for designers to choose a style that suits them; unfortunately, for some designers this choice seems influenced by the Jackson Pollock school of art. We’ve advised you to be consistent, to use plenty of white space and indentation, to choose sensible names, to add comments at difficult sections, to actively think, as you write your code, about making it as readable as you can.
This is doubly true if you ever contemplate sharing a library extension with the rest of the community. This example, with the name changed, is from a file in the Archive:
[xxxx i j;
if (j==0) rtrue;
if (i in player) rtrue;
if (i has static || (i has scenery)) rtrue;
action=##linktake;
if (runroutines(j,before) ~= 0 || (j has static || (j has scenery))) {
print "You'll have to disconnect ",(the) i," from ",(the) j," first.^";
rtrue;
}
else {
if (runroutines(i,before)~=0 || (i has static || (i has scenery))) {
print "You'll have to disconnect ",(the) i," from ",(the) j," first.^";
rtrue;
}
else
if (j hasnt concealed && j hasnt static) move j to player;
if (i hasnt static && i hasnt concealed) move i to player;
action=##linktake;
if (runroutines(j,after) ~= 0) rtrue;
print "You take ",(the) i," and ",(the) j," connected to it.^";
rtrue;
}
];
Here’s the same routine after a few minutes spent purely on making it
more comprehensible; we haven’t actually tested that it (still) works,
though that second else
looks suspicious:
[ xxxx i j;
if (i in player || i has static or scenery || j == nothing) return true;
action = ##LinkTake;
if (RunRoutines(j,before) || j has static or scenery)
"You'll have to disconnect ", (the) i, " from ", (the) j, " first.";
else {
if (RunRoutines(i,before) || i has static or scenery)
"You'll have to disconnect ", (the) i, " from ", (the) j, " first.";
else
if (j hasnt static or concealed) move j to player;
if (i hasnt static or concealed) move i to player;
if (RunRoutines(j,after)) return true;
"You take ", (the) i, " and ", (the) j, " connected to it.";
}
];
We hope you’ll agree that the result was worth the tiny extra effort. Code gets written once; it gets read dozens and dozens of times.
Shortcuts¶
There are a few statement shortcuts, some more useful than others, which you’ll come across.
These five lines all do the same thing:
return true; return 1; return; rtrue; ]; ! at the end of a standalone routine
These four lines all do the same thing:
return false; return 0; rfalse; ]; ! at the end of an embedded routine
These four lines all do the same thing:
print "string"; new_line; return true; print "string^"; return true; print_ret "string"; "string";
These lines are the same:
print value1; print value2; print value3; print value1, value2, value3;
These lines are the same:
<action noun second>; return true; <<action noun second>>;
These lines are also the same:
print "^"; new_line;
These
if
statements are equivalent:if (MyVar == 1 || MyVar == 3 || MyVar == 7) ... if (MyVar == 1 or 3 or 7) ...
These
if
statements are equivalent as well:if (MyVar ~= 1 && MyVar ~= 3 && MyVar ~= 7) ... if (MyVar ~= 1 or 3 or 7) ...
In an
if
statement, the thing in parentheses can be any expression; all that matters is its value: zero (false) or anything else (true). For example, these statements are equivalent:if (MyVar ~= false) ... if (~~(MyVar == false)) ... if (MyVar ~= 0) ... if (~~(MyVar == 0)) ... if (MyVar) ...
Note that the following statement specifically tests whether
MyVar
containstrue
(1), not whether its value is anything other than zero.if (MyVar == true) ...
If
MyVar
is a variable, the statementsMyVar++;
and++MyVar;
work the same asMyVar = MyVar + 1;
For example, these lines are equivalent:MyVar = MyVar + 1; if (MyVar == 3) ... if (++MyVar == 3) ... if (MyVar++ == 2) ...
What’s the same about
MyVar++
and++MyVar
is that they both add one toMyVar
. What’s different about them is the value to which the construct itself evaluates:MyVar++
returns the current value ofMyVar
and then performs the increment, whereas++MyVar
does the “+1” first and then returns the incremented value. In the example, ifMyVar
currently contains 2 then++MyVar
returns 3 andMyVar++
returns 2, even though in both cases the value ofMyVar
afterwards is 3. As another example, this code (from Helga in “William Tell”):Talk: self.times_spoken_to = self.times_spoken_to + 1; switch (self.times_spoken_to) { 1: score = score + 1; print_ret "You warmly thank Helga for the apple."; 2: print_ret "~See you again soon.~"; default: return false; } ],
could have been written more succinctly like this:
Talk: switch (++self.times_spoken_to) { 1: score++; print_ret "You warmly thank Helga for the apple."; 2: print_ret "~See you again soon.~"; default: return false; } ],
Similarly, the statements
MyVar--;
and--MyVar;
work the same asMyVar = MyVar - 1;
Again, these lines are equivalent:MyVar = MyVar - 1; if (MyVar == 7) ... if (--MyVar == 7) ... if (MyVar-- == 8) ...
“number” property and “general” attribute¶
The library defines a standard number
property and a standard
general
attribute, whose roles are undefined: they are
general-purpose variables available within every object to designers as
and when they desire.
We recommend that you avoid using these two variables, primarily because
their names are, by their very nature, so bland as to be largely
meaningless. Your game will be clearer and easier to debug if you
instead create new property variables – with appropriate names – as
part of your Object
and Class
definitions.
Common properties and attributes¶
As an alternative to creating new individual properties which apply only to
a single object (or class of objects), it’s possible to devise properties
and new attributes which, like those defined by the library, are available
on all objects. The need to do this is actually quite rare, and is mostly
confined to library extensions (for example, the pname.h
extension
which we encountered in Captain Fate: take 3 gives every object a pname
property
and a phrase_matched
attribute). To create them, you would use these
directives near the start of your source file:
Attribute attribute;
Property property;
We recommend that you avoid using these two directives unless you really do need to affect every object – or at least the majority of them – in your game. There is a limit of forty-eight attributes (of which the library currently defines around thirty) and sixty-two of these common properties (of which the library currently defines around forty-eight). On the other hand, the number of individual properties which you can add is virtually unlimited.
Setting up the object tree¶
Throughout this guide, we’ve defined the initial position of each object
within the overall object tree either by explicitly mentioning its
parent’s obj_id
(if any) in the first line of the object definition
– what we’ve been calling the header information – or, for a few
objects which crop up in more than one place, by using their
found_in
properties. For example, in “William Tell” we defined
twenty-seven objects; omitting those which used found_in
to define
their placement at the start of the game, we’re left with object
definitions starting like this:
Room street "A street in Altdorf"
Room below_square "Further along the street"
Furniture stall "fruit and vegetable stall" below_square
Prop "potatoes" below_square
Prop "fruit and vegetables" below_square
NPC stallholder "Helga" below_square
Room south_square "South side of the square"
Room mid_square "Middle of the square"
Furniture pole "hat on a pole" mid_square
Room north_square "North side of the square"
Room marketplace "Marketplace near the square"
Object tree "lime tree" marketplace
NPC governor "governor" marketplace
Object bow "bow"
Object quiver "quiver"
Arrow "arrow" quiver
Arrow "arrow" quiver
Arrow "arrow" quiver
Object apple "apple"
You’ll see that several of the objects begin the game as parents:
below_square
, mid_square
, marketplace
and quiver
all
have child objects beneath them; those children mention their parent as
the last item of header information.
There’s an alternative object syntax which is available to achieve the same object tree, using “arrows”. That is, we could have defined those parent-and-child objects as:
Room below_square "Further along the street"
Furniture -> stall "fruit and vegetable stall"
Prop -> "potatoes"
Prop -> "fruit and vegetables"
NPC -> stallholder "Helga"
Room mid_square "Middle of the square"
Furniture -> pole "hat on a pole"
Room marketplace "Marketplace near the square"
Object -> tree "lime tree"
NPC -> governor "governor"
Object quiver "quiver"
Arrow -> "arrow"
Arrow -> "arrow"
Arrow -> "arrow"
The idea is that an object’s header information either starts with an
arrow, or ends with an obj_id
, or has neither (having both isn’t
permitted). An object with neither has no parent: in this example,
that’s all the Rooms
, and also the bow
and the quiver
(which
are moved to the player object
in the Initialise
routine) and
the apple (which remains without a parent until Helga gives it to
William).
An object which starts with a single arrow ->
is defined to be a
child of the nearest previous object without a parent. Thus, for
example, the tree
and governor
objects are both children of the
marketplace
. To define a child of a child, you’d use two arrows
-> ->
, and so on. In “William Tell”, that situation doesn’t occur;
to illustrate how it works, imagine that at the start of the game the
potatoes and the other fruit and vegetables where actually on the
stall. Then we might have used:
Room below_square "Further along the street"
Furniture -> stall "fruit and vegetable stall"
Prop -> -> "potatoes"
Prop -> -> "fruit and vegetables"
NPC -> stallholder "Helga"
...
That is, the objects with one arrow (the stall
and stallholder
)
are children of the nearest object without a parent (the Room
), and
the objects with two arrows (the produce) are children of the nearest
object defined with a single arrow (the stall
).
The advantages of using arrows include:
- You’re forced to define your objects in a “sensible” order.
- Fewer
obj_ids
may need to be used (though in this game it would make no difference).
The disadvantages include:
- The fact that objects are related by the physical juxtaposition of their definitions is not necessarily intuitive to all designers.
- Especially in a crowded room, it’s harder to be certain exactly how the various parent–child relationships are initialised, other than by carefully counting lots of arrows.
- If you relocate the parent within the initial object hierarchy to a higher or lower level, you’ll need also to change its children by adding or removing arrows; this isn’t necessary when the parent is named in the child headers.
We prefer to explicitly name the parent, but you’ll encounter both forms very regularly.
Quotes in “name” properties¶
We went to some lengths, way back in Things in quotes, to explain
the difference between double quotes "..."
(strings to be output) and
single quotes '...'
(input tokens – dictionary words). Perhaps
somewhat unfortunately, Inform allows you to blur this clean distinction:
you can use double quotes in name properties and Verb directives:
NPC stallholder "Helga" below_square
with name "stallholder" "greengrocer" "monger" "shopkeeper" "merchant"
"owner" "Helga" "dress" "scarf" "headscarf",
...
Verb "talk" "t//" "converse" "chat" "gossip"
* "to"/"with" creature -> Talk
* creature -> Talk;
Please don’t do this. You’ll just confuse yourself: those are dictionary words, not strings; it’s just as easy – and far clearer – to stick rigidly to the preferred punctuation.
Obsolete usages¶
Finally, remember that Inform has been evolving since 1993. Over that
time, Graham has taken considerable care to maintain as much
compatibility as possible, so that games written years ago, for earlier
versions of the compiler and the library, will still compile today.
While generally a good thing, this brings the disadvantage that a
certain amount of obsolete baggage is still lying around. You may, for
example, see games using Nearby
directives (denotes parentage,
roughly the same as ->
) and near
conditions (roughly, having the
same parent), or with " \ "
controlling line breaks in long
print
statements. Try to understand them; try not to use them.