Inform Release Notes

Inform Compiler 6.30 to 6.36

Introduction

This is a maintenance release of the Inform system for creating adventure games, intended to address issues that have arisen since the 6.21 compiler and 6/10 library files were released in 1999, and subsequently since the 6.30 compiler was released in 2004. The focus is primarily on fixing problems, but a small number of enhancements are included.

Although just about all known bugs are fixed, the approach to enhancing Inform is more conservative. The selection of suggestions to implement has been governed by three factors:

Having said that, this release does incorporate one major advance. It is based on Andrew Plotkin’s bi-platform — Z-machine and Glulx — compiler and library files, which were in turn derived from the the 6.21 compiler and 6/10 library. The result is that the two Virtual Machine (VM) strands have merged into a single compiler and library which, although continuing by default to produce Z-code, can alternatively generate code for the Glulx VM if you supply the -G compiler switch. There’s more on this topic in Support for Glulx. Before that, though, we’ll summarise the changes to the compiler and to the library files.

If you’ve translated the Inform library into another language, you may find Information for translators helpful.

Since the first release of the Inform 6.3 compiler, the Inform 6 library has been split into a separate project, maintained at https://gitlab.com/DavidGriffith/inform6lib.

Acknowledgements

Far too many people contributed towards this release — reporting and resolving bugs, making helpful suggestions, providing support and facilities, testing, and so on — for their names to be individually listed. So instead, this is a general thank-you to everybody who has made this release happen, and specific ones to Graham for permitting it in the first place, and to Andrew for his pioneering work on Glulx.

Compiler 6.36

These are the changes delivered in version 6.36 of the Inform compiler.

Features added

Bugs fixed

Compiler 6.35

These are the changes delivered in version 6.35 of the Inform compiler.

Features added

Bugs fixed

Compiler 6.34

These are the changes delivered in version 6.34 of the Inform compiler.

Features added

Bugs fixed

Compiler 6.33

These are the changes delivered in version 6.33 of the Inform compiler.

Features added

Bugs fixed

Compiler 6.32

These are the changes delivered in version 6.32 of the Inform compiler.

Features added

Items of the form [C63NNN] quote the feature’s reference number in the ‘Compiler’ section of the Inform Patch List.

Bugs fixed

Items of the form [C63NNN] quote the bug’s reference number in the ‘Compiler’ section of the Inform Patch List.

Compiler 6.31

These are the changes delivered in version 6.31 of the Inform compiler.

Bugs fixed

Items of the form [C63NNN] quote the bug’s reference number in the ‘Compiler’ section of the Inform Patch List.

Compiler 6.30

These are the changes delivered in version 6.30 of the Inform compiler.

Features added

Bugs fixed

Items of the form [C62NNN] quote the bug’s reference number in the ‘Compiler’ section of the Inform Patch List.

Library 6/11

At the same time as the Inform 6.30 release, a new version of the Inform library was made. These are the changes delivered in version 6/11 of the Inform library.

Features added

Library routines

Several new library routines are provided to harmonise commonly-encountered differences between the Z-machine and Glulx VMs (see also Library routines available only in Glulx).

KeyCharPrimitive()

waits for a single key, and returns the character from 1-255 (or, for Glulx, one of the Glk special key codes.). For Glulx only, an extended form is available — see Library routines available only in Glulx.

KeyDelay(time)

waits time tenths of a second for a single key. If no key is pressed within that period it returns zero; otherwise it returns the character from 1-255.

ClearScreen()
ClearScreen(selector)

ClearScreen() clears both the status line and the main window. The cursor moves to the top of the screen. The routine should be followed by a call to MoveCursor() or MainWindow(). Alternatively, using ClearScreen(selector): if selector is 0, both are cleared; if selector is 1 only the status line is cleared; if selector is 2 only the main window is cleared.

MoveCursor()
MoveCursor(line, column)

MoveCursor() selects the status line for output. MoveCursor(line, column) selects the status line for output and moves the cursor to the given line and column within the status area, where line 1 is the top line and column 1 is the far left. (This is necessary because the Glk convention is to number both lines and columns from 0 rather than 1.)

MainWindow()

selects the main (buffered) text window for output.

StatusLineHeight(lines)

sets the height of the status line in lines. The standard DrawStatusLine() calls this every turn, which isn’t a bad thing, since StatusLineHeight() is smart. If you replace DrawStatusLine(), maintain this convention. (The library menu routines fiddle with the status line, and it’s up to DrawStatusLine() to reset it after the menus are over.)

A new library variable gg_statuswin_cursize holds the current setting (in both VMs).

ScreenWidth()

returns the number of characters that can be printed in a monospaced font between the left and right borders of the currently selected window. For Glulx only, the extended form ScreenWidth(win) works on a specified window id; note that the results are unreliable if the normal style for that window uses a proportional font.

ScreenHeight()

returns the height in lines of the main window.

SetColour(fg, bg)
SetColour(fg, bg, selector)

SetColour(fg, bg) sets the current foreground and background text colours, using the same codes as the @set_colour opcode in the Z-machine (1=default, 2=black, 3=red, 4=green etc.). Using SetColour(fg, bg, selector), colours can be set separately in each window: if selector is 0, both are set (the top window will have inverted colours for the Z-machine); if selector is 1 only the status line is affected; if selector is 2 only the main window is affected.

All colour functions are effective only if the library variable clr_on is set to non-zero.

The advantage over @set_colour is that when the player restores a saved game or types UNDO, the colours will be correct for that state of the game.

For Glulx, the routine produces an appropriate effect if style hints are enabled by the interpreter; it also clears the screen. For the Z-machine, a separate call to ClearScreen() is required.

These associated constants are now provided for use with SetColour(); the final three are useful also with ClearScreen():

  Constant CLR_DEFAULT 1; 
  Constant CLR_BLACK 2; 
  Constant CLR_RED 3; 
  Constant CLR_GREEN 4; 
  Constant CLR_YELLOW 5; 
  Constant CLR_BLUE 6; 
  Constant CLR_MAGENTA 7; 
  Constant CLR_CYAN 8; 
  Constant CLR_WHITE 9; 
  Constant CLR_PURPLE 7; 
  Constant CLR_AZURE 8; 
  Constant WIN_ALL 0; 
  Constant WIN_STATUS 1; 
  Constant WIN_MAIN 2;

DecimalNumber(num)

prints num as a decimal number (it is in fact identical to print num;). It may be useful in conjunction with...

PrintToBuffer(array, arraylen, string)
PrintToBuffer(array, arraylen, obj)
PrintToBuffer(array, arraylen, obj, prop)
PrintToBuffer(array, arraylen, routine, arg1, arg2)

prints its arguments — a string, an object’s name, the value of an object’s property, or a routine with up to two arguments — to the buffer array. The number of characters written to the buffer is a word at array-->0 (and is the value returned by the routine); the actual characters start at array->WORDSIZE. The maximum number of characters is specified in arraylen; for the Z-machine, an overrun caused by printing more than this value will produce an error message that you have corrupted the contents of memory beyond the array (for Glulx, the output is automatically truncated at the specified arraylen).

For Glulx, see also PrintAnyToArray() in Library routines available only in Glulx, which has slightly extended capabilities, and which returns the number of characters written rather than writing them at array-->0.

Length(string)
Length(obj, prop)

returns the number of characters in the string. Note that this prints to one of the parser arrays, and therefore it is your responsibility to ensure that the length cannot be greater that 160 characters.

UpperCase(char)
LowerCase(char)

return char in upper or lower case (if it was alphabetic), or unchanged (otherwise). Changes affecting A-Z and a-z are always reliable; changes to accented characters will not work if you have supplied a compiler switch -C2 through -C9, or used a Zcharacter directive to adjust the standard ZSCII character set.

PrintCapitalised(obj, prop, flag, nocaps)

is based upon PrintOrRun(obj, prop, flag). PrintOrRun() tests obj.prop, and either runs it (if a Routine), or prints it (if a String). In the latter case, a newline is then output unless flag is true. PrintCapitalised() does all that; the difference is that the first letter of any output is in upper case unless nocaps is true.

Cap(string, nocaps)

prints the string with the first letter in upper case, unless nocaps is true. Can also be used as a print rule:
  print ..., (Cap) myString, ...;

Centre(string)
Centre(obj, prop)

prints a single-line string approximately centrally between the left and right borders of the screen by preceding it with an appropriate number of spaces. The routine works only for monospaced fonts (that is, after font off;), and is only likely to work well in the main Glulx window if the normal style for TextBuffer uses a non-proportional font. It is however useful for centring information in the status line. Can also be used as a print rule:
  print ..., (Centre) myString, ...;

PrintOrRunVal(value, flag)

if value refers to to an object, prints that object’s name; if value refers to a routine, runs that routine; if value refers to a string, prints that string (with a terminating newline unless flag is true).

Bugs fixed

Items of the form [L610NN] quote the bug’s reference number in the ‘Library’ section of the Inform Patch List.

Support for Glulx

One of the limitations of the Z-machine is the size of the largest game that it supports: 256Kb if you compile a version 5 game, or 512Kb if you compile for version 8. If you find yourself up against this limit and you’ve tried all the standard tricks to save a few bytes here and there, then it’s time to switch to Glulx. That’s easy to do: you just supply the -G compiler switch, the compiler generates Glulx code, and any Glulx interpreter will be able to run it.

Actually, it isn’t always quite that simple...

Knowing which is which

As mentioned earlier, the compiler automatically defines a couple of useful constants. If you’re compiling for the Z-machine then TARGET_ZCODE is defined; if you’re compiling for Glulx then you’re given TARGET_GLULX instead. You can use these with the Ifdef directive, like this:
  #Ifdef TARGET_ZCODE; 
  ! Z-machine code here 
  #Endif;
  #Ifdef TARGET_GLULX; 
  ! Equivalent Glulx code here 
  #Endif; 
or more commonly like this:
  #Ifdef TARGET_ZCODE; 
  ! Z-machine code here 
  #Ifnot;
  ! Equivalent Glulx code here 
  #Endif; 
You’ll find a lot of this if you look in the library files, but it’s less frequently needed in a source file.

Glulx differences

The two VMs are not identical, and you need to be aware of their differences.

Word size

The most basic difference between Glulx and the Z-machine is that words are four bytes long instead of two. All Glulx variables are 32-bit values, the stack contains 32-bit values, property entries are 32-bit values, and so on.

In most Inform programming, you don’t need to worry about this change at all. For example, if you have an array

  Array mylist --> 10;
...then Z-code Inform allocates ten words — that is, twenty bytes — and you can access these values as mylist-->0 through mylist-->9. If you compile the same code under Glulx Inform, the compiler again allocates ten words — now forty bytes — and you can still access them as mylist-->0 through mylist-->9. Everything works the same, except that the array can contain values greater than the Z-machine’s limit of 65535.

Table arrays also refer to two- or four-byte word values, and the first word is the length of the array. String and -> arrays, and the -> notation, still refer to single bytes. You should not have to modify your code here, either.

There are two important cases where you will have to modify your code. First is the .# operator. The expression obj.#prop returns the length of the property in bytes. Since properties almost always contain words rather than bytes, it is very common to have Z-machine code like:

  len = (obj.#prop) / 2; 
  for (i=0 : i<len : i++) 
      print (string) (obj.&prop)-->i; 
In Glulx Inform programs, it is necessary to divide by 4 instead of by 2, so you should replace the above code with:
  len = (obj.#prop) / WORDSIZE; 
  for (i=0 : i<len : i++) 
   print (string) (obj.&prop)-->i; 
This will compile and run correctly in both VMs.

The other circumstance where your code may need modifying in this manner is when using the ‘print to array’ feature. Code like this:

  Array mybuf buffer 100; ! must be big enough for largest string you'll print 
  mystr = "hello"; 
  mystr.print_to_array(mybuf);
results in the first word of mybuf containing 5 (the number of characters in mystr), and the following five bytes containing ‘h’, ‘e’, ‘l’, ‘l’ and ‘o’. In the Z-machine, you could then output the characters from the array with either of these code fragments:
  len = 2 + mybuf-->0 
  for (i=2 : i<len : i++)
   print (char) mybuf->i; 

  len = mybuf-->0 
  for (i=0 : i<len : i++)
   print (char) mybuf->(i+2);
Again, you can make the code safe for both VMs if you change “2” to “WORDSIZE”:
  len = WORDSIZE + mybuf-->0 
  for (i=WORDSIZE : i<len : i++)
   print (char) mybuf->i; 

  len = mybuf-->0 
  for (i=0 : i<len : i++)
   print (char) mybuf->(i+WORDSIZE);
See also Features available only in Glulx for details of print_to_array under Glulx.

Directives

Glulx handles the majority of Inform directives, with two exceptions:

Statements

Glulx handles the majority of Inform statements, with a few exceptions. If you try to use any of these statements in Glulx, you will cause a compilation error: Glulx now supports the Inform style statement, mapping the Z-machine forms onto Glk styles:
Inform statementEquivalent Glk style
style roman;style_Normal
style reverse;style_Alert
style bold;style_Subheader
style underline;style_Emphasized
style fixed;style_Preformatted
However, it is important to remember that, with Glulx, the appearance of a game is under the player’s control — Glulx interpreters enable the font parameters associated with each style to be specified at run-time. Therefore, you should use styles with caution, and if necessary alert the player to the settings which your game expects.

Character handling

Unlike the Z-machine, which internally uses the ZSCII character set (see the Inform Designer’s Manual Table 2 on p. 519), Glulx uses strings consisting of either 8-bit characters in the ISO 8859-1 (Latin-1) encoding (which is the same as ZSCII for character values 0-127, but different for character values 128-255), or 32-bit Unicode characters. In general you don't need to worry about which string representation is used: the compiler will figure it out for you. The impact is as follows:

Compiler switches

We’ve already mentioned the -G command line switch, which causes the compiler to output code for Glulx rather than for the Z-machine. There are a few other changes in this area (see the Inform Designer’s Manual Table 3 on p. 521).
SwitchToMeaning
-koff/onincompatible with -G (output debugging information)
-v*3 to 8incompatible with -G (set Z-machine Version)
-Goff/oncompile for Glulx VM
-Hoff/onuse Huffman compression on Glulx strings (on by default)
-Xoff/onincompatible with -G (include Infix debugger)

Glulx additions

Glulx offers some additional capabilities above those of the Z-machine:

Features available only in Glulx

Library routines available only in Glulx

KeyCharPrimitive(win, nostat);

If win is nonzero, the character input request goes to that Glk window (instead of gg_mainwin, the default.) If nostat is nonzero, any window rearrangement event is returned immediately as value 80000000 (instead of the default behavior, which is to call DrawStatusLine() and keep waiting.)

PrintAnything(thingie, ...);

In the Z-machine, strings and routines are “packed” addresses, dictionary words are normal addresses, and game objects are represented as sequential numbers from 1 to #top_object. These ranges overlap; a string, a dictionary word, and an object could conceivably all be represented by the same numeric value.

In Glulx, all those things are represented by normal addresses, so different items will always have different values. Furthermore, the first byte found at the address is an identifier value, which specifies what kind of item the address contains.

PrintAnything() prints any thingie — string, routine (with optional arguments), object, object property (with optional arguments), or dictionary word — known to the library.

CallingIs equivalent to
PrintAnything()(nothing printed)
PrintAnything(0)(nothing printed)
PrintAnything(string)print (string) "string";
PrintAnything(dictionaryword)print (address) 'dictionaryword';
PrintAnything(obj)print (name) obj;
PrintAnything(obj, prop)obj.prop();
PrintAnything(obj, prop, args...)obj.prop(args...);
PrintAnything(routine)routine();
PrintAnything(routine, args...)routine(args...);
Extra arguments after a string or dictionary word are safely ignored. The (first) argument you pass in is always interpreted as a thingie reference, not as an integer. This is why none of the forms shown above print out an integer. However, you can get the same effect by calling
  PrintAnything(DecimalNumber, num);
...which is where the DecimalNumber() routine comes in handy. You can also, of course, use other library routines, and do tricks like
  PrintAnything(EnglishNumber, num); 
  PrintAnything(DefArt, obj);
None of this may seem very useful; after all, there are already ways to print all those things. But PrintAnything() is vital in implementing the following routine:

PrintAnyToArray(array, arraylen, thingie, ...);

This works the same way, except that instead of printing to the screen, the output is diverted to the given array.

The first two arguments must be the array address and its maximum length. Up to that many characters will be written into the array; any extras will be silently discarded. This means that you do not have to worry about array overruns.

The PrintAnyToArray() routine returns the number of characters generated. (This may be greater than the length of the array. It represents the entire text that was output, not the limited number written into the array.)

It is safe to nest PrintAnyToArray() calls. That is, you can call PrintAnyToArray(routine), where routine() itself calls PrintAnyToArray(). (However, if they try to write to the same array, chaos will ensue.)

It is legal for arraylen to be zero (in which case array is ignored, and may be zero as well.) This discards all of the output, and simply returns the number of characters generated. You can use this to find the length of anything — even a function call.

Entry points available only in Glulx

An entry point is a routine which you can provide in your code, or leave out; the library will call it if it’s present, ignore it if not — see §21 of the Inform Designer’s Manual.

The library has some entry points which aid in writing more complicated interfaces — games with sound, graphics, extra windows, and other fancy Glk tricks. If you’re just writing a standard Infocom-style game, you can ignore this section.

HandleGlkEvent(ev, context, abortres)

This entry point is called every time a Glk event occurs. The event could indicate nearly anything: a line of input from the player, a window resize or redraw event, a clock tick, a mouse click, or so on.

The library handles all the events necessary for a normal Infocom-style game. You need to supply a HandleGlkEvent() routine only if you want to add extra functionality. The ev argument is a four-word array which describes the event. ev-->0 is the type of the event; ev-->1 is the window involved (if relevant); and ev-->2 and ev-->3 are extra information. The context argument is 0 if the event occurred during line input (normal commands, YesOrNo(), or some other use of the KeyboardPrimitive() library routine); 1 indicates that the event occurred during character input (any use of the KeyCharPrimitive() library routine). The abortres argument is used only if you want to cancel player input and force a particular result; see below.

If you return 2 from HandleGlkEvent(), player input will immediately be aborted. Some additional code is also required:

If you return -1 from HandleGlkEvent(), player input will continue even after a keystroke (for character input) or after the enter key (for line input). You must re-request input by calling request_char_input or request_line_input. Any other return value from HandleGlkEvent() (a normal return, rfalse, or rtrue) will not affect the course of player input.

InitGlkWindow(winrock)

This entry point is called by the library when it sets up the standard windows: the story window, the status window, and (if you use quote boxes) the quote box window. The story and status windows are created when the game starts (before Initialise()). The quote window is created and destroyed as necessary.

InitGlkWindow() is called in five phases:

  1. The library calls InitGlkWindow(0). This occurs at the very beginning of execution, even before Initialise(). You can set up any situation you want. (However, remember that the story and status windows might already exist — for example, if the player has just typed RESTART.) This is a good time to set gg_statuswin_size to a value other than 1. Return 0 to proceed with the standard library window setup, or 1 if you’ve created all the windows yourself.

  2. The library calls InitGlkWindow(GG_MAINWIN_ROCK), before creating the story window. This is a good time to set up style hints for the story window. Return 0 to let the library create the window; return 1 if you have yourself created a window and stored it in gg_mainwin.

  3. The library calls InitGlkWindow(GG_STATUSWIN_ROCK), before creating the status window. Again, return 0 to let the library do it; return 1 if you have created a window and stored it in gg_statuswin.

  4. The library calls InitGlkWindow(1). This is the end of window setup; you can take this opportunity to open other windows. (Or you can do that in your Initialise() routine. It doesn’t matter much.)

  5. The library calls InitGlkWindow(GG_QUOTEWIN_ROCK), before creating the quote box window. This does not occur during game initialization; the quote box window is created during the game, whenever you print a quote, and destroyed one turn later. As usual, return 1 to indicate that you’ve created a window in gg_quotewin. (The desired number of lines for the window can be found in gg_arguments-->0.)

However you handle window initialization, remember that the library requires a gg_mainwin. If you don’t create one, and don’t allow the library to do so, the game will shut down. Contrariwise, the status window and quote windows are optional; the library can get along without them.

IdentifyGlkObject(phase, type, ref, rock)

This entry point is called by the library to let you know what Glk objects exist. You must supply this routine if you create any windows, filerefs, file streams, or sound channels beyond the standard library ones. (This is necessary because after a RESTORE, RESTART, or UNDO command, your global variables containing Glk objects will be wrong.)

IdentifyGlkObject() is called in three phases:

  1. The library calls IdentifyGlkObject() with phase==0. You should set all your Glk object references to zero.

  2. The library calls IdentifyGlkObject() with phase==1. This occurs once for each window, stream, and fileref that the library doesn’t recognize. (The library handles the two standard windows, and the files and streams that have to do with saving, transcripts, and command records. You only have to deal with objects that you create.) You should set whatever reference is appropriate to the object. For each object: type will be 0, 1, 2 for windows, streams, filerefs respectively; ref will be the object reference; and rock will be the object’s rock, by which you can recognize it.

  3. The library calls IdentifyGlkObject() with phase==2. This occurs once, after all the other calls, and gives you a chance to recognize objects that aren’t windows, streams, or filerefs. If you don’t create any such objects, you can ignore that bit. But you should also take the opportunity to update all your Glk objects to the game state that was just started or restored. (For example, redraw graphics, or set the right background sounds playing.)

Information for translators

The library is designed to be easily translatable, and there are currently versions available in French, German and several other languages. If you maintain one of these translations, the following information may be useful to you.

English.h

This is the “language definition file”, and in translation is replaced by French.h, German.h, etc. The following changes have been made:

Grammar.h

This file contains grammars for English verbs like TAKE and DROP, and in translation is replaced by FrenchG.h, GermanG.h, etc. The following changes have been made: