In this chapter, we explore a number of ways to go beyond the traditional text-only, one-story-file-only model for IF.
These relatively exotic features are more demanding of the interpreter which a player uses than a plain text story file would be. They can only be used if the project is being compiled to the Glulx story file format (see the Settings panel for the project), and even then, the player will need to have a good Glulx interpreter - one which is reasonably up to date and well-written, that is - to be sure of everything working as intended.
Looking around a bookshop, perhaps half of all the books published have illustrations. The proportion may be lower for novels, but if we count maps or other occasional diagrams, even the fiction section turns out to be surprisingly pictorial. Illustrations do not suit every book, but they are an option we would like to have available.
In the cultural history of IF, graphics in text adventures have sometimes been looked at with suspicion. Mostly this is because attempts in the 1980s were not very successful, because computer graphics were so poor then (by modern standards). It may be that some people also felt that the takeover of computer games by graphical interfaces was the death knell of IF. But pictures are now rendered in superb quality by computers, and the death of IF turned out to be an exaggeration, so it is time to move on.
Whether to have illustrations ought to be an artistic choice, like whether to include a romantic sub-plot or how much of the back story is revealed. But there are practical considerations too. The most successful illustrated books are those whose pictures are well-chosen, have a sense of design to them, and above all are consistent. Consider how much worse off Winnie the Pooh would have been if a selection of random teddy-bear drawings had been used, instead of E. H. Shepherd's beautifully conceived world; or a cookery book in which the recipes are all photographed at different distances and light levels. IF writers may want to look for collaborators with a visual eye, just as most novelists do not draw their own illustrations.
Another consideration is that displaying images is more complicated for computers than displaying text. Not all devices can show pictures (consider handheld gadgets) and if they do, they may use different colour ranges or resolutions. So IF with pictures is always just a bit less portable than IF without, and because of that we must next look again at IF story file formats.
Back in Chapter 2, we saw that the Settings panel allows any given Inform project to be produced in either of two possible story file formats. Recall that story files are the released IF works: what the player sees. The source text, the Index, and so on are not part of this.
A story file is not like a word-processed document, or a photograph. There are many rival formats for these - for instance, an image on a web page might be in JPEG or PNG format, among many others - but basically they are simple things for the reader to look at, and see everything in one go. An IF story file is more complicated, because the "reader" reacts to it, types in to it, is surprised by it, never quite knows what might happen next.
A story file is in fact a computer program in its own right, but not a program like iTunes or Firefox which runs on a typical home or business computer. Instead it is a program for an imaginary computer, called a "virtual machine" or "VM". This has a design ideally suited to IF, and it would be the perfect IF player's computer if only it actually existed. Because it doesn't, the player instead runs an "interpreter" program like Windows Glulxe or Zoom or Spatterlight - and this one is a program like iTunes or Firefox - and the interpreter acts as a middle-man. It creates a software version of the virtual machine, and then runs the IF story file on that VM. This sounds slow and impractical, but in fact it works well, and is also much safer since programs on the VM are not allowed to touch the real computer - so they cannot at all easily contain viruses or other malware. (In theory a malicious story file might try to exploit a bug in one of the various VM implementations in use, just as malicious image files have been used to attack bugs in web browsers, but this has never in practice happened. Nothing can be absolutely safe, but a story file belongs in the "mostly harmless" category of files - like images - rather than the "how far do you trust this person?" category - like programs.)
The different formats of story file are programs for different virtual machines. Just as Windows and Mac OS X offer basically similar services to the user but are very different in appearance and their workings, so the different VMs are quite different. Some can display pictures, others not.
Inform provides basic support for displaying pictures and leaves more exotic effects for Extensions to provide. But either way, for reasons explained in the previous section, we can only have pictures if the Settings for the project are set to the Glulx story file format.
Inform calls these pictures "figures", following the usual Inform analogy with books. We will think of our work of IF as being like a mostly textual book which in broken up with illustrations here and there - Figure 1, Figure 2, and so on. These might be used to mark each new chapter of the plot, or each new location: whatever the author would like. So the first thing we need to do is decide when pictures should appear.
The second thing to do is to get hold of the pictures we want to use. These might be photographs, or artwork, or diagrams: anything, really, but we will need them to be in either JPEG or PNG format. Inform does not itself try to be an image editor, or an artwork program - there are many such programs already which do these things much better than Inform could.
The pictures then need to be put in a special place where Inform can reach them. Suppose the Inform project is called Example.inform. Then we need to create a folder alongside it called "Example.materials", and create a further folder inside that called "Figures". The actual images go inside "Figures". So we might then have files like so:
Example.inform
Example.materials
Figures
Woodlands.png
Blackberry.jpg
Red Admiral Butterfly.png
The ".materials" folder for an Inform project will turn out to have many other uses in the chapter on Publishing, and will be explained further there.
Inside Inform, the source text for a project always tries to avoid talking about filenames - we need a better way to refer to the individual figures.
We do this by declaring each figure with a sentence like the following examples:
Figure of Woodlands is the file "Woodlands.png".
Figure 2 is the file "Red Admiral Butterfly.png".
Figure names can consist of any text provided that text starts with the word "Figure". So "Figure 3 - Woodlands", for instance, or even "Figure W" would have been just as good as "Figure of Woodlands". Books tend to number figures, but then, in a book the order in which they appear is known in advance - which might not be true in IF.
The file names must be exactly those used in the Figures folder. We need not declare every image kept there, but those we don't declare - remember Blackberry.jpg? - cannot be displayed.
We can preview the stock of figures by going to the table of figures in the Contents index for a project (once the project has been built, that is, so that its index is up to date). This preview shows thumbnail forms of the pictures, the names, the formats and the image sizes in pixels. A warning triangle is shown for any images in the wrong format, or which are missing from the Figures folder.
Inform's basic picture support simply allows figures to be shown at particular times. Once seen, they scroll away, just as text does once it has been printed. These pictures are really part of the stream of narrative. (If we would like icons or other images to be permanently present on screen, and divide the screen up in pictorial ways to achieve interesting layouts, we need to use special extensions to access Glulx's more exotic features.)
Displaying a picture is therefore like printing some text. Rather than
say "The woodlands stretch from here to the horizon.";
we would use:
Once again, note that the "display" phrase does nothing unless the Settings for the project are set to the Glulx story file format. When a Glulx work is released as a blorb (the default setting for the way releases occur: see the chapter on Publishing), all the images used are automatically included.
Inform also supports the playing back of recorded sounds, which might be anything from a three-second sound effect for a creaking door to an epic orchestral symphony. Sound support is very newly added to the system and work is still in progress. In particular, sounds are not played by Inform for OS X (although it does produce valid blorbed Glulx story files), though they should be audible from within the Inform application for Windows.
Once again, sound effects are supported by Inform 7 only on the Glulx platform, and even then we must be prepared for the fact that not all interpreters will be able to play them back. We must also bear in mind that a sound recording is a large pile of bits, and that adding any kind of sounds will greatly increase the size of the Blorb file for the released Glulx story file.
The sound files provided must have one of two formats: AIFF or Ogg Vorbis. AIFF is a traditional format in the recording industry, though it is more familiar to Mac OS X users than Windows users. It is uncompressed, giving what can be excellent audio quality, but at the cost of sometimes enormous file sizes - perhaps as much as 10 MB per minute, though this can be greatly reduced by lowering the sampling frequency, and halved again by dropping from stereo to mono.
Except for very short sound effects, we recommend using Ogg Vorbis instead. This is a compressed format whose file sizes will typically be more like 1 MB per minute. Inform uses Ogg Vorbis as the only format safe from licencing and patent disputes. (We would very much have liked to provide MP3 support, but this is no longer legally possible for free software.)
Support for Ogg Vorbis is not built in to either Windows or Mac OS X, and any sound recording you make will probably have to be made first to another format (perhaps AIFF or WAV), and then converted. See xiph.org/vorbis for encoding software which can convert from other sound formats to Vorbis.
Lastly, it must be remembered that recording industry bodies are very hostile to established copyright law covering fair use, parody, quotation of insubstantial passages, etc., when it comes to mixing or using commercially released music. They are well-resourced and highly litigious. If you use sound effects not originated by yourself, you do so at your own risk, even if what you do is perfectly legal on any reading of the statutes.
Sound effects are accommodated on the same basis as illustrations. The relevant media files need to be placed in a subfolder of the project's ".materials" folder, but this time called Sounds rather than Figures, so for instance:
Example.inform
Example.materials
Figures
Woodlands.png
Blackberry.jpg
Red Admiral Butterfly.png
Sounds
Rustling leaves.ogg
Again, these must be declared in the source text:
Sound of rustling leaves is the file "Rustling leaves.ogg".
And they can be played using a special phrase:
play (sound name)
This phrase causes the sound effect to be played. If the option "one time only" is used, it will have no effect if the sound effect has been played before. Example:
play the sound of rustling leaves;
It's conventional for web pages to provide "alt-text" for significant images displayed, so that partially sighted or blind users can get an idea of what is being shown. Inform allows figures to be given these short descriptions like so:
Figure 2 is the file "butterfly.jpg" ("A red admiral butterfly.").
As we'll see, the same can be done for the cover image:
Release along with cover art ("A cathedral at sunset.").
And also for sounds:
Fugue is the file "Bach.ogg" ("A church organ playing a Bach fugue.").
(i) Names for figures, such as "Figure of Woodlands", are values for a special kind of value called "figure name". This can in turn be used to define variables, properties and phrases:
The turn card image is a figure name that varies.
An Old Master is a kind of thing. An Old Master has a figure name called appearance. Figure 1 is the file "Giaconda.jpg". The Mona Lisa is an Old Master. The appearance of the Mona Lisa is Figure 1.
To place (F - a figure name) in the gallery: ...
(ii) Similarly, names for sound effects, such as "Sound of rustling leaves", are values for the kind of value "sound name".
(iii) In the released, blorbed-up Glulx file, figures and sound effects are internally given resource ID numbers which count upwards from 2 in order of their declaration. (Figure and sound numbers can thus be intermingled, if their declarations are.) Resource ID number 1 is reserved for the image of the cover art, if there is any. (See the chapter on Publishing.) To obtain these numbers, if we need them, we can use:
Glulx resource ID of (sound name) ⇒ number
This phrase produces the ID number used in the eventual Glulx file for the given sound effect.
(iv) Glulx hackers may also like to know that whenever Inform 7 builds a project for Glulx, the Inform 6 code it generates always contains a full copy of John Cater's definitive header file "infglk.h".
Once an Inform project is released, it is playable as a "story file", which is in effect a computer program for a specially IF-adapted design of computer. Story files run in what in computing is sometimes called a "sandbox", a kind of safe play area where it can be guaranteed that they cannot do any harm. This is good, because it means a story file can't be infected with viruses or other malware. If the project's Settings panel has the story file format set to the Z-machine, the story file is so thoroughly boxed in that it cannot even see the bigger computer beyond: it lives in a world of its own. But the Glulx format opens the door a crack, allowing the story file to read and write a small number of data files, which live in a single folder on the bigger computer's hard drive.
Why might we want this? Among the reasons are -
- to remember what has happened in previous attempts by the player;
- to store the player's preferences;
- in a two-part story, where each part is an independently released story file, to allow Part I to save some information about its ending which Part II could then pick up and make use of;
- to communicate with some external program, such as an Internet service.
Like figures and sounds, files must be declared before they can be used. For instance:
The File of Glaciers is called "ice".
This creates a new named constant "File of Glaciers" to refer to the file, throughout the source text. We use this name for it whether or not the actual disc file exists yet: it might be one that will only be created if something unusual happens in play, for instance.
Quoted filenames should contain only letters and digits, should be 23 characters or fewer, and should begin with a letter. (In particular they can contain no slashes or dots - no subfolders or extensions can be indicated.) The actual filename this translates to will vary from platform to platform, but "ice.glkdata" is typical, stored in some sensible folder.
Every file has an owner - not a person, but the project which normally writes to it. Inform assumes that the current project will be owning any file which it declares - the File of Glaciers, for instance. But we can optionally specify that it is owned by somebody else:
The file of Boundaries (owned by another project) is called "milnor".
The file of Spectral Sequences (owned by project "4122DDA8-A153-46BC-8F57-42220F9D8795") is called "adams".
Inform uses ownership to make sure that we do not accidentally read in a file which has nothing to do with us, but merely happens to use the same name. Thus it is an error to read a file whose ownership does not agree with our declaration. Saying that a file is owned by "another project" allows us to read it whatever the owner is (so this can be used for files shared between multiple projects); specifying exactly where it needs to come from allows us to pass information from one project to another. Note that we identify projects using the IFID number - this can be found in the Contents index for a project, or by typing VERSION during play; see the chapter on Publishing for more about IFIDs.
Files are indexed in the Contents index, alongside figures and sound effects.
Two technicalities. First, constants such as "File of Glaciers" are of a kind of value called "external file" (compare "figure name" and "sound name"). Second, Inform's file-handling is provided for the Glulx machine, which in turn uses the Glk interface. This allows for either text or binary files. Inform's higher-level phrases to do with files, described in this chapter, all use text files, and all declared files are text files by default. But we can optionally add the keyword "binary" to declare a binary file, if needed:
The binary File of Glaciation Data is called "icedata".
The main use for files is to store and retrieve data, and the most flexible form of data used by Inform is the Table, so facilities are provided which make it as easy as possible to write and read the contents of a table to files. If so, the file must contain just one single table: so to write multiple tables, we need to write multiple files, one for each.
To save the contents of a table to a file, we use the phrase:
write (external file) from (table name)
This phrase causes the entire contents of the given table to be written out to the given file. Note that files must have been declared, and must be referred to by their Inform names, not by textual filenames. Example:
write File of Glaciation Data from the Table of Antarctic Reserves
Any blank rows in the table are automatically moved to the bottom, and only the non-blank rows are written.
To load a file back into a table,
read (external file) into (table name)
This phrase causes the entire contents of the given table to be read in from the given file. Note that files must have been declared, and must be referred to by their Inform names, not by textual filenames. Example:
read File of Glaciation Data into the Table of Antarctic Reserves
Any rows left spare at the foot of the table are automatically blanked. On the other hand if the file is too large to fit into the table - with too many columns or too many rows - a run-time problem is produced.
We can check if a file already exists using:
if (external file) exists:
This condition is true if the file-system used by the player appears to contain a file with the right name. For example, if we declared:
The binary File of Glaciation Data is called "icedata".
and then tested
if the File of Glaciation Data exists, ...
then Inform would search for a file called "icedata". (The arrangements for where this might be stored, and its filename extension, vary from platform to platform.)
One unfortunate restriction must be kept in mind. Some of what is stored in tables is solid information whose meaning never changes: the number 342, for instance, means the same to everyone. But other information depends entirely on the current location of certain structures in memory - for instance, a rule is internally referred to by its memory location. This potentially changes each time Go or Replay is clicked, and so it is not safe to pass it from one copy to another, or from one project to another. The only tables which Inform allows us to write into files are those containing "safe" data: numbers, units, times of day and kinds of value with named alternatives. Scenes, rules or rulebooks, in particular, are not allowed.
Examples
000.
Keeping a preference file that could be loaded by any game in a series.
000.
Remembering the fates of all previous explorers of the labyrinth.
000.
A scoreboard that keeps track of the ten highest-scoring players from one playthrough to the next, adding the player's name if he has done well enough.
Text can also be saved to a file, and again all file-handling is automatic:
write (text) to (external file)
This phrase makes the given text become the entire contents of the named file. Note that files must have been declared, and must be referred to by their Inform names, not by textual filenames. Example:
write "Jackdaws love my big sphinx of quartz." to the file of Abecedary Wisdom;
append (text) to (external file)
This phrase adds the given text to the end of the current contents of the named file (creating it if it does not exist on disc). Note that files must have been declared, and must be referred to by their Inform names, not by textual filenames. Example:
append "Jinxed wizards pluck ivy from the big quilt." to the file of Abecedary Wisdom;
The quoted text can, of course, contain substitutions, so can be long and complex if need be.
Text from a file is printed back with the text substitution:
say "[text of (external file)]"
This text expands to the contents of the named file. Note that files must have been declared, and must be referred to by their Inform names, not by textual filenames. Example:
"[text of the File of Abecedary Wisdom]"
To copy one file to another, for instance,
write "[text of the file of Abecedary Wisdom]" to the file of Secondary Wisdom;
Examples
000.
Notebooks in which the player can record assorted notes throughout play.
000.
An expansion on the notebook, allowing the player somewhat more room in which to type his recorded remark.
Provided we declare the files in the right way, it is easy for one project to read a file created by another project.
But if we want more rapid communication, between two projects which are each playing at the same time, we need to be more careful. What if project A tries to read the file at the same moment that project B is writing it?
To avoid this, we have a concept of files being "ready". A file is ready if it exists, and is completely written, and not in use elsewhere. We have already seen:
if the file of Invariants exists...
But now we want a stronger condition:
if ready to read (external file):
This condition is true if the file exists and is marked as being ready to read; that is, it is not in a state where another program is currently writing it. Example:
if ready to read the file of Invariants, ...
A file cannot be ready to read if it does not exist, so this is a stronger condition. If A and B are attempting communication in real time, both running at once, then Project A should check that an external file owned by B is ready before it tries to read it. Files can also be marked as ready or not ready, in effect claiming them, thus:
mark (external file) as ready to read
This phrase marks that we have finished writing to the given file, so that any external program is welcome to read it now. Example:
mark the file of Invariants as ready to read;
mark (external file) as not ready to read
This phrase marks that we are about to start writing to the given file, so that any external program should wait until we're finished if it wants to read the file. Example:
mark the file of Invariants as not ready to read;
Possibilities really begin to open up when project A is our story file, but B is not another story file at all: it is some external program such as a Web service, say. (Of course this is harder to set up, since the player needs to have both A and B running at the same time, but for stories running on an Internet server this can all be made seamless.)
When Inform begins writing a table, or text, to a file, it initially marks the file as not ready: only when the table or text is completely written and the file about to close is the file marked as ready.
In order to write non-story-file programs as B, communicating with story files as A, we need to know the file format used by Inform. An Inform file is currently a Unix text file (with 10 as the line division character), encoded as ASCII Latin-1. (We would like to use Unicode at some point in the future, but the Glk and Glulx layers are still not fully converted to Unicode.) It opens with a single header line in the form:
The opening character is an asterisk if the file is currently ready, a hyphen if the file is currently not ready. The IFID between the slashes is the IFID number of the project which last wrote to the file. (Marking "ready" or "not ready" does not count as a write for this purpose.) If an external program wrote the file, it should call itself something which will not clash with any story file's IFID. The leafname is the filename text used inside the story file where the file was declared. For instance:
* //4122DDA8-A153-46BC-8F57-42220F9D8795// ice
Example
000.
Using external files, together with a simple Unix script running in the background, to provide live news headlines inside a story file.