For when the One Great Scorer comes
To write against your name,
He marks – not that you won or lost –
But how you played the game.
— Grantland Rice (1880–1954), Alumnus Football
There are some constants which, if defined in your code before the
library files are included, change the standard game rules or tell
the Inform library about your game. Two such constants appeared back
in §4:
the strings of text Story
and Headline
.
Constant Story "ZORK II"; Constant Headline "^An Interactive Plagiarism^ Copyright (c) 1995 by Ivan O. Ideas.^";
· · · · ·
The library won't allow the player to carry an indefinite
number of objects. As was mentioned in §21,
the limit is the value of capacity
for the current
player-object, which you're free to vary during play. The library
sets up the capacity
of the usual player-object to be
equal to a constant called MAX_CARRIED
, which is
normally 100. But you can define it differently, and ‘Ruins’
does:
Constant MAX_CARRIED = 7;
For these purposes a container
counts
as only one object, even if it contains hundreds of other objects.
Many games, perhaps too many, involve collecting
vast miscellanies of items until a use has been found for each. A
small value of MAX_CARRIED
will then annoy the player
unreasonably, whereas a large one will stretch plausibility. The
standard resolution is to give the player a sack for carrying spare
objects, and the Inform library provides a feature whereby the
designer can nominate a given container
to be this
“sack”:
Object satchel "satchel" with description "Big and with a smile painted on it.", name 'satchel', article 'your', when_closed "Your satchel lies on the floor.", when_open "Your satchel lies open on the floor.", has container open openable; Constant SACK_OBJECT = satchel;
(This is from ‘Toyshop’: the ‘Ruins’ have been sacked too many times as it is.) The convenience this offers is that the game will now automatically put old, least-used objects away into the sack as the game progresses, provided the sack is still being carried:
>get biscuit
(putting the old striped scarf into the canvas rucksack to make room)
Taken.
· · · · ·
The “Invisiclues” hints of some of the reissued Infocom games sometimes included a category called “For Your Amusement”, listing some of the improbable things the game can do. Only the victorious should read this, as it might spoil surprises for anyone else. You can, optionally, provide such “amusing” information by defining the constant…
Constant AMUSING_PROVIDED;
… and also providing an entry point routine
called Amusing
. For a player who has won the game, but
not one who has merely died, the usual question
Would you like to RESTART, RESTORE a saved game or QUIT?
will then become
Would you like to RESTART, RESTORE a saved game, see some suggestions for AMUSING things to do or QUIT?
(The best way to provide such suggestions, if there are many, is to use a menu system like that described in §44.) One of the worst-kept secrets of the Inform library is that an option not mentioned by this question is to type “undo”, which will undo the last move and restore the player to life. If you feel that this option should be mentioned, define the constant:
Constant DEATH_MENTION_UNDO;
Finally, this end-of-game question will also mention the possibility of typing “full” to see a full score breakdown, if tasks are provided (see below).
· · · · ·
The other constants you are allowed to define help
keep the score. There are two scoring systems provided by the library,
side by side: you can use both or neither. You can always do what you
like to the library's score variable in any case, though the “fullscore”
verb might not then fully account for what's
happened. Whatever scoring
system you use, you should define MAX_SCORE
, as
‘Ruins’ for instance does by declaring:
Constant MAX_SCORE = 30;
This is the value which the library tells to the player as the maximum score attainable in text like:
You have so far scored 0 out of a possible 30, in 1 turn.
Note that the library does not check
that this is the actual maximum score it's possible to clock up:
and nor does it cause the game to be automatically won if the
maximum is achieved. The game is won when and only when deadflag
is set to 2 (see §21), regardless of
score.
The simpler scoring system awards points for the
first time certain objects are picked up, and the first time certain
places are entered. (As long as there is light to see by: no points
unless you can recognise that you've arrived somewhere interesting.)
To make an item or a place carry a points bonus, give it the attribute
scored
. You may also want to vary the amounts of these
bonuses by defining two constants:
OBJECT_SCORE
points for picking up a scored object (normally 4);
ROOM_SCORE
points for entering a scored room (normally 5)
The more elaborate scoring system keeps track of
which “tasks” the player has accomplished. These are
only present if the constant TASKS_PROVIDED
is
defined, and then the further constant NUMBER_TASKS
should indicate how many tasks have to be accomplished. If this value
is N, then the tasks are numbered 0, 1, 2, …,
N − 1. The number of points gained by solving each
task must be defined in a ->
array with
N entries called task_scores
, like so:
Constant TASKS_PROVIDED; Constant NUMBER_TASKS = 5; Constant MAX_SCORE = 25; Array task_scores -> 3 7 3 5 7;
Thus task 0 scores three points, task 1 scores seven
points and so on. Since the entries in a ->
array have
to be numbers between 0 and 255, no task can have a negative score
or a score higher than 255. Besides a points score, each task has
a name, and these are printed by an entry point routine called
PrintTaskName
. For instance (‘Toyshop’):
[ PrintTaskName task_number; switch (task_number) { 0: "eating a sweet"; 1: "driving the car"; 2: "shutting out the draught"; 3: "building a tower of four"; 4: "seeing which way the mantelpiece leans"; } ];
Finally, the game's source code should call
Achieved(task_number)
to tell the library that the
given task has been completed. If this task has been completed
before, the library will do nothing: if not, the library will award
the appropriate number of points. The verb “full” will
give a full score breakdown including the achieved task in all
future listings.
· · · · ·
When points are awarded by a call to Achieved
,
or by the player picking up a scored
object, or visiting a
scored
place, or simply by the source code itself altering
the score
variable, no text is printed at the time.
Instead, the library will normally notice at the end of the turn in
question that the score has changed, and will print a message like:
[Your score has gone up by three points.]
Not all players like this feature, so it can be turned
on and off with the “notify” verb, but by default it is
on. The designer can also turn the feature off and on: it is off
if the library's variable notify_mode
is false
,
on if it is true
.
· · · · ·
Another (optional) entry point routine, called
PrintRank
, gets the chance to print text additional
to the score. It's called PrintRank
because the
traditional “something additional” is a ranking based
on the current score. Here is ‘Ruins’:
[ PrintRank; print ", earning you the rank of "; if (score == 30) "Director of the Carnegie Institution."; if (score >= 20) "Archaeologist."; if (score >= 10) "Curiosity-seeker."; if (score >= 5) "Explorer."; "Tourist."; ];
· · · · ·
Besides the score breakdown, two more verbs are
usually provided to the player: “objects” and “places”.
The former lists off all the objects handled by the player and where
they are now; the latter lists all the places visited by the player.
In some game designs, these verbs will cause problems: you can get
rid of them both by defining the constant NO_PLACES
.
•▲
EXERCISE 57
Suppose one single room object is used internally for the 64 squares
of a gigantic chessboard, each of which is a different location to
the player. Then “places” is likely to result in only
the last-visited square being listed. Fix this.
· · · · ·
The rest of this section runs through some simple “special effects” which are often included in games. See Chapter VII for much more on this, and in particular see §44 for using the "Menus.h" library extension.
The first effect is hardly special at all: to ask
the player a yes/no question. To do this, print up the question
and then call the library routine YesOrNo
, which
returns true/false accordingly.
The status line is perhaps the most distinctive feature of Infocom games in play. This is the (usually highlighted) bar across the top of the screen. Usually, the game automatically prints the current game location, and either the time or the score and number of turns taken. It has the score/turns format unless the directive
Statusline time;
has been written in the program, in which case the game's 24-hour clock is displayed. See §20 for more on time-keeping.
▲
If you want to change this, you need to Replace
the
parser's DrawStatusLine
routine. This requires
some assembly language programming: there are several examples
of altered status lines in the exercises to §42.
· · · · ·
Many games contain quotations, produced with box
statements like so:
box "I might repeat to myself, slowly and soothingly," "a list of quotations beautiful from minds profound;" "if I can remember any of the damn things." "" "-- Dorothy Parker";
A snag with printing such boxes is that if you do it
in the middle of a turn then it will probably scroll half-off the
screen by the time the game finishes printing for the turn. The right
time to do so is just after the prompt (usually “>”)
is printed, when the screen will definitely scroll no more. You
could use the Prompt:
slot in LibraryMessages
to achieve this (see §25), but a
more convenient way is to put your box-printing into the entry point
routine AfterPrompt
, which is called at this time in
every turn.
•
EXERCISE 58
Devise a class Quotation
, so that calling QuoteFrom(Q)
for any quotation Q
will cause it to be displayed at
the end of the current turn, provided it hasn't been quoted before.
•
REFERENCES
‘Advent’ contains ranks and an Amusing
reward
(but doesn't use either of the scoring systems provided by the library,
instead working by hand).
•‘Balances’ uses
scored
objects (for its cubes).
•‘Toyshop’ has
tasks, as above.
•‘Adventureland’
uses its TimePasses
entry point to recalculate the
score every turn (and watch for victory).