Suppose we have a book that the player must consult page-by-page, and we want to be able to accept all of the following input:
> READ BOOK (to choose a random page and read it)
> READ PAGE 1 IN BOOK
> READ PAGE 2
> READ THE LAST PAGE OF THE BOOK
> READ THE NEXT PAGE
> READ PREVIOUS PAGE IN BOOK
> READ THE FIRST PAGE
One approach would be to write many different understand rules and actions: one action for reading randomly, one for reading a specific page, one for reading the first page, one for reading the previous page, one for reading the next page, and one for reading the last page. But this gets tedious to construct and maintain.
More usefully, we could consider that all of the last four options are essentially the same action at heart: the player is asking to read a page in the book using a name rather than a number, and we will have to perform a minor calculation to discover what the number should be. Here's an implementation using named values to resolve this problem:
"Pages"
The Library is a room. The sinister book is carried by the player. The sinister book has a number called the last page read. The sinister book has a number called the length. The length of the sinister book is 50.
Understand the command "read" as something new.
Understand "read [something]" or "consult [something]" or "read in/from [something]" as reading. Reading is an action applying to one thing, requiring light.
Understand "read [number] in/from/of [something]" or "read page [number] in/from/of [something]" or "look up page [number] in/from/of [something]" or "consult page [number] in/from/of [something]" as reading it in. Reading it in is an action applying to one number and one thing, requiring light.
Named page is a kind of value. The named pages are first page, last page, next page, previous page.
To decide what number is the effective value of (L - last page):
decide on the length of the book.
To decide what number is the effective value of (F - first page):
decide on 1.
To decide what number is the effective value of (N - next page):
let X be the last page read of the book plus 1;
decide on X.
To decide what number is the effective value of (P - previous page):
let X be the last page read of the book minus 1;
decide on X.
Understand "read [named page] in/from/of [something]" or "read the [named page] in/from/of [something]" as reading it relatively in. Reading it relatively in is an action applying to one named page and one thing, requiring light.
Does the player mean reading something in the sinister book: it is very likely.
This is the book requirement rule:
if the player is not carrying the sinister book, say "You're not reading anything." instead.
Check reading it relatively in:
if the second noun is not the sinister book, say "There are no pages in [the second noun]." instead;
abide by the book requirement rule.
Carry out reading it relatively in:
let N be the effective value of the named page understood;
now the number understood is N;
try reading N in the book.
Check reading it in:
if the second noun is not the sinister book, say "There are no pages in [the second noun]." instead;
abide by the book requirement rule.
Check reading it in:
if the number understood is greater than the length of the sinister book, say "There are only [length of sinister book in words] pages in the book." instead;
if the number understood is less than 1, say "The page numbering begins with 1." instead.
Carry out reading it in:
read page number understood.
Check reading:
if the noun is not the sinister book, say "There are no pages in [the noun]." instead;
abide by the book requirement rule.
Carry out reading:
let N be a random number between 1 and the length of the sinister book; now the number understood is N;
say "You flip the pages randomly and arrive at page [the number understood]:[paragraph break]";
try reading the number understood in the sinister book.
Table of Book Contents
page
|
content
|
2
|
"dhuma jyotih salila marutam / samnipatah kva meghah / samdes arthah kva patukaranaih / pranibhih prapaniyah"
|
13
|
"amathesteron pws eipe kai saphesteron"
|
50
|
"Rrgshilz maplot..."
|
To read page (N - a number):
now the last page read of the sinister book is N;
if there is a content corresponding to a page of N in the Table of Book Contents:
choose row with a page of N in the Table of Book Contents;
say "You read: '[content entry]'[paragraph break]";
otherwise:
say "Page [N] appears to be blank."
To read page (N - 47):
say "Your eyes burn; your ears ring. Beneath your gaze, the dreadful sigils writhe, reminding you of that which lies outside the edges of the universe...";
end the story saying "You have lost your remaining sanity".
Test me with "read from the sinister book / read the book / read the next page / read page 2 / read previous page / g / read the first page / read the last page of the book / read the next page / read 47 in book".
Now and then in IF there is a situation where we need to ask the player for a numbered choice rather than an ordinary action command. What's more, that numbered choice might change during the game, so we don't want to just hard-wire the meanings of "1", "2", and "3" whenever the player types them.
A better trick is to keep a list or table (we'll use a table here because it involves slightly less overhead) recording what the player's numerical choices currently mean. Then every time the player selects a number, the table is consulted, and if the number corresponds to something, the player's choice is acted on.
In our example, we'll have a transporter pad that can take the player to any room in the game that he's already visited. (Just for the sake of example, we'll start him off with a few pre-visited rooms.)
"Down in Oodville"
Section 1 - Method
Understand "[number]" as selecting.
Selecting is an action applying to one number.
Check selecting: [assuming we don't want to be able to transport from just anywhere]
if the player is not on the transporter pad:
say "You can transport only from the transporter pad. From other places than the transporter room, you can HOME to your base ship, but not leap sideways to other locations.";
empty the transport options instead.
Check selecting:
if the number understood is greater than the number of filled rows in the Table of Transport Options or the number understood is less than one:
say "[The number understood] is not a valid option. ";
list the transport options instead.
Carry out selecting:
let N be the number understood; [not actually a necessary step, but it makes the next line easier to understand]
choose row N in the Table of Transport Options;
if the transport entry is a room:
move the player to the transport entry;
otherwise:
say "*** BUG: Improperly filled table of transport options ***" [It should not be possible for this to occur, but we add an error message for it so that, if it ever does, we will know what is causing the programming error in our code]
To list the transport options:
let N be 1;
say "From here you could choose to go to: [line break]";
repeat through the Table of Transport Options:
say " [N]: [transport entry][line break]";
increment N.
To empty the transport options:
repeat through the Table of Transport Options:
blank out the whole row; [first we empty the table]
To load the transport options:
repeat with interesting room running through visited rooms which are not the Transporter Room:
choose a blank row in the Table of Transport Options;
now the transport entry is the interesting room.
Table of Transport Options
transport
an object
with 3 blank rows. [In the current scenario, the number of blank rows need never be greater than the number of rooms in the game, minus the transport room itself.]
Understand "home" as homing. Homing is an action applying to nothing.
Check homing:
if the player is in the Transporter Room:
say "You're already here!" instead.
Carry out homing:
move the player to the transporter room.
Section 2 - Scenario
The Transporter Room is a room.
Oodville is a visited room.
Midnight is a visited room. The Diamond City is west of Midnight.
The transporter pad is an enterable supporter in the Transporter Room. "The transporter pad in the middle of the floor is currently dull blue: powered but unoccupied."
After entering the transporter pad:
say "The transporter beeps and glows amber as you step onto its surface. A moment later a hologram displays your options. [run paragraph on]";
empty the transport options;
load the transport options;
list the transport options.
Test me with "get on pad / 0 / -1 / 8 / 2 / look / w / home / get on pad / get off pad / 3".
If we wanted to replace the regular command structure entirely with numbered menus, or use menus to hold conversation options, we could: several Inform extensions provide these functions.
"Straw Into Gold"
The Cell is a room. Rumpelstiltskin is an improper-named man in the Cell. Rumpelstiltskin can be identified or unidentified. Rumpelstiltskin is unidentified.
R-name is a kind of value. The R-names are dwarf, guy, dude, and man-thing. Rumpelstiltskin has an R-name. Understand "[R-name]" as Rumpelstiltskin.
Our example is slightly complicated by the fact that "man" is a name already known to Inform, so we can't re-use it as a kind of value. This is possible to work around, though:
Understand "man" as man-thing.
Now we borrow from the Activities chapter to look at the exact wording of the player's command:
After reading a command:
if the player's command includes "[R-name]",
now the R-name of Rumpelstiltskin is the R-name understood;
if the player's command includes "Rumpelstiltskin":
now Rumpelstiltskin is identified;
now Rumpelstiltskin is proper-named.
Rule for printing the name of Rumpelstiltskin when Rumpelstiltskin is unidentified:
if the R-name of Rumpelstiltskin is man-thing:
say "man";
otherwise:
say "[R-name]".
Test me with "x dwarf / x guy / x dude / look / x rumpelstiltskin / look / x man".