What's New in Inform 5.5, v1502 ------------------------------- (5 July 1995) Version 5.5 is the first upgrade of the Inform compiler to provide new features (rather than just fix bugs) for about 10 months. For the last year or so, my intention has been to keep the Inform language stable, but to continually enhance the library. Although these additions do change the language, they are hopefully minor and logical, and several have long been asked for. Inform 5.5 removes no features of 5.4 (except in so far as it tests for a few more definite errors, so some old but subtly wrong code may not compile: this is probably a good thing). However, it does now produce warnings if the source code uses obselete features (leftovers from Inform 1 right up to those from Inform 5.4). The new syntax is so much nicer that I suggest changing over to it rather than switching off these warnings at the command line. The short answer is: tidier object-name printing, more logical and flexible syntax for creating arrays, tables of data, slightly better grammar replacement, a switch statement and provision for compiling very much larger games (up to twice the size of the previous maximum, which was pretty huge anyway). *** It is recommended that Inform 5.5 should only be used with *** *** library release 5/10 and later *** (there was a minor bug in 5/9 which can cause compilation errors: an example of "old but subtly wrong code"). In any case, accompanying this release is library 5/11. 0. Bugs fixed from v1405 (all minor) ------------------------------------- (a) comments caused problems when coming between a loop construct (e.g. "for (i=1:i<10:i++)") and its block of code. (b) using the "box" command inside an embedded routine (which is seldom sensible, actually!) sometimes produced mysterious compilation errors. (c) the code for explicitly-defined string constants, such as Constant Game "Lost Trifles of Bluffocom"; has been thoroughly rewritten, since the old method produced wrong results in some cases (such as if a global variable had been given as initial value a string, e.g. by: global Var="hello";). (d) initial values like strings and dictionary words for global variables (at declaration time) now work properly. (e) removal of a foolish restriction rather than bug: string constants had to be preceded by a # in usage, (e.g. print #Dragons_Name;) for no good reason. (E.g., print Dragons_Name; now works.) (f) the #r$RoutineName construction didn't work when RoutineName was within the first 256 bytes of the code area (only library internals live there, and this bug has never been observed in honest code!) (g) actually, this is the same as in v. 5.4, but I seem to have forgotten to publicise it: the function indirect(R); (to call the function with address R, so for instance indirect(#r$MyFunction)) can now take an optional second and third argument. indirect(R,x); calls function R with one parameter, x, and indirect(R,x,y); calls it with two, x and y. (h) the notoriously unhelpful (though happily rare) "a label has moved between passes" error message has been rewritten to suggest the likely cause: it now reads An error has occurred just before this point with what Inform thought was a constant. Perhaps it was a global variable used before its definition in the code. (i) local variables can't be called "sp" (this is the name of the stack pointer, so crashes result) and an error is now produced if they are. 0'. Bugs fixed in, and additions made to, the beta-version ----------------------------------------------------------- A beta-testing version of Inform 5.5 was (silently by me) put onto the archive for porters to experiment with (though in the event many other people found it). This version, v1501, included a serious bug (l) and several improvements have been made since: (j) the assembly opcode @set_colour now works (actually, this bug is a year old, and was simultaneously found by two different people in the last couple of months). (k) values in switch statements are now allowed to take the form "x to y" (for x<=v<=y), stolen from Modula-3 at Gareth Rees's suggestion. (l) a bug in final file output caused incorrect game files to be produced if no version-number had been explicitly set (it now by default produces legal V5 files). (m) details for the VMS port (for VAX or Alpha), the Linux, OS/2 and Mac ports have been updated; the latter now includes a Macintosh Programmer's Workshop version. (n) it's difficult to compile dictionary words which contain apostrophes (e.g., words such as "jemima's") as constants under I5.4; under I5.5, apostrophes ' in dictionary words should now always be written with an ^. For example, the constant 'isaac^s' refers to the word "isaac's". (o) if you specify a property in an object description but give it no data values, I5.5 now supplies a 0 word as value (5.4 used to compile an empty list, which was logical but apt to confuse the interpreter). (p) the grammar enhancements in (10) below were added. (q) different file extensions for all of V3 to V8 inclusive can now be made, if the port makes suitable #defines in header.h. (r) giving a non-default output filename at the command line didn't work in v1501, owing to a foolish bug. (s) fuller details are given below of the V8 alterations needed to "Zip". (t) overflow of the buffer used for text-transcription (to make "proofs" of the text in a game) is now checked for; previously this might have caused memory (typically the symbols table) to be corrupted on very large games when compiling with -r set. 1. Printing ------------ Recall that the "print" and "print_ret" commands take a list of items, separated by commas, printing each in turn. (The latter then prints a new-line and returns true.) The new preferred syntax for these items is one of: print this number "string" print this string char print the character this is the ASCII code of object print the given object's true short name (this shouldn't be used except by the Library, as it will fail to notice the short_name routines many objects provide) (the) expression the definite article and short-name of this object (The) expression the same, but capitalising the definite article (a) expression the indefinite article and short-name (name) expression just the short-name (char) expression same as print char, for consistency (number) expression print the number but in words, not numerals (string) expression print the string at this (packed) address: good for printing out property values which are strings in double-quotes (address) expression print the string at this byte address: good for printing out dictionary words and little else (Routine) expression print nothing: but call Routine(expression) in the expectation that it will print something These new features are cosmetic, but considerably tidy the look of code. E.g., one would write print_ret (The) x1, " explodes messily. Perhaps it was unwise to \ drop it into ", (the) x2, "."; to produce, say, The hand grenade explodes messily. Perhaps it was unwise to drop it into the glassworks. whereas in 5.4 it would have to have been CDefArt(x1); print " explodes messily. Perhaps it was unwise to \ drop it into "; DefArt(x2); "."; 2. Global variables, tables and arrays --------------------------------------- The whole syntax for declaring global variables has been tidied up and extended. The new syntax for declaring a variable "frotz" is: Global frotz; Ordinary variable, set to 0 Global frotz = ; ..., set to this value Global frotz ; But since one very rarely wants to change the value of "frotz" in this case (it will remain the address of the array throughout the game), the more usual course is to declare Array frotz ; which is identical in effect, except that "frotz" is now a constant. (This can be convenient: it can now be quoted in other array definitions, for instance, or as a property value.) There are four types of array: Syntax Pronounced Contents -> byte array with entries written array->0 up to array->(n-1) (can hold chars and numbers between 0 and 255) --> word array with entries array-->0 up to array-->(n-1) (can hold any Inform number) string string a byte array, in which byte 0 holds the number of data entries in the array table table a word array, in which word 0 holds the number of data entries in the array The array data can also be given in several ways: a single value - allocate this many zero entries; two or more values (divided by spaces) - these are the entries; a string in double-quotes - the entries are the ASCII values of the characters of this string, in sequence; or, as [; ; ....; ; ]; which makes the entries these given values. Semicolons in between the braces are ignored. (The usefulness of this is that very long declarations, which would otherwise overflow Inform source code lines, can be made.) These initial values can be any legal constant (for the first time this allows dictionary words and routine names): e.g., 105 "a quoted string" 'c' 'dictionary' #r$RoutineName For instance, Array frotz -> 20; makes an array of 20 bytes, entries of which can be read or written as frotz->0 ... frotz->19. The initial values are all zero. Whereas: Array frotz -> 4 8 12 16 20; makes an array of 5 bytes, initially holding these five values. Array colours --> "Red" "Yellow" "Blue"; ... print (string) colours-->1; will print "Yellow". Array frotz string "blorple"; makes frotz point to an array with contents 7 'b' 'l' 'o' 'r' 'p' 'l' 'e' (This differs from Array frotz -> "blorple" in creating the initial 7.) A typical large table might have the form of: Array Holidays table [; "New Year's Day" "Twelfth Night"; "Ash Wednesday" "Good Friday"; "Martin Luther King Day"; ]; For instance, the names of songs played by the radio in "Curses" occupies a (pretty enormous) table, and print (string) RadioSongs-->(random(RadioSongs-->0)); prints out a random name from it. In older versions of Inform the keywords "data", "initial" and "initstr" would have been used to initialise byte arrays suitably, and (annoyingly) word arrays couldn't be initialised to non-zero values at all. These keywords still work, but are no longer needed. 3. Function calls ------------------ Can now take up to 7 arguments, instead of being restricted to 3. (Except in version-3 games, but I hope everyone's gone over to Advanced (version-5) now.) 4. Switch statements --------------------- Inform now has a "switch" statement similar to that of C. That is, switch(expression) { value1: ... value2: ... ... default: ... } evaluates the expression, and executes the first ... code if it has value1, the second ... if it has value2, and so on: if it has none of the values given, the default ... code is run. (The "default" clause is optional, but if present must be the last clause.) (Unlike in C, there is no "case fall-through"; that is, once the value1 code is executed, control automatically resumes from after the end of the switch statement.) Each value can in fact be a list of values, comma-separated. A value can either be a single Inform constant or something in the form constant1 to constant2 (the range being inclusive). So, for example: switch(random(6)) { 1: "A snake slithers."; 2 to 4: "An elephant bellows."; default: "The jungle is ominously silent."; } The "default" clause can now also be put into embedded routines, like before rules. So, for instance, a room can have the "before" value before [; Jump: "The ceiling is too low."; Look, Inv, Wait: ; default: "An invisible force holds you inactive."; ]; and the "default" rule is run if none of the others are. Note that the Look, Inv and Wait actions are unaffected as we have explicitly done nothing to them. 5. Abbreviate directive ------------------------ The Abbreviate directive can now take a list of abbreviation strings, not just one at a time (and this saves writing out "Abbreviate" 64 times). E.g. Abbreviate ". " ", " " the " "The" "You" "ing" "you" "and" "ight" "with" "all" "'s no" "which" "It is" " th" " no" "t o" "e s" "e i" " to" "e o" "e a" " ma" "t i" " fi" "e w" " for" " con" " ba" "d o" " ro" " di" " can" " lo" "t s" "t w" " com" " ho" " ga" "tion" " from" " ha" "ter" " ea" "This" " hi" " pr" " un" "d s" " fa" "urs" "'s ~" " ex" "der" "d a" " gr" " cl" "d i" " po" " door" "her" " a "; is a plausible set of abbreviation strings, saving about 20K on a 256K game file when Inform is compiling with the "economy" switch -e set. 6. Testing the version number ------------------------------ A special rule now applies to the Ifdef directive. #Ifdef VN_****; ... #Endif; where **** is the four-digit number n, will now compile the code ... exactly if the current Inform version number is at least n. Thus, #Ifdef VN_1501; print "The all new Inform show!^"; #Endif; compiles only under this and later updates. 7. More flexible < > construct ------------------------------- Previously, constructions like ; were rather restricted, in that Action always had to be an action name and noun and second couldn't be compound expressions. The latter restriction is now removed, and the former almost so: if Action is given in brackets (...) then it's worked out as an expression in the usual way. (Brackets are needed to tell Inform that the first word shouldn't be tested as a constant with ## in front, which is what it does with action names.) So, for instance, << (magic_action(frotz_spell)) noun.door_dir memory.number >>; will now compile. All previously-allowed < > and << >> expressions should compile just as they always did. 8. Versions 7 and 8 -------------------- Two new types of story file are implemented: versions 7 and 8, newly created for the purpose. (Inform can produce version 6, but this isn't recommended for text-only games as version 6 is very hard to fully interpret because of complex graphical features.) Version 8 is the one recommended for use, if a game of over 256K is required: it runs up to 512K in length. Inform syntax is fully portable between version-5 (the default) and version-8; all you need to do is to compile with -v8 set. At present versions 7 and 8 are not interpreted by the interpreters in public circulation. However, V8 is particularly easy to add to Mark Howell's "Zip" interpreter; see the end of this file. A fairly large V8 file (a V8 form of the author's new game "Jigsaw") will soon be available for experiment. Support for these new versions should percolate through the public domain in due course, since it's so easily coded, but if you're in a hurry you need only distribute your game with a suitably doctored interpreter. It should be added that a 512K Inform game would be gargantuan, 2.5 times the size of "Curses" (which is itself large as games go) - easily large enough to code the entire Zork and Enchanter trilogies into one. 9. At the command line, and file inclusion ------------------------------------------- Apart from -v8 (and -v7), there are two new command line switches: to change the error message style to Microsoft standard format, and to suppress warnings about obselete usages. There is one new memory command, $huge, which is an abbreviation for lots of large memory settings and saves bother when games have become pretty enormous. If a command line argument begins with a plus sign +, then the rest of that word is taken to be a directory in which the Library can be found. (This is convenient so that only one copy of the Library need be kept, which several projects in different directories can make use of.) E.g., I use something like inform -Tdcx $huge +Library curses/curses games/curses.z5 The Include directive has a convenient new feature: if the file-name to include begins with a >, like so Include ">endgame"; then the file "endgame" is sought from the same directory as the one doing the inclusion. (Otherwise, if it has a / in, it's treated literally, and if not it's sought from the library directory.) This is much less trouble when a game's source code is segmented into many files, as it usually is for large projects. 10. Grammar replacement ------------------------ In library grammar, some verbs have many synonyms: for instance, "attack" "break" "smash" "hit" "fight" "wreck" "crack" "destroy" "murder" "kill" "torture" "punch" "thump" are all treated as identical. But you might want to distinguish between murder and lesser crimes. For this, try Extend only "murder" "kill" replace * animate -> Murder; The keyword 'only' tells Inform to extract the two verbs "murder" and "kill". These then become a new verb which is initially an identical copy of the old one, but then 'replace' tells Inform to throw that away in favour of an entirely new grammar. Similarly, Extend only "get" * "with" "it" -> Sing; makes "get" behave exactly like "take" (as usual) except that it also recognises "with it", so that "get with it" makes the player sing but "take with it" doesn't. You can now also add new verb synonyms. For instance, Verb "acquire" "grab" = "take"; gives the "take" verb two new synonyms. Appendix: Adding Version 8 support to the "Zip" interpreter ----------------------------------------------------------- Only one routine need be changed, "configure". In my copy of the source (10 March 93) it now looks as below (note that in my copy only one if statement has been added). /* * configure * * Initialise global and type specific variables. * */ #ifdef __STDC__ static void configure (zbyte_t min_version, zbyte_t max_version) #else static void configure (min_version, max_version) zbyte_t min_version; zbyte_t max_version; #endif { zbyte_t header[PAGE_SIZE]; read_page (0, header); datap = header; h_type = get_byte (H_TYPE); if (h_type < V4) { story_scaler = 2; story_shift = 1; property_mask = P3_MAX_PROPERTIES - 1; property_size_mask = 0xe0; } else { story_scaler = 4; story_shift = 2; property_mask = P4_MAX_PROPERTIES - 1; property_size_mask = 0x3f; } if (h_type == 8) /* This is my amendment for V8... */ { h_type=V5; story_scaler = 8; story_shift = 3; } /* ...ending here. */ if (h_type < min_version || h_type > max_version || (get_byte (H_CONFIG) & CONFIG_BYTE_SWAPPED)) fatal ("wrong game or version"); h_config = get_byte (H_CONFIG); h_version = get_word (H_VERSION); h_data_size = get_word (H_DATA_SIZE); h_start_pc = get_word (H_START_PC); h_words_offset = get_word (H_WORDS_OFFSET); h_objects_offset = get_word (H_OBJECTS_OFFSET); h_globals_offset = get_word (H_GLOBALS_OFFSET); h_restart_size = get_word (H_RESTART_SIZE); h_flags = get_word (H_FLAGS); h_synonyms_offset = get_word (H_SYNONYMS_OFFSET); h_file_size = get_word (H_FILE_SIZE); if (h_file_size == 0) h_file_size = get_story_size (); h_checksum = get_word (H_CHECKSUM); h_alternate_alphabet_offset = get_word (H_ALTERNATE_ALPHABET_OFFSET); datap = NULL; }/* configure */