This manual describes the `Inform' compiler for adventure games. Copyright (C) 1994 Graham Nelson The Inform Designer's Manual **************************** Last updated 29/9/94 Introduction ************ I will build myself a copper tower With four ways out and no way in But mine the glory, mine the power... -- Louis MacNeice (1907--1963), `Flight of the Heart' Inform: an adventure game compiler ================================== Inform is an adventure game compiler, and this is the book to read about it. Infocom format `story files' (Adventure games, that is) can be played on almost any computer, ancient or modern, and interpreters which run them are widely available, from personal organisers to mainframes. They represent probably the most portable form in which games can ever be written, as no alteration whatever is required to move a game from one model of computer to another. Inform is not just a compiler but an `operating system' too: its library (a suite of standard game routines) allows designers to begin coding at once. An Inform source file need not contain any of the parser code, or the running of the `game universe', only descriptions and exceptions to the usual rules. This world is quite rich already, having over 80 verbs and an extensive grammar: the library understands rooms, objects, duplicates, containers, doors, things on top of other things, light, scoring, switching things on and off, opening, closing and locking things, entering things, travelling about in them and so forth. The parser it uses (which can be entirely invisible to the designer, but is programmable and very flexible) is sophisticated enough to handle ambiguities, clarify its input by asking questions and to cope properly with plurals, vagueness, conversation, pronouns and the player becoming someone else in mid-game. This manual makes occasional reference to the example games provided with Inform, at present `Advent' (a full version of the original mainframe Adventure, which contains a good deal of `everyday Inform'), `Toyshop' (a collection of unusual objects to play with) and `Balances' (a short story consisting of puzzles making great use of the parser's flexibility). Try to get hold of these programs and preferably print them out. There is also a `Shell' game consisting of the minimum code to get going, but all 14 lines of it are given anyway (*note Starting::.). History of the Designer's Manual ================================ The text of this book has evolved from five earlier editions. The old manual was in places rather technical, with a makeshift and sometimes defensive tone (`Inform is an easel, not a painting'). There were specifications of the run-time code format and literary critiques of games gone by: like an oven manual padded out with both a cookery book and a detailed plan of the gas mains. This book contains just the instructions for the oven. So there are three `companion volumes'. `The Craft of Adventure' is an essay on the design of adventure games; `The Specification of the Z-Machine' covers the run-time format and Inform assembly language, its lowest level; and `The Inform Technical Manual' documents chiefly internals, for compiler maintenance and porting. In trying to be both a tutorial and reference work in a budget of about 100 pages of A4, this book aims itself in style halfway between the two extremes of manual, Tedium and Gnawfinger's `Elements of Batch Processing in COBOL-66', third edition, and Mr Blobby's `Blobby Book of Computer Fun'. (This makes some sections both leaden *and* patronising.) I have tried to make every passage tell the truth, so that even early sections are reliable for reference purposes. Passages which divert the main story, usually to tell the unexpurgated truth when this may just confuse the newcomer, are marked with warning exclamation marks, thus: `!!!!'. Lengthy examples are left as exercises, with answers in the Appendix; which also contains a language specification, some reference lists, definitions of attributes and properties, and a large index. The manual was converted to Texinfo format by Gareth Rees. Copyright and copying permissions ================================= The copyright on Inform, the program and its source code, its example games and documentation (including this book) is retained by Graham Nelson, who asserts the moral right to be identified as its author. Having said this, I am happy for it to be freely distributed to anybody who wants a copy, provided that: (a) distributed copies are not substantially different from those archived by the author, (b) this and other copyright messages are always retained in full, and (c) no profit is involved. However, a story file produced with the Inform compiler (and libraries) then belongs to its author, and may be sold for profit if desired, provided that its game banner contains the information that it was compiled by Inform, and the Inform version number. At present, the best source for Inform material (executables of the compiler for different machines, source code, the library files and example games) is the anonymous ftp site `ftp.gmd.de', and its home directory is: /if-archive/infocom/compilers/inform Credits and dedication ====================== Some of the ideas of Inform came from an incremental multi-player game called Tera, on the Cambridge University mainframe, written by Dilip Sequeira and the author in 1990 (whose compiler was called Teraform); in turn, this stole a little from David Seal and Jonathan Thackray's game assembler; which dates back to the late 1970s and was written for `Acheton', perhaps the first worthwhile game written outside America. Still, much of the Inform kernel derives ultimately from the `IEEE Computer' article `Zork: A Computerized Fantasy Simulation Game' by P. David Lebling, Marc S. Blank and Timothy A. Anderson; and more was suggested by Richard Tucker, among others. With the advent of Inform 5 in mid-1994, I feel that the underlying language came to a satisfactory state. The library files, however, may well go on being improved, since users can update these much more easily than the compiler (changes to which mean work for many people). The present library files (release 5/4) are greatly enhanced from the Inform 5.2 files, but compatible with them (except in handling plural nouns and that the obselete attribute `autosearch' has been withdrawn). The list of those who have helped the project along is legion: I should like to thank them all, porters, users and critics alike, but especially Volker Blasius, Paul David Doherty, Mark Howell, Bob Newell, Robert Pelak, Gareth Rees, Jørund Rian, Dilip Sequeira, Richard Tucker and Christopher Wichura. One final word. I should like to dedicate this book, impertinently perhaps, to our illustrious predecessors: Willie Crowther, Don Woods and the authors of Infocom, Inc. Graham Nelson Magdalen College, Oxford September 1994 1 Getting started ***************** 1.1 The `Shell' game ==================== The very first thing to try is to compile the `Hello Cruel World' game, a very short test file supplied with Inform. If that compiles and runs properly (producing a short page of text, then finishing), try the following: Constant Story "SHELL"; Constant Headline "^An Interactive Skeleton^\ Copyright (c) 1994 by (your name here).^"; Include "Parser"; Include "VerbLib"; Object Blank_Room "Blank Room" with description "An empty room." has light; [ Initialise; location=Blank_Room; "^^^^^Welcome to the shell...^^"; ]; Include "Grammar"; end; If this compiles, Inform is probably set up and working properly. It takes a short while to compile, because it `includes' three large standard files, containing a large amount of code. (`Include' lines are also sometimes written `#include' to make C programmers feel more at home.) The library files are: `Parser' The core of the game, and a full parser `Verblib' Routines for many game verbs, like `take' `Grammar' A grammar table to decode the player's input from Together, they make up the `library'. They can certainly be modified by designers, but great effort has gone into making sure the need hardly ever arises. Apart from that, the code contains: 1. strings giving the name of the game, and a copyright message, to be printed out at the appropriate moments; 2. a routine, called `Initialise', which is run when the game begins, and simply sets where the player starts (in the obvious place!) and prints a welcoming message; 3. an object, to be the only room of the game. The `Shell' game is very boring: there is nothing for the player to do but wait and quit. 1.2 Making simple objects ========================= In Inform, everything is an object: rooms and things to be picked up, scenery, intangible things like mist and even some abstract ideas (like the direction `north'). More about this later: for now, here's a new object, to go under the `Blank_Room' definition: Nearby cone "green cone" with name "green" "cone"; (`Nearby' just means it's an object inside the last thing declared as an `Object', in this case the Blank Room.) A green cone now appears in the Blank Room. The player can call it either `green cone', `cone' or even `green'. It can be taken, dropped, looked at, looked under and so on. This is still rather plain. Examining the cone sees `nothing special about the green cone", for instance. So we might extend the definition by: Nearby cone "green cone" with name "green" "cone" "emerald", initial "Nearby is an emerald green cone, one foot high."; The `initial' message now appears when we arrive in the Empty Room. Taking things a little further... Nearby cone "green cone" with name "green" "cone" "emerald" "marzipan", initial "Nearby is an emerald green cone, one foot high.", description "The cone seems to be made of emerald-coloured \ marzipan." has edible; (Note that the description is split across two lines: the `\' makes the message come out as one sentence without a huge space.) Now if we examine the cone, we get its surprising `description': and the player is allowed to eat the cone. !!!! `name', `description' and `initial' are examples of `properties', while `edible' is an `attribute': the difference is that the former have values, whereas the latter are just on or off. So far, we're just filling in a form, and we could go much further doing this, but that wouldn't be much of an example. Instead, some honest programming: Nearby cone "green cone" with name "green" "cone" "emerald" "marzipan", initial "Nearby is an emerald green cone, one foot high.", description "The cone seems to be made of emerald-coloured \ marzipan.", after [; Take: "Taken. (Your hands are smeared with marzipan.)"; Drop: "The cone drops to the floor and sags a little."; ], has edible; The property `after' doesn't just have a string for a value: it has a routine of its own. Now what happens is that when an action happens to the cone, the `after' routine is called to apply any special rules about the cone. In this case, `Take' and `Drop' are the only actions tampered with: and the only effect is that the usual messages (`Taken.' `Dropped.') are replaced. !!!! There's no real difference between routines like `Initialise', which are defined outside objects at the `top level', and routines inside objects like this `after' routine: except that outside routines have to have names, since they aren't identified by belonging to any particular object. 1.3 Adding code to objects ========================== Still, the cone doesn't actually do anything! So here it is with a (completely unfair) puzzle added: Nearby cone "green cone" with name "green" "cone" "emerald" "marzipan", initial "Nearby is an emerald green cone, one foot high.", description "The cone seems to be made of emerald-coloured \ marzipan.", before [; Eat: if (random(100) <= 30) { deadflag = 1; "Unfortunately, you seem to be allergic to almonds."; } "You nibble at a corner of the cone."; ], after [; Take: "Taken. (Your hands are smeared with marzipan.)"; Drop: "The cone drops to the floor and sags a little."; ], has edible; The `before' routine is called before the player's intended action takes place. So when the player tries typing, say, `eat the cone', what happens is: in 30% of cases, she dies of almond poisoning; and in the other 70%, she simply nibbles a corner of the cone (without actually consuming it completely). !!!! Like many programming languages, Inform braces together blocks of code. `deadflag' is a global variable, whose value does not belong to any particular object (or routine). It is defined somewhere in the depths of the library: it's usually 0; setting it to 1 causes the game to be lost, and setting it to 2 causes a win. In either case, the usual rule for the `Eat' action is never applied. This is because, although it isn't obvious from the code, the routine actually returns a value, true or false. And the command "Unfortunately, you seem to be allergic to almonds."; not only prints the message (together with a carriage return), but also returns true from the `before' routine. Since the routine normally returns false, the library knows that something has happened to interrupt the usual rules of the game. 1.3.1 Exercise: the green cone ------------------------------ Extend the green cone so that it is described as sagging after it has been dropped back on the ground. (You need a few more properties for this.) *Note Answer (the green cone)::. 2 Ingredients and lexicon ************************* Properly speaking, `Inform' means the compiler and its language: whereas this book is chiefly about the `library' of code which comes with it. For most practical purposes this makes no difference - but it's worth remembering that in the last resort you can change almost anything about how the library works. 2.1 The basic ingredients of a game =================================== The basic ingredients of an Inform game are: *Objects*, *Routines*, *Actions* and *Grammar*. We have already seen examples of Objects and Routines. Actions happen in the course of play. The main loop of a game looks like this: 1. Ask the player to type something 2. Parse this (i.e., decide what it means) and generate any actions it calls for 3. Work out the consequences of these actions and tell the player 4. Worry about time passing by, and other things happening a process which ends only in victory or death. (In this respect it is unlike life.) Probably the most complicated programming in any adventure game goes into the parser. Inform's parser is fairly advanced, and is designed with the intention of being highly programmable at all levels to suit your game: so this manual will be returning to features of the parser again and again. It isn't perfect by any means, but it does understand things like: throw three of the coins into the fountain write funny on the lighted cube take the sword and all the crowns what is a grue dwarf, give me the battleaxe It also asks questions when it needs to, makes inferences from partial requests and tries to make good guesses when faced with ambiguous requests. You can teach it new verbs and new forms of old ones: this is Grammar. (The library starts with about 85 verbs, not counting synonyms.) Until you need to start programming it, the parser sits in the background, and you need to know nothing about it. But the parser is where Actions come from. An Action can have up to two objects attached to it, and represents something which the player is trying to do. For instance, Inv Take sword Insert gold_coin cloth_bag are all actions. (By this stage the game has reduced them to three numbers each, the action number and two object numbers: there's no text floating about.) !!!! Actions are also sometimes produced as a knock-on effect by other actions, and they can also be produced by your own code: you can even create new actions altogether, which are treated no differently from the standard ones. 2.1.1 Exercise: actions ----------------------- Compile one of the supplied example games, say `Toyshop', with the `DEBUG' option defined (*note Debugging::.) so that you can use the special `actions' verb to see exactly what all the actions are as they happen. *Note Answer (actions)::. 2.2 Introduction to the Z-machine ================================= !!!! Inform can produce two kinds of game: `Standard' and `Advanced'. Code is in almost every case portable between these two forms, so that you can easily make a version each way from the same program. The latter is better, but just in case you have a small computer (such as a personal organiser) or a very bad run-time interpreter, you might prefer the former. Occasionally this manual will mention a difference between these forms. !!!!! At this point, we should describe the imaginary machine, sometimes called the Z-machine (Z is for `Zork') which Inform compiles games for. An `interpreter' is a program which simulates this imaginary machine, so that it can run the game files that Inform produces: the same files will work on any interpreter on any machine. Interpreters are available for very many machines indeed. The Z-machine is, except in a few corners, a beautiful design and is described in great detail in the `Specification of the Z-Machine'. For now, a few words about the memory map. The memory of this imaginary computer is 128K long for Standard games and 256K long for advanced ones: the format is extremely well compressed, so this is actually quite a large memory. !!!!! Numbers are stored in 16-bit words (2 bytes long), and are signed in the usual way, so they hold values -32768 <= n <= 32767 with the hexadecimal value `$ffff' being the same as -1. The sign doesn't mean anything for addresses, of course, but there's a catch: given that a 2-byte number can only hold a value 0 <= n <= 65535, how can an address in a 128K or 256K memory map be held? The answer is that there are two kinds of address: a `packed address' and a `byte address'. A byte address is just an address in the bottom 64K of memory, and the Z-machine is arranged so that everything which could ever change is there: so these are the only addresses which matter very much. Packed addresses are addresses divided by 2 (or by 4 in Advanced games), so they can only refer to even (divisible by four) locations in memory. They hold the addresses of long strings and routines, which Inform is careful always to put at even (divisible by four) locations. 3 Objects, properties and attributes ************************************ Objects make up the substance of the world. That is why they cannot be composite. - Ludwig Wittgenstein (1889-1951), `Tractatus' ...making philosophical sense of change runs up against what seem to be impossible philosophical difficulties. Aristotle... focuses on the central case of an object coming to have a property that it formerly lacked. - Julia Annas, `Classical Greek Philosophy' 3.1 The tree of objects ======================= The objects of the game form what is sometimes called a `tree', though a better analogy would be a forest, and anyway one usually draws the whole thing upside down and uses the language of `families' - calling them `children' and `parents' of each other. Anyway, here's an example: Meadow | Mailbox -> Player | | Note Sceptre -> Cucumber -> Torch -> Magic Rod | Battery This is a family tree, then: each object has a parent, a sibling and a child, though that object may be the special `nothing' object. (The `Cucumber' has child `nothing', for instance.) As the game goes on, objects move around: when an object moves, all its possessions (that is, children) go with it. The Inform command to move an object is move object to new-owner; and it must be emphasized that this prints nothing on the screen, and indeed does nothing except to change the tree above. Inform provides special functions for reading this tree. `parent', `sibling' and `child' all do the obvious things, and in addition there's a function called `children' which counts up how many children an object has (only children: grandchildren aren't counted). For instance, parent ( Mailbox ) = Meadow children ( Player ) = 4 child ( Sceptre ) = 0 sibling ( Torch ) = Magic Rod !!!! `nothing' isn't really an object: it's just a convenient name for the number 0, which is the object number meaning `no such object'. It isn't a good idea to meddle with `nothing', or to apply functions like `parent' to it, but then there's never any need. !!!!! When an object is added to the possessions held by another, it appears at the end of the list. Thus, the eldest child is first in the list and the youngest is last. Inform also provides functions `youngest' end of list of possessions `eldest' same as `child' `younger' same as `sibling', i.e., rightwards in the picture `elder' reverse of `sibling', i.e., leftwards in the picture 3.2 Attributes ============== Objects have more to them than just where they are in the tree. They also have collections of variables attached to them. Firstly, there are flags, called `attributes', which can be either set or clear. These might be such conditions as `giving light', `currently worn' or `is one of the featureless white cubes'. Attributes all have names, which are a single word: like `light', for instance, which indicates that something is giving off light. There are about 30 defined by the library. They can be tested with a condition like if (obj has locked) "But it's locked!"; and can be set with the `give' command. So, for instance, give brass_lantern light; give iron_door locked; and they can be taken away with give brass_lantern ~light; give fake_coin ~scored; the `~' sign (or tilde) standing for negation. In fact you can give or take many at one go, so for instance give wooden_door open openable ~locked; !!!! You can make your own attributes with a directive like Attribute offered_to_ogre; at the start of the program. (A `directive' is a command to Inform directly, and happens at compile-time: not something in a routine which is to happen in the course of play.) !!!!! In Standard games there are few spare attributes available because the library takes most of them. To get around this limit there's a convenient dodge. It sometimes happens that an attribute is only meaningful for a particular kind of object: for instance, `spell has been read' might only be meaningful for a `scroll'. With care, therefore, one may re-use the same attribute to have different meanings for different kinds of object. The syntax to declare that an attribute is being reused is Attribute alias ; Thereafter Inform will treat the new and old attribute names as referring to the same attribute: it's up to the programmer to make sure this does not lead to inconsistencies. (The library already indulges in a certain amount of this chicanery.) 3.3 Properties ============== Secondly, there are `properties'. These are far more elaborate, and not every object has every property. For instance: not every object has the `door_to' property (it holds the place a door leads to, so things other than doors don't usually have it). The current value of a property is got at by constructions like: iron_door.door_to crystal_bridge.door_to green_cone.before diamond.initial You can read the value of `door_to' for something like the `diamond', and you'll just get a dull value like `nothing', but you can't write to it: that is, you can't change `diamond.door_to' unless you declared a `door_to' property for it. !!!! You can also define your own properties (again, subject to availability, because the library claims many of them). Property door_to; Property article "a"; Property blorple_routine $ffff; are all examples of the `Property' directive. In the case of `article', we are saying that the value `"a"' should be the default value for any object which doesn't declare an `article'. !!!!! Properties can also `alias'. They can also be declared as `long' or `additive', which is a complicated matter: basically, we'll come to `additive' when discussing classes, and properties which might start as small numbers (less than 256) and be changed into large ones in play, ought to be declared as `long'. As will be seen from examples, a property value can be many things: a string like `"frog"', a number such as `$ffff' (this is the Inform way of writing numbers in hexadecimal), an object or a routine. You can change the current value by something like location.door_to = hall_of_mists; brass_lantern.short_name = "dimly lit brass lantern"; grenade.time_left = 45; *WARNING:* The game may crash at run-time if you attempt to write to a property field which an object hasn't got. So although you can read an undeclared property (you just get the default value), you can't write to one. (Also, you can't extend a property beyond its length: see below.) !!!! The Inform language does not have types as such, and strings and routines are stored as numbers: as their addresses inside the virtual machine, in fact. This means that Inform thinks "Hello there" + 45 + 'a' is a perfectly sensible calculation. It's up to you to be careful. !!!!! A property can hold more than just one number (be it interpreted as a string, a routine or whatever): it can hold a small array of numbers. In Standard games it can have four numbers (8 bytes' worth), and in Advanced games 32 (64 bytes). For instance, an entry in an object definition might read found_in Marble_Hall Arched_Passage Stone_Stairs, storing a sequence of three values (all objects) in the `found_in' property. To read or write to this array, you need to know its (byte) address and its length. The operators `object.&property' and `object.#property' tell you these. (Be warned: `object.#property' tells you the number of bytes, not the number of words.) If you give a property more than 8 bytes of data in a Standard game, Inform warns you and takes only the first 8, but does not cause an error: this is so that, say, Object ... with name "radio" "wireless" "transistor" "portable" "stereo" "tranny", ... will compile either way (but the last two synonyms for `"radio"' will not enter the dictionary if it's being compiled in Standard form, since a name takes two bytes). 3.3.1 Exercise: property addresses ---------------------------------- !!!!! Use the `object.&property' construction to find out whether the object in variable `obj' has the `door_to' property defined or not. *Note Answer (property addresses)::. 3.4 Making object definitions ============================= Time to make some object definitions. A typical object definition looks something like: Object trapdoor "hinged trapdoor" attic with name "hinged" "trap" "door" "trapdoor", when_open "A hinged trapdoor in the floor stands open, and \ light streams in from below.", when_closed "There is a closed trapdoor in the middle of the \ floor.", door_to house, door_dir d_to, has door static open light openable; This is the conventional way to lay out an `Object' declaration: with the header first, then `with' a list of properties and their starting values, finishing up with the attributes it initially `has'. `trapdoor' is the name given to the object in the program, and it becomes a constant from then on (whose value is the number of the object). The `attic' is the object which the `trapdoor' starts out belonging to (as any player of `Curses' will know). Some objects start out not belonging to anything (rooms, for example, or treasures which only appear half-way through). You can declare these as belonging to `nothing', but it's better just to miss this out altogether: Object trapdoor "hinged trapdoor" with ... If you do declare an object already belonging to another, as above, then the other object must already have been defined. (This is no real restriction, and ensures that you can't set up a `loop' - one in another in a third in the first, for instance.) Objects can also be declared, in an identical way, by the `Nearby' directive. The only difference is that no initial-owner object can be given; it starts out belonging to the last thing declared as an `Object'. For example, in Object hillside "Panoramic Hillside" with ... Nearby scenery "scenery" with ... the `hillside' is a room to which the `scenery' will belong. Otherwise, `Nearby' is the same as `Object', and this is just a convenience to make it easier to move things around in Inform code by cutting definitions out and pasting them in elsewhere. Some properties of objects should be routines. For instance, an object can have a `describe' property which is a routine to print out a description of it. These could just be listed as names of routines, but usually the actual routine is written out then and there as the property value. For instance, in the classic Adventure object Nearby tasty_food "tasty food" with description "Sure looks yummy!", initial "There is tasty food here.", name "food" "ration" "rations" "tripe" "yummy" "tasty" "delicious" "scrumptious", after [; Eat: "Delicious!"; ], article "some" has edible; the `after' property does not name a routine but instead defines it. No name is needed or given for the routine. (The semicolon after the `[' is needed to show that the routine has no local variables.) The routine must end with either `],' or `];'. If `],' the object definition can resume where it left off, with further properties. (If it ends with `];', then the object definition ends where the routine finishes.) !!!! The rules for embedded routines are not quite the same as those for ordinary routines. By default, embedded routines return `false', or 0 (instead of `true', or 1, which other routines return by default). Also, the handy shorthand: Action [, Action2 ...] : ...some code... is provided, which executes the code only if the action being considered is the one named. !!!!! It actually does this by setting a special variable called `sw__var' to the action number: when it compiles `Action1:' it just compiles an `if' statement which sees whether `sw__var' has value `Action1'. !!!! Properly speaking, the full syntax of the header is `Object' OBJ-NAME-1 ... OBJ-NAME-N `"short name"' [PARENT-OBJ] *or* `Nearby' OBJ-NAME-1 ... OBJ-NAME-N `"short name"' *or* `Class' CLASS-NAME and the parent object must have already been defined. A `Class' definition is very like an object definition, and will be discussed later on. The syntax for an object, then, is HEADER [`,'] `class' CLASS-1 ... CLASS-N [`,'] `with' PROPERTY-NAME-1 VALUE-1 ... VALUE-N`,' PROPERTY-NAME-2 VALUE-1 ... VALUE-N`,' ... PROPERTY-NAME-N VALUE-1 ... VALUE-N [`,'] `has' ATT-1 ... ATT-N Although it's conventional to write `class', `with' and `has' in this order, actually they can be in any order and any or all can be omitted altogether: and the commas in square brackets `[,]' are optional in between these fields. (The classes listed under `class' are those which the object inherits from.) !!!! One property is treated differently from the others, and this is the special property `name'. Its data must be a list of English words in double-quotes, as in all the above examples. (Probably the most annoying restriction of Standard games is that this means only 4 names at most can be accommodated in that format: you get up to 32 in Advanced games.) It will probably confuse the parser if any of these names is something like `"my"', `"the"', `"all"', `"except"' or `"this"'. Numbers can safely be used, however. !!!!! In fact, `name' contains a list of byte addresses of dictionary words. A quick way to print out the first word in the `name' list is therefore print_addr obj.name; !!!!! The attributes and so on can be taken away as well as added, thus: ... has light ~scored; which is sometimes useful to over-ride an inheritance from a class definition. !!!!! A final curiosity of the `Object' syntax is that objects can be given more than one name. This is a hangover from much earlier days of Inform, to do with re-using the same physical object in different logical ways, something which the author does not commend to anyone. 4 Places, scenery and the map ***************************** It was a long cylinder of parchment, which he unrolled and spread out on the floor, putting a stone on one end and holding the other. I saw a drawing on it, but it made no sense. - John Christopher, `The White Mountains' And if no piece of chronicle we prove, We'll build in sonnets pretty rooms; As well a well wrought urn becomes The greatest ashes, as half-acre tombs. -- John Donne (1571?--1631), `The Canonization' 4.1 Joining rooms together ========================== Back to our example. Throw away the old `blank room' and replace it by Object Square_Room "Square Room" with description "A broad, square room, ten yards on a side, \ floored with black and white chequered tiles." has light; (We also have to change the `Initialise' routine to make this the place where the player begins, since the Blank Room no longer exists.) Like the blank room, this one has `light'. (If it didn't, the player would never see it, since it would be dark, and the player hasn't yet been given a lamp or torch of some kind.) So where is the light coming from? Nearby chandelier "crystal chandelier" with name "crystal" "chandelier", initial "A crystal chandelier hangs from far above, casting \ light in little rainbows across the floor.", description "The crystal is beautiful cut-glass." has static; This is part of the fittings, hence the `static' attribute (which means it can't be taken or moved). But what about the rainbows? Nearby rainbows "rainbows" with name "rainbow" "rainbows", description "Caused by diffraction, or something like that - \ you were never very good at physics." has scenery; Being `scenery' makes the object not only static but also not described by the game unless actually examined by the player. A true perfectionist might alter it to: Nearby rainbows "rainbows" with name "rainbow" "rainbows", description "Caused by diffraction, or something like that - \ you were never very good at physics.", before [; Take, Push, Pull, Turn: "But the rainbows are made of light."; ], has scenery; Let us now add a second room: Object Corridor "Sloping Corridor" with description "This corridor slopes upward and out of the \ square room.", d_to Square_Room, s_to Square_Room, u_to "The slope becomes impossibly steep, and you retreat.", cant_go "The corridor runs up and down." has light; and extend the Square Room to: Object Square_Room "Square Room" with description "A broad, square room, ten yards on a side, \ floored with black and white chequered tiles. A doorway \ in the centre of the north side opens onto a rising \ corridor.", u_to Corridor, n_to Corridor has light; The player can now go from one to the other. The properties `u_to', `d_to', `n_to' (and so on) declare what lies in the directions `up', `down', `north' (and so on). If they aren't declared, one cannot go that way. Notice that they can be either a room or a message which is printed if the player tries to go in the given direction. In the Square Room, if the player tries to go, say, east, she gets a message along the lines of `You can't go that way.', which is not very helpful. In the Corridor, the `cant_go' message is printed instead. (In fact, as is often the case with properties, instead of giving an actual message you can instead give a routine to print one out, so that what is printed varies with circumstances.) Map connections are all one-way, in themselves: they just happen to be defined here so that they appear two-way. 4.2 Adding code to rooms ======================== Rooms also have rules of their own. We might write: Object Corridor "Sloping Corridor" with description "This corridor slopes upward and out of the \ square room: the floor underfoot is a little sticky.", d_to Square_Room, s_to Square_Room, u_to "The slope becomes impossibly steep, and you retreat.", cant_go "The corridor runs up and down.", before [; Take: if (noun == cone) "The cone seems to be stuck to the floor here."; ], has light; and now the cone (if dropped there) cannot be taken from the floor of the Sloping Corridor. The variables `noun' and `second' hold the first and second nouns supplied with an action. Rooms have `before' and `after' routines just as objects do. !!!!! Sometimes the room may be a different one after the action has taken place. The `Go' action, for instance, is offered to the `before' routine of the room which is being left, and the `after' routine of the room being arrived in. For example: after [; Go: if (noun==in_obj) print "How grateful you are to get out of the \ rain...^"; ], will print the message when the room is entered via the `in' direction. (Note that the message is printed with the `print' command. This means that it does not automatically return true: in fact, it returns false, so the game knows that the usual rules still apply. Also, no new-line is printed automatically: but the `^' symbol means `print a new-line', so one is actually printed.) !!!! Directions (such as `north') are objects called `n_obj', `s_obj' and so on: in this case, `in_obj'. (They are not to be confused with the property names `n_to' and so on.) Moreover, you can change these directions (as far as Inform is concerned, only things in the special object `compass' can be directions). 4.2.1 Exercise: world colours ----------------------------- !!!! In the first millenium A.D., the Mayan peoples of the Yucatán Peninsula had `world colours' white (*sac*), red (*chac*), yellow (*kan*) and black (*chikin*) for what we call the compass bearings north, east, south, west (for instance west is associated with `sunset', hence black, the colour of night). Implement this. *Note Answer (world colours)::. 4.2.2 Exercise: reflecting the map ---------------------------------- !!!! (Cf. `Trinity'.) How can the entire game map be suddenly east-west reflected? *Note Answer (reflecting the map)::. 4.2.3 Exercise: reflecting directions ------------------------------------- !!!!! Even when the map is reflected, there may be many room descriptions referring to `east' and `west' by name. Reflect these too. *Note Answer (reflecting directions)::. 5 Causing actions and making new ones ************************************* Only the actions of the just Smell sweet and blossom in their dust. -- James Shirley (1594--1666), `The Contention of Ajax and Ulysses' ...a language obsessed with action, and with the joy of seeing action multiply from action, action marching relentlessly ahead and with yet more actions filing in from either side to fall into neat step at the rear, in a long straight rank of cause and effect, to what will be inevitable, the only possible end. - Donna Tartt, `The Secret History' 5.1 Causing actions to happen ============================= One often wants to simulate the effect of a player typing something. For instance, in the Pepper Room the air is full of pepper and every turn the player sneezes and drops something at random. If the code to do this simply removes an object and puts it on the floor, then it might accidentally provide a solution to a problem like `the toffee apple sticks to your hands so you can't drop it'. This can at least be coded like this: You sneeze convulsively, and lose your grip on the toffee apple... The toffee apple sticks to your hand! which (though not ideal) is much better. As an example, here is another piece of scenery to clutter up the tiny map so far: Object low_mist "low mist" with name "low" "swirling" "mist", article "the", initial "A low mist swirls about your feet.", description "It carries the unmistakable odour of cinnamon.", found_in Square_Room Corridor, before [; Smell: <>; ], has static; This mist is found in both the Square Room and the Corridor: note that the `found_in' property has a list of places as its value. The player will find that smelling the mist produces the same message as looking at it. The command <>; causes the game to behave exactly as if the player had typed `examine the mist' at the keyboard: that is, the `Examine' action happens, applied to the `low_mist' object. (`self' always means the object whose routine this is. In this case, it's really a bit pointless since <>; does just the same thing.) After going through the business of examining the mist, the routine then returns `true', or 1 (in this case, so that the normal rules for smelling something are stopped in their tracks). If instead ; had been used, the same would have happened, but the routine would not have been returned from. So the mist would be examined; and then the routine for `Smell' would resume, and since there's nothing more to it, it would return false; whereupon the library's usual rules would make it say something like `You smell nothing unusual.'. All actions can be written this way: ; <>; will, for instance, look around, then behave as if the player had asked to throw the cone at the chandelier, then return true. !!!! Internally, actions like `Look', `ThrowAt' and `Take' are stored as numbers, and the number associated with an action can be got at by `x = ##Take;' for instance. The variable `action' holds the current action number and sometimes it's convenient to use this directly. For instance, imagine a mirror hung very far above the player. Given: before [; if (action==##Examine) rfalse; "The mirror is too high up, out of reach."; ] the player will only be able to examine the mirror, and nothing else. This prevents the library from ever saying something like `You push the mirror but nothing happens.', which would be misleading. !!!! An action corresponds to a routine somewhere which actually carries out the looking, throwing at, taking and so forth. (Well, in fact there are also some special actions called `fake actions' which don't correspond to such routines, as we shall come to later.) These have the same name as the action with `Sub' (short for `subroutine') on the end: so, `TakeSub' for instance. 5.2 Actions defined by the library ================================== !!!! The actions implemented by the library are in three groups. The useful ones are in groups 2 and 3: 1. Quit, Restart, Restore, Verify, Save, ScriptOn, ScriptOff, Pronouns, Score, Fullscore, LMode1, LMode2, LMode3, NotifyOn, NotifyOff; 2. Inv, InvTall, InvWide, Take, Drop, Remove, PutOn, Insert, Transfer, Empty, Enter, Exit, GetOff, Go, GoIn, Look, Examine, Give, Show, Unlock, Lock, SwitchOn, SwitchOff, Open, Close, Disrobe, Wear, Eat; 3. Yes, No, Burn, Pray, Wake, WakeOther [person], Kiss, Think, Smell, Listen, Taste, Touch, TouchThing, Dig, Cut, Jump [jump on the spot], JumpOver, Tie, Drink, Fill, Sorry, Strong [swear word], Mild [swear word], Attack, Swim, Swing [something], Blow, Rub, Set, WaveHands [ie, just "wave"], Wave [something], Pull, Push, PushDir [push something in a direction], Turn, Squeeze, LookUnder [look underneath something], Search, ThrowAt, Answer, Buy, Ask, Sing, Climb, Wait, Sleep Of course, the player can type all manner of things to get these. For instance, `take off shirt' and `remove the shirt' both cause the `Disrobe' action. Your code can ignore this complication. !!!!! Group 1 actions are called `meta' - they are outside the game proper, and your code is unable to interfere with them. (If you want a room where the game can't be saved, as for instance `Spellbreaker' cunningly does, you'll have to tamper with `SaveSub' directly, using a `Replace'd routine.) Although all actions call the `before' routines, not all of them bother to check `after' routines. For instance, since the built-in `SmellSub' routine just says `You smell nothing out of the ordinary', there would be no point calling `after' routines - nothing, after all, has been done. (These are the group 3 actions above.) !!!! The ones which actually do something, and call `after', are: Inv, Take, Drop, Remove, PutOn, Insert, Exit, Go, Look, Examine, Unlock, Lock, SwitchOn, SwitchOff, Open, Close, Disrobe, Wear, Eat, Search. !!!!! Some other group 2 actions use these `after' routines indirectly - if the player empties a sack out onto the floor, this is deemed to be a sequence of `Drop' actions, for instance, and if a sack is emptied into a packing case this is considered a multiple insertion. !!!!! `Search' (the searching or looking-inside-something action) is a slightly special case between groups 2 and 3. It never actually does anything beyond printing messages. What happens is that if it would be sensible to look inside the object (i.e., if it's an open or transparent container and there is light), `after' is called beforehand. In this way you can use `before' to trap searching of random scenery, and `after' to alter rules for listing contents of containers. 5.3 Making new actions ====================== The library's actions are easily added to. For instance, add the routine: [ BlorpleSub; "You speak the magic word ~Blorple~. Nothing happens."; ]; (somewhere after the `Initialise' routine, say, to be tidy). There is now an action `Blorple' (though it doesn't do anything very interesting). One can use the command `;' to make it happen, and could change the `before' routine of, say, the Corridor, to make `Blorple' do something exciting in that one place. In other words, `Blorple' is now an action just like any other. But the player can't yet type `blorple' and get this response, because although the action exists, it hasn't been written into the grammar of the game. The grammar is a large table (mostly written out in the `Grammar' library file). It can easily be added to, and in this case we simply add the lines Verb "blorple" * -> Blorple; immediately after the inclusion of the `Grammar' file. (The spacing is just a matter of convention.) This is about as simple as grammar lines come, and means that only the word `blorple' can be used as a verb, and it can't apply to any noun or nouns. (Far more about grammar later.) !!!! It is time to be more precise about the exact sequence of events. Suppose the player is in the Bedquilt Room, and types `drop oyster'. Once it has checked that this is a reasonable command, the normal rules can be interrupted at eight different stages: 1. Call `GamePreRoutine' (if there is one). If this returns true, stop here. 2. Call the `before' of the player. If this returns true, stop here. 3. Call the `before' of Bedquilt Room. If this returns true, stop here. 4. Then the `before' of the oyster. If this returns true, stop here. 5. Actually drop the object. 6. Call the `after' of the player. If this returns true, stop here. 7. Call the `after' of Bedquilt Room. If this returns true, stop here. 8. Then the `after' of the oyster. If this returns true, stop here. 9. Call `GamePostRoutine' (if there is one). If this returns true, stop here. 10. Print `Dropped.' !!!!! `GamePreRoutine' and `GamePostRoutine' are examples of `entry points', like `Initialise': routines you can provide to make global rule changes. Their use is a drastic measure to be avoided if possible. The player's own `before' and `after' will be discussed in \S 11. !!!!! `Fake actions' are just like real actions, except that they don't occur in any game grammar, so they're never generated by the parser. They're a neat way to pass `messages' from one object to another one. Because they aren't defined implicitly in the grammar, they have to be explicitly declared before use, by the directive: `Fake_Action' ACTION-NAME 5.3.1 Exercise: medicine bottle ------------------------------- !!!!! How can you make a medicine bottle, which can be opened in a variety of ways in the game, so that the opening-code only occurs in the bottle definition? *Note Answer (medicine bottle)::. 6 Containers, supporters and sub-objects **************************************** The concept of a surface is implemented as a special kind of containment. Objects which have surfaces on which other objects may sit are actually containers with an additional property of `surfaceness'. - P. David Lebling, `Zork and the Future' 6.1 Introduction to containers ============================== Objects can be inside or on top of one another. An object which has the `container' attribute can contain things, like a box: one which has `supporter' can hold them up, like a table. (An object can't have both at once.) It can hold up to 100 items, by default: this is set by the `capacity' property. However, one can only put things inside a container when it has `open'. If it has `openable', the player can open and close it at will. (Unless it also has `locked'.) To complicate matters, some containers are `transparent' (so that the player can see inside them even when they are closed) and some are not. Containers (and supporters) are able to react to things being put inside them, or removed from them, by acting on the `Receive' and `LetGo' actions. !!!! `LetGo' and `Receive' are actually two of the fake actions: they are the actions `Insert' and `Remove' looked at from the container's point of view. 6.1.1 Exercise: two boxes ------------------------- Make a glass box and a steel box, which behave differently when the lamp is shut up inside them. *Note Answer (two boxes)::. 6.1.2 Exercise: toothed bag --------------------------- Make the following, rather acquisitive bag: >put fish in bag The bag wriggles hideously as it swallows the fish. >get fish The bag defiantly bites itself shut on your hand until you desist. *Note Answer (toothed bag)::. 6.2 Containers which can be locked ================================== Objects which have `locked' cannot be opened, be they doors or containers (or both). But objects which have `lockable' can be locked or unlocked with the appropriate key, which is declared in the `with_key' property. (If it is undeclared, then no key will fit.) As a final example of a container, this is a fairly typical locked cupboard: Nearby cupboard "bolted cupboard" with name "bolted" "cupboard", describe [; if (self hasnt open) "^A shut cupboard is bolted to one wall."; "^Bolted up on one wall is an open cupboard."; ], with_key key has locked container openable lockable static; 6.3 Other uses for containers ============================= Now suppose you want to make a portable television set which has four different buttons on it. Obviously when the television moves, its buttons should move with it, and the sensible way to arrange this is to make the four buttons possessions of the `television' object. However, the television isn't a `container', and the player can't normally `see' (that is, refer to) the possessions of an object. So how do we bring the buttons into the player's `view' without making them removable, or allowing anyone to put extra things `into' the television? This is what the `transparent' attribute is for: it is an extremely useful device to allow the player to `see' (i.e., talk about) the contents of an object. 6.3.1 Exercise: television set ------------------------------ Implement a television set with attached power button and screen. *Note Answer (television set)::. 7 Doors ******* Standing in front of you to the north, however, is a door surpassing anything you could have imagined. For starters, its massive lock is wrapped in a dozen six-inch thick iron chains. In addition, a certain five-headed monster... - Marc Blank and P. David Lebling, `Enchanter' O for doors to be open and an invite with gilded edges To dine with Lord Lobcock and Count Asthma. -- W. H. Auden (1907--1973), `Song' A useful kind of object is a `door'. This need not literally be a door: it might be a rope-bridge or a ladder, for instance. To set up a `door': 1. give the object the `door' attribute; 2. set the `door_to' property to the destination; 3. set the `door_dir' property to the direction which that would be, such as `n_to'; 4. make the room's map connection in that direction point to the door itself. For example, here is a closed and locked door: Object In_Immense_N_S_Passage "Immense N/S Passage" with description "One end of an immense north/south passage.", s_to In_Giant_Room, n_to RustyDoor; Nearby RustyDoor "rusty door" with description "It's just a big iron door.", name "door" "hinge" "hinges" "massive" "rusty" "iron", when_closed "The way north is barred by a massive, rusty, \ iron door.", when_open "The way north leads through a massive, rusty, iron \ door.", door_to In_Cavern_With_Waterfall, door_dir n_to, with_key set_of_keys has static door openable lockable locked; (Note that the door is `static' - otherwise the player could pick it up and walk away with it!) The properties `when_closed' and `when_open' give descriptions appropriate for the door in these two states. Doors are rather one-way: they are only really present on one side. If a door needs to be accessible (openable and lockable from either side), a neat trick is to make it present in both locations and to fix the `door_to' and `door_dir' to the right way round for whichever side the player is on. Here, then, is a two-way door: Object Grate "steel grate" with name "grate" "lock" "gate" "grille" "metal" "strong" "steel" "grating", description "It just looks like an ordinary grate mounted in \ concrete.", with_key set_of_keys, door_dir [; if (location==Below_The_Grate) return u_to; return d_to; ], door_to [; if (location==Below_The_Grate) return Outside_Grate; return Below_The_Grate; ], describe [; if (self has open) "^The grate stands open."; if (self hasnt locked) "^The grate is unlocked but shut."; rtrue; ], found_in Below_The_Grate Outside_Grate has static door openable lockable locked; where `Below_The_Grate' has `u_to' set to `Grate', and `Outside_Grate' has `d_to' set to `Grate'. The grate can now be opened, closed, entered and locked from either above or below. !!!! At first sight, it isn't obvious why doors have the `door_dir' property. Why does a door need to know which way it faces? The idea is that if there's an open door in the south wall, a player can go through it either by typing `south' or `enter door'. So what the `Enter' action does (provided the door is actually open) is to cause the `Go' action with the given direction. !!!! This has one practical consequence: if you put `before' and `after' routines on the `Enter' action for the `Grate', they only apply to a player typing `enter grate' and not to one just typing `down'. The way to trap both at once is to write a routine for the `d_to' property of `Outside_Grate'. 8 Switchable objects ******************** In one corner there is a machine which is reminiscent of a clothes dryer. On its face is a switch which is labelled `START'. The switch does not appear to be manipulable by any human hand (unless the fingers are about 1/16 by 1/4 inch)... - `Zork' Objects can also be `switchable'. This means they can be turned off or on, as if they had some kind of switch on them. The object has the attribute `on' if it's on. For example: Object searchlight "Gotham City searchlight" skyscraper with name "search" "light" "template", article "the", description "It has some kind of template on it.", when_on "The old city searchlight shines out a bat against \ the feather-clouds of the darkening sky.", when_off "The old city searchlight, neglected but still \ functional, sits here." has switchable static; Here is a lamp which provides for batteries which will some day run down, though the code to actually deplete these batteries (by decrementing `time_left' every turn it's switched on) is omitted: Nearby brass_lantern "brass lantern" with name "lamp" "lantern" "shiny" "brass", when_off "There is a shiny brass lamp nearby.", when_on "Your lamp is here, gleaming brightly.", time_left 330, before [; Examine: print "It is a shiny brass lamp"; if (brass_lantern hasnt on) ". It is not currently lit."; if (brass_lantern.time_left < 30) ", glowing dimly."; ", glowing brightly."; Burn: <>; Rub: "Rubbing the electric lamp is not particularly \ rewarding. Anyway, nothing exciting happens."; SwitchOn: if (brass_lantern.time_left <= 0) "Unfortunately, the batteries seem to be dead."; ], after [; SwitchOn: give brass_lantern light; SwitchOff: give brass_lantern ~light; ], has switchable; 9 Things to enter, travel in and push around ******************************************** ...the need to navigate a newly added river prompted the invention of vehicles (specifically, a boat). - P. David Lebling, Marc Blank and Timothy Anderson Some objects in a game are `enterable', which means that a player can get inside them. The idea of `inside' here is that the player is only half-in, as with a car or a psychiatrist's couch. (If it's more like a prison cell, then it should be a separate place.) In practice one often wants to make an `enterable' thing also a `container', or, as in this example, a `supporter': Object chair "dentist's chair" surgery with name "dentists" "chair" "couch", has enterable supporter; All the classic games have vehicles (like boats, or fork lift trucks, or hot air balloons) which the player can journey in, so Inform makes this easy. Here is a simple case: Object car "little red car" cave with name "little" "red" "car", description "Large enough to sit inside. Among the controls \ is a prominent on/off switch. The numberplate is KAR 1.", when_on "The red car sits here, its engine still running.", when_off "A little red car is parked here.", before [; Go: if (car has on) "Brmm! Brmm!"; print "(The ignition is off at the moment.)^"; ], has switchable enterable static container open; Actually, this demonstrates a special rule. If a player is inside an `enterable' object and tries to move, say `north', the `before' routine for the object is called with the action `Go', and `n_obj' as the noun. If it returns false, the game disallows the attempt to move (as usual). If it returns true, then the vehicle and player move together via the game's usual map. !!!! Because you might want to drive the car `out' of a garage, the `out' verb does not make the player get out of the car. Usually the player has to type something like `get out' to make this happen, though of course the rules can be changed. !!!! Objects like the car (if the hand brake is off) or, say, an antiquated wireless on casters, are obviously too heavy to pick up but the player should at least be able to push them from place to place. When the player tries to do this, the `PushDir' action is generated. Now, if the `before' routine returns false, the game will just say that the player can't; and if it returns true, the game will do nothing at all, guessing that the `before' routine has already printed something more interesting. So how does one actually tell Inform that the push should be allowed? The answer is that one has to do two things: call the `AllowPushDir()' routine (a library routine), and then return true. So, for example, Nearby huge_ball "huge plaster-of-paris ball" with name "huge" "plaster" "ball", description "A good eight feet across, though fairly \ lightweight.", initial "A huge plaster-of-paris ball rests here, eight feet \ wide.", before [; PushDir: AllowPushDir(); rtrue; Take, Remove: "There's a lot of plaster in an eight-foot \ sphere."; ], after [; PushDir: "The ball rattles about and is hard to stop once \ underway."; ], has static; 9.0.1 Exercise: the red car --------------------------- Alter the car so that it won't go up or down stairs. *Note Answer (the red car)::. 10 Living creatures and conversation ************************************ To know how to live is my trade and my art. -- Michel de Montaigne (1533--1592), `Essays' Everything that can be said can be said clearly. -- Ludwig Wittgenstein (1889--1951), `Tractatus' This rummage through special kinds of objects finishes up with the most sophisticated kind: living ones. Animate objects (such as sea monsters, mad aunts or nasty little dwarves) have a property called `life', containing their rules. This behaves just like a `before' or `after' routine, but only the following actions apply: `Attack' The player is making hostile advances... `Kiss' ... or excessively friendly ones. `ThrowAt' The player asked to throw `noun' at the creature. `Give' The player asked to give `noun' to the creature... `Show' ... or just to show it. `Ask' The player asked the creature about something: `noun' holds the word. `Answer' The player tried either of: `answer WORD to troll' `trool, SOMETHING NOT UNDERSTOOD' In either case the variable `special_word' is set to the dictionary entry of the first word, or 0 if it isn't in the dictionary, and `special_number' is set to an attempt to read it as a number. (For instance, `computer, 143' will cause `special_number' to be set to 143.) `Order' On the other hand, an `Order' happens when the parser does understand what the player wants the creature to do: e.g., `troll, go south'. `action', `noun' and `second' are set up as usual: in this case `action=##Go' and `noun=s_obj', while `second=0'. If the routine doesn't exist, or returns false, events will take their usual course. Here is a full example: Object snake "sullen snake" mists with name "sullen" "snake", description "Perhaps a boa constrictor. Perhaps not.", life [; Order: if (action==##Go) "The snake wasn't born yesterday."; Attack: "Lazily, the snake dodges your attack."; Kiss: "What a repulsive idea."; ThrowAt: print "Effortlessly, the snake dodges "; DefArt(inp1); "."; Answer, Show: "The snake disdains to comment."; Ask: if (noun == 'mists') "~Healthy and good for the skin, mists.~"; "~I'm only the obligatory monster.~"; Give: if (noun has edible) { remove noun; "~Mmm! Thanks! I still hate you, though.~"; } "~Bleurghh! Are you trying to poison me?~"; ], has animate; Of course an `animate' still has `before' and `after' routines like any other, so you can trap many other kinds of behaviour. (The library understands that, for example, an `animate' creature cannot be taken.) !!!! `DefArt' is a routine which prints the short name of an object along with its definite article, usually `the'. Sometimes creatures should be `transparent', sometimes not. Consider these two cases of `animate' characters, for instance: * an urchin with something bulging inside his jacket pocket; * a hacker who has a bunch of keys hanging off his belt. The hacker is `transparent', the urchin not. That way the parser prevents the player from referring to whatever the urchin is hiding (even if the player has played the game before, and knows what is in there and what it's called). But the player can look at and be tantalised by the hacker's keys. !!!! Another way to trap some of these actions (those which do not involve conversation, i.e., other than `Order', `Answer' or `Ask') is to use `before' in the ordinary way. This lets you change rules about, say, giving things to people in particular places. The `ThrowAt' action is also an ordinary one (for coconut shies, greenhouse windows and the like) but is given to `life' as well because most creatures react to it, and it's a convenience to have all the rules for a creature in one place. !!!! A footnote about `Order': this is another `fake action'. The `before' and `after' routines of a room can't detect the player having given a request to another character. Also, if you want the snake to obey when the player tells it to take something, you have to write the code to do the actual taking yourself. This isn't any problem in practice. (Try looking at the code for `Christopher' in the `Toyshop' example game.) 11 Starting, moving, changing and killing the player **************************************************** There are only three events in a man's life; birth, life and death; he is not conscious of being born, he dies in pain and he forgets to live. - Jean de la Bruyère (1645-1696) That is the road we all have to take - over the Bridge of Sighs into eternity. - Søren Kierkegaard (1813-1855) The player normally begins in the room which `location' is set to, and setting `location' is the only absolute obligation on a game's `Initialise' routine. (The room may be initially dark if you so choose, or rather if you provide no light source.) In fact `location' could be set to a chair or bed just as easily if the player is to start seated or lying down. If you would like to give the player some items to begin with, `Initialise' should also `move' them to `player'. To move the player about (for teleportation of some kind), two things must be done: to move the player object, by move player to newroom; and also to change the `location' variable, which says which room to display on the status line: location = newroom; The cleanest way to move the player is call `PlayerTo(place);' which also sorts out things like only printing the new room's description if there's enough light there to see by. !!!! In general `location' can be different from `parent(player)' in two ways: it can be `Darkness' (that is, the special object `thedark', which behaves somewhat like a room), or it can be the actual room while `parent(player)' is something the player sits inside, like (say) a jeep. !!!! Calling `PlayerTo(place, 1);' will move the player without printing anything, and in particular without printing any room description. The player's whole persona can easily be changed, by setting player.before = #r$MyNewRule; where `MyNewRule' is a new `before' rule to be applied to every action of the player's (or similarly for an `after' rule). For instance, if a cannon goes right next to the player, a period of deafness might ensue, and this rule could stop the `Listen' action from taking its normal course. !!!! In fact a much more powerful trick is available: the `player' can actually become a different character in the game, allowing the real player at the keyboard to act through someone else. Calling `ChangePlayer(obj)' will transform the player to `obj'. There's no need for `obj' to have names like `me' or `myself'; the parser understands these words automatically to refer to the currently-inhabited `player' object. However, it must provide a `number' property (which the library will use for workspace). The maximum number of items the player can carry as that object will be its `capacity'. Finally, since `ChangePlayer' prints nothing, you may want to conclude with a `<>;' `ChangePlayer' has many possible applications. The player who tampers with Dr Frankenstein's brain transference machine may suddenly become the Monster strapped to the table. A player who drinks too much wine could become a `drunk player object' to whom many different rules apply. The `snavig' spell of `Spellbreaker', which transforms the player to an animal like the one cast upon, could be implemented thus. More ambitiously, a game could have a stock of half a dozen main characters, and the focus of play can switch between them. A player might have a team of four adventurers to explore a dungeon, and be able to switch the one being controlled by typing the name. (In this case, an `AfterLife' routine - see below - may be needed to switch the focus back to a still-living member of the team after one has met a sticky end.) !!!! Calling `ChangePlayer(object,1);' will do the same but make the game print `(as Whoever)' during room descriptions. The player is still alive for as long as the variable `deadflag' is zero. When set to 1, the player dies; when set to 2, the player wins; and all higher values are taken as more exotic forms of death. Now Inform does not know what to call these exotica: so if they should arise, it calls the `DeathMessage' routine, which is expected to look at `deadflag' and can then print something like `You have changed'. Many games allow reincarnation (or, as David M. Baggett points out, in fact resurrection). You too can allow this, by providing an `AfterLife'. This routine gets the chance to do as it pleases before any `You are dead' type message appears, including resetting `deadflag' back to 0 - which causes the game to proceed in the normal way, rather than end. `AfterLife' routines can be tricky to write, though, because the game has to be set to a state which convincingly reflects what has happened. (For instance, try collapsing the bridge in `Advent' by leading the bear across it, then being reincarnated and returning to the scene.) 11.0.1 Exercise: *wayhel* ------------------------- !!!! In Central American legend, a sorceror can transform himself into a *nagual*, a familiar such as a spider-monkey; indeed, each individual has an animal self or *wayhel*, living in a volcanic land over which the king, as a jaguar, rules. Turn the player into his *wayhel*. *Note Answer (wayhel)::. 12 Printing out names of objects ******************************** And we were angry and poor and happy, And proud of seeing our names in print. -- G. K. Chesterton (1874-1936), `A Song of Defeat' Inform has a special form of print command for this, `print object', but *do not use it*. Instead, call one of the following library routines: `DefArt(obj)' Print the object with its definite article `CDefArt(obj)' The same, but capitalised `InDefArt(obj)' Print the object with indefinite article `PrintShortName(obj)' Print the object's short name alone The indefinite article for an object is held in the property `article' and is assumed to be `a' if none is declared. That means that if the short name starts with a vowel, you need to set it to `an'. But `article' offers much more amusement: a platinum bar, an orange balloon, your Aunt Jemima, some bundles of reeds, far too many marbles are all examples of `article's. Definite articles are always `the' unless an object is given the attribute `proper', which makes it a proper noun and so not take a definite article at all. Thus the platinum bar, Aunt Jemima, Elbereth are all printed by `DefArt', the latter two being `proper'. As we shall later see (*note Naming of names::.), changing the short name is easy. !!!!! The reason `print object' is unsafe is that it prints the real, `hardware and unchangeable' short name, whereas we want everything to work nicely when the user changes the short name of an object in play: so the library routines almost all indirect through `PrintShortName' (except in two cases to do with `ChangePlayer', since the current player's short name is always `yourself'). 12.0.1 Exercise: printing objects --------------------------------- !!!! Why does print CDefArt(obj), " falls to the floor.^"; seem to work, but mysteriously print the number 1 after the name of the object? *Note Answer (printing objects)::. 13 Classes of objects ********************* In most games there are groups of objects with certain rules in common. As well as individual objects, Inform allows one to define classes in almost exactly the same way. The only difference between the layout of a class and object definition is that a class has no short name or initial location (since it does not correspond to a single real item). For example: Class Treasure with depositpoints 10, after [; Take: if (location==Inside_Building) score=score-self.depositpoints; score=score+5; "Taken!"; Drop: score=score-5; if (location==Inside_Building) { score=score+self.depositpoints; "Safely deposited."; } ], has valuable; An object of this class inherits the properties and attributes it defines: in this case, an object of class `Treasure' picks up the given score and rules automatically. So Nearby bars_of_silver "bars of silver" class Treasure with description "They're probably worth a fortune!", initial "There are bars of silver here!", article "some", name "silver" "bars"; inherits the `depositpoints' value of 10 and the rules about taking and dropping. If the silver bars had themselves set `depositpoints' to 15, say, then the value would be 15: i.e., the class would be over-ridden. !!!! `depositpoints' isn't a library property: it's one defined in the game this example is drawn from, the `Advent' example game. We could also, for instance, have: Nearby cake "valuable cake" class Treasure with description "Exquisite!", initial "There's a valuable cake here!", after [; Eat: "Your most expensive meal in ages, but worth it."; ], name "valuable" "cake" has edible; Now the cake has two `after' rules. Both apply, but the rule in the cake itself takes precedence, i.e., happens first. !!!! An object can inherit from several classes at once. Moreover, a class can itself inherit from other classes, so one can easily make a class for `like Treasure but with only 8 `depositpoints''. !!!!! The `class' field of an object definition contains a list of classes, `class' C1 ... Cn in which case the object inherits first from C1, then from C2 and so on. C2 over-rides C1 and so on along the line. These classes may well disagree with each other, so the order matters. If C1 says `depositpoints' is 5, C3 says it is 10 but the object definition itself says 15 then the answer is 15. !!!!! With some properties, the value is not replaced but added to: this is what happened with `after' above. These properties are those which were declared as `additive', e.g., by Property additive before $ffff; For instance, the standard Inform properties `name' and `before' are both additive. So we could add `name "treasure",' to the properties in the class definition for `Treasure', and then all objects of that class would respond to the word `treasure', as well as their own particular names. 14 Daemons and the passing of time ********************************** Some, such as Sleep and Love, were never human. From this class an individual daemon is allotted to each human being as his `witness and guardian' through life. - C. S. Lewis (1898-1963), `The Discarded Image' Some daemon stole my pen (forgive th' offence) And once betrayed me into common sense. -- Alexander Pope (1688--1744), `The Dunciad' 14.1 Daemons (routines that run each turn) ========================================== By tradition, a daemon is an event which happens each turn while it is `active'. The classic example is of a dwarf which appears in the cave: it has a daemon routine attached for moving about, throwing knives at the player and other pleasantries. Each object can have a `daemon' of its own. This is set going, and stopped again, by calling the (library) routines StartDaemon(the-object); StopDaemon(the-object); Once active, the `daemon' property of the object is called as a routine each turn. Daemons are often started by a game's `Initialise' routine, and active throughout. !!!! Be warned: this continues to happen even if the daemon is associated with a room or item which has been left behind by the player. Actually this is very useful, as it means daemons can be used for `tidying-up operations', or for the consequences of the player's actions to catch up with him. Daemons often run a fair amount of code. (There are plenty of good examples in `Toyshop' and `Advent'.) They shouldn't be ridiculously slow if they will run more than once. And some daemons so sit in the background for enormously long times: for instance, the daemon in `Advent' which hides until the player has managed to get all the treasures, then pounces. Such daemons ought to check their condition and return as quickly as possible if it fails. 14.1.1 Exercise: the thief -------------------------- Many games contain `wandering monsters', characters who walk around the map (usually hand-coded and not moving far abroad). Use a daemon to implement one who wanders as freely as the player, like the thief in `Zork I'. *Note Answer (the thief)::. 14.1.2 Exercise: weights ------------------------ Use a background daemon to implement a system of weights, so that the player can only carry a certain weight before her strength gives out and she is obliged to drop something. It should allow for feathers to be lighter than lawn-mowers. *Note Answer (weights)::. 14.2 Timers and fuses ===================== A `timer' (these are traditionally called `fuses' but the author can stand only so much tradition) can alternatively be attached to an object. (An object can't have both a `timer' and a `daemon' going at the same time.) A timer is started with StartTimer(the-object, time); in which case it will `go off' (alarm clock-style) in the given number of turns. This means that its `time_out' property will be called (as a routine), once and once only, when the time comes. It can be deactivated (so that it will never go off) by calling StopTimer(the-object); A timer is *required* to provide a `time_left' property, to hold the amount of time left. If it doesn't, the library will print an error message at run-time. You can alter `time_left' yourself, but setting it to 0 does not stop the timer: use the routine `StopTimer' for that. !!!! In early releases of the library, a `daemon' needed a `time_left' as well, which was illogical and a nuisance: anyway, it's no longer the case. !!!! Normally, you can only have 32 timers or daemons active at the same time as each other (there may be any number of inactive ones). But this limit is easily raised: just define the constant `MAX_TIMERS' to some larger value, putting the definition in your code before the `Parser' file is included. 14.3 Routines that run when an object is in scope ================================================= There is yet a third form of timed event. If a room provides an `each_turn' routine, then this will be called in each turn when the player is in it; if an object provides `each_turn', this is called whenever the object is nearby. For instance, a radio might blare out music whenever it is nearby; a sword might glow whenever monsters are nearby; or a stream running through several forest locations might occasionally float objects by. `Each turn' is entirely separate from daemons and timers. Although an object can't have both a timer and a daemon at the same time, it can have an `each_turn' at the same time, and this is quite useful, especially to run creatures. An ogre with limited patience can therefore have an `each_turn' routine which worries the player (`The ogre stamps his feet angrily!', etc.) while also having a timer set to go off when his patience runs out. !!!! `Nearby' actually means `in scope', a term which will be properly explained later. The idea is based on line of sight, which works well in most cases. !!!!! But it does mean that the radio will be inaudible when shut up inside most containers - which is arguably fair enough - yet audible when shut up inside transparent, say glass, ones. You can always change the scope rules using an `InScope' routine to get around this. In case you want to tell whether scope is being worked out for ordinary parsing reasons or instead for `each_turn' processing, look at the `et_flag' variable (0 in the former case, 1 in the latter). Powerful effects are available this way - you could put the radio in scope within all nearby rooms so as to allow sound to travel. Or you could make a thief audible throughout the maze he is wandering around in, as in `Zork I'. The library also has the (limited) ability to keep track of time of day as the game goes on. If you're writing a game with the time instead of the score and turns on the status line, you can set the time by `SetTime(' 60 * HOURS + MINUTES`,' RATE `);' The current time is held in the variable `the_time' and runs on a 24-hour clock. The RATE controls how rapidly time is moving: a RATE of 0 means it is standing still (that is, that the library doesn't change it: your routines still can). A positive RATE means that that many minutes pass between each turn; and negative RATE means that many turns pass between each minute. The time still won't appear on the game's status line unless you set Statusline time; as a directive somewhere in your code. And remember to start off the clock by calling `SetTime' in your `Initialise' routine, if you're going to use it. !!!! Exactly what happens at the end of each turn is: 1. The turns counter is incremented. 2. The 24-clock is moved on. 3. Daemons and timers are run (in no guaranteed order). 4. `each_turn' takes place for the current room, and then for everything nearby (that is, in scope). 5. The game's global `TimePasses()' routine is called. 6. Light is re-considered (it may have changed as a result of events since this time last turn). The sequence is abandoned if at any stage the player dies or wins. 14.3.1 Exercise: passing midnight --------------------------------- How could you make your game take notice of the time passing midnight, so that the day of the week could be nudged on? *Note Answer (passing midnight)::. 14.3.2 Exercise: suspended in mid-air ------------------------------------- !!!! Suppose the player is magically suspended in mid-air, but that anything let go of will fall out of sight. The natural way to code this is to use a daemon which gets rid of anything it finds on the floor (this is better than just trapping `Drop' actions because objects might end up on the floor in many different ways). Why is using `each_turn' better? *Note Answer (suspended in mid-air)::. 14.3.3 Exercise: varying rates of time -------------------------------------- How would a game work if it involved a month-long archaeological dig, where anything from days to minutes pass between successive game turns? *Note Answer (varying rates of time)::. 15 Adding verbs and grammar to the parser ***************************************** Grammar, which can govern even kings. -- Molière (1622--1673), `Les Femmes savantes' Language disguises thought ... The tacit conventions on which the understanding of everyday language depends are enormously complicated. - Ludwig Wittgenstein, `Tractatus' The next few sections will delve deep into the parser. Inform goes to a great deal of trouble to make the parser as `open-access' as possible, because a parser cannot ever be general enough for every game without being extremely modifiable. So there are very many ways to customise the Inform parser, hopefully without the user needing to understand much about how it works (because this is quite a long story). 15.1 Adding grammar for new verbs ================================= The first essential requirement of any parser is to accept the addition of the grammar for a new verb. In Inform code, grammar should appear at the end of the source code for a game, and of course most of it is written out in the `Grammar' header file, which all games using the library include. After this inclusion, you can add extras. The directive for this is called `Verb', and here's a typical example: some of the library's grammar for `take': Verb "take" "get" "pick" "lift" * "out" -> Exit * multi -> Take * multiinside "from" noun -> Remove * "in" noun -> Enter * multiinside "off" noun -> Remove * "off" held -> Disrobe * "inventory" -> Inv; This declares a verb, for which `take', `get' and so on are synonyms, and which can take seven different courses. !!!! It should be noted that these really are synonyms: the parser thinks `take' and `get' are exactly the same. Sometimes this has odd results, so that although `get in bed' is correctly understood as a request to enter the bed, `take in washing' is misunderstood as a request to enter the washing. You could either get around this by writing separate grammars for the two nearly-synonyms, or can cheat and see if the variable `verb_word=='take'' or `'get''. Mostly, though, you don't mind if a few odd things are accepted by the parser: what matters more is that sensible things are not rejected by it. When it ploughs through what the player has typed, the parser tries to match each line in turn, starting at the top. The first line will only be matched if the player has typed something like `get out'. The second line is more interesting: it allows the player to type a single object or a list of objects. So the second line could be matched by `take the banana' `get all the fruit except the apple' There need be no grammar at all after the verb: for example, a grammar for `inventory' could be as simple as Verb "invent" "inv" "i" * -> Inv; After the `->' in each line is the name of an action. This is how actions are defined: they are the names which appear in grammar lines like this one. Remember that if you do create an action this way, you also have to write a routine to execute the action, even if it's one which doesn't do very much. For instance: [ XyzzySub; "Nothing happens."; ]; Verb "xyzzy" * -> Xyzzy; will make a new magic-word verb `xyzzy', which always says `Nothing happens' - always, that is, unless some `before' rule gets there first, as it might do in certain magic places. (The name of the routine is always the name of the action with `Sub' appended.) The new action `Xyzzy' is treated exactly like all the standard Inform actions: `##Xyzzy' gives its action number, and you can write `before' and `after' rules for it in `Xyzzy:' fields just as you would for, say, `Take'. 15.2 Grammar tokens =================== The individual words in the grammar (after the `*' and before the `->') are called `tokens'. In increasing order of complexity, this is the complete list: `"WORD"' that literal word only `noun' any object in scope `held' object held by the player `creature' an object in scope which is `animate' `multi' one or more objects in scope `multiheld' one or more held objects `multiexcept' one or more in scope, except the other `multiinside' one or more in scope, inside the other `ATTRIBUTE' any object in scope which has the attribute `noun = ROUTINE' any object in scope passing the given test `scope = ROUTINE' an object in this definition of scope `special' *any* single word or number `number' a number only `ROUTINE' any text accepted by the given routine In the case of `noun = 'ROUTINE, the parser applies the following test: the variable `noun' is set to the object in question, and the routine is called. If it returns true, the parser accepts the object, and otherwise it rejects it. `number' matches any decimal number from 0 upwards (though it rounds off large numbers to 10000), and also matches the numbers `one' to `twenty' written in English. The token `special' is now obselete. For now, we shall take `in scope' to mean `visible to the player'. It is quite important in some cases to know exactly what this means, so a better definition will be given later. The `held' token is convenient for two reasons. Firstly, many actions only sensibly apply to things being held (such as `Eat' or `Wear'), and using this token in the grammar you can make sure that the action is never generated by the parser unless the object is being held. That saves on always having to write `You can't eat what you're not holding' code. Secondly, suppose we have grammar Verb "eat" * held -> Eat; and the player types `eat the banana' while the banana is plainly in view, on a shelf, say. It would be rather petty of the game to refuse on the grounds that the banana is not being held. So the parser will generate a `Take' action for the banana and then, if the `Take' action succeeds, an `Eat' action. (Notice that the parser does not just pick up the object, but issues an action in the proper way - so if the banana had rules making it too slippery to pick up, it won't be picked up.) The `multi-' tokens indicate that a list of one or more objects can go here. (The parser works out all the things the player has asked for, sorting out plural nouns and words like `except' by itself, and then generates actions for each one.) `multiexcept' is provided to make commands like `put everything in the rucksack' parsable: the `everything' is matched by all of the player's possessions except the rucksack (this stops the parser from generating an action to put the rucksack inside itself). Similarly `multiinside' handles: `remove everything from the cupboard' A restriction here is that a single grammar line can only contain one `multi-' token: so `hit everything with everything' can't be parsed (straightforwardly: you can parse anything with a little more effort). On the whole, this is no bad thing. The reason not all nouns can be multiple in the first place is that too helpful a parser makes too easy a game. You probably don't want to allow something like `unlock the mystery door with all the keys' - you want the player to suffer having to try them one at a time, or else to be thinking. (Of course if you do want to allow this it is easy enough to change the grammar: the point is that you have the option.) We can also sort out objects according to attributes that they have: Verb "use" "employ" "utilise" * edible -> Eat * clothing -> Wear ... and so on ... * enterable -> Enter; and this is how attributes are used as tokens. (The library grammar does not contain such an appallingly convenient verb!) Since you can define your own attributes, it is therefore easy to make a grammar line which matches only your own class of object. !!!! A footnote: the `creature' token is thus equivalent to writing `animate'; but it dates back to the earliest days of Inform, and does no harm. Sometimes even that isn't flexible enough. Here is a verb, `free', which should only apply to animal kept in a cage: [ CagedCreature; if (noun in wicker_cage) rtrue; rfalse; ]; Verb "free" "release" * noun=CagedCreature -> FreeAnimal; So that only nouns which pass the `CagedCreature' test are allowed. (The `CagedCreature' routine can appear anywhere in the code, though it's tidier to keep it nearby.) 15.3 Parsing numbers ==================== So far, all the tokens were to tell the parser which of the objects in scope were acceptable. Exactly what `in scope' means will be gone into later, and so will the powerful `scope='... token. Here we next want the parser to match things other than names of objects and prepositions like `into'. The simplest useful case is of numbers, for example: Verb "type" * number -> TypeNum; so that the TypeNum action will happen if the player types `type 504' or `type seventeen'. !!!! In fact, you're allowed to provide your own number-parsing routine, so many sneaky possibilities are open here - Roman numerals, coordinates like `J4', long telephone numbers (which would be too large to fit into integer variables in Inform), understanding English numbers and so on. This takes the form [ ParseNumber buffer length; ! returning 0 if no match is made, or the number otherwise ]; and examines the supposed `number' held at the byte address `buffer', which is a row of ASCII characters of the given `length'. If you declare a `ParseNumber' routine, then the parser will always try this first when trying to decode something as a number (and if it fails will fall back on its own number-parsing routine). !!!!! For not-very-good internal reasons, `ParseNumber' can't return 0 to mean the number zero. Probably `zero' won't be needed too often, but if it is you can always return some value like 1000 and code the verb in question to understand this as 0. !!!!! You can give a routine to parse anything: [ French w n; w=NextWord(); if (w=='un' or 'une') n=1; if (w=='deux') n=2; if (w=='trois') n=3; if (w=='quatre') n=4; if (w=='cinq') n=5; if (n==0) return -1; parsed_number = n; return 1; ]; will detect low numbers in French, and could be used by something like: Verb "type" * French -> TypeFrenchNum * number -> TypeNum; The specification for such a routine is as follows: it is to use `NextWord' (possibly several times) to look at the appropriate words which the user has typed. The variable `wn' (current word number) may also be helpful. The routine must return `-1' if the user's input isn't understood, `1' if there is a numerical value resulting, or `n' if object number n is understood. In the case of a number, the actual value should be put into the variable `parsed_number'. On an unsuccessful match (returning -1) it doesn't matter what the final value of `wn' is. On a successful one it should be left pointing to the next thing *after* what the routine understood. (Since `NextWord' moves `wn' on by one each time it is called, this happens automatically unless the routine has read too far.) 15.3.1 Exercise: footnotes -------------------------- (A beautiful feature stolen from David M. Baggett's game `The Legend Lives', which uses it to great effect.) Some games produce footnotes every now and then. Arrange matters so that these are numbered `[1]', `[2]' and so on in order of appearance, to be read by the player when `footnote 1' is typed. *Note Answer (footnotes)::. 15.4 Extending the library grammar ================================== That's all about tokens: back to verb definition commands, because some of the above examples are slightly contrived - they create wholly new and unlikely verbs. More often, one would want, say, a big array of labelled buttons, any of which could be pushed. So the verb for `push' ought to be extended, and for this the `Extend' directive is provided: Extend "push" * Button -> PushButton; A routine called `Button' could then be written to accept things like `button j16', `d11', `a5 button'. The point of `Extend' is that it is against the spirit of the library to alter the standard library files - including the grammar table - unless absolutely necessary. !!!!! Actually, there's a better way to provide these buttons, so that they work with any verb automatically, and that's to make a button object with a `parse_name' routine which cunningly extracts the button number as it parses: see the game `Balances' for an example of this. Normally, extra lines of grammar are added at the bottom of those already there. This may not be what you want. For instance, `take' has a grammar line * multi -> Take quite early on. So if you want to add a grammar line which diverts `take something-edible' to a different action, like so: * edible -> Eat then it's no good adding this at the bottom of the `Take' grammar, because the earlier line will always be matched first. Thus, you really want to insert your line at the top, not the bottom, in this case. The right command is Extend "take" first * edible -> Eat; You might even want to actually replace the old grammar completely, not just add a line or two. For this, use Extend "push" replace * Button -> PushButton; and now `push' can be used only in this way. To sum up, `Extend' can take three keywords: `replace' completely replace the old grammar with this one `first' insert the new grammar at the top of the old one `last' insert the new grammar at the bottom of the old one with `last' being the default (which doesn't need to be said explicitly). 15.5 Meta verbs and unknown verbs ================================= !!!! Another convenience is to define `meta' verbs - verbs which are not really part of the game, such as `save', `score' and `quit'. (You probably want to add your own debugging commands as meta-verbs: if you make them ordinary verbs, then they may work most of the time, but might be interfered with by game rules, and will take up `game time'.) To declare a verb as `meta', just add the word after the command. For instance, the library defines Verb meta "score" * -> Score; The parser will match the grammar exactly in the normal way, but treats the resulting action as outside the game - taking no time up, and possible at any moment. !!!!! There are (a few) times when verb definition commands are not enough. For example, in the original `Advent' (or `Colossal Cave'), the player could type the name of a not-too-distant place which had previously been visited, and be taken there. There are several ways to code this - say, with 60 rather similar verb definitions, or with a single `travel' verb which has 60 synonyms, whose action routine looks at the parser's `verb_word' variable to see which one was typed - but here's another. The library will call the `UnknownVerb' routine (if you provide one) when the parser can't even get past the first word. This has two options: it can return false, in which case the parser just goes on to complain as it would have done anyway. Otherwise, it can return a verb word which is substituted for what the player actually typed. Here is a foolish example: [ UnknownVerb w; if (w=='shazam') { print "Shazam!^"; return 'inventory'; } rfalse; ]; which responds to the magic word `shazam' by printing `Shazam!' and then, rather disappointingly, taking the player's inventory. But in the example above, it could be used to look for the word `w' through the locations of the game, store the place away in some global variable, and then return `'go''. The `GoSub' routine could then be fixed to look at this variable. !!!!! If you allow a flexible collection of verbs (say, names of spells or places) then you may want a single `dummy' verb to stand for whichever is being typed. This may make the parser produce strange questions because it is unable to sensibly print the verb back at the player, but you can fix this using the `PrintVerb' entry point (*note Library entry points::.). See the exercise below. 15.5.1 Exercise: unknown verbs ------------------------------ !!!!! Why is it usually a bad idea to print text out in an `UnknownVerb' routine? *Note Answer (unknown verbs)::. 15.5.2 Exercise: control panel ------------------------------ !!!! A tricky one: code a spaceship whose control panel has five sliding controls, each of which can be set to a numerical value, so that the game looks like: >look Machine Room There is a control panel here, with five slides, each of which can be set to a numerical value. >push slide one to 5 You set slide number 1 to the value 5. >examine the first slide Slide number 1 currently stands at 5. >set four to six You set slide number 4 to the value 6. *Note Answer (control panel)::. 15.5.3 Exercise: quoted strings ------------------------------- !!!!! An even trickier one: write a parsing routine which will accept any amount of text (including spaces, full stops and commas) between double-quotes as a single token. *Note Answer (quoted strings)::. 15.5.4 Exercise: named rooms ---------------------------- !!!!! And another: implement the Crowther and Woods feature of moving from one room to another by typing its name, discussed above, using a dummy verb and without using `Replace' to change any library routines. *Note Answer (named rooms)::. 16 Scope and what you can see ***************************** He cannot see beyond his own nose. Even the fingers he outstretches from it to the world are (as I shall suggest) often invisible to him. - Max Beerbohm (1872-1956), of George Bernard Shaw Wherefore are these things hid? -- William Shakespeare (1564--1616), `Twelfth Night' 16.1 What `in scope' means ========================== Time to say what `in scope' means. This definition is one of the most important algorithms in the game, although it is buried inside the parser, because it decides what the player is allowed to refer to: so here it is in full. The following are in scope: the player's immediate possessions; the 12 compass directions; *if* there is light, the objects in the same place as the player. In addition, if an object is in scope then its immediate possessions are in scope, *if* it is `see-through', which means that: the object has `supporter', *or* the object has `transparent', *or* the object is an `open' `container'. The definition of when there is light will be gone into in the next section. !!!! Many things are still in scope in a dark room - so the player can still turn a lamp on if it's being carried. On the other hand, a player who puts the lamp on the ground and turns it off then loses the ability to turn it back on again, because it is out of scope. (This can be changed; *Note Changing scope::.) !!!! The compass direction objects make sense as objects. The player can always type something like `attack the south wall' (and a `before' rule for the room in question could make something unusual happen as a result). The compass directions are visible in the dark, as otherwise the player could not still walk around. !!!! The parser applies scope rules to other people too, not just the player. Thus `dwarf, drop sword' will be accepted even if the sword is invisible to the player provided it is visible to the dwarf. !!!! Items with the `concealed' attribute may be concealed from ordinary descriptions - but the parser still sees them: they are still in scope. (If you want things to be both concealed and unreferrable-to, put them somewhere else!) !!!!! Actually, the above definition is not quite right, because the compass directions are not in scope when the player asks for a plural number of things, like `take all the knives'. (This makes some of the parser's plural algorithms run faster.) Also, for a `multiexcept' token, the other object is not in scope; and for a `multiinside' token, only objects in the other object are in scope. This makes `take everything from the cupboard' work in the natural way. 16.2 Changing the scope rules ============================= The rest of this section is about how to change the scope rules. As usual with Inform, you can change them globally, but it's more efficient and safer to do so only locally. A typical example: how do we allow the player to ask questions like the traditional `what is a grue'? The `grue' part ought to be parsed as if it were a noun, so that we could distinguish between, say, a `garden grue' and a `wild grue'. So it isn't good enough to look only at a single word. Here is one solution: Object questions "qs"; [ QuerySub; print_paddr noun.description; new_line; ]; [ Topic i; if (scope_stage==1) rfalse; if (scope_stage==2) { objectloop (i in questions) PlaceInScope(i); rtrue; } "At the moment, even the simplest questions confuse you."; ]; where the actual questions are themselves objects which belong to the `questions' object, like so: Object q1 "long count" questions with name "long" "count", description "The Long Count is the great Mayan cycle of time, \ which began in 3114 BC and will finish with the world's \ end in 2012 AD."; and we also have a grammar line: Verb "what" * "is" scope=Topic -> Query * "was" scope=Topic -> Query; (Note that the `questions' and `q1' objects are out of the game for every other purpose. The name `qs' doesn't matter; the individual questions are named so that the parser might be able to say `Which do you mean, the long count or the short count?' if the player just asked `what is the count'.) Here's what happens: when the parser reaches `scope=Topic', it calls the `Topic' routine with the variable `scope_stage' set to 1. The routine should return 1 (true) if it is prepared to allow multiple objects to be accepted here, and 0 (false) otherwise: as we don't want `what is everything' to list all the questions and answers in the game, we return false. A little later on in its machinations, the parser again calls `Topic' with `scope_stage' now set to 2. `Topic' is now under an obligation to tell the parser which objects are to be in scope. It can call two parser routines to do this. ScopeWithin(object) puts everything inside the object into scope, though not the object itself; PlaceInScope(object) puts just a single object into scope. It is perfectly legal to declare something in scope that `would have been in scope anyway': or even something which is in a different room altogether from the actor concerned, say at the other end of a telephone line. Our scope routine `Topic' should then return `0 (false)' to carry on with the usual scope rules, so that everything that would usually be in scope still is, and `1 (true)' to tell the parser not to put any more objects into scope. (So at `scope_stage' 2 it is quite permissible to do nothing but return false, whereupon the usual rules apply.) `Topic' returns true because it wants only question topics to be in scope, not question topics together with everything near the player. If the player had typed `what is the long count', that would be all that happens and all would be well. On the other hand, if the player typed `what is the lgon cnout' owing to a bout of dyslexia, the error message which the parser would usually produce (`You can't see any such thing') would be unsatisfactory. So if parsing failed at this token, then `Topic' is called at `scope_stage' 3 to print out a suitable error message. (It must provide one.) !!!! The `scope=' notation is available only under Inform 5.4 and later. !!!! Note that `ScopeWithin(object)' extends the scope down through its possessions according to the usual rules (i.e., depending on their transparency, whether they're containers and so in). The definition of `Topic' above shows how to put just the direct possessions into scope. 16.2.1 Exercise: purloin verb ----------------------------- Write a token which puts everything in scope, so that you could have a debugging `purloin' verb which could take anything (regardless of where it was and the rules applying to it). *Note Answer (purloin verb)::. 16.3 Changing the global scope rules ==================================== Changing the global definition of scope should be done cautiously (there may be unanticipated side effects); bear in mind that scope decisions need to be taken often - every time an object token is parsed, so perhaps five to ten times in every game turn - and hence moderately quickly. To do this one provides a routine called InScope(actor) where the `actor' is the person whom the parser is working out scope for; usually, then, `actor=player', but this should not be assumed to be always the case. If the routine decides that a particular object should be in scope for the actor, it should execute `InScope' and `ScopeWithin' just as above, and return true or false (as if it were at `scope_stage' 2). Thus, it is vital to return false in circumstances when you don't want to intervene. !!!! In the case of a token `scope='ROUTINE, the ROUTINE gets first crack at scope-changing, and `InScope' will only be reached if it returns false to signify `carry on'. Here are some examples. Firstly, as promised above, how to change the rule that `things you've just dropped disappear in the dark': [ InScope person i; if (person==player && location==thedark) objectloop (i near player) if (i has moved) PlaceInScope(i); rfalse; ]; With this routine added, the objects in the dark room the player is in are in scope only if they have `moved' (that is, have been held by the player in the past); and even then, are in scope only to the player. !!!! By combining an `InScope' routine with token-parsing routines in grammar, dramatic effects are possible. See the example game `Balances', for instance. 16.3.1 Exercise: light switch ----------------------------- !!!!! Code the following puzzle, which has nasty scope complications. In an initially dark room there is a light switch. Provided you've seen the switch at some time in the past, you can turn it on and off - but before you've ever seen it, you can't. Inside the room is nothing you can see, but you can hear a dwarf breathing. If you tell the dwarf to turn the light on, he will. *Note Answer (light switch)::. 16.4 Parser error messages ========================== !!!!! Once you begin programming the parser on a large scale, you soon reach the point where the parser's ordinary error messages no longer appear sensible. So that you can change the rules even at this last hurdle, the parser calls your `ParserError' routine (if you provide one): the argument is the error type, and you return true to tell the parser to shut up (as you have already printed a better error message than it would have done). The error types are all defined as constants: `STUCK_PE' I didn't understand that sentence. `UPTO_PE' I only understood you as far as... `CANTSEE_PE' You can't see any such thing. `TOOLIT_PE' You seem to have said too little! `NOTHELD_PE' You aren't holding that! `MULTI_PE' You can't use multiple objects with that verb. `MMULTI_PE' You can only use multiple objects once on a line. `VAGUE_PE' I'm not sure what `it' refers to. `EXCEPT_PE' You excepted something not included anyway! `ANIMA_PE' You can only do that to something animate. `VERB_PE' That's not a verb I recognise. `SCENERY_PE' That's not something you need to refer to... `ITGONE_PE' You can't see ~it~ (the spell book) at the moment. `JUNKAFTER_PE' I didn't understand the way that finished. `TOOFEW_PE' Only five of those are available. `NOTHING_PE' Nothing to do! `NUMBER_PE' I didn't understand that number. `ASKSCOPE_PE' *whatever the scope routine prints* Each time the parser gives up on a line of grammar, it has hit one of these conditions. A verb may have many lines of grammar; so by the time the parser wants to print an error, all of them must have failed. The error message it prints is the most `interesting' one (meaning, lowest down this list). 17 The light and the dark ************************* The library maintains light by itself, and copes with events like: a total eclipse of the Sun; fusing all the lights in the house; your lamp going out; a dwarf stealing it and running away; dropping a lit match which you were seeing by; putting your lamp into an opaque box and shutting the lid; black smoke filling up the glass jar that the lamp is in; the dwarf with your lamp running back into your now-dark room. The point of this list is to demonstrate that light versus darkness is tricky to get right, and that it is best left to the library. Your code needs only to do something like give lamp light; remove match; give glass_jar ~transparent; move dwarf to Dark_Room; and can leave the library to sort out the consequences. As the above suggests, the `light' attribute means that an object is giving off light, or that a room is currently lit (e.g., by being out of doors in day-time). If you simply never want to have darkness, and some games never do, a sneaky way of doing it is to put the line give player light; in `Initialise'. The game works as if the player herself were glowing enough to provide light to see by. So there's never darkness near the player. The definition of `when there is light' is complicated, involving recursion both up and down. Remember that the parent of the player object may not be a room; it may be, say, a red car whose parent is a room. There is light exactly when the parent of the player `offers light'. An object `offers light' if: it itself has the `light' attribute set, *or* any of its immediate possessions `have light', *or* it is see-through and its parent offers light, *or* it is enterable and its parent offers light; while an object `has light' if: it currently has the `light' attribute set, *or* it is see-through and one of its immediate possessions has light. The process of checking this stops as soon as light is discovered. The routines `OffersLight(object)' and `HasLightSource(object)' which return true or false and have no side effects (that is, they merely report on the current state without changing anything), are available and might occasionally be useful. !!!! So light is cast up and down the tree of objects. In certain contrived circumstances this might be troublesome: perhaps an opaque box, whose outside is fluorescent but whose interior is dark, and which contains an actor who needs not to have other contents of the box in scope... `contrived' being the word. The dilemma could be solved by putting an inner box in the outer one. Each turn, light is reconsidered. The presence or absence of light affects the `Look', `Search', `LookUnder' and `Examine' actions, and (since this is a common puzzle) also the `Go' action: you can provide a routine called DarkToDark() and if you do then it will be called when the player goes from one dark room into another dark one. If you want, you can take the opportunity to kill the player off or extract some other forfeit. If you provide no such routine, then the player can move about freely (subject to any rules which apply in the places concerned). 17.0.1 Exercise: heliotropic troll ---------------------------------- How would you code a troll who is afraid of the dark, and needs to be bribed but will only accept a light source... so that the troll will be as happy with a goldfish bowl containing a fluorescent jellyfish as he would be with a lamp? *Note Answer (heliotropic troll)::. 18 On inventories and lists *************************** As some day it may happen that a victim must be found I've got a little list -- I've got a little list Of society offenders who might well be underground, And who never would be missed Who never would be missed! -- W. S. Gilbert (1836--1911), `The Mikado' 18.1 Producing lists ==================== The library often needs to reel off a list of objects: when an `Inv' (inventory) action takes place, for instance, or when describing the contents of a container or a room. Lists are difficult to print out correctly `by hand', because there are many cases to get right (especially when taking plurals into account). Fortunately, the library's list-maker is open to the public. The routine to call is: WriteListFrom(object, style); where the list will start from the given object and go along its siblings. (Thus, to list all the objects inside `X', list from `child(X)'.) What the list looks like depends on the style, which is a bitmap you can make by adding up some of the following constants: `NEWLINE_BIT' New-line after each entry `INDENT_BIT' Indent each entry by depth `FULLINV_BIT' Full inventory information after entry `ENGLISH_BIT' English sentence style, with commas and `and' `RECURSE_BIT' Recurse downwards with usual rules `ALWAYS_BIT' Always recurse downwards `TERSE_BIT' More terse English style `PARTINV_BIT' Only brief inventory information after entry `DEFART_BIT' Use the definite article in list `WORKFLAG_BIT' At top level (only), only list objects which have the `workflag' attribute The best way to use this is to experiment. For example, a `tall' inventory is produced by: WriteListFrom( child(player), FULLINV_BIT + INDENT_BIT + NEWLINE_BIT + RECURSE_BIT ); and a `wide' one by: WriteListFrom( child(player), FULLINV_BIT + ENGLISH_BIT + RECURSE_BIT ); which produce effects like: >inventory tall You are carrying: a bag (which is open) three gold coins two silver coins a bronze coin four featureless white cubes a magic burin a spell book >inventory wide You are carrying a bag (which is open), inside which are three gold coins, two silver coins and a bronze coin, four featureless white cubes, a magic burin and a spell book. (except that the `You are carrying' part is not done by the list-maker, and nor is the final full stop in the second example). The `workflag' is an attribute which the library scribbles over from time to time as temporary storage, but you can use it with care. In this case it makes it possible to specify any reasonable list. 18.1.1 Exercise: double inventory --------------------------------- Write a `DoubleInvSub' action routine to produce an inventory like so: You are carrying four featureless white cubes, a magic burin and a spell book. In addition, you are wearing a purple cloak and a miner's helmet. *Note Answer (double inventory)::. 18.2 Changing an object's description in a list =============================================== Some objects ought to print something different when they appear in inventory lists: a wine bottle should say how much wine is left, for instance. By giving an object an `invent' routine, you can alter the usual rules. A typical inventory list looks like: >i You are carrying: a green cone a pair of hiking boots your satchel (which is open) a green cube and each line goes through two stages. Firstly, the object's indefinite article and name are printed. Secondly, little informative messages like `(which is open)' are printed, and inventories are given for the contents of open containers. An object's `invent' routine gets two chances to interfere with this, one before each stage. So what happens is this: 1. The global variable `inventory_stage' is set to 1. 2. The `invent' routine is called (if there is one). If it returns true, stop here. 3. The indefinite article and short name are printed. 4. The global variable `inventory_stage' is set to 2. 5. The `invent' routine is called (if there is one). If it returns true, go to step 8. 6. One or more of the following are printed, as appropriate: (providing light) (being worn) (which is open) (which is closed) 7. If it is an open container, its contents are inventoried. 8. Linking text (such as a new-line or a comma) is printed, according to the current style. Note that if an `invent' routine does return true, then the contents are never reached. For example, here is an `invent' routine for a matchbook: invent [ i; if (inventory_stage==2) { i=self.number; if (i==0) print " (empty)"; if (i==1) print " (1 match left)"; if (i>1) print " (",i," matches left)"; } ], (The rest of the matchbook definition is given in the `Toyshop' example game.) !!!!! Changing the rules at `inventory_stage' 1 is kept on for more peculiar effects (and is not really recommended). 19 The naming of names ********************** The Naming of Cats is a difficult matter, It isn't just one of your holiday games; You may think at first I'm as mad as a hatter When I tell you, a cat must have THREE DIFFERENT NAMES. -- T. S. Eliot (1888--1965), `The Naming of Cats' Bulldust, coolamon, dashiki, fizgig, grungy, jirble, pachinko, poodle-faker, sharny, taghairm - Catachrestic words from Chambers English Dictionary Providing an `invent' routine doesn't work quite so well for the match as for the matchbook. You might try: invent [; if (inventory_stage==2) { if (self has light) print " (burning away)"; rtrue; } ], !!!! Returning true here is a trick to stop `(providing light)' from appearing as well - after all, that's redundant. This does work but there's a better way, which is to change the object's short name itself, from `unlit match' to `lit match' say. So another property, called `short_name', is available. Normally, the short name of an object is given in the header of the object definition. But if a `short_name' routine is given, then whenever the game wants to write out the name of the object, the following happens: 1. If the `short_name' is a string, it's printed and that's all. 2. If it is a routine, then it is called. If it returns true, that's all. 3. The `real' short name (the one in the object header) is printed. So, as promised, here's the better way to code the match: short_name [; if (self has light) print "burning match"; else print "unlit match"; rtrue; ], Note that rooms can also be given a `short_name' routine, which might be useful to code, say, a grid of four hundred almost exactly similar locations, called `Area 1' up to `Area 400'. !!!! Now, though, the player is going to want to type `drop the burning match", or `light the unlit match'. (Indeed, there might be two matches, only one of which can be called `unlit'.) It's perfectly straightforward to alter the `name' property of an object (provided one remembers what it is: a list of (machine) words, being the byte addresses in memory of words in the dictionary) but, again, there is a better and more powerful way. This is to give the match object a `parse_name' routine. A `parse_name' routine is expected to try to match the text which the user has typed (available one word at a time from the `NextWord()' routine), and to return how many words it managed to match. It is required to go as far as it can, i.e., not to stop if the first word makes sense, but to keep reading and find out how many words in a row make sense. It should return: `0' if the text didn't make any sense at all, `k' if k words in a row of the text seem to refer to the object, or `-1' to make the parser just do what it would normally do. For example: Nearby thing "weird thing" with parse_name [ i; while (NextWord()=='weird' or 'thing') i++; return i; ]; This definition duplicates (very nearly) what the parser would have done if the weird thing had been defined as: Nearby thing "weird thing" with name "weird" "thing"; Which isn't very useful. But the match can now be coded up with parse_name [ i j; if (self has light) j='burning'; else j='unlit'; while (NextWord()=='match' or j) i++; return i; ], so that `burning' only applies when it is, and similarly `unlit'. (Actually the parser automatically recognises `unlit' and `lit', so this last part was unnecessary.) 20 Plural names and groups of similar objects ********************************************* Abiit ad plures. -- Petronius (?--c. 66), `Cena Trimalchionis' A notorious problem for adventure game parsers is to handle a collection of, say, ten gold coins, allowing the player to use them independently of each other, while gathering them together into groups in descriptions and inventories. This is relatively easy in Inform, and only in really hard cases do you have to provide code. There are two problems to be overcome: firstly, the game has to be able to talk to the player in plurals, and secondly vice versa. First, then, game to player: Class gold_coin_class with name "gold" "coin", plural "gold coins"; (and similar silver and bronze coin classes here) Object bag "bag" with name "bag" has container open openable; Nearby co1 "gold coin" class gold_coin_class; Nearby co2 "gold coin" class gold_coin_class; Nearby co3 "gold coin" class gold_coin_class; Nearby co4 "silver coin" class silver_coin_class; Nearby co5 "silver coin" class silver_coin_class; Nearby co6 "bronze coin" class bronze_coin_class; Now we have a bag of six coins. The player looking inside the bag will get >look inside bag In the bag are three gold coins, two silver coins and a bronze coin. How does the library know that the three gold coins are the same as each other, but the others different? It doesn't look at the classes but the names. It will only group together things which: 1. have a `plural' set, 2. are `indistinguishable' from each other. Indistinguishable means they have the same `name' words as each other (possibly in a different order), so that nothing the player can type will separate the two. !!!! Actually, the library is cleverer than this. What it groups together depends slightly on the context of the list it's writing out. When it's writing a list which prints out details of which objects are providing light, for instance (like an inventory), it won't group together two objects if one is lit and the other isn't. Similarly for objects with visible possessions or which can be worn. !!!!! This all gets even more complicated when the objects have a `parse_name' routine supplied, because then the library can't use the `name' fields to tell them apart. If they have different `parse_name' routines, it decides that they're different. But if they have the same `parse_name' routine, there is no alternative but to ask them. What happens is that 1. A variable called `parser_action' is set to `##TheSame'; 2. Two variables, called `parser_one' and `parser_two' are set to the two objects in question; 3. Their `parse_name' routine is called. If it returns: `-1' the objects are declared `indistinguishable', `-2' they are declared different. 4. Otherwise, the usual rules apply and the library looks at the ordinary `name' fields of the objects. (`##TheSame' is a fake action.) The implementation of the `Spellbreaker cubes' in the `Balances' game is an example of such a routine (so that if the player writes the same name on several of the cubes, they become grouped together). Note that this whole set-up is such that if the author of the `parse_name' routine has never read this paragraph, it doesn't matter and the usual rules take their course. !!!!! You may even want to provide a `parse_name' routine just to speed up the process of telling two objects apart - if there were 30 gold coins the parser will be doing a lot of work comparing all their names, but you can make the decision much faster. Secondly, the player talking to the computer. This goes a little further than just copies of the same object: many games involve collecting a number of similar items, say a set of nine crowns in different colours. Then you'd want the parser to recognise things like: > drop all of the crowns except green > drop the three other crowns even though the crowns are not identical. The simple way to do this is just to put `"crowns"' in their name fields, and this works perfectly well most of the time. !!!!! But it isn't ideal, because then the parser will think > drop crowns refers to a single object, and won't deduce that the player wants to pick up all the sensibly available crowns. So the complicated (but better way) is to make the `parse_name' routine tell the parser that yes, there was a match, but that it was a plural. The way to do this is to set `parser_action' to `##PluralFound' (another fake action). So, for example: Class crown_class with parse_name [ i j; for (::) { j=NextWord(); if (j=='crown' or j==self.name) i++; else { if (j=='crowns') { parser_action=##PluralFound; i++; } else return i; } } ]; 20.0.1 Exercise: cherubim ------------------------- Write a `cherub' class so that if the player tries to call them `cherubs', a message like `I'll let this go by for now, but the plural of cherub is cherubim" appears. *Note Answer (cherubim)::. 21 Miscellaneous constants and scoring ************************************** Some game rules can be altered by defining `constants' at the start of the program, before the library files are included. Two constants it *must* provide are the strings `Story' and `Headline': Constant Story "ZORK II"; Constant Headline "^An Interactive Plagiarism^\ Copyright (c) 1993 by Ivan O. Ideas.^"; All the rest are optional. 21.1 Limiting the objects carried by the player =============================================== The library won't allow the player to carry an indefinite number of objects: the limit allowed is the constant `MAX_CARRIED', which you may define if you wish. If you don't define it, it's 100, which roughly removes the rule. (In fact you can change this `live', in that it is actually `player.capacity' which is consulted; the only use of `MAX_CARRIED' is to set this up to an initial value.) If you define `SACK_OBJECT' to be some container, then the player will automatically put old, least-used objects away in it as the game progresses, provided it is being carried. This is a feature which endears the designer greatly to players. 21.2 Providing closing credits ============================== Another constant is `AMUSING_PROVIDED'. If you define this, the library knows to put an `amusing' option on the menu after the game is won. It will then call `Amusing()' from your code when needed. You can use this to roll closing credits, or tell the player various strange things about the game, now that there's no surprise left to spoil. 21.3 The scoring system ======================= The other constants you are allowed to define help the score routines along. 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 `score' variable in any case.) One scores points for getting certain items or reaching certain places; the other for completing certain actions. The constants are: `MAX_SCORE' The maximum game score (by default 0) `NUMBER_TASKS' Number of individual `tasks' to perform (1) `OBJECT_SCORE' Bonus for first picking up a `scored' object (4) `ROOM_SCORE' Bonus for first entering a `scored' room (5) and then the individual tasks have scores, as follows: Global task_scores initial t1 t2 ... tn; Within your code, when a player achieves something, call `Achieved(task)' to mark that the task has been completed. It will only award points if this task has not been completed before. There do not have to be any `tasks': there's no need to use the scoring system provided. Tasks (and the verb `full' for full score) will only work at all if you define the constant `TASKS_PROVIDED'. A routine called `PrintRank', which you can provide if you want, gets the chance to print something additional to the score, such as rankings. For instance: [ PrintRank; print ", earning you the rank of "; if (score >= 348) "Grandmaster Adventurer!"; if (score >= 330) "Master, first class."; if (score >= 300) "Master, second class."; if (score >= 200) "Junior Master."; if (score >= 130) "Seasoned Adventurer."; if (score >= 100) "Experienced Adventurer."; if (score >= 35) "Adventurer."; if (score >= 10) "Novice."; "Amateur."; ]; `PrintTaskName' prints the name of a game task (such as `driving the car'). Of course, this is only ever called in a game with `TASKS_PROVIDED' defined. For instance, [ PrintTaskName ach; if (ach==0) "eating a sweet"; if (ach==1) "driving the car"; if (ach==2) "shutting out the draught"; if (ach==3) "building a tower of four"; if (ach==4) "seeing which way the mantelpiece leans"; ]; Normally, an Inform game will print messages like [Your score has gone up by three points.] when the score changes (by whatever means). The player can turn this on and off with the `notify' verb; by default it is on. You can alter the flag `notify_mode' yourself to control this. 21.4 Asking `yes or no' questions of the player =============================================== Sometimes you want to ask the player a yes/no question. If so, call `YesOrNo()', a library routine: it returns true/false accordingly (and doesn't print any question, which is up to you). This saves a lot of bother programming the parser. 21.5 Replacing library routines =============================== !!!!! Occasionally you need to rewrite one of the library routines. But the danger of doing so is that it is then necessary to keep a copy of the library for every game, which is clearly unsatisfactory. So: for example, if the directive REPLACE BurnSub; is placed in your file *before the library files are included*, Inform ignores the definition of `BurnSub' in the library files. (You then have to define a routine called `BurnSub' yourself, or Inform will complain that the program refers to a routine which isn't there.) And this is the very last resort, and so the end of the manual proper. 22 Debugging verbs and tracing ****************************** If builders built buildings the way programmers write programs, the first woodpecker that came along would destroy civilisation. - old computing adage 22.1 Debugging verbs ==================== The two problems with debugging a game are working out exactly what it's doing, and making things happen which wouldn't normally be allowed (such as giving yourself the trident now rather than playing for two hours to find it). Inform provides a small suite of debugging verbs to help with this, but only if the game defines the constant `DEBUG' before including the library files. (Just in case you forget to take this out again, the letter D appears in the game banner to indicate this.) You then get the following verbs, which can be used at any time in play: purloin ANYTHING abstract ANYTHING to ANYTHING goto NUMBER tree tree THING actions actions on actions off routines routines on routines off timers timers on timers off trace trace on trace off trace 1 TO 5 You can `purloin' any item or items in your game at any time, wherever you are. You can likewise `abstract' any item to any other item (meaning: move it to the other item). To get a listing of the objects in the game and how they contain each other, use `tree', and to see the possessions of one of them alone, use `tree THAT'. Finally, you can go to any object: but since rooms don't have names understood by the parser, you have to give the object number of the place you want to go to. (The `tree' listing will tell you these numbers.) Turning on `actions' gives a trace of all the actions which take place in the game (the parser's, the library's or yours); turning on `routines' traces every object routine (such as `before' or `life') that is ever called, except for `short_name' (as this would look chaotic, especially on the status line). Turning on `timers' shows the state of all active timers and daemons each turn. 22.2 `Infix', Inform's debugger =============================== Infix, Dilip Sequeira's source-level debugger for Inform, is currently in an advanced state of preparation: it is an enhanced form of Mark Howell's Zip interpreter providing for breakpoints, tracing and so forth. It should fairly soon be publically archived with the rest of the Inform project. !!!! For Infix's benefit, Inform (if compiling with the option set) produces a file of `debugging information' (cross-references of the game file with the source code), and anyone interested in writing an Inform utility program may want to know the format of this file: see the short C program Infact which prints out the debugging information file in English. 22.3 How to crash an interpreter ================================ On most interpreters, though, run-time crashes are mysterious (because they were written on the assumption that all existing Infocom game files were free from error). Zip is rather more generous and will usually tell you why and where the problem is; given a game file address you can work back to the problem point in the source either with Mark Howell's txd (disassembler) or by running Inform with the assembler trace option on. Here are all the ways I know to crash an interpreter at run-time (with high-level Inform code, that is; if you insist on using assembly language or the `indirect' function you're raising the stakes), arranged in decreasing order of likelihood: * Writing to a property of an object which it hasn't got; * Dividing by zero, possibly by calling `random(0)'; * Giving a string or numerical value for a property which can only legally hold a routine, such as `before', `after' or `life'; * Applying `parent', `child' or `children' to the `nothing' object; * Using `print object' on the `nothing' object, or for some object which doesn't exist (always use the routines `DefArt', `CDefArt' or `PrintShortName' as appropriate, which are safeguarded and anyway implement higher-level features); * Using `print_addr' or `print_paddr' to print from an address outside the memory map of the game file, or an address at which no string is present (this will result in random text appearing, possibly including unprintable characters, which may conceivably crash your terminal); * Setting the `location' variable to zero, or some non-existent object, since the interpreter prints the name of this object on the status line; in any case it is safer to use `PlayerTo' than to meddle directly with this; * Running out of stack space in a recursive loop (though this has never actually happened to anyone I know of). 22.4 Tracing the parser ======================= !!!! There are times when it's hard to work out what the parser is up to and why (actually, most times are like this). The parser is written in levels, the lower levels of which are murky indeed. Most of the interesting things happen in the middle levels, and these are the ones for which tracing is available. The levels which can be traced are: Level 1 Grammar lines Level 2 Individual tokens Level 3 Object list parsing Level 4 Resolving ambiguities and making choices of object(s) Level 5 Comparing text against an individual object `trace' or `trace on' give only level 1 tracing. Be warned: `trace five' can produce reams of text when you try anything at all complicated: but you do sometimes want to see it, to get a list of exactly everything that is in scope and when. There are two levels lower than that but they're too busy doing dull spade-work to waste time on looking at `parser_trace'. (There's also a level 0, but it consists mostly of making arrangements for level 1, and isn't very interesting.) !!!!! Finally, though this is a drastic measure, you can always compile your game `-g' (`debugging code') which gives a listing of every routine ever called and their parameters. This produces an enormous melée of output. (In Inform 5.3, though not earlier versions, you can declare a routine with an asterisk `*' as its first local variable, which produces such tracing only for that one routine.) 23 Limitations on the run-time format ************************************* How wide the limits stand Between a splendid and an happy land. -- Oliver Goldsmith (1728--1774), `The Deserted Village' The Infocom run-time format is well-designed, and has three major advantages: it is compact, widely portable and can be quickly executed. Nevertheless, like any rigidly defined format (which it clearly must be), it imposes limitations. These are not by any means pressing. Inform itself has a flexible enough memory-management system not to impose artificial limits on numbers of objects and the like. There are two sizes of game available: Standard (or version 3) and Advanced (or version 5). Always use the latter if you can; it is very much to be preferred. (There are very slight risks associated with exotic assembly-language coding in Advanced games: but unless you deviate far from normal Inform practice, and code on a very low level, these will never trouble you.) There are still 8-bit computers in use which are too small for Advanced games, but most people should have no problem. *Memory* The total size of a Standard game is at most 128K; of an Advanced game, 256K. Because games are encoded in a very compressed form, and because the centralised library of Inform makes the compiler fairly efficient in terms of not duplicating code, even 128K allows for a game at least half as large again as a typical old-style Infocom game. No game yet written under Inform has reached the 256K mark, not even the final version of `Curses' (at 224K, already substantially bigger and denser than any Infocom game). *Vocabulary* There is no theoretical limit, and vocabularies in excess of 2000 are quite sensibly possible. (A typical large game will muster 1000.) *Dictionary resolution* In Standard games, dictionary words are truncated to their first 6 letters, which can be a nuisance; in Advanced, 9 letters, which never is. *Attributes, properties, names* Standard games only provide 32 attributes, 30 properties and at most 8 bytes of data per property: the library consumes most of this ration, and the 8 bytes restriction means that an object can only have four words in its `name'. This is perfectly manageable, if unsatisfactory. Advanced games, which provide 48 attributes, 62 properties and 64 bytes of data (hence 32 names), effectively make none of these restrictions serious. *Special effects* Standard games cannot have special effects such as bold face and underlining. *Note Styles::. *Objects* Standard games can have at most 255 objects, enough for a large Infocom-size game but not much more. Advanced games have no such restriction, the number being in principle unlimited. Objects cannot dynamically be created or destroyed, but this is imitated easily enough. *Global variables* There can only be 240 of these, and the Inform compiler uses 5 as scratch space, while the library uses slightly over 100; but since a typical game uses only a dozen of its own, code being almost always object-oriented, the restriction is never even nearly felt. (Recall that arrays take only one global each.) *`Undo'* Standard games do not provide an `undo' verb, but Advanced ones (or rather, the Inform library when compiled on such) do. !!!! There is a yet larger Infocom format, version 6, which lifts even the mild restrictions of version 5 games, with memory maps of up to 600K or so: version 6 has only recently been fully deciphered, but Inform is already capable of producing such files, and Mark Howell's `Zip' interpreter is (at time of writing) making good progress in running them. A 600K Inform game would be gargantuan: by the time one comes along, there should be no problem in running it. !!!!! Having said all this, if memory does become short, there is a standard mechanism for saving about 8-10% of the memory. Inform does not usually trouble to, since there's very seldom the need, and it makes the compiler run about 10% slower. What you need to do is define abbreviations. For instance, the directive Abbreviate " the "; (placed before any text appears) will cause the string ` the ' to be internally stored as a single `letter', saving memory every time it occurs (about 2500 times in `Curses', for instance). You can have up to 64 abbreviations. A good list of abbreviations can be found in the `Technical Manual': basically, avoid proper nouns and instead pick on short combinations of a space and common two- or three-letter blocks. You can even get Inform to work out by itself what a good stock of abbreviations would be: but be warned, this makes the compiler run about 29000% slower. 24 A very few, not very special effects *************************************** Yes, all right, I won't do the menu... I don't think you realise how long it takes to do the menu, but no, it doesn't matter, I'll hang the picture now. If the menus are late for lunch it doesn't matter, the guests can all come and look at the picture till they are ready, right? - John Cleese and Connie Booth, `Fawlty Towers' As the previous section suggested, some special effects are not to be encouraged: they reduce portability a little, and anyway Inform is for text games. 24.1 The status line ==================== 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; is present, in which case it shows the game's 24-hour clock. !!!!! If you *really* want to change this, then `Replace' the parser's private `DrawStatusLine' routine. But be sure to test the result on more than one interpreter, in case you've made a mistake which doesn't show up on one quirky implementation. (In any case, you can only change it at all on Advanced games.) 24.1.1 Exercise: status line ---------------------------- Alter the `Advent' example game to display the number of treasures found instead of the score and turns on the status line. *Note Answer (status line)::. 24.2 Character graphics ======================= About character graphic drawings: on some machines, text will by default be displayed in a proportional font (i.e., one in which the width of a letter depends on what it is, so that for example an `i' will be narrower than an `m'). If you want to display a diagram made up of letters, such as a map, you will have to turn this off, for which the `font' command is provided. Write `font off' before printing character graphics, and remember to write `font on' again afterwards. *WARNING:* Don't turn the `font' on and off in the middle of a line; this doesn't look right on some machines. 24.3 Putting quotes in boxes ============================ A distinctive feature of later Infocom games was their use of epigrams. Inform can do this, too, but only for Advanced games. The assembly language required is easy but a nuisance, so there is a command to do it, `box'. For example, box "Beware of the Dog"; or box "I might repeat to myself, slowly and soothingly," "a list of displays beautiful from minds profound;" "if I can remember any of the damn things." "" "-- Dorothy Parker"; Note that a string of lines is given (without intervening commas) and that a blank line is given by a null string. Remember that the text cannot be too wide or it will look awful on a small screen. The author takes the view that this device is amusing for irrelevant displays but irritating when it conveys vital information (such as, `Beware of the Dog', for instance). Also, some people might be running your game on a laptop with a vertically challenged screen, so it is polite to provide a `quotes off' verb. 24.4 Interactive menus ====================== Sometimes one would like to provide a menu of text options (for instance, when producing instructions which have several topics, or when giving clues). This can be done with the `DoMenu' routine, which imitates the traditional `Invisiclues' style. However, it only looks good for Advanced games; for Standard games, it's simply textual. (In an Advanced game, by setting `pretty_flag=0' you can get this simple text version instead; which is a good idea for machines with very small screens.) Here is a typical call to `DoMenu': DoMenu("There is information provided on the following:^\ ^ Instructions for playing\ ^ The history of this game\ ^ Credits^", #r$HelpMenu, #r$HelpInfo); Note the layout, and especially the carriage returns. The second and third arguments are themselves routines: the notation `#r$', seldom seen in high-level Inform, allows routine names to become ordinary numerical values. The first routine, in this case called `HelpMenu', is supposed to look at the variable `menu_item'. In the case when this is zero, it should return the number of entries in the menu (3 in the example). In any case it should set `item_name' to the title for the page of information for that item; and `item_width' to half its length in characters (this is used to centre titles on the screen: it is unfortunately tricky for the game to compute the length of a static string, so you have to tell it). In the case of item 0, the title should be that for the whole menu. The second routine, `HelpInfo' above, should simply look at `menu_item' (1 to 3 above) and print the text for that selection. After this returns, normally the game prints `Press [Space] to return to menu' but if the value 2 is returned it doesn't wait. Menu items can safely launch whole new menus, and it is easy to make a tree of these (which will be needed when it comes to providing hints across any size of game). 24.4.1 Exercise: invisiclues ---------------------------- Code an `Invisiclues'-style sequence of hints for a puzzle, revealed one at a time, as a menu item. *Note Answer (invisiclues)::. 24.5 Text styles ================ Finally, though it should only be used sparingly (and again is restricted to Advanced games), one can change the text style. The command for this is `style' and its effects are loosely modelled on the VT100 (design of terminal). The style can be `style roman', `style bold', `style reverse' or `style underline'. Again, remember that poor terminals may not be able to display these, so you shouldn't hide crucial information in them. 25 Dirty tricks *************** !!!!! The dirtiest tricks of all are those which bypass Inform's higher levels and program the Z-machine directly. There is an element of danger in assembly language programming, in that some combinations of unusual opcodes can look ugly on some incomplete or wrongly-written interpreters: so if you're doing anything complicated, test it as widely as possible. Note that none of the interesting effects work at all on Standard games, only Advanced games. The best-researched and most reliable interpreter available by far is Mark Howell's Zip; as it's also the fastest, it will hopefully `take over' entirely. Next comes the InfoTaskForce, which is thorough and should give no serious trouble, but was written when the format was a little less well understood, and so (in some ports) gets some (rare) screen effects wrong. (It also lacks an `undo' feature, so the `undo' verb automatically provided by the library routines won't work under ITF.) The other two publically-available interpreters are pinfocom and zterp, but these are unable to run Advanced games. In the last resort, sometimes it's possible to use one of Infocom's own supplied interpreters with a different game from that it came with; but only sometimes, as they may have inconvenient filenames `wired into them'. The author recommends that anyone using assembly-language features get hold of both ITF and Zip, and test on both. This all sounds rather unportable, though actually the core described below is pretty reliable. But remember that the unportability does have some genuine cause. Your game may be running on a screen which is anything from a 64 by 9 pocket organiser up to a 132 by 48 X-window. Anyone wanting to really push the outer limits (say, by implementing Space Invaders or NetHack) will need to refer to `The Specification of the Z-Machine', which also documents Inform assembly language format. Screen tricks are the commonest. An upper-level screen (which usually holds the status line) can be split off from the main screen: split_window n; creates one which is n lines tall. (This doesn't change the display, and it can be resized at any time: but it needs to be tall enough to include all the lines you want to write to, as otherwise the interpreter may flounder: some will scroll the upper window, others won't.) The main screen is numbered 0, and the upper one 1; text output is switched between them by set_window n; The lower window is just a text stream whose cursor position cannot be set: on the other hand, when it is returned to, the cursor will be where it was before it was left. Within the upper window, the cursor can be moved by set_cursor line column; where (1,1) is the top left character. Printing on the upper window overlies printing on the lower, is always done in a fixed-space font and does not appear in a printed transcript of the game. However, before printing to the upper window, it is essential to change the printing format - that is, the `buffer_mode' opcode. Before printing, execute buffer_mode 0; and when returning to the normal screen, buffer_mode 1; Otherwise, dodgy interpreters (like ITF) may continue trying to split lines at word breaks, and make a horrid mess. A convenient way to clear the screen is erase_window $ffff; but don't chance this in reverse video mode! (And don't assume that `erase_window' can erase individual windows - it should, but may not on bad interpreters.) Players can be gratuitously annoyed (on most machines, anyway) by the `beep' opcode. This is the only remotely portable sound effect. The keyboard can be read in remarkably flexible ways, using the `aread' and `read_char' opcodes. aread text parse time function; will read from the keyboard, putting text into the `text' buffer, tokenising it onto the end of the `parse' buffer (unless this is zero), and calling function(time); every `time' seconds that the user is thinking: the process ends if ever this function returns true. Thus (by `Replace'ing the `Keyboard' routine in the library files) you could, say, move around all the characters every ten seconds of real time. read_char 1 time function RESULT; where RESULT is a variable, will store in that variable the ASCII value of a single keypress. Once again, the `function' is called every `time' seconds and may stop this process early. Function keys return special values from 129 onwards, in the order: cursor up, down, left, right, function key f1, ..., f12, keypad digit 0, ..., 9. Leafing through the dictionary of opcodes will reveal a few other interesting features. It's possible to change the input and output streams which, although only Zip gets this right, may be convenient for debugging purposes (creating scripts of all typed commands, for example). Finally, there are opcodes which tokenise (that is, simply compare dictionary entries against) arbitrary strings from arbitrary dictionaries, and which translate small doses of ASCII to internal Z-machine string format. Actually, one can avoid the need for this in many cases, by programming the parser correctly: see, for instance, the `Balances' game which manages without these features. A The Inform language ********************* This is going to be a long appendix, full of lists and tables: but it has to appear somewhere, if only for reference. (Some technical commands for internal use only are skipped over: see the `Technical Manual' for details of these.) A.1 File format =============== When Inform reads in a file, it treats a few characters in special ways. The character `!' means the rest of the line (up to the next new-line) is a comment, and Inform throws it away. Tab characters are treated as spaces. Backslashes `\' fold strings together, so that the new-line and all subsequent spaces are ignored. New-lines have no significance; statements (and directives) are separated by semicolons `;'. A.2 Directives ============== These are commands directly to the Inform compiler, like `Object'. They can, but need not, be prefaced by a hash character, `#'. The directives which Inform understands are: `Abbreviate STRING' Declare an abbreviation `Attribute NAME' Make a new attribute `Class ...' Define a new class `Constant NAME VALUE' Define a named constant `End' End compilation here `Endif' End of conditional compilation `Extend ...' Extend the grammar for an existing verb `Fake_action NAME' Make a new `fake action' `Global NAME ...' Declare a global variable `Ifdef NAME' Compile only if constant is defined `Ifndef NAME' Compile only if constant is undefined `Ifnot' Compile only if previous `If...' failed `Ifv3' Compile only for Standard games `Ifv5' Compile only for Advanced games `Include FILENAME' Include that file here `Nearby' Make an object inside the last `Object' `Object' Make an object `Property NAME ...' Make a new property `Replace ROUTINE' Don't compile this library routine `Release NUMBER' Set the game's Release Number `Serial STRING' Set the game's Serial Number `Statusline ...' Make the status line show score or time `Switches SWITCHES' Set default compilation switches `Verb ...' Declare the grammar for a new verb The release number of a game (by default 1) is generally an edition number; the serial number is the compilation date in the form 940924, that is, yymmdd. Inform sets this automatically on machines where the date is accessible, so the `Serial' directive is provided only for use on machines without such an internal clock. !!!! Conditional compilation allows code for routines which need only exist in some `versions' of your games. For instance, print "Welcome to the "; #IFV3; print "Standard"; #IFNOT; print "Advanced"; #ENDIF; print " version of Zork LVI."; (The `#IFNOT' clause is optional.) Note the trailing semicolon: Inform is not C! Such clauses may be nested up to 32 deep, and may contain whole routines. They may not, however, conditionally give *part* of a statement. Thus, for instance, print #IFV3; "Standard"; #IFNOT; "Advanced"; #ENDIF; is *not* legal. !!!!! The following directives are recondite and not for public use: Default Dictionary Listsymbols Listdict Listverbs Lowstring Stub System_file Trace Btrace Etrace Ltrace Notrace Nobtrace Noetrace Noltrace A.3 Variables and arrays ======================== There are two kinds of variable, global and local (plus one special one). Variables are all two-byte integers, which are treated as signed when it makes sense to do so (e.g., in asking whether one is positive or not) but not when it doesn't (e.g., when it is used as an address). Global variables must be declared before use, by the `Global' directive: Global VARNAME = INITIAL-VALUE `data' SIZE `string' SIZE `initial' VALUE-1 ... VALUE-N `initstr' TEXT For instance: Global turns = 1; Global buffer string 120; ! text buffer holding 120 characters Global task_scores initial 4 5 9 1 2 3 0; ! a 7-byte array Global players_name initstr "Graham"; ! an array of 6 chars When you declare a variable as an array (by `data', `string', `initial' or `initstr') what actually happens is that Inform allocates as much space as you ask for, somewhere inside the machine, and stores the address of this array in the variable. You can get at entries of the array by buffer->entry buffer-->entry which read (or write to) the `entry'-th byte (in the case of `->') or word (for `-->'). A `data' array is initially full of zeros, while an `initial' array contains the given (byte) values, which all have to be constants for obvious reasons. A `string' array is a special kind of `data' array: the first byte contains its length (in bytes), and the rest are initially zero. `initstr' is the same but initialised to the given string. The text here is plain ASCII, and is not encrypted as constant strings tend to be. (This string format is used by the parser.) In addition, a routine can have from none up to 15 local variables. *WARNING:* There is also a stack, but it should be tampered with only carefully in times of dire need. Never call a variable `sp', as this is the stack pointer, and if you must use the stack at all, be careful not to leave values on it: or your game may crash 1000 turns later, serving you right. A.4 Constants ============= Inform constants can take many forms. The obvious ones are numbers, 123 $ee05 $$11011001 being examples in decimal, hexadecimal and binary respectively. There are also `##Action' (The number of) the given action `"some text"' (The packed address of) the given string `'c'' (The ASCII code for) the given character `'word'' (The byte address of) its dictionary entry There is slight potential for confusion here: `'A'' evaluates to 65, but `'an'' to the dictionary address of the word `an'. Note that the construction `'sponge'' actually enters the word `sponge' into the dictionary if it wasn't already there. These are all legal constants: 31415 -1 $ff $$1001001 'lantern' ##Look 'X' "an emerald the size of a plover's egg" "~Hello,~ said Peter.^~Hello, Peter,~ said Jane.^" A.5 Quoted strings ================== Inside the text of a string, the character `^' is replaced by a new-line character, and the character `~' by a double-quote mark: these both make strings much easier to type. Inside a string (under Inform 5.3 or later) `@@'NUMBER produces the character whose ASCII value is NUMBER, and you can use this to get untypeable characters from foreign character sets. (Or, for example, a literal backslash, by `@@92'.) For the `@' string escape and other obscure constant forms such as `#r$', see the `Technical Manual'. A.6 Routines ============ Routines start with a `[' and end with a `]'. That is, they open with `[' ROUTINE-NAME LOCAL-VAR-1 ... LOCAL-VAR-N`;' giving the names of local variables for the routine ($0\leq n\leq 15$). The routine ends with just `];'. (Routines embedded in object definitions are the same, except that no routine-name is given, and they may end in `],' if the object definition continues.) The first few local variables also hold the arguments passed to the routine when the function is called. That is, if you have a routine [ Look from i j; ...some code...; ]; and it is called by `Look(attic);' then the local variable `from' will initially have the value `attic'. The rest all start out at zero. From Inform 5.3, if the first variable is given as `*' then tracing code is compiled to print details each time the routine is called. Function calls (that is, calls to routines) are legal with between 0 and 3 arguments, and every routine always returns a value. If execution runs into the `];' at the bottom, that value is `true' (or 1) for an ordinary routine, or `false' (or 0) for an embedded one. A.7 Labels ========== In case you want to `jump' around inside a routine, you can define labels with a statement starting with a full stop, like `.labelname;'. It is legal, though ill-advised, to jump out of one routine and into another. Label names are global, so the same label name can't be used in two different routines. A.8 Operators ============= Arithmetic (and other) expressions can contain the following: `+ -' plus, minus `* / % & |' times, divide, remainder, bitwise and, bitwise or `-> -->' byte array, word array entry `. .& .#' property, property address, property length `-' unary minus `++ --' incrementing and decrementing variables (as in C) The order of precedence is as shown: i.e., those on each line are equally potent, more potent than those above but less than those beneath. Expressions are not allowed to contain conditions, nor assignments: `2+(i=3/j)' is not a legal expression. Some legal examples: 4*(x+3/y) Fish(x)+Fowl(y) lamp.time buffer->5 Note that `++' and `--' can only be applied to variables, not to properties or array entries. A.9 Assignments =============== There are four forms allowed are: VARIABLE `=' VALUE`;' BYTE-ARRAY`->'ENTRY `=' VALUE`;' WORD-ARRAY`-->'ENTRY `=' VALUE`;' OBJECT`.'PROPERTY `=' VALUE`;' For example: i=-15-j; i=j-->1; albatross.weight = albatross.weight + 1; (paintpot.&roomlist)-->i = location; turns++; One can also `inc' or `dec' (increment or decrement) a variable, with `inc score;' being equivalent to the more modern `score++;' Although these look logical, they are not allowed: paintpot.#roomlist = 5; paintpot.&roomlist = array; because one cannot change the size or address of a property in play. *WARNING:* A division by zero error (such as `n/0' or `n%0') may crash the game at run time. *WARNING:* Attempting to write to a property which an object does not have may crash the game at run time. A.10 Conditions =============== A simple condition is A RELATION B where the relation is one of `==' `a' equals `b' `~=' `a' doesn't equal `b' `< > >= <=' numeric (signed) comparisons `has' object `a' has attribute `b' `hasnt' object `a' hasnt attribute `b' `in' object `a' is currently held by object `b' `notin' ... is not... With `==' (and `~=') only, one may also write the useful construction SOMETHING `==' V1 [`or' V2 [`or' V3]] which is true if the first something is any of the values given. (An idiosyncracy of Inform, for `hardware reasons', is that you can only have three). Conditions can be combined by the `&&' and `||' operators: CONDITION1 `&&' CONDITION2 CONDITION1 `||' CONDITION2 true if both, or either (respectively) are true. These are always tested left to right until the outcome is known. So, for instance, i==1 || Explode(2)==2 does not call `Explode' if `i' is 2. Examples of legal conditions are: i==1 or 2 or 3 door has open || (door has locked && key in player) A.11 Built-in functions ======================= A very few functions are built into the language of Inform itself (rather than written out longhand in the library files), but they behave like any other routines. They are: `parent(obj)' parent of object `sibling(obj)' sibling of object `child(obj)' eldest child of object `children(obj)' number of (direct) children of object `eldest(obj)' same as `child' `youngest(obj)' youngest child of object `elder(obj)' elder sibling of object `younger(obj)' same as `sibling' `random(x)' uniformly random number between 1 and $x$ `indirect(addr)' call routine with address `addr' *WARNING:* `random(0)' may cause a division by zero error. A.12 Printing commands ====================== A string on its own, such as "The world explodes in a puff of garlic."; is printed, with a new-line, and the current routine is returned from with return value `true', i.e., 1. In addition: `new_line' prints a new-line `print ...' prints the given things `print_ret ...' prints, new-lines and returns 1 `spaces n' prints n spaces `print_addr a' print string at byte address `a' `print_paddr a' print string at packed address `a' `font on/off' turns proportional fonts on/off `style ...' in Advanced games, sets text style `box "s1" ... "sn"' in Advanced games, puts up display box `inversion' prints out the current Inform version number `print' and `print_ret' take a comma-separated list of things to print out, which can be: `"strings"', `char n' (print the character with this ASCII value) or `variable' (print out the variable as a signed number). The text `style' can be any of roman reverse bold underline A.13 Manipulating objects ========================= `remove obj' removes object from the tree `move o1 to o2' moves `o1' to become youngest child of `o2' `give obj a1 ... an' gives attributes to `obj' Attributes beginning with a `~' are taken away rather than given. A.14 Returning from routines ============================ `return' Return true, i.e., 1 `return x' Return value $x$ `rtrue' Return true, i.e., 1 `rfalse' Return false, i.e., 0 A.15 Blocks of code =================== A block of code may be a single instruction or a series of several, in which case it must be enclosed in braces `{' and `}'. Thus, for instance, in if (i==1) print "The water rises!"; if (i==2) { print "The water rises further..."; water++; } the `if' statements contain a block of code each. Blocks can be nested inside each other up to 32 deep. An `if' statement (for example) is a single statement even when it contains a great deal of code in its block: so, for example, if (i>1) if (water<10) "The water is beginning to worry you."; is legal. (One small exception: an `if' followed by an `else' counts as two statements, and this means Inform handles hanging elses rather poorly: so it's wise to brace so that `else' is clearly unambiguous.) A.16 Control constructs ======================= Inform provides: `if' CONDITION BLOCK1 `[else' BLOCK2`]' `while' CONDITION BLOCK `do' BLOCK `until' CONDITION `for ('INITIALISE`:'TEST`:'EACH TIME`)' `objectloop ('VARIABLE `in' OBJECT`)' `objectloop ('VARIABLE `from' OBJECT`)' `objectloop ('VARIABLE `near' OBJECT`)' `break' `jump' LABEL Some of these will be familiar to most. The `for' construct is essentially the same as that in C, except for the colons `:' (which in C would be semicolons). Its carries out the initial assignment(s), then executes the code for as long as the condition holds, executing the end assignment after each pass through the code. For instance, for (i=1:i<=10:i++) print i, " "; counts to 10. All three clauses are optional, and the empty condition is always true; multiple assignments can be made. For instance: for (i=0,j=10:i<10:i++,j--) print i, " + ", j, " = ", i+j, "^"; for (::) print "Ha!^"; the latter laughing maniacally forever. `break' breaks out of the current loop (not quite the same as breaking out the current block of code: `if' statements don't count as loops in this regard). `objectloop' goes through the object tree, and is extremely useful. `from' means from the given object through its siblings; `in' means through all children of the given object, and `near' means through all children of the parent of the object. For instance, the following do the same thing: objectloop (x in lamp) { ... } for (x=child(lamp): x~=0: x=sibling(x)) { ... } Note that the library creates a variable called `top_object' holding the highest existing object number: so a way to loop over every object defined in your own code is for (i=player+1: i<=top_object: i++) ... since `player' is always set to `selfobj', which is the last object created by the library. *WARNING:* When looping through the object tree, be careful if you are altering it at the same time. For instance, objectloop (x in rucksack) remove x; is likely to go horribly wrong - it's safer not to cut down a tree while actually climbing it. The safe way is to keep lopping branches off, while (child(x)~=0) remove child(x); A.16.1 Exercise: primes ----------------------- Write a routine to print out prime factorisations of numbers from 2 to 100. *Note Answer (primes)::. A.17 Invoking actions ===================== The commands `<' ACTION `['FIRST-OBJECT `['SECOND-OBJECT`]]' `>' `<<' ACTION `['FIRST-OBJECT `['SECOND-OBJECT`]]' `>>' cause the given actions to take place. In the latter case, the current routine then returns 1, or true. B Library, properties and attributes ************************************ A Model must be built which will get everything in without a clash; and it can do this only by becoming intricate, by mediating its unity through a great, and finely ordered, multiplicity. - C. S. Lewis (1898-1963), `The Discarded Image' B.1 Library objects =================== The library defines the following special objects: `compass' To contain the directions. A direction object provides a `door_dir' property, and should have the `direction' attribute. A compass direction with `enterable', if there is one (which there usually isn't), will have an `Enter' action converted to `Go'. `n_obj' Both the object signifying the abstract concept of `northness', and the `north wall' of the current room. (Thus, if a player types `examine the north wall' then the action `Examine n_obj' will be generated.) Its `door_dir' property holds the direction property it corresponds to (`n_to'). `s_obj' `e_obj' `w_obj' `ne_obj' `nw_obj' `se_obj' `sw_obj' Similar. `u_obj' `d_obj' `in_obj' `out_obj' Similar: the parser uses these if the player refers to `ceiling', `floor'. (`in_obj' and `out_obj' differ slightly, because `in' and `out' are verbs with other effects in some cases.) `thedark' A pseudo-room representing `being in darkness'. `location' is then set to this room, but the player object is not moved to it. Its `description' can be changed to whatever `It is dark here' message is desired. `selfobj' The player object. However, do not refer to `selfobj' directly: always refer to `player', a variable whose value is usually indeed `selfobj' but which might become `green_frog' if the player is transformed into one. B.2 Library attributes ====================== Here is a concise account of all the normal rules concerning all the library's attributes. (Except that: rules about how the parser sorts out ambiguities are far too complicated to include here, but should not concern designers anyway; and the definitions of `scope' and `darkness' are given in the main text.) Of course these rules are all modifiable using suitable `before' and `after' rules. But a good deal of pragmatism has gone into their design, or rather evolution. `absent' A `floating object' (one with a `found_in' property, which can appear in many different rooms) which is `absent' will no longer appear in the game. `animate' `Is alive (human or animal)'. Can be spoken to in `richard, hello' style; matches the `creature' token in grammar; picks up `him' or `her' (according to gender) rather than `it', likewise `his'; an object the player is changed into becomes `animate'; some messages read `on whom', etc., instead of `on which'; can't be taken; its subobjects `belong to' it rather than `are part of' it; messages don't assume it can be `touched' or `squeezed' as an ordinary object can; the actions `Attack', `ThrowAt' are diverted to `life' rather than rejected as being `futile violence'. `clothing' Can be worn. `concealed' `Concealed from view but present'. The player's object has this; an object which was the player until `ChangePlayer' happened, loses this property; a `concealed' `door' can't be entered; does not appear in room descriptions. `container' Affects scope (*note Scope::.) and light (*note Light::.); object lists recurse through it if `open' (or `transparent'); may be described as closed, open, locked, empty; a possession will give it a `LetGo' action if the player tries to remove it, or a `Receive' if something is put in; things can be taken or removed from it, or inserted into it, but only if it is `open'; likewise for `transfer' and `empty'; room descriptions describe using `when_open' or `when_closed' if given; if there is no defined `description', an `Examine' causes the contents to be searched (i.e., written out) rather than a message `You see nothing special about...'; `Search' only reveals the contents of `container's, otherwise saying `You find nothing'. `direction' If the player tries to walk this way or enter it, a `Go' rather than `Enter' action is generated. (Used only in one line of grammar, but a crucial one.) `door' `Is a door or bridge'. Room descriptions describe using `when_open' or `when_closed' if given; and an `Enter' action becomes a `Go' action. If a `Go' has to go through this object, then: if `concealed', the player `can't go that way'; if not `open', then the player is told either that this cannot be ascended or descended (if the player tried `up' or `down' which went through it), or that it is in the way (otherwise); but if neither, then its `door_to' property is consulted to see where it leads; finally, if this is zero, then it is said to `lead nowhere' (a safety precaution) and otherwise the player actually moves to the location. `edible' Can be eaten (and thus removed from game). `enterable' Affects scope (*note Scope::.) and light (*note Light::.); only an `enterable' on the floor can be entered. `female' Only applies to `animate's (and cannot have a `found_in' list for arcane reasons), and only affects the parser's use of pronouns: it says `her' is appropriate but `him' and `his' are not. `general' A general-purpose attribute, defined by the library but never looked at or altered by it. This is left free to mean something different for each object: often used by programmers for something like `the puzzle for this object has been solved'. `light' `Is giving off light.' *Note Light::. Also: the parser understands `lit', `lighted', `unlit' using this; inventories will say `(providing light)' of it, and so will room descriptions if the current `location' is ordinarily dark; it will never be automatically put away into the player's `SACK_OBJECT' (as it might plausibly be inflammable or the main light source). `lockable' Can be locked or unlocked by a player holding its key object, which is in the property `with_key'; if a `container' and also `locked', may be called `locked' in inventories. `locked' Can't be opened. If a `container' and also `locked', may be called `locked' in inventories. `moved' `Has once been held by the player'. Objects (immediately) owned by the player after `Initialise' has run are given it; at the end of each turn, if an item is newly held by the player and is `scored', it is given `moved' and `OBJECT_SCORE' points are awarded; an object's `initial' message only appears in room descriptions if it is unmoved. `on' `Switched on.' A `switchable' object with this is described by `with_on' in room descriptions; it will be called `switched on' by `Examine'. `open' `Open door or container'. Affects scope (*note Scope::.) and light (*note Light::.), and: lists (such as inventories) recurse through an `open' `container'; if a `container', called open by some descriptions; things can be taken or removed from an `open' `container'; similarly inserted, transferred or emptied. An `open' `door' can be entered. Described by `when_open' in room descriptions. `openable' Can be opened or closed, unless `locked'. `proper' Its short name is a proper noun, and never preceded by `the' or `The'. The player's object must have this (so something changed into will be given it). `scenery' Not listed (by the library) in room descriptions, `not portable' to be taken; `you are unable to' pull, push, or turn it. `scored' The player gets `OBJECT_SCORE' points for picking it up for the first time; or, if a room, `ROOM_SCORE' points for visiting it for the first time. `static' `Fixed in place' if player tries to take, remove, pull, push or turn. `supporter' `Things can be put on top of it'. Affects scope (*note Scope::.) and light (*note Light::.); object lists recurse through it; a possession will give it a `LetGo' action if the player tries to remove it, or a `Receive' if something is put in; things can be taken or removed from it, or put on it; likewise for transfers; a player inside it is said to be `on' rather than `in' it; room descriptions list its contents in separate paragraphs if it is itself listed. `switchable' Can be switched on or off; listed as such by `Examine'; described using `when_on' or `when_off' in room descriptions. `talkable' Player can talk to this object in `thing, do this' style. (This is useful for microphones and the like.) `transparent' Affects scope (*note Scope::.) and light (*note Light::.). It roughly means `contents always visible'. A `transparent' container is treated as if it were `open' for printing of contents. `visited' Given to a room when a `Look' first happens there: if this room is `scored' then `ROOM_SCORE' points are awarded. Affects whether room descriptions are abbreviated or not. `workflag' Temporary flag used by Inform internals, also available to outside routines; can be used to select items for some lists printed by `WriteListFrom'. `worn' `Item of clothing being worn'. Should only be held by an object being immediately carried by player. Affects inventories; doesn't count towards the limit of `MAX_CARRIED'; won't be automatically put away into the `SACK_OBJECT'; a `Drop' action will cause a `Disrobe' action first; so will `PutOn' or `Insert'; you can't insert or remove something from a `worn' container. Note that very few attributes sensibly apply to rooms: only really `light', `scored' and `visited', together with `general' if you choose to use it. Note also that an object cannot be both a `container' and a `supporter'; and that the old attribute `autosearch', which was in earlier releases, has been withdrawn as obselete. B.3 Library properties ====================== Next, definitions of properties which sensibly apply to `objects' (meaning, things which are not rooms): `after' List of rule routines to be applied in sequence after an action takes place. (For a room, receives all actions in that room; for a player-object, all actions by the player as that object.) `article' Indefinite article for object (defaults to `a'). `before' List of rule routines to be applied in sequence before an action takes place. (For a room, receives all actions in that room; for a player-object, all actions by the player as that object.) `cant_go' Message to print (or routine to print one) when a player tries to go in an impossible direction from this room (defaults to `You can't go that way.') `capacity' Number of objects a `container' or `supporter' can hold (defaults to 100); number of things the player can carry (when the player is this object), initially set to `MAX_CARRIED' for the standard player object. `daemon' Routine to run each turn (once activated by a `StartDaemon' and until stopped by a `StopDaemon'). Note: the same object cannot have both a `daemon' and a `time_out'. `describe' Routine (only) to describe object: over-rides a room's `description' to give main room description, if set; over-rides an object's usual listing when it appears in a room description. `description' `Examine' message, or routine to print one out, for an object; main room description, or routine to print one, for a room. `door_dir' Direction (that is, direction property, such as `n_to', not direction object) that a `door' goes via, or a routine which returns this information; also used for direction objects. *Note Compass: Library objects. `door_to' Room that a `door' connects to, or routine returning this; a value of 0 means `leads nowhere' (but this is not the ideal way to implement such a door). `each_turn' String to print, or list of routines to run, each turn that the object is in scope, after all timers and daemons have run. `found_in' List of rooms in which a `floating object' (that is, one present in multiple rooms) is found, unless it has `absent', in which case it is nowhere to be found. `initial' Initial description of object not yet picked up, or routine to print one. `invent' Routine to change its inventory listing. *Note Inventories::. `life' List of rule routines to be applied in sequence after an action making sense for an `animate' object takes place. `name' List of dictionary words referring to an object (note: uniquely, these dictionary words are given in double quotes `"thus"', whereas in all other circumstances they would be `'thus''; part tradition, part defence against `'a'' single-letter ambiguity). For a room, lists dictionary words which `do not need to be referred to in this game'. `number' A general purpose property left free: conventionally holding a number like `number of turns' battery power left'. (Except: an object to be used as a player-object must provide one, and it isn't left free.) `parse_name' Routine (only) to parse object's name (this overrides the `name' and is also used in determining if two objects are describably identical). *Note Naming of names::. `plural' The plural name of an object (when in the presence of others like it), or routine to print it. `short_name' The short name of an object (like `brass lamp'), or routine to print it. `time_left' Number of turns left until timer (if set, which must be done using `StartTimer') goes off. `time_out' Routine to run when timer goes off (having been set by `StartTimer' and not in the mean time stopped by `StopTimer'). Note: the same object cannot have both a `daemon' and a `time_out'. `when_closed' `Look' description of something closed (`door' or `container'), or routine to print one. `when_open' `Look' description of something open (`door' or `container'), or routine to print one. `when_off' `Look' description of a `switchable' which isn't `on', or routine to print one. `when_on' `Look' description of a `switchable' which is `on', or routine to print one. `with_key' Key object needed to lock/unlock a `lockable' object; player must explicitly name it as the key being used and be holding it at the time to use it. May be 0, in which case no key fits. together with the properties n_to s_to ne_to se_to u_to d_to e_to w_to nw_to sw_to in_to out_to each of which can be map connections to other routines, or strings (in which case they are printed if the player tries to go that way, but the move is not allowed and no further action is taken), or routines (in which case they are called to decide whether the map connection exists or not, and if so where it leads). *WARNING:* Do not confuse `n_to' and so on with the 12 direction objects, `n_obj' et al. C All the entry points, library routines and constants ****************************************************** C.1 Library entry points ======================== Entry points are routines which you can provide, if you choose to, and which are called by the library routines to give you the option of changing the rules. All games *must* define an `Initialise' routine, which is obliged to set the `location' variable to a room; the rest are optional. `AfterLife' When the player has died (a condition signalled by the variable `deadflag' being set to a non-zero value other than 2, which indicates winning), this routine is called: by setting `deadflag=0' again it can resurrect the player. `Amusing' Called to provide an `afterword' for players who have won (for instance, it might advertise some features which a successful player might never have noticed). (But only if you have defined the constant `AMUSING_PROVIDED' in your own code.) `DarkToDark' Called when a player goes from one dark room into another one; a good excuse to kill the player off in anyone's book. `DeathMessage' Prints up `You have died' style messages, for `deadflag' values of 3 or more. (If you choose ever to set `deadflag' to such.) `GamePostRoutine' A kind of super-`after' rule, which applies to all actions in the game, whatever they are: use only in the last resort. `GamePreRoutine' A kind of super-`before' rule, which applies to all actions in the game, whatever they are: use only in the last resort. `Initialise' A compulsory routine, which must set `location' and is convenient for miscellaneous initialising, perhaps for random settings. `InScope' An opportunity to place extra items in scope during parsing, or to change the scope altogether. (If `et_flag' is 1 when this is called, the scope is being worked out for `each_turn' reasons; otherwise for everyday parsing.) `LookRoutine' Called at the end of every `Look' description. `NewRoom' Called when the room changes, before any description of it is printed. (This happens no matter how the change of room occurred.) `ParseNumber' An opportunity to parse numbers in a different (or additional) way. `ParserError' The chance to print different parser error messages (like `I don't understand that sentence'). `PrintRank' Completes the printing of the score. (You might want to change this, so as to make the ranks something like `junior astronaut' or `master catburglar' or whatever suits your game.) `PrintVerb' A chance to change the verb printed out in a parser question (like `What do you want to (whatever)?') in case an unusual verb via `UnknownVerb' has been constructed. Returns true (or 1) if it has printed something. `PrintTaskName' Prints the name of a game task (such as `driving the car'). `TimePasses' Called after every turn (but not, for instance, after a command like `score' or `save'). It's much more elegant to use timers and daemons, or `each_turn' routines for individual rooms - using this is a last resort. `UnknownVerb' Called by the parser when it hits an unknown verb, so that you can transform it into a known one. C.2 Library routines ==================== Library routines which are `open to the public': `Achieved(task)' Indicate the `task' is achieved (which only awards score the first time). `AllowPushDir()' Signal that an attempt to push an object from one place to another should be allowed. `CDefArt(object)' Print the capitalised definite article and short name of `object'. `ChangePlayer(object, flag)' Cause the player at the keyboard to play as the given object, which must have a `number' property supplied. If the `flag' is set to 1, then subsequently print messages like `(as Ford Prefect)' in room description headers. This routine, however, prints nothing itself. `DefArt(object)' Print the definite article and short name of `object'. `DoMenu(text,routine,routine)' Produce a menu. `EnglishNumber(x)' Prints out `x' in English (e.g., `two hundred and seventy-seven'). `HasLightSource(object)' Returns true if `object' `has light'. `InDefArt(object)' Print the indefinite article and short name of `object'. `NextWord()' Returns the next dictionary word in the player's input, moving the word number `wn' on by one. Returns -1 if the player's input has run out, and 0 if the word is not in the dictionary. `OffersLight(object)' Returns true if `object' `offers light'. `PlaceInScope(object)' Puts `object' into scope for the parser. `PlayerTo(place, flag)' Move the player to `place'. Unless `flag' is given and is 1, describe the player's surroundings. `PrintShortName(object)' Print the short name of `object'. (This is protected against `object' having a meaningless value.) `ScopeWithin(object)' Puts the contents of `object' into scope, recursing downward according to the usual scope rules. `SetTime(time,rate)' Set the game clock (a 24-hour clock) to the given `time' (in seconds since the start of the day), to run at the given rate RATE: 0 means it does not run; if RATE > 0 then RATE seconds pass every turn; if RATE < 0 then -RATE turns pass every second. `ScopeWithin(object)' Puts the contents of `object', recursing downward according to the usual scope rules. `StartDaemon(object)' Makes the daemon of `object' active, so that its `daemon' routine will be called every turn. `StartTimer(object, time)' Starts the timer of `object', set to go off in `time' turns, at which time its `time_out' routine will be called (it must provide a `time_left' property). `StopDaemon(object)' Makes the daemon of `object' inactive, so that its `daemon' routine is no longer called. `StopTimer(object)' Stops the timer of `object', so that it won't go off after all. `TryNumber(wordnum)' Tries to parse the word at `wordnum' as a number (recognising decimal numbers and English ones from `one' to `twenty'), returning -1000 if it fails altogether, or the number. Values exceeding 10000 are rounded down to 10000. `WriteListFrom(object, style)' Write a list of `object' and its siblings, with `style' a bitmap of options. `YesOrNo()' Assuming that a question has already been printed, wait for the player to type `yes' or `no', returning true or false accordingly. C.3 Library constants ===================== And, finally, the constants: `AMUSING_PROVIDED' To indicate that an `Amusing' routine is provided. `DEBUG' To include the special `debugging' verbs. `Headline' Style of game and copyright message. `MAX_CARRIED' Maximum number of (direct) possessions the player can carry. `MAX_SCORE' Maximum game score. `MAX_TIMERS' Maximum number of timers or daemons active at any one time (defaults to 32). `NUMBER_TASKS' Number of `tasks' to perform. `OBJECT_SCORE' Score for picking up a `scored' object for the first time. `ROOM_SCORE' Score for visiting up a `scored' room for the first time. `SACK_OBJECT' Object which acts as a `rucksack', into which the game automatically tidies away things for the player. `Story' Story name, conventionally in CAPITAL LETTERS. `TASKS_PROVIDED' To indicate that `tasks' are provided. D All the Inform error messages ******************************* Inform can produce about 230 different error messages. The error messages were tidied-up and made more consistent in Inform 5.4, which the text here comes from, but earlier editions were similar. Since interpreters can in some cases crash horribly when given incorrect files, Inform never writes a file which caused an error, though it will permit files which incurred only warnings. D.1 Fatal errors ================ To begin with, fatal errors (which stop Inform in its tracks) come in three kinds, the first containing only this one: Too many errors: giving up After 100 errors, Inform stops (in case it has been given the wrong source file altogether). Secondly, file input/output can go wrong. Most commonly, Inform has the wrong filename: Couldn't open input file Couldn't open output file Couldn't open transcript file Couldn't open debugging information file Couldn't open temporary file 1 Couldn't open temporary file 2 Too many files have included each other: increase #define MAX_INCLUSION_DEPTH (Temporary files are used (on most machines) for temporary storage space during compilation. They are removed afterwards.) The last error only occurs if 5 files all include each other. Increasing this `#define' means re-compiling Inform from its C source, a drastic measure. The remaining file-handling errors usually mean that the disc is full: something has gone wrong with an already-open file. I/O failure: couldn't read from source file I/O failure: couldn't write to temporary file 1 I/O failure: couldn't reopen temporary file 1 I/O failure: couldn't read from temporary file 1 I/O failure: couldn't write to temporary file 2 I/O failure: couldn't reopen temporary file 2 I/O failure: couldn't read from temporary file 2 I/O failure: couldn't write to story file I/O failure: couldn't write to transcript file I/O failure: can't write to debugging information file The third class of fatal error is Inform running out of memory. It might fail drastically, having not enough memory to get started, as follows... Couldn't allocate memory Couldn't allocate memory for an array (There are four similar `hallocate' errors unique to the PC `Quick C' port.) More often it will run out in the course of compilation, like so: The memory setting (which is at present) has been exceeded. Try running Inform again with $= on the command line. For details of the memory settings, *Note Memory settings::. D.2 Errors ========== There are a few conventions. Anything in double-quotes is a display from your source code; other strings are in single-quotes. A message like Expected ... but found "..." means that Inform expected something different from what it found; if it doesn't say what it found, this usually means it found nothing (i.e., the statement was incomplete). Messages in the form No such ... as "..." Not a ...: "..." mean that a name is unrecognised in the former case (say, a typing error might produce this), or is recognised but means something else in the latter case (an attempt to use a routine where a property is expected would give such an error). To begin with, the source-code format may go awry: Too many tokens on line (note: to increase the maximum, set $MAX_TOKENS=some-bigger-number on the Inform command line) Line too long (note: to increase the maximum length, set $BUFFER_LENGTH=some-bigger-number on the Inform command line) Too much text for one pair of "s to hold Too much text for one pair of 's to hold Open quotes " expected for text but found Close quotes " expected for text but found (Usually `BUFFER_LENGTH' allows about 2000 characters per line.) When giving something (such as an object) an internal name, there are rules to be obeyed; for instance, you can't give an object the same name as a property already declared: Symbol name expected Symbol names are not permitted to start with an '_' Symbol name is too long: Duplicated symbol name: At the top level, the most common `no such command' error is Expected an assignment, command, directive or opcode but found which means Inform didn't even understand the first word of the command. Directives to Inform can produce the following errors: All 32 attributes already declared (compile as Advanced game to get an extra 16) All 48 attributes already declared All 30 properties already declared (compile as Advanced game to get an extra 32) All 62 properties already declared Expected an attribute name after 'alias' Expected a property name after 'alias' 'alias' incompatible with 'long' 'alias' incompatible with 'additive' 'alias' refers to undefined attribute 'alias' refers to undefined property All 235 global variables already declared Expected 'string', 'data', 'initial', 'initstr' or '=' but found Use of `alias' is rare (except inside the library files). The last of these errors means that a global variable has been wrongly initialised, and a common cause of this is typing, say, `global trolls 5;' instead of `global trolls = 5;'. '*' divider expected, but found No such token as Expected '=' after 'scope' but found Expected routine after 'scope=' but found Expected routine after 'noun=' but found '=' is only legal here as 'noun=Routine' '->' clause missing No such action routine as Not an action: Too many lines of grammar for verb: increase #define MAX_LINES_PER_VERB There is no previous grammar for the verb Two different verb definitions refer to Expected 'replace', 'last' or 'first' but found These are the grammatical errors, the last three concerning `extend'ed verb definitions. Normally one gets 16 grammar lines per verb. It's probably better to write grammar more carefully (using routines to parse adjectives more carefully, for instance) than to exceed this, as the game parser will otherwise slow down. The object/class definition errors are largely self-explanatory: Object/class definition finishes with ',' Two commas ',' in a row in object/class definition No such attribute as No such class as Expected 'with', 'has' or 'class' in object/class definition but found Expected an (internal) name for object but found the string An object must be defined after the one which contains it: (so far) there is no such object as Not an object: No such property as Not a property: Miscellaneous errors complete the list produced by mostly-uncommon directives: Expected ';' after 'include ' but found A 'switches' directive must come before constant definitions Expected 'score' or 'time' after 'statusline' but found The serial number must be a 6-digit date in double-quotes The version number must be 3 to 6: 3 for Standard games and 5 for Advanced Expected 'on' or 'off' after 'font' but found Defaulted constants can't be strings Must specify 0 to 3 variables in 'stub' routine Expected 'full' or nothing after 'etrace' but found Too many abbreviations declared All abbreviations must be declared together It's not worth abbreviating Expected a 'string' value No such directive as Conditional compilation happens as a result of one of `#IFDEF', `#IFV3' or `#IFV5'. (The former tests whether a constant is defined; the latter test for version-3 (Standard) games or version-5 (Advanced) games.) An `#IFNOT' section is optional but the closing `#ENDIF' is compulsory. In these error messages `#IF...' means any of the three opening clauses. '#IF...' nested too deeply: increase #define MAX_IFDEF_DEPTH '#ENDIF' without matching '#IF...' '#IFNOT' without matching '#IF...' Two '#IFNOT's in the same '#IF...' End of file reached inside '#IF...' Routines begin with a `[' and some local variables and end with `]': Routine has more than 15 local variables The earliest-defined routine is not allowed to have local variables Expected local variable but found ',' or ':' (probably the ';' after the '[ ...' line was forgotten) Misplaced ']' Comma ',' after ']' can only be used inside object/class definitions Expected ',' or ';' after ']' but found Expected ';' after ']' but found The error messages for expressions are fairly simple. Note, however, that one is not allowed to type, say, `lamp.number++;' but must instead write `lamp.number = lamp.number + 1;' (the `++' and `--' operators can only be applied to variables, not properties). Expected condition but found expression Unexpected condition Expected an assignment but found Expected an assignment but found an expression Attempt to use void as a value Attempt to use an assignment as a value Attempt to use a condition as a value Operator has too few arguments Operator has too many arguments '++' and '--' can only apply directly to variables At most three values can be separated by 'or' 'or' can only be used with the conditions '==' and '~=' Too many brackets '(' in expression Brackets '(' too deeply nested Missing bracket ')' in function call Spurious comma ',' Misplaced comma ',' Wrong number of arguments to system function 'children' takes a single argument 'youngest' takes a single argument 'elder' takes a single argument 'indirect' takes at least one argument Type mismatch in argument A function may be called with at most 3 arguments Malformed statement 'Function(...);' Spurious terms after function call Spurious terms after assignment Spurious terms after expression Next, constants. Note that dictionary words cannot start with a non-alphabetic character, which means that Infocom-style `debugging verbs' which traditionally begin with a `#' are not allowed. No such variable as No such constant as Not a constant: Reserved word as constant: No such routine as Dictionary words must begin with a letter of the alphabet Dictionary word not found for constant Loop constructs: note that `old-style' `for' loops are a rather obselete form (e.g., `for i 1 to 10'), and the more flexible style `for (i=1:i<=10:i++)' is now preferred. 'if' statement with more than one 'else' 'else' attached to a loop block 'for' loops too deeply nested ':' expected in 'for' loop Second ':' expected in 'for' loop Concluding ')' expected in 'for' loop 'to' missing in old-style 'for' loop 'to' expected in old-style 'for' loop Final value missing in old-style 'for' loop '{' required after an old-style 'for' loop Old-style 'for' loops must have simple final values Open bracket '(' expected in 'objectloop' 'objectloop' must be 'from', 'near' or 'in' something Close bracket ')' expected in 'objectloop' Braces '{' are compulsory unless the condition is bracketed Unmatched '}' found Brace mismatch in previous routine Inform checks the level of braces when a routine closes so that it can recover as quickly as possible from a mismatch; this last error means at least one brace is still open when the routine finishes. The object to 'give' to must be a variable or constant Expected some attributes to 'give' Expected 'to ' in 'move' Expected 'to' in 'move' but found Expected ',' in 'print' list but found Expected 'style' to be 'roman', 'bold', 'underline' or 'reverse' but found Expected a parse buffer for 'read' Expected some properties to 'write' The object to 'write' must be a variable or constant Expected property value to 'write' Expected 'byte' or 'word' in 'put' Expected 'byte' or 'word' in 'put' but found These are miscellaneous commands and `put' and `write' are quite obselete. Only action commands like `' remain: Action name too long or a string: perhaps a statement accidentally ended with a comma? Action commands must take the form '< Action ... >' The longest action command allowed is '' Angle brackets do not match Action given in constant does not exist Action name over 60 characters long: Expected ':' (after action) but found The first of these may be caused by something like: "You load the crossbow bolt.", Drop: "The bolt falls to the floor with a thump."; where a comma has been typed instead of a semicolon after the first string, so that Inform thinks you are giving a rule for two actions, one being `Drop' and the other apparently called `"You load the crossbow bolt."'. D.3 Internal and assembler errors ================================= By now we have descended to the ninth circle of Inform: the assembler. These errors are fairly unmysterious (if only because they seldom happen), but sometimes one is told something odd like No such label as _f456 which is caused by Inform failing to recover properly from a previous brace mismatch error. Just ignore this and fix the earlier error (which will also have been reported). The `no such variable' error is occasionally seen when an unknown variable is first referred to by being written to. No such assembly opcode as Too many arguments Can't store to that (no such variable) Branch too far forward: use '?' No such return condition for branch Can't branch to a routine, only to a label No such label as Not a label: To conclude with, there are a few internal error messages: A label has moved between passes because of a low-level error just before (perhaps an improper use of a routine address as a constant) Object has altered in memory usage between passes: perhaps an attempt to use a routine name as value of a small property Duplicated system symbol name Internal error - unknown directive code Internal error - unknown compiler code The last three should not happen. The first two occasionally do, and cause some confusion. Inform performs two tests regularly as a safeguard to make sure that code has not come out substantially different in its two passes. The first failure occurs in code, the second for object/class definitions. Whatever caused this should be something unusual, low-level and just before the error occurred: if you get these errors with innocent high-level code, then probably Inform should provide a suitable error message, so please email the author with a sample of offending code. D.4 Warnings ============ Inform can produce any number of warnings without shutting down. Note that Inform tries to give only warnings when it hits Advanced-game features in what is to be a Standard game, for the sake of portability. Nevertheless it is probably better to use `#IFV3' and `#IFV5' clauses around any pieces of code which are to be different in these two versions (if, indeed, you want two different versions). Local variable unused: Since it is defined before inclusion of the library, game-play will begin not at 'Main' but at the routine Ignoring Advanced-game status routine Ignoring this Advanced-game command Missing ','? Property data seems to contain the property name Standard-game limit of 8 bytes per property exceeded (use Advanced to get 64), so truncating property Ignoring Advanced-game opcode Ignoring Standard-game opcode E Compiler options and memory settings ************************************** I was promised a horse, but what I got instead was a tail, with a horse hung from it almost dead. -- Palladas of Alexandria, translated by Tony Harrison *The reader is warned that some details in this section are slightly different on different machines.* E.1 Compiler options ==================== On most machines, Inform is run from the command line, by a command like inform -xv5 balances and simply typing `inform' will produce a good deal of help information about the command line options available. The command line syntax is `inform' SWITCHES SETTINGS SOURCE FILE OUTPUT FILE where only the SOURCE FILE is mandatory. (By default, the full names to give the source and output files are derived in a way suitable for the machine Inform is running on: on a PC, for instance, `advent' may be understood as asking to compile `advent.inf' to `advent.z5'.) The switches are given in one or more groups, preceded by a minus sign `-' in the usual Unix command-line style. The current list of legal switches is: a list assembly-level instructions compiled b give statistics and/or line/object list in both passes c more concise error messages d contract double spaces after full stops in text e economy mode (slower): make use of declared abbreviations f frequencies mode: show how useful abbreviations are g with debugging code: traces all function calls h print this information i ignore default switches set within the file j list objects as constructed k output Infix debugging information to "Game_Debug" l list all assembly lines m say how much memory has been allocated n print numbers of properties and attributes o print offset addresses p give percentage breakdown of story file r record all the text to "Game_Text" s give statistics t trace Z-code assembly u work out most useful abbreviations v3 compile to version-3 (Standard) story file v4 compile to version-4 (Plus) story file v5 compile to version-5 (Advanced) story file v6 compile to version-6 (graphical) story file w disable warning messages x print # for every 100 lines compiled (in both passes) z print memory map of the Z-machine T enable throwback of errors in the DDE (Thus, as long as your name doesn't have a `q' or `y' in it, you can amuse yourself typing your name in as a switch and see what it does.) Note that these switches can also be selected by putting a `switches' directive into the source code before anything else, such as Switches xdv5s; The most useful switches are `v3' and `v5', which choose between Standard and Advanced games. For example, the above line is from the example game `Advent', which is consequently compiled to an Advanced game. (If no choice is stated, a Standard game results.) The options `v4' and `v6' are provided for completeness but their use is not recommended. Many of the remaining switches make Inform produce extra output, but do not affect its compilation: `a b l m n t' are tracing options to help with maintaining Inform, or for debugging assembly language programs `o p s z' will print out information about the final game file, the `s' (statistics) option being particularly useful to keep track of how large the game is growing `c w T' in `c' mode, Inform does not quote whole source lines together with error messages; in `w' mode it suppresses warnings; in `T' mode, which is only present on the Acorn Archimedes, error throwback will occur in the `Desktop Development Environment' `f' indicates roughly how many bytes the abbreviations saved `h' prints out the help information (and is equivalent to just typing `inform') `j x' make Inform print out steady text to prove that it's still awake: on very slow machines this may be a convenience `k' writes a `debugging information' file for the use of the Infix debugger (similarly, the filename is something suitable for the machine) `r' is intended to help with proof-reading the text of a game, and transcribes all of the text in double-quotes to the given file (whose name is something suitable for the machine) `u' will try to work out a good set of abbreviations to declare for your game, but *extremely slowly* (a matter of hours) and *consuming very much memory* (perhaps a megabyte) This leaves three more switches which actually alter the game file which Inform would compile: `d' converts text like "...with a mango. You applaud..." into the same with only a single space after the full stop, which will prevent an interpreter from displaying a spurious space at the beginning of a line when a line break happens to occur exactly after the full stop; this is to help typists who habitually double-space `e' only in `economy' mode does Inform actually process abbreviations, because this is seldom needed and slows the compiler by 10% or so; the game file should not play any differently if compiled this way, but will probably be shorter, if your choice of abbreviations was sensible `g' will make Inform automatically compile trace-printing code on every function call; in play this will produce reams of text (several pages between each chance to type commands) but is sometimes useful. Note that in Inform 5.3 or later, this can be set on an individual command by writing `*' as its first local variable, without use of the `g' switch `i' overrides any switches set by `switches' directives in the source code; so that the game can be compiled with different options without having to alter that source code. E.2 Memory settings =================== Inform's memory management is about as flexible as it can be given that it has to run in some quite hostile environments. In particular, it is unable to increase the size of any stretch of memory once allocated, so if it runs out of anything it has to give up. If it does run out, it will produce an error message saying what it has run out of and how to provide more. There are two main choices: `$small' and `$large'. (Which one is the default depends on the computer you use.) Even `$small' is large enough to compile all the example games, including `Advent'. `$large' is large enough to compile almost anything (including the largest version of `Curses' which ever existed, a draft very close to 256K long). A typical game, compiled with `$large', will cause Inform to allocate about 336K of memory: and the same game about 100K less under `$small'. (These values will be rather lower if the computer Inform runs on has 16-bit integers.) In addition, Inform physically occupies about 170K (on my computer). Thus, the total memory consumption of the compiler at work will be between 4 to 500K. Running inform $list will list the various settings which can be changed, and their current values. Thus one can compare small and large with: inform $small $list inform $large $list If Inform runs out of allocation for something, it will generally print an error message like: "Game", line 1320: Fatal error: The memory setting MAX_OBJECTS (which is 200 at present) has been exceeded. Try running Inform again with $MAX_OBJECTS= on the command line. and indeed inform $MAX_OBJECTS=250 game (say) will tell Inform to try again, reserving more memory for objects this time. Note that settings are made from left to right, so that for instance inform $small $MAX_ACTIONS=200 ... will work, but inform $MAX_ACTIONS=200 $small ... will not because the `$small' changes `MAX_ACTIONS' again. Changing some settings has hardly any effect on memory usage, whereas others are expensive to increase. To find out about, say, `MAX_VERBS', run inform $?MAX_VERBS (note the question mark, which may need to be escaped using e.g., `\?' if your shell interprets `?' as a filename wildcard) which will print some very brief comments. F Answers to all the exercises ****************************** F.1 Answer: the green cone ========================== Nearby cone "green cone" with name "green" "cone" "emerald" "marzipan", describe [; if (cone has moved) "A misshapen cone of green marzipan sits here."; "Nearby is an emerald green cone, one foot high."; ], description "The cone seems to be made of emerald-coloured \ marzipan.", before [; Eat: if (random(100) <= 30) { deadflag = 1; "Unfortunately, you seem to be allergic to almonds."; } "You nibble at a corner of the cone."; ], after [; Take: "Taken. (Your hands are smeared with marzipan.)"; Drop: cone.description = "The cone is a vague green mess."; "The cone drops to the floor and sags a little."; ], has edible; The old `initial' message has gone. Instead, we have provided a `describe' routine. Whenever the game has to describe the cone in the description of a place, it will call this routine. The `moved' attribute is held only by an object which has at some time in the past been taken. So the cone is now perfect and untouched until taken and dropped, whereupon it becomes misshapen. Also, the act of dropping the cone now changes the description which appears when a player examines it. F.2 Answer: actions =================== You may be surprised how many actions take place: often more than one per turn. F.3 Answer: property addresses ============================== `if (obj.&door_to == 0) { ... }' F.4 Answer: world colours ========================= Define four objects along the lines of: Object white_obj "white wall" compass with name "white" "sac" "wall", article "the", door_dir n_to has direction scenery; and add the following line to `Initialise': remove n_obj; remove e_obj; remove w_obj; remove s_obj; (We could even `alias' a new property `white_to' to be `n_to', and then enter map directions in the source code using Mayan property names.) As a fine point of style, turquoise (*yax*) is the world colour for `here', so add a grammar line to make this cause a `look': Verb "turquoise" "yax" * -> Look; F.5 Answer: reflecting the map ============================== [ SwapDirs o1 o2 x; x=o1.door_dir; o1.door_dir=o2.door_dir; o2.door_dir=x; ]; [ ReflectWorld; SwapDirs(e_obj,w_obj); SwapDirs(ne_obj,nw_obj); SwapDirs(se_obj,sw_obj); ]; F.6 Answer: reflecting directions ================================= This is a prime candidate for using variable strings `@nn', a topic properly covered in the `Inform Technical Manual', but briefly: at the head of the source, define Lowstring east_str "east"; Lowstring west_str "west"; and then add two more routines to the game, [ NormalWorld; String 0 #east_str; String 1 #west_str; ]; [ ReversedWorld; String 0 #west_str; String 1 #east_str; ]; where `NormalWorld' is called in `Initialise' or to go back to normal, and `ReversedWorld' when the reflection happens. Write `@00' in place of `east' in any double-quoted printable string, and similarly `@01' for `west'. It will be printed as whichever is currently set. (Inform provides up to 32 such variable strings.) F.7 Answer: medicine bottle =========================== Declare a fake action called, say, `OpenUp'. Then: Object medicine "guaranteed child-proof medicine bottle" cupboard with name "medicine" "bottle", description "~Antidote only: no preventative effect.~", before [; Open, Unlock: "It's adult-proof too."; Openup: give self open ~locked; "The bottle cracks open!"; ], has container openable locked; Any other code in the game can execute `' to crack open the bottle. F.8 Answer: two boxes ===================== Nearby glass_box "glass box with a lid" with name "glass" "box" "with" "lid" has container transparent openable open; Nearby steel_box "steel box with a lid" with name "steel" "box" "with" "lid" has container openable open; F.9 Answer: toothed bag ======================= Object bag "toothed bag" room with name "toothed" "bag", description "A capacious bag with a toothed mouth.", before [; LetGo: "The bag defiantly bites itself shut on your hand \ until you desist."; Close: "The bag resists all attempts to close it."; ], after [; Receive: print "The bag wriggles hideously as it swallows "; DefArt(inp1); "."; ], has container open; F.10 Answer: television set =========================== Object television "portable television set" lounge with name "tv" "television" "set" "portable", before [; SwitchOn: <>; SwitchOff: <>; Examine: <>; ], has transparent; Nearby power_button "power button" with name "power" "button" "switch", after [; SwitchOn, SwitchOff: <>; ], has switchable; Nearby screen "television screen" with name "screen", before [; Examine: if (power_button hasnt on) "The screen is black."; "The screen writhes with a strange Japanese cartoon."; ]; F.11 Answer: the red car ======================== Change the car's `before' to before [; Go: if (noun==d_obj or u_obj) { print "(The car will never get over those stairs.)^"; rfalse; } if (car has on) "Brmm! Brmm!"; print "(The ignition is off at the moment.)^"; ], F.12 Answer: *wayhel* ===================== The common man's *wayhel* was a lowly mouse. Since we think much more highly of the player: Object hog "Warthog" Caldera with name "wart" "hog" "warthog", description "Muddy and grunting.", number 0, initial "A warthog snuffles and grunts about in the ash.", before [; if (player==self && action~=##Go or ##Look or ##Examine) "Warthogs can't do anything as tricky as that!"; ], has animate proper; and we just `ChangePlayer(warthog);'. Note that this `before' rule is carefully written only to affect actions of the player-as-warthog. If the player-as-human should find and try to `take warthog', this `before' routine won't interfere. F.13 Answer: printing objects ============================= Because `CDefArt(obj)' is a function call which, as it happens, returns the value true, or 1 (not that this signifies anything), and `print' thinks it is printing out a number. F.14 Answer: the thief ====================== This is a crude implementation, for brevity (the real Zork I thief has an enormous stock of attached messages). Object thief "thief" Danger_Zone with name "thief", each_turn "^The thief growls menacingly.", daemon [ i p j n k; if (random(3)~=1) rfalse; p=parent(thief); objectloop (i in compass) { j=p.(i.door_dir); if (j>player && j<=top_object && j hasnt door) n++; } if (n==0) rfalse; k=random(n); n=0; objectloop (i in compass) { j=p.(i.door_dir); if (j>player && j<=top_object && j hasnt door) n++; if (n==k) { move self to j; if (p==location) "^The thief stalks away!"; if (j==location) "^The thief stalks in!"; rfalse; } } ]; This thief walks at random and cannot pass through doors, bridges and the like (because these may be locked or have rules attached); it's only a first approximation, and in a good game one should occasionally see the thief do something surprising, such as open a secret door. F.15 Answer: weights ==================== First define a new property for object weight: Property weight 10; (10 being an average sort of weight). Containers weigh more when they hold things, so we will need: [ WeightOf obj t i; t = obj.weight; objectloop (i in obj) t = t + WeightOf(i); return t; ]; Now for the daemon which monitors the player's fatigue: Object weigher "weigher" with number 500, time_left 5, daemon [ w s b bw; w = WeightOf(player) - 100 - player.weight; s = self.number; s = s - w; if (s <0) s = 0; if (s > 500) s = 500; self.number = s; if (s == 0) { bw = -1; objectloop(b in player) if (WeightOf(b) > bw) { bw = WeightOf(b); w = b; } print "^Exhausted with carrying so much, you decide \ to discard "; DefArt(w); print ": "; <>; } w = s/100; if (w == self.time_left) rfalse; if (w == 3) print "^You are feeling a little tired.^"; if (w == 2) print "^You possessions are weighing you down.^"; if (w == 1) print "^Carrying so much weight is wearing you out.^"; self.time_left = w; ]; Notice that items are actually dropped with `Drop' actions: one of them might be, say, a wild boar, which would bolt away into the forest when released. The daemon tries to drop the heaviest item. (Obviously a little improvement would be needed if the game contained, say, an un-droppable but very heavy ball and chain.) Now the daemon is going to run every turn forever, but needs to be started: so put `StartDaemon(weigher);' into the game's `Initialise' routine. F.16 Answer: passing midnight ============================= Either set a daemon to watch for `the_time' suddenly dropping, or put such a watch in the game's `TimePasses' routine. F.17 Answer: suspended in mid-air ================================= Because you don't know what order daemons will run in. A `fatigue' daemon which makes the player drop something might come after the `mid-air' daemon has run for this turn. Whereas `each_turn' happens after daemons and timers have run their course, and can fairly assume no further movements will take place this turn. F.18 Answer: varying rates of time ================================== It would have to provide its own code to keep track of time, and it can do this by providing a `TimePasses()' routine. Providing `time' or even `date' verbs to tell the player would also be a good idea. F.19 Answer: footnotes ====================== Constant MAX_FOOTNOTES 10; global footnotes_seen data MAX_FOOTNOTES; global footnote_count; [ Note n i pn; for (i = 0: i < footnote_count: i++) if (n == footnotes_seen->i) pn = i; if (footnote_count == MAX_FOOTNOTES) "** MAX_FOOTNOTES exceeded! **"; if (pn == 0) { pn = footnote_count++; footnotes_seen->pn = n; } print " [", pn + 1, "]"; ]; [ FootnoteSub n; if (noun > footnote_count) { print "No footnote [",noun,"] has been mentioned.^"; rtrue; } if (noun == 0) "Footnotes count upward from 1."; n = footnotes_seen->(noun-1); print "[", noun, "] "; if (n==0) "This is a footnote."; if (n==1) "D.G.REG.F.D is inscribed around English coins."; if (n==2) "~Jackdaws love my big sphinx of quartz~, for example."; ]; Verb "footnote" "note" * number -> Footnote; And then call, for instance, `Note(1);' to refer in the game to the `English coins' footnote. F.20 Answer: unknown verbs ========================== Because the parser might go on to reject the line it's working on: for instance, if the player typed `inventory splurge' then the message `Shazam!' followed by a parser complaint will be somewhat unedifying. F.21 Answer: control panel ========================== Here goes: we could implement the buttons with five separate objects, essentially duplicates of each other. (And by using a class definition, this wouldn't look too bad.) But if there were 500 slides this would be less reasonable. [ ASlide w n; if (location ~= Machine_Room) ! Slides only make sense in return -1; ! the Machine Room w = NextWord(); if (w == 'slide') w = NextWord(); n = 0; if (w == 'first' or 'one') n=1; if (w == 'second' or 'two') n=2; if (w == 'third' or 'three') n=3; if (w == 'fourth' or 'four') n=4; if (w == 'fifth' or 'five') n=5; if (n == 0) return -1; ! Failure! w = NextWord(); if (w ~= 'slide') wn--; ! Move word counter back to ! first misunderstood word parsed_number=n; return 1; ! Success! ]; Global slide_settings data 10; ! Ten bytes of data to hold ! five words for the settings ! (all initially zero) ! An interesting point here is that "noun" and "second" contain the ! appropriate numbers, and not objects: this all happens automatically [ SetSlideSub; slide_settings-->(noun-1) = second; print_ret "You set slide number ", noun, " to the value ", second, "."; ]; [ XSlideSub; print_ret "Slide number ", noun, " currently stands at ", slide_settings-->(noun-1), "."; ]; Extend "set" first * ASlide "to" number -> SetSlide; Extend "push" first * ASlide "to" number -> SetSlide; Extend "examine" first * ASlide -> XSlide; F.22 Answer: quoted strings =========================== The blackboard code in `Toyshop' contains just such a routine: Global from_char; Global to_char; [ QuotedText i j f; i = parse->((++wn) * 4 - 3); if (buffer->i == '"') { for (j = i + 1: j <= (buffer->1) + 1: j++) if (buffer->j == '"') f = j; if (f == 0) return -1; from_char = i + 1; to_char = f - 1; if (from_char>to_char) return -1; while (f > (parse->(wn * 4 - 3))) wn++; wn++; return 1; } return -1; ]; Note that in the case of success, the word marker `wn' is moved beyond the last word accepted (since the Z-machine automatically tokenises a double-quote as a single word). The routine then tells the parser it has parsed a number, which is a white lie. What it actually does is to record the byte positions where the quoted text starts and finishes in the raw text `buffer' so that an action routine can easily extract the text and use it later. (Note that `""' with no text inside is not matched by this routine but only because the last `if' statement throws out that one case.) F.23 Answer: named rooms ======================== Define two properties: Property place_name; Property long to_places; The scheme will work like this: a named room should have the `place_name' property set to a single dictionary word; say, the Bedquilt cave could be called `'bedquilt''. Then in any room, a list of those other rooms which can be moved to in this way should appear in the `to_places' entry. For instance, to_places Bedquilt Slab_Room Twopit_Room; Now the code: see if a not-understood verb is a place name of a nearby room, and if so store that room's object number in `goto_room', converting the verb to a dummy. Global goto_room = 0; [ UnknownVerb word p i; p = location.&to_places; if (p == 0) rfalse; for (i = 0: (2 * i) < location.#to_places: i++) if (word == (p-->i).place_name) { goto_room = p-->i; return 'go#room'; } rfalse; ]; [ PrintVerb word; if (word=='go#room') { print "go to "; PrintShortName(goto_room); rtrue; } rfalse; ]; (The supplied `PrintVerb' is icing on the cake: so the parser can say something like `I only understood you as far as wanting to go to Bedquilt.' if need be.) It remains only to put in code and grammar for the dummy verb: [ GoRoomSub; if (goto_room hasnt visited) "But you have never been there."; PlayerTo(goto_room); ]; Verb "go#room" * -> GoRoom; Note that if you don't know the way, you can't go there! A purist might prefer instead to not recognise the name of an unvisited room, back at the `UnknownVerb' stage, to avoid the player being able to deduce names of nearby rooms from this `error message'. F.24 Answer: purloin verb ========================= A slight refinement of such a `purloin' verb is already defined in the library (if the constant `DEBUG' is defined), so there's no need. Here's how it could be done: [ Anything i; if (scope_stage==1) rfalse; if (scope_stage==2) { for (i=1:i<=top_object:i++) PlaceInScope(i); rtrue; } "No such in game."; ]; (This disallows multiple matches for efficiency reasons - the parser has enough work to do with such a huge scope definition as it is.) Now the token `scope=Anything' will match anything at all, even things like the abstract concept of `east'. F.25 Answer: light switch ========================= For good measure, we'll combine this with the previous rule about `moved' objects being in scope in the dark. The following can be inserted into the `Shell' game: Object coal "dull coal" Blank_Room with name "dull" "coal"; Object Dark_Room "Dark Room" with description "An empty room with a west exit.", each_turn [; if (self has general) self.each_turn=0; else "^You hear the breathing of a dwarf."; ], w_to Blank_Room; Nearby light_switch "light switch" with name "light" "switch", initial "On one wall is the light switch.", after [; SwitchOn: give Dark_Room light; SwitchOff: give Dark_Room ~light; ], has switchable static; Nearby diamond "shiny diamond" with name "shiny" "diamond" has scored; Nearby dwarf "dwarf" with name "voice" "dwarf", life [; Order: if (action == ##SwitchOn && noun == light_switch) { give Dark_Room light general; give light_switch on; "~Right you are, squire.~"; } ], has animate; [ InScope person i; if (parent(person) == Dark_Room) { if (person == dwarf || Dark_Room has general) PlaceInScope(light_switch); } if (person == player && location == thedark) { objectloop (i near player) if (i has moved || i == dwarf) PlaceInScope(i); } rfalse; ]; Note that the routine puts the light switch in scope for the dwarf - if it didn't, the dwarf would not be able to understand `dwarf, turn light on', and that was the whole point. F.26 Answer: heliotropic troll ============================== Just test if `HasLightSource(gift)==1'. F.27 Answer: double inventory ============================= [ DoubleInvSub i count1 count2; print "You are carrying "; objectloop (i in player) { if (i hasnt worn) { give i workflag; count1++; } else { give i ~workflag; count2++; } } if (count1==0) print "nothing."; else WriteListFrom(child(player), FULLINV_BIT + ENGLISH_BIT + RECURSE_BIT + WORKFLAG_BIT); if (count2==0) "."; print ". In addition, you are wearing "; objectloop (i in player) { if (i hasnt worn) give i ~workflag; else give i workflag; } WriteListFrom(child(player), ENGLISH_BIT + RECURSE_BIT + WORKFLAG_BIT); "."; ]; F.28 Answer: cherubim ===================== Global c_warned = 0; Class cherub_class with parse_name [ i j flag; for (flag = 1: flag == 1: flag = 0) { j=NextWord(); if (j=='cherub' or j==self.name) flag=1; if (j=='cherubs' && c_warned==0) { c_warned=1; parser_action=##PluralFound; flag=1; print "(I'll let this go once, but the plural of \ cherub is cherubim.)^"; } if (j=='cherubim') { parser_action=##PluralFound; flag=1; } i++; } return i-1; ]; Then again, Shakespeare even writes `cherubins' in `Twelfth Night', so who are we to censure? F.29 Answer: status line ======================== First put the directive `Replace DrawStatusLine;' before including the library. Then add the following routine anywhere after `treasures_found', an `Advent' variable, is defined: [ DrawStatusLine; @split_window 1; @set_window 1; @set_cursor 1 1; style reverse; spaces (0->33)-1; @set_cursor 1 2; PrintShortName(location); if (treasures_found > 0) { @set_cursor 1 50; print "Treasure: ", treasures_found; } @set_cursor 1 1; style roman; @set_window 0; ]; F.30 Answer: invisiclues ======================== Note the magic line of assembly code here, which only works for Advanced games: [ GiveHint hint keypress; print_paddr hint; new_line; new_line; @read_char 1 0 0 keypress; if (keypress == 'H' or 'h') rfalse; rtrue; ]; And a typical menu item using it: if (menu_item==1) { print "(Press ENTER to return to menu, or H for another hint.)^^"; if (GiveHint("(1/3) What kind of bird is it, exactly?") == 1) return 2; if (GiveHint("(2/3) Magpies are attracted by shiny items.") == 1) return 2; "(3/3) Wave at the magpie with the kitchen foil."; } F.31 Answer: primes =================== `Primes(100)', where: [ Primes i j k l; for (j = 2: j <= i: j++) { print j, " : "; l=j; while (l > 1) for (k=2: k<=l: k++) if (l % k == 0) { l=l/k; print k, " "; break; } new_line; } ]; (which was the first algorithm ever compiled by Inform). G Index of concepts ******************* * Menu: * `Acheton': Credits. * `Advent': Memory settings. * `Advent': The player. * `Advent': Classes of objects. * `Advent': Misc grammar. * `Advent': About daemons. * `Advent': Status line. * `Advent': Compiler options. * `Balances': Plural names. * `Balances': Extending. * `Colossal Cave': Misc grammar. * `Curses': Memory settings. * `Curses': Object definitions. * `Enchanter': Doors. * `Hello Cruel World': Shell. * `Shell': Shell. * `Spellbreaker': Library actions. * `Spellbreaker': The player. * `The Legend Lives': Numbers. * `Toyshop': Changing lists. * `Toyshop': Answer (quoted strings). * `Toyshop': About daemons. * `Trinity': Room code. * `Zork I': Each turn. * `Zork I': About daemons. * `Zork': Switches. * `Zork': Z-machine. * abbreviations: Compiler options. * abbreviations: Limitations. * abstract verb: Debugging verbs. * action groups: Library actions. * action numbers: Causing actions. * actions: Debugging verbs. * actions: Causing actions. * actions: Basic ingredients. * actions: Invoking actions. * actions verb: Debugging verbs. * actions verb: Basic ingredients. * addresses: Z-machine. * Advanced games: Dirty tricks. * Advanced games: Z-machine. * Advanced games: Styles. * Advanced games: Object definitions. * Advanced games: Properties. * Advanced games: Warnings. * Advanced games: Limitations. * Advanced games: Compiler options. * almond poisoning: Adding code. * Anderson, Timothy: Vehicles. * appallingly convenient verb: Tokens. * archaeological dig: Each turn. * Area 400: Naming of names. * Aristotle: Objects. * arithmetic operators: Operators. * arrays: Variables and arrays. * asking questions: Changing scope. * assembler errors: Internal and assembler errors. * assembly language: Dirty tricks. * assignments: Assignments. * attributes: Simple objects. * attributes: Attributes. * attributes in library: Library attributes. * Auden, W. H.: Doors. * audibility: Each turn. * background daemon: About daemons. * backslash: File format. * backslash: Quoted strings. * bag of six coins: Plural names. * Baggett, David M.: Numbers. * Baggett, David M.: The player. * banana: Tokens. * batteries: Switches. * Bedquilt Room: New actions. * Beerbohm, Max: Scope. * binary: Constants. * blackboard: Answer (quoted strings). * Blank, Marc: Vehicles. * Blank, Marc: Doors. * blocks of code: Blocks of code. * boldface: Styles. * bolted cupboard: Other containers. * Booth, Connie: Special effects. * brace mismatch: Errors. * braces: Blocks of code. * brain transference machine: The player. * brass lantern: Switches. * built-in functions: Built-in functions. * byte address: Z-machine. * Cambridge University: Credits. * ceiling: Library objects. * chair: The player. * changing scope: Global scope. * changing the player: The player. * character graphic: Fixed font. * cherubim: Answer (cherubim). * cherubim: Plural names. * Chesterton, G. K.: Printing of names. * Christopher, John: Places. * class errors: Errors. * classes: Classes of objects. * clearing the screen: Dirty tricks. * Cleese, John: Special effects. * closing credits: Amusing. * clues: Menus. * command line syntax: Compiler options. * comments: File format. * companion volumes: History. * compiler switches: Compiler options. * conditional compilation: Directives. * conditional compilation: Errors. * conditions: Conditions. * constants: Constants. * control constructs: Control constructs. * copyright: Copyright. * crashing the interpreter: Crashes. * creature token: Tokens. * crowns: Plural names. * Crowther, Willie: Credits. * crystal chandelier: Map. * cursor keys: Dirty tricks. * daemon running order: Answer (suspended in mid-air). * daemons: Debugging verbs. * daemons: About daemons. * darkness: Scope rules. * de la Bruyère, Jean: The player. * de Montaigne, Michel: Creatures. * debugging code: Trace. * debugging information file: Compiler options. * debugging verbs: Debugging verbs. * default value of properties: Properties. * definite article: Printing of names. * definition of darkness: Light. * dentist's chair: Vehicles. * depressed philosophers: The player. * dictionary errors: Errors. * dictionary resolution: Limitations. * dictionary words: Library properties. * direction objects: Library objects. * direction objects: Answer (world colours). * direction objects: Library properties. * direction objects): Room code. * direction properties: Library properties. * directions: Map. * directions: Scope rules. * directive: Attributes. * directives: Directives. * dirty tricks: Dirty tricks. * displays beautiful: Boxes. * division by zero: Assignments. * Donne, John: Places. * double inventory: Answer (double inventory). * double inventory: Lists. * double spacing: Compiler options. * double-quote: Answer (quoted strings). * dramatic effects: Global scope. * drawings: Fixed font. * drunk player object: The player. * dummy verb: Answer (named rooms). * earshot: Each turn. * economy mode: Compiler options. * Eliot, T. S.: Naming of names. * embedded routines: Object definitions. * embedded routines: Routines. * entry points: Library entry points. * epigrams: Boxes. * error messages: Error messages. * exotic forms of death: The player. * expression errors: Errors. * expressions: Operators. * fake actions: Causing actions. * fake actions: Creatures. * fake actions: Basic containers. * fake actions: Plural names. * fatal errors: Fatal errors. * fatigue daemon: Answer (weights). * file format: File format. * flags: Attributes. * flexible verbs: Misc grammar. * floating object: Library attributes. * floor: Library objects. * fluorescent jellyfish: Light. * focus of game: The player. * footnotes: Numbers. * footnotes: Answer (footnotes). * foreign character sets: Quoted strings. * Frankenstein: The player. * free verb: Tokens. * function arguments: Routines. * function keys: Dirty tricks. * fuses: Timers. * Gilbert, W. S.: Inventories. * glass box: Basic containers. * global variables: Errors. * Goldsmith, Oliver: Limitations. * goto verb: Debugging verbs. * Grammar: Basic ingredients. * Grammar: Shell. * grammar lines: New verbs. * grammar tokens: Tokens. * grammatical errors: Errors. * green cone: Simple objects. * grues: Changing scope. * hacker and urchin: Creatures. * hanging elses: Blocks of code. * hangover: Object definitions. * has light: Light. * hash character: Directives. * held token: Tokens. * hexadecimal: Properties. * Ideas, Ivan O.: Scoring. * in scope: Scope rules. * indefinite article: Printing of names. * indistinguishable: Plural names. * Infact: Infix. * Infix: Compiler options. * Infix: Infix. * Infocom, Inc.: Credits. * Inform 5.2: Credits. * InfoTaskForce: Dirty tricks. * inheritance: Object definitions. * inheritance: Classes of objects. * initial possessions: The player. * internal errors: Internal and assembler errors. * interpreter: Z-machine. * interpreters: Dirty tricks. * inventories: Changing lists. * Invisiclues: Answer (invisiclues). * Invisiclues: Menus. * junior astronaut: Library entry points. * keyboard: Dirty tricks. * Kierkegaard, Søren: The player. * labels: Labels. * large memory: Memory settings. * Lebling, P. David: Containers. * Lebling, P. David: Vehicles. * Lebling, P. David: Doors. * Lewis, C. S.: Library. * Lewis, C. S.: Daemons. * lexicon: Basic ingredients. * library: Ingredients. * library routines: Library routines. * light and dark: Light. * light switch: Answer (light switch). * light switch: Global scope. * limitations: Limitations. * line of sight: Each turn. * listing objects: Lists. * little red car: Answer (the red car). * little red car: Vehicles. * local variables: Variables and arrays. * Long Count: Changing scope. * loop over every object: Control constructs. * low mist: Causing actions. * low numbers in French: Numbers. * MacNeice, Louis: Introduction. * making actions: New actions. * making attributes: Attributes. * making grammar: New actions. * making grammar: New verbs. * making properties: Properties. * map: Doors. * map: Map. * master catburglar: Library entry points. * matchbook: Changing lists. * Mayan directions: Answer (world colours). * Mayan directions: Room code. * me: The player. * medicine bottle: New actions. * memory consumption: Memory settings. * memory management: Memory settings. * memory map: Z-machine. * memory settings: Memory settings. * memory settings: Fatal errors. * memory size: Limitations. * menu of text options: Menus. * mid-air location: Each turn. * Molière: The parser. * moving the player: The player. * multiexcept token: Tokens. * multiheld token: Tokens. * myself: The player. * nagual: Answer (wayhel). * nagual: The player. * named rooms: Misc grammar. * named rooms: Answer (named rooms). * narrow inventory: Lists. * Nelson, Graham: Copyright. * NetHack: Dirty tricks. * normal rules: Causing actions. * normal rules: Library attributes. * notify verb: Score. * noun token: Tokens. * number token: Tokens. * number-parsing: Numbers. * numbers: Z-machine. * object definitions: Object definitions. * object errors: Errors. * Object syntax: Object definitions. * objects: Object tree. * objects in library: Library objects. * offers light: Light. * ogre with limited patience: Each turn. * out verb: Vehicles. * output streams: Dirty tricks. * oyster: New actions. * packed address: Z-machine. * Parker, Dorothy: Boxes. * Parser: Shell. * parser: The parser. * parser questions: Misc grammar. * parser speed: Errors. * parser tracing and levels: Trace. * parsing quoted strings: Misc grammar. * parsing quoted strings: Answer (quoted strings). * passing messages: New actions. * Pepper Room: Causing actions. * perfectly sensible: Properties. * Peter and Jane: Constants. * pinfocom: Dirty tricks. * plagiarism: Scoring. * plaster of paris: Vehicles. * player's origin: The player. * plural objects: Plural names. * Pope, Alexander: Daemons. * precedence: Classes of objects. * prime factorisations: Answer (primes). * prime factorisations: Control constructs. * printing commands: Printing commands. * proper noun: Printing of names. * properties: Simple objects. * properties: Properties. * properties in library: Library properties. * proportional font: Fixed font. * puff of garlic: Printing commands. * purloin verb: Debugging verbs. * purloin verb: Changing scope. * purloin verb: Answer (purloin verb). * questions: Changing scope. * questions (yes or no): Yes or no. * quoted text: Answer (quoted strings). * quotes off verb: Boxes. * radio: Each turn. * rainbows: Map. * real time: Dirty tricks. * Rees, Gareth: History. * reflecting the map: Answer (reflecting the map). * reflecting the map): Room code. * release number: Directives. * replacing grammar: Extending. * resurrection: The player. * returning from routines: Returning from routines. * reusing attributes: Attributes. * reverse video: Styles. * roman text: Styles. * routine errors: Errors. * routine tracing: Trace. * routines: Routines. * routines verb: Debugging verbs. * rucksack: Tokens. * run-time crashes: Crashes. * run-time format: Limitations. * running out of memory: Fatal errors. * rusty door: Doors. * scope: Scope rules. * score notification: Score. * score verb: Misc grammar. * scoring systems: Score. * screen tricks: Dirty tricks. * Seal, David: Credits. * searchlight: Switches. * see-through: Scope rules. * serial number: Directives. * Shakespeare, William: Scope. * Shakespeare, William: Answer (cherubim). * Shaw, George Bernard: Scope. * shazam verb: Misc grammar. * Shirley, James: Actions. * silver bars: Classes of objects. * Sloping Corridor: Map. * small array: Properties. * small memory: Memory settings. * snavig spell: The player. * Snow, C. P.: Light. * sound effect: Dirty tricks. * source-level debugger: Infix. * Space Invaders: Dirty tricks. * spaceship control panel: Misc grammar. * spaceship control panel: Answer (control panel). * special effects: Special effects. * special objects: Library objects. * special token: Tokens. * Spellbreaker cubes: Plural names. * Square Room: Map. * stack pointer: Variables and arrays. * Standard games: Z-machine. * Standard games: Compiler options. * Standard games: Dirty tricks. * Standard games: Warnings. * Standard games: Attributes. * Standard games: Object definitions. * Standard games: Limitations. * Standard games: Properties. * statistics: Compiler options. * status line: Status line. * status line: Crashes. * steel box: Basic containers. * steel grate: Doors. * story files: Inform. * stream: Each turn. * style of list: Lists. * sullen snake: Creatures. * switches (on command line): Compiler options. * sword: Each turn. * symbol names: Errors. * synonyms: New verbs. * take verb: New verbs. * Tartt, Donna: Actions. * tasty food: Object definitions. * team of four adventurers: The player. * teleportation: The player. * television set: Other containers. * television set: Answer (television set). * Tera: Credits. * Texinfo: History. * text style: Styles. * Thackray, Jonathan: Credits. * The Prisoner: Vehicles. * thief in `Zork I': About daemons. * thief in `Zork I': Answer (the thief). * thief in `Zork I': Each turn. * tidying-up operations: About daemons. * time of day: Each turn. * time sequence: Each turn. * timed input: Dirty tricks. * timers: Timers. * timers: Debugging verbs. * timers verb: Debugging verbs. * toffee apple: Causing actions. * token for `any object': Answer (purloin verb). * tokenisation: Answer (quoted strings). * tokens: Tokens. * toothed bag: Answer (toothed bag). * toothed bag: Basic containers. * trace verb: Debugging verbs. * tracing a routine: Trace. * tracing routines: Debugging verbs. * tracing the parser: Trace. * transcript: Dirty tricks. * treasure class: Classes of objects. * tree of objects: Object tree. * tree verb: Debugging verbs. * troll: Light. * two-way door: Doors. * txd (disassembler): Crashes. * types (lack of): Properties. * underlining: Styles. * undo verb: Limitations. * undo verb: Dirty tricks. * urchin and hacker: Creatures. * valuable cake: Classes of objects. * variable strings: Answer (reflecting directions). * variables: Variables and arrays. * vehicles: Vehicles. * VerbLib: Shell. * version 6: Limitations. * very last resort: Replace. * vocabulary size: Limitations. * VT100: Styles. * walls: Library objects. * wandering monsters: About daemons. * warnings: Warnings. * warthog: Answer (wayhel). * weights: Answer (weights). * weights: About daemons. * weird thing: Naming of names. * what is a grue: Changing scope. * wide inventory: Lists. * Wittgenstein, Ludwig: The parser. * Wittgenstein, Ludwig: Creatures. * woodpecker: Debugging. * Woods, Don: Credits. * world colours: Answer (world colours). * world colours: Room code. * xyzzy verb: New verbs. * Z-machine: Z-machine. * zero: Numbers. * Zip: Dirty tricks. * Zip: Crashes. * Zip: Infix. * zterp: Dirty tricks. H Index of functions, properties, attributes etc ************************************************ * Menu: * *: Trace. * *: Routines. * ++: Errors. * -: Errors. * ->: Variables and arrays. * ->: Variables and arrays. * ->: New verbs. * @@: Quoted strings. * @nn: Answer (reflecting directions). * absent: Library attributes. * Achieved: Library routines. * Achieved(task): Score. * actor: Global scope. * additive: Properties. * additive: Classes of objects. * after: Simple objects. * after: Library properties. * after: Library actions. * after: Room code. * AfterLife: Library entry points. * AfterLife: The player. * alias: Errors. * alias: Properties. * alias: Attributes. * AllowPushDir: Library routines. * AllowPushDir: Vehicles. * ALWAYS_BIT: Lists. * Amusing: Library entry points. * AMUSING_PROVIDED: Amusing. * AMUSING_PROVIDED: Library constants. * animate: Library attributes. * ANIMA_PE: Parser errors. * Answer: Creatures. * aread: Dirty tricks. * article: Printing of names. * article: Library properties. * Ask: Creatures. * ASKSCOPE_PE: Parser errors. * Attack: Creatures. * Attribute: Attributes. * autosearch: Library attributes. * beep: Dirty tricks. * before: Library properties. * before: Adding code. * Blorple: New actions. * box: Boxes. * break: Control constructs. * buffer_mode: Dirty tricks. * CANTSEE_PE: Parser errors. * cant_go: Map. * cant_go: Library properties. * capacity: Basic containers. * capacity: Library properties. * capacity: The player. * CDefArt: Library routines. * CDefArt(obj): Printing of names. * ChangePlayer: Library routines. * ChangePlayer: The player. * child: Crashes. * child: Object tree. * children: Crashes. * class: Object definitions. * Class: Object definitions. * class: Classes of objects. * clothing: Library attributes. * compass: Library objects. * concealed: Scope rules. * concealed: Library attributes. * container: Basic containers. * container: Library attributes. * daemon: About daemons. * daemon: Library properties. * DarkToDark: Library entry points. * DarkToDark: Light. * data: Variables and arrays. * deadflag: The player. * deadflag: Adding code. * DeathMessage: The player. * DeathMessage: Library entry points. * DEBUG: Library constants. * DEBUG: Basic ingredients. * DEBUG: Debugging verbs. * DefArt: Library routines. * DefArt(obj): Printing of names. * DEFART_BIT: Lists. * describe: Library properties. * describe: Object definitions. * description: Library properties. * direction: Library attributes. * DoMenu: Library routines. * DoMenu: Menus. * door: Doors. * door: Library attributes. * door_dir: Doors. * door_dir: Doors. * door_dir: Library properties. * door_to: Library properties. * door_to: Doors. * d_obj: Library objects. * each_turn: Library properties. * each_turn: Each turn. * edible: Library attributes. * elder: Object tree. * eldest: Object tree. * EnglishNumber: Library routines. * ENGLISH_BIT: Lists. * Enter: Doors. * enterable: Library attributes. * enterable: Vehicles. * erase_window: Dirty tricks. * et_flag: Each turn. * EXCEPT_PE: Parser errors. * Extend: Extending. * e_obj: Library objects. * Fake Action: New actions. * female: Library attributes. * first: Extending. * font: Fixed font. * for: Control constructs. * found_in: Library properties. * found_in: Properties. * found_in: Causing actions. * FULLINV_BIT: Lists. * GamePostRoutine: Library entry points. * GamePostRoutine: New actions. * GamePreRoutine: Library entry points. * GamePreRoutine: New actions. * general: Library attributes. * Give: Creatures. * give: Attributes. * Global: Variables and arrays. * Go: Room code. * Go: Vehicles. * has: Object definitions. * has: Attributes. * HasLightSource: Library routines. * HasLightSource(object): Light. * Headline: Library constants. * if: Blocks of code. * InDefArt: Library routines. * InDefArt(obj): Printing of names. * INDENT_BIT: Lists. * indirect: Crashes. * initial: Library properties. * initial: Simple objects. * initial: Library properties. * initial: Variables and arrays. * Initialise: The player. * Initialise: Shell. * Initialise: Library entry points. * Initialise: Each turn. * Initialise: Library entry points. * initstr: Variables and arrays. * initstr: Variables and arrays. * InScope: Library entry points. * Insert: Basic containers. * invent: Changing lists. * invent: Library properties. * inventory_stage: Changing lists. * in_obj: Library objects. * ITGONE_PE: Parser errors. * jump: Labels. * JUNKAFTER_PE: Parser errors. * Kiss: Creatures. * last: Extending. * LetGo: Basic containers. * life: Creatures. * life: Library properties. * light: Library attributes. * light: Attributes. * location: The player. * lockable: Library attributes. * locked: Library attributes. * locked: Basic containers. * long: Properties. * LookRoutine: Library entry points. * Lowstring: Answer (reflecting directions). * MAX_CARRIED: Library constants. * MAX_CARRIED: Max carried. * MAX_SCORE: Library constants. * MAX_SCORE: Score. * MAX_SCORES: Score. * MAX_TIMERS: Library constants. * MAX_TIMERS: Timers. * meta: Library actions. * meta: Misc grammar. * MMULTI_PE: Parser errors. * move: Object tree. * moved: Library attributes. * moved: Global scope. * MULTI_PE: Parser errors. * name: Object definitions. * name: Naming of names. * name: Object definitions. * name: Library properties. * Nearby: Object definitions. * NEWLINE_BIT: Lists. * NewRoom: Library entry points. * NextWord: Numbers. * NextWord: Library routines. * ne_obj: Library objects. * NOTHELD_PE: Parser errors. * nothing: Object tree. * nothing: Crashes. * NOTHING_PE: Parser errors. * notify_mode: Score. * number: Library properties. * NUMBER_PE: Parser errors. * NUMBER_TASKS: Library constants. * NUMBER_TASKS: Score. * NUMBER_TASKS: Score. * nw_obj: Library objects. * n_obj: Library objects. * Object: Object definitions. * objectloop: Control constructs. * OBJECT_SCORE: Library constants. * OBJECT_SCORE: Score. * OBJECT_SCORE: Score. * OffersLight: Library routines. * OffersLight(object): Light. * on: Switches. * on: Library attributes. * open: Library attributes. * open: Basic containers. * openable: Basic containers. * openable: Library attributes. * or: Conditions. * Order: Creatures. * out_obj: Library objects. * parent: Crashes. * parent: Object tree. * parsed_number: Numbers. * ParseNumber: Library entry points. * ParseNumber: Numbers. * ParserError: Library entry points. * ParserError: Parser errors. * parser_action: Plural names. * parser_one: Plural names. * parser_two: Plural names. * parse_name: Plural names. * parse_name: Naming of names. * parse_name: Library properties. * PARTINV_BIT: Lists. * PlaceInScope: Changing scope. * PlaceInScope: Library routines. * PlayerTo: The player. * PlayerTo: Crashes. * PlayerTo: Library routines. * plural: Plural names. * plural: Library properties. * print: Room code. * print object: Printing of names. * print object: Crashes. * PrintRank: Library entry points. * PrintRank: Score. * PrintShortName: Library routines. * PrintShortName(obj): Printing of names. * PrintTaskName: Library entry points. * PrintVerb: Misc grammar. * PrintVerb: Library entry points. * print_addr: Crashes. * print_paddr: Crashes. * proper: Printing of names. * proper: Library attributes. * Property: Properties. * PushDir: Vehicles. * random: Crashes. * read_char: Dirty tricks. * Receive: Basic containers. * RECURSE_BIT: Lists. * Remove: Basic containers. * Replace: Replace. * replace: Extending. * ROOM_SCORE: Score. * ROOM_SCORE: Score. * ROOM_SCORE: Library constants. * SACK_OBJECT: Library constants. * SACK_OBJECT: Max carried. * scenery: Library attributes. * scenery: Map. * SCENERY_PE: Parser errors. * ScopeWithin: Library routines. * ScopeWithin: Library routines. * scope_stage: Changing scope. * scored: Library attributes. * Search: Library actions. * self: Causing actions. * selfobj: Library objects. * selfobj: Control constructs. * SetTime: Each turn. * SetTime: Library routines. * set_cursor: Dirty tricks. * set_window: Dirty tricks. * se_obj: Library objects. * short_name: Naming of names. * short_name: Library properties. * Show: Creatures. * sibling: Object tree. * sp: Variables and arrays. * special_number: Creatures. * special_word: Creatures. * split_window: Dirty tricks. * StartDaemon: Library routines. * StartDaemon: About daemons. * StartTimer: Library routines. * StartTimer: Timers. * static: Library attributes. * static: Doors. * Statusline: Each turn. * StopDaemon: About daemons. * StopDaemon: Library routines. * StopTimer: Timers. * StopTimer: Library routines. * Story: Library constants. * string: Variables and arrays. * String: Answer (reflecting directions). * STUCK_PE: Parser errors. * style: Styles. * supporter: Library attributes. * supporter: Basic containers. * supporter: Vehicles. * switchable: Library attributes. * switchable: Switches. * sw_obj: Library objects. * sw__var: Object definitions. * s_obj: Library objects. * talkable: Library attributes. * TASKS_PROVIDED: Score. * TASKS_PROVIDED: Library constants. * task_scores: Score. * TERSE_BIT: Lists. * thedark: The player. * thedark: Library objects. * TheSame: Plural names. * the_time: Each turn. * ThrowAt: Creatures. * ThrowAt: Creatures. * TimePasses: Library entry points. * time_left: Library properties. * time_out: Library properties. * time_out: Timers. * TOOFEW_PE: Parser errors. * TOOLIT_PE: Parser errors. * top_object: Control constructs. * transparent: Other containers. * transparent: Creatures. * transparent: Library attributes. * transparent: Basic containers. * TryNumber: Library routines. * UnknownVerb: Misc grammar. * UnknownVerb: Library entry points. * UPTO_PE: Parser errors. * u_obj: Library objects. * VAGUE_PE: Parser errors. * Verb: New verbs. * VERB_PE: Parser errors. * visited: Library attributes. * when_closed: Doors. * when_closed: Library properties. * when_off: Library properties. * when_on: Library properties. * when_open: Library properties. * when_open: Doors. * with: Object definitions. * with_key: Library properties. * with_key: Lockable containers. * wn: Numbers. * workflag: Library attributes. * WORKFLAG_BIT: Lists. * worn: Library attributes. * WriteListFrom: Library routines. * WriteListFrom: Lists. * w_obj: Library objects. * YesOrNo: Library routines. * YesOrNo: Yes or no. * younger: Object tree. * youngest: Object tree.