NAME

ifm -- Interactive Fiction mapper


SYNOPSIS

ifm [options] [file]


DESCRIPTION

IFM is a language for keeping track of your progress through an Interactive Fiction game, and a program for producing various different sorts of output using it. You can record each room you visit and its relation to other rooms, the initial locations of useful items you find, and the tasks you need to perform in order to solve the game.

The IFM mapping commands are designed so that you can easily add to the map as you explore the game. You type in the rooms you visit and the directions you move in to reach other rooms, and IFM calculates the position of each room in relation to the others. A map can consist of several independent sections, allowing you to divide up the map however you like. See MAKING MAPS for an example of how to make a map.

The IFM task commands, if used, allow you to specify the order in which game-solving tasks must be done. The IFM program can then calculate and print a high-level 'walkthrough' of the game. See SOLVING THE GAME for examples of how to do this.

Several output formats are available, including PostScript and ASCII text -- see OUTPUT FORMATS. Some of the output formats have a set of variables which control the appearance of the output -- see Output Variables.


OPERATION

On startup, IFM does the following. Firstly, the system initialization file is read. This sets defaults used by everyone. This file is called ifm-init.ifm, and is found by searching a standard set of directories. You can override this search path by setting the environment variable IFMPATH to be a colon-separated list of directories.

Then, if you have a personal initialization file, that is read. This file is found in the directory specified by your HOME environment variable. On Unix systems it is called .ifmrc, and on Win32 systems it is called ifm.ini. You can use this file to customize the default variable settings for different types of output.

Then input from the file(s) on the command-line is read. If no files were specified, stdin is read. A filename equal to - also indicates that stdin should be read at that point.

If any of the -map, -items, -tasks or -show options was specified, the appropriate output is produced. If not, only a syntax check of the input is done.

When producing output, the output format specified by the -format option is used. If this was not specified, the first format in the list which supports this type of output is chosen.

Some of the output formats use additional library files to do their work. For example, the PostScript output format adds a standard ``prolog'' file to all output. These files are found using the same search path as the system initialization file (see above).

Here's a list of the command-line options (which can be abbreviated):

-m, -map
Draw a map of the game.

-i, -items
Print a list of items which appear in the game.

-t, -tasks
Print a list of tasks required to solve the game.

-f, -format format
Specify the output format (see OUTPUT FORMATS).

-o, -output file
Write to the specified file, instead of stdout.

-w, -nowarn
Don't print warnings.

-d, -debug
Print debugging information.

-s, -show type
Show one of several types of information, and exit. See below for a list of valid arguments.

-I, -include dir
Prepend the specified directory to the library and include file search path. This option may be repeated.

-noinit
Don't read your personal init file.

-version
Print the program version.

-help
Just print some usage information.

Here's a list of the -show options (which can be abbreviated):

path
Show the directories that are searched for library and include files.

vars
Show a complete list of defined variables, in a format suitable for feeding back into IFM. See Variables.


MAKING MAPS

This section gives you a tour of the main commands for making maps. It's not complete -- see LANGUAGE for a full list of commands.


Introduction to Maps

The traditional Infocom-style way of drawing Interactive Fiction maps is the 'boxes-and-lines' method. For example:

    +--------------+   +--------------+   +--------------+
    |              |   |              |   |              |
    |    Kitchen   |---|    Lounge    |   |     Study    |
    |              |   |              |   |              |
    +--------------+   +--------------+   +--------------+
            |       \          |                  |
            |        \         |                  |
            |         \        |                  |
    +--------------+   +--------------+           |
    |              |   |              |           |
    |    Garage    |   |  Dining Room |-----------+
    |              |   |              |
    +--------------+   +--------------+

This is the style of map that IFM produces. Rooms are represented as boxes on a square grid, and links between the rooms are drawn as lines connecting them. Links emanate from rooms in any of the eight standard compass directions, and also follow the grid.

In the following sections, we'll introduce the IFM commands used to draw the example map above.


Adding Rooms

To draw the example map from the previous section, you first choose an arbitrary start location: the kitchen (when mapping a real game, you'd choose your actual start location). To add the kitchen, just type this:

    room "Kitchen";

Now you're in the kitchen. Suppose, if this were a real game, that you first went south to explore the garage. That can be added to the map like this:

    room "Garage" dir south;

Now you've said which way you went to get to the garage, and since you were in the kitchen, IFM knows that the garage is south of the kitchen. By the way, 'south' can be abbreviated 's' (and similarly for all other directions), just like in the games.

Ok, you're in the garage. Unfortunately, that's a dead end and you have to retrace your steps to the kitchen. You've already mapped that, so there's no need to do anything. Now you head off east to the lounge. Now, you're moving from the kitchen again but IFM thinks you're in the garage (IFM's idea of 'where you are' is always the last room mentioned). You need a way of referring to the kitchen again -- to do that, you add a 'tag' to it by changing the 'kitchen' line like this:

    room "Kitchen" tag Kitchen;

The tag name can be any name you like. You might think that you could refer to the kitchen by using the name in quotes, but that would mean you could never have two distinct rooms with the same name. Another advantage of tags is that they can be much shorter than the real room names. The tag 'K' would be just as valid in the example above (though not as readable).

Now you can refer to the kitchen by its tag, so you can move east from it into the lounge like this:

    room "Lounge" dir e from Kitchen;

The 'from' clause tells IFM where you're moving from. If it's omitted, it assumes you're moving from the last room mentioned.

Continuing your exploration, you move south into the dining room:

    room "Dining Room" dir s;

You exit the dining room to the east, and turn a corner north before entering the study. How can you represent the corner faithfully on the map? Like this:

    room "Study" dir e n;

This says that you move east, then north, to get to the study. Now, what if someone locked the study door behind you and the only way out was through the window? That's a one-way trip into the study, which you can indicate using the 'oneway' attribute like this:

    room "Study" dir e n oneway;

This is indicated on the map by an arrow.


Adding Links

The map as it stands is not complete -- the diagonal link between the kitchen and the dining room is missing (because you didn't go that way in visiting the kitchen or the dining room). To add it, you need to modify the dining room command like this:

    room "Dining Room" dir s link Kitchen;

The 'link' clause creates a straight-line link between the current room and the room with the specified tag name (in this case, the kitchen).

Note that if this link needed to turn corners, as in the study example above, then that method of linking the rooms wouldn't have worked. In that case, you'd have to use the stand-alone 'link' command. For example:

    link Diner to Kitchen dir n nw;

This assumes you've given the dining room the tag name 'Diner'. The link starts off going north, then turns northwest, and finally goes toward the kitchen. Note that in a 'link' command, if you omit the final direction which leads to the linked room, it is added automatically.


Other Directions

Suppose that there were steps down from the kitchen into the garage, and that you wanted to indicate that you could up or down as well. You could do that using the 'go' clause, like this:

    room "Garage" dir s go down;

This indicates that the actual direction travelled is downwards, but it is still represented as south on the map. The 'go' clause accepts 'up', 'down', 'in' and 'out'. As with compass directions, 'up' and 'down' may be abbreviated as 'u' and 'd'.


Room Exits

At various points in a game, you arrive in a room with many directions to explore. It is useful to be able to mark some of these directions as unexplored, so that you can come back and explore them later. You could mark these by creating dummy rooms in those directions, but this is tedious. Alternatively, you can use the 'exit' clause, like this:

    room "Dining Room" dir s exit nw e;

This says that there are two unexplored exits from this room, in the northwest and east directions. When a map is drawn, this fact will be displayed by a small line poking out of the room in those directions.

When you come to actually explore those directions, and add links to new rooms, the corresponding room exit markers will no longer be drawn. So you can leave the exit clauses in if you want.


Map Sections

In IFM, rooms are divided into groups called 'map sections'. Each room in a map section has an explicit spatial relationship to all the other rooms in that section. A room which is obtained by moving via a 'dir' clause from a previous room is on the same map section as the previous room, since its co-ordinates can be calculated relative to it.

There are several reasons why it might be a good idea to split a game map into different sections:

IFM manages independent map sections automatically, by deciding which rooms are on which section. No special command is needed to start a new map section -- simply define a room which has no connection to any previous room, by leaving out the 'dir' clause (note that that's how the kitchen starts out, in the example).

Rooms on different map sections are completely separate, and you may not link them via the 'link' command. However, you can indicate where a room on one section is connected to a room on another, using the 'join' command:

    join Room1 to Room2;

As usual, 'Room1' and 'Room2' are tag names. You can also use 'join' as a clause in a room command (usually done with the room starting in a new section):

    room "Basement" join Ground_Floor;

The 'joined' status of the two rooms is indicated on the map, the exact manner depending on the output format.

Each map section can be given a title using the 'map' command, like this:

    map "Kensington Gardens";

This names the next map section that hasn't been named. Note that you should have as many 'map' commands as you have map sections, although this isn't enforced -- any names that are missing will be assigned default names, and extra names will be ignored. It's conventional to give a 'map' command just before starting a new map section.

In rare circumstances (e.g. a three-dimensional maze) you may need to have rooms on the same map section which are not connected to each other. The room 'dir' clause creates an implicit link from the previous room by default, but you can stop this from happening by using the 'nolink' attribute. As a trivial example:

    room "One Side of Wall" tag this_side;
    room "Other Side of Wall" dir e nolink tag other_side;

    room "Underground Passage" tag passage_1;
    room "Underground Passage" tag passage_2 dir e;

    join this_side to passage_1 go down;
    join passage_2 to other_side go up;

In this example, there are two map sections: above ground, and below ground. But the two above-ground rooms are not connected directly.


Adding Items

As well as rooms, IFM can indicate the initial rooms of various items found in the game. To add an item, use the 'item' command like this:

    item "Spoon" in Kitchen;

The 'in' clause can be omitted, and then the room defaults to the last room mentioned. You can also add an arbitrary note to each item (e.g. to remind you what it's for) using the 'note' attribute:

    item "Spoon" in Kitchen note "Stirs tea";

Here's the completed map description for the above example, with a few other items thrown in:

    # Example map.

    room "Kitchen" tag Kitchen;
    item "spoon" note "Stirs tea";
    item "sink";
    item "monkey";

    room "Garage" dir s go down;
    item "monkey wrench" note "For wrenching monkey out of sink";

    room "Lounge" dir e from Kitchen;
    item "TV set";

    room "Dining Room" dir s link Kitchen;
    item "table";
    item "chair";

    room "Study" dir e n oneway;

See Map Output for how to display it.


Conflicts

After creating a map from a real game and sending the results through IFM, you may get warnings which talk about things overlapping. This is due to two rooms, or a room and a link, wanting to occupy the same space on the map. There are several ways that this could occur:


SOLVING THE GAME

As well as making a map of your game, IFM can be used to record the steps you took to solve it. The IFM program can then calculate a (fairly) optimal solution. This section is a guide to how to do it. Again, it's not a complete specification -- see LANGUAGE for that.


Introduction to Tasks

The basic game-solving action is called a task in IFM-speak. To introduce a task, you use the 'task' command, like this:

    task "Put the fluffy bunny in the incinerator";

Most tasks need to be done in a certain room. The default is that a task must be done in the last room mentioned. You can change that by using the 'in' clause, just as for items. Some tasks can be done anywhere -- you can say 'in any' to indicate that. As usual, you can add tags to tasks in order to refer to them later.

The game solver in IFM divides tasks into two fundamental types: 'safe' and 'unsafe'. An unsafe task is one that, if done, might get you into a position where solving the game is impossible. The game solver avoids doing unsafe tasks until it really has to.


Requiring Tasks

A lot of tasks require a previous task to be done first. To say this, you use the 'after' clause:

    task "Press incinerator start switch" tag press_switch;
    ...
    task "Put the fluffy bunny in the incinerator" after press_switch;

As a shortcut, to avoid having to tag many tasks, you can say 'after last' to indicate the last task mentioned. For example, if the two tasks above could be done in the same room, you could say

    task "Press incinerator start switch" tag press_switch;
    task "Put the fluffy bunny in the incinerator" after last;

Alternatively, you could merge the two into a single task -- the simplicity or complexity of tasks is up to you.

The 'after' clause only says that a task will come after another -- it doesn't specify how soon after. But in some situations it is essential that a task immediately follows a specific previous task, without deviation. You can use the task 'follow' clause to specify this. For example:

    room "Mission Control" ...;
    task "Activate launch sequence" tag activate;
    ...
    room "Rocket Cabin" ...;
    task "Fasten seat belt" follow activate;

The 'follow' clause creates a chain of tasks that must be done one after the other. The game solver will not attempt the first task in the chain until it knows that all the tasks are possible (i.e. all the prerequisites for each task in the chain are satisfied). Also, if one of the tasks in the chain is unsafe, then all previous tasks in the chain are also marked unsafe.

Of course, you can only have a single task in a 'follow' clause -- the immediately preceding task. It is an error for two or more tasks to try to immediately follow the same task.


Requiring Items

For a lot of tasks, you need to have one or more items in your possession. You can indicate this by using the 'need' clause, like this:

    task "Put the fluffy bunny in the incinerator" need bunny;

Here, 'bunny' is the tag name of the corresponding item. You can list more than one item tag -- e.g. 'need bunny asbestos_gloves'.

Note that you don't need to add tasks to get required items yourself -- the game solver does that automatically. It knows it has to get all the items which appear in 'need' clauses.


Obtaining Items

Some tasks need to be done before you can get an item. One way to indicate this is with the task 'get' clause:

    task "Put money in drinks machine" need coin get lemonade;

This naturally implies that all tasks which supply an item (via the 'get' clause) must be done before any task which needs that item.

An alternate way to phrase this is with the item 'after' clause, which says that the item can't be picked up until a specified task is done. This is a common combination in IFM:

    task "Put money in drinks machine" need coin;
    item "lemonade" hidden after last;

Some items are only available before doing a certain task. You can use the 'before' clause to say that:

    item "precious diamond" before trigger_alarm;

Some items can only be picked up if you're already carrying another -- use the 'need' clause for that:

    item "hot coal" need tongs;

Sometimes, doing a task not only allows you to get an item, but also puts it in your inventory. You can say that using the 'give' clause:

    task "Buy beer" need money give beer_mug;

Note that 'give' overrides all other restrictions placed on getting items.


Losing Items

Some tasks cause items to be destroyed. You can say that with the 'lose' clause:

    task "Light bonfire" need match lose match;

This naturally implies that all other tasks which need the item must be done before the task that destroys it. A 'drop' task is never generated for items that are lost in this way.

Incidentally, you can use the special tag 'it' to refer to the last room, item or task tag name within a command. So the previous example could also have been written

    task "Light bonfire" need match lose it;


Dropping Items

Normally, you don't have to add explicit drop-item tasks -- IFM knows when an item is no longer useful, and drops it automatically. But sometimes items need to be dropped only temporarily. You can do that using the 'drop' clause:

    task "Throw spear at tree stump" need spear drop it tag target_practice;

In this example, the spear is dropped in the same room that the task was done in. If you ever need the spear for anything else, it will be picked up by the game solver. Note that an item will only be dropped if it is being carried -- mentioning an item in a 'drop' clause does not imply that it's needed to do the task.

Sometimes items must be dropped in a different room to the one you're in. You can use the 'in' clause to modify things:

    room "Top of Chute";
    task "Put laundry in chute" need laundry drop it in Bottom_of_Chute;

In other cases, you need to drop all the items you're carrying, or all except certain items. You can use 'drop all' and 'drop all except' to say that.

Normally, if an item is dropped but is needed again for some other task, there is nothing to stop the game solver picking it up again (provided there's a path to the room the item was dropped in). But sometimes you need to drop an item and not pick it up again until you've done something else. You can use the 'until' clause to say that:

    task "Put coin in slot" give chocolate drop coin until open_machine;

A task which drops items will be marked unsafe if there is no path back to the dropped items.


Leaving Items

There are some situations where your movement is blocked if you are carrying particular items. You can use the 'leave' attribute of rooms, links and joins to specify a list of items that must be left behind before using them. For example:

    room "Bottom of Canyon";
    item "heavy boulder" tag boulder;

    room "Top of Canyon" dir n go up leave boulder;

If the 'leave' clause appears before the 'dir' clause, that means the items must be dropped before entering the room (from any direction). It is generally the case that, if an attribute could apply to a room or its implicit link with the previous one, its position relative to the 'dir' clause is what decides it.

You can also say 'leave all', which means that you must leave all the items you're currently carrying, and 'leave all except', which omits certain items from being left behind.

When finding a solution, the game solver will carry items until it is forced to drop them. If the dropped items are needed later, the game solver will try to come back and get them. If it is trying to do a task which requires items, it will choose a route to get to the task room which doesn't involve dropping any of the needed items.


Limiting Movement

Sometimes an item is required, or a task needs to be done, before movement in a certain direction is possible. To represent this, you can give 'need' and 'after' clauses to rooms, links and joins. For example:

    ...
    room "Cemetery" dir s from winding_path;
    task "Unlock the iron door" need rusty_key;
    room "Crypt" dir s go down after last;
    ...

Here's another example:

    ...
    room "Dimly-lit Passage" dir e;
    room "Dark Passage" dir e need candle;
    ...

In this case it is the link between the two rooms that is blocked off until the candle is obtained. If the 'dir' clause had appeared before the 'need' clause, the restriction would apply to the room itself (i.e. no entering the room from any direction without the candle).

In some cases, doing a task closes off a room, link or join so that it can't be used any more. You can use the 'before' clause to indicate this:

    room "Bank Vault" tag Vault;
    room "Bank Entrance" tag Entrance dir e before trigger_alarm;

All tasks which close things off like this are marked unsafe, since they could block off a crucial path through the game.

Sometimes in a game there is the situation where a path is closed off and, later on in the game, reopened again. A single link or join can't represent this. However, there's nothing to stop you from using two or more joins between the same rooms. If you mark them with the 'hidden' attribute, they won't appear on the map either. For example, this line could be added to the previous example to provide an escape route:

    join Vault to Entrance go e after disable_alarm hidden;


Movement Tasks

There are several different ways of moving around in a game. The usual way is to say the direction you want to go in. Another way is to do something else which results in movement. A good example is the magic word XYZZY from Colossal Cave. It acts exactly like a movement command, in that you can use it again and again and it moves you somewhere predictable. The best way to represent this in IFM is to use a join to connect the two rooms, and specify the command used to do the movement via the 'cmd' clause, like this:

    join Debris_Room to Building after examine_wall cmd "XYZZY";

Yet another way of moving around is a one-off event that 'teleports' you to a different room. You can indicate that this happens using the task 'goto' clause, and supplying the tag name of the destination room. For example:

    task "Get captured by goblins" goto Dungeon;

As soon as the task is done, you 'teleport' to the new location -- no intervening rooms are visited. Note that because each task is only done once, this method of travel can only be used once. Note also that the 'drop' and 'leave' actions are done before teleporting you to the new location (so if you drop items in the 'current room', you will be teleported away from the dropped items).


Scoring Points

Many games have some sort of scoring system, whereby you get points for doing various things. In IFM you can record this using the 'score <integer>' clause, which can apply to rooms, items or tasks. For rooms, it's the score you get when visiting it for the first time. For items, it's the score for first picking it up. For tasks, it's the score for doing that task.

If an item has a score, but is given to the player via a task 'give' clause, then its score is added to the score for that task instead. If an item has a score and also has the (obsolete) 'given' attribute, the score won't be registered at all. So don't use 'given' -- use the task 'give' clause instead.


Finishing the Game

Usually a game finishes when you complete some final task. You can indicate which task this is using the 'finish' attribute. This attribute can attach to rooms, items or tasks, giving three different types of finish condition: entering a room, picking up an object or doing a task. If the game solver ever manages to do something which is flagged with the 'finish' attribute, it considers the game solved and stops. Any extra things left to do will not be done, even if they score points.


Finding a Solution

Here's what the game solver does in order to come up with a solution to the game. First, extra internal tasks are generated. These are tasks to (a) get items which are scored, (b) get items which are required for explicitly-mentioned tasks to be done, (c) get items which are needed to go in certain directions, and (d) go to rooms which are scored.

Next, all the rooms are connected using their links and joins. This means that for each room, a list is made of all other rooms reachable in one move. Note that it is possible for some rooms to be unreachable -- for example, all rooms in a section where there is no 'join' to rooms on other sections.

Then the task dependencies are calculated. A dependency is where one task must be done before another. The task dependencies are examined to see if there are any cycles -- that is, chains of tasks where each one must be done before the next, and the last must be done before the first. If there are any, then the game is unsolvable, since none of the tasks in a cycle can be done.

If there are no cyclic dependencies, the task list is 'traversed' to find a sequence which solves the game while satisfying the task dependencies. The start room is the room which was first mentioned in the input (but this can be changed -- see LANGUAGE). While there are tasks left in the task list, the following steps are performed:

  1. The map is traversed to find the distances of all rooms from the current room. Then the task list is sorted in order of ascending distance of the rooms they must be done in. Tasks which can be done in any room count as having distance zero.

  2. Scan the sorted task list to find the nearest possible task. For a task to be possible, the player must (a) have all required items, (b) have done all required previous tasks, and (c) be able to get from the current room to the task room via a path which doesn't require items not yet collected, or tasks not yet done, or which involves dropping needed items on the way.

    Priority is given to safe tasks. For a task to be safe, it must (a) not have previously been marked unsafe (e.g. because it closes off map connections) and (b) there must be a return path from the task room back to the current one. The second condition is to avoid taking a one-way trip before preparing properly.

    If there are any safe tasks, the nearest one will be done next regardless of how close an unsafe task is. If there are no safe task, the nearest unsafe task will be chosen.

  3. If there was a possible task, do it and remove it from the list. Move to the room the task was done in (if any). If not, then the game is unsolvable. Give up.

The game solver automatically keeps track of which items the player is carrying, and knows when an item will no longer be needed -- that's when all tasks requiring that item have been done. When that happens, a 'drop' task for the item is done. An exception is items which are needed to move in a certain direction (see the last example above). In that case the item is never dropped, since the game solver can't rule out having to go that way again.

Some items, although they have no direct game-solving function, need to be carried throughout the game -- e.g. some sort of container for carrying other things. You can mark such items with the 'keep' attribute -- then they will never be dropped.


Tweaking the Solution

There will be some situations (quite a few, actually) where the game solver doesn't do things the way you want it to. This section gives a few tips, and some new keywords, for modifying things.

Making things safe

Firstly, the game solver is completely paranoid. It has to be, because it doesn't do any lookahead past the current task. It won't do anything unsafe (e.g. go to a room to do a task when there's no immediate return journey) unless there's nothing safe left to do. It will quite happily plod halfway across the map to pick something up rather than do something a bit scary in the next room.

However, you can reassure it with the task 'safe' attribute. Adding this to a task tells the solver that this task is safe, regardless of what it thinks. So if you know that a one-way trip can eventually be returned from, by doing other tasks, you can stop the solver from avoiding it. But bear in mind that by doing this you are taking full responsibility if the solver gets stuck.

Changing path lengths

Another thing the solver doesn't know about is how easy or difficult it is to get from place to place on the map. Suppose you're in a game which is on two levels separated by a tiresome set of access doors with ID cards. The connection between the levels may only be two rooms on the map, but it's a lot more in terms of typing. You can avoid unnecessary trips through these doors by artificially changing the 'length' of the connection between levels, by using the 'length' attribute of links and joins:

    room "Level A" tag LA ...;
    room "Level B" tag LB dir e length 50;

In this way, by choosing an appropriate number for the length, you make it appear to the solver that all the rooms in level A are closer to each other than any of the rooms in level B. This means that priority will be given to tasks in rooms in the same level as you are now, (hopefully) minimizing the number of level changes. Note that the 'length' attribute doesn't affect map drawing at all.

Closing off paths

There may be times when you want a map connection to appear on the map, but not be used in solving the game -- for example, it may be certain death to go that way. You can use the 'nopath' attribute of rooms, links and joins to indicate this. It doesn't affect map output in any way.

Another use for this attribute is to force the game solver to do things in a different order. This might be preferable to adding fake task dependencies.

Keeping fixes together

It's probably best to keep all your 'game tweaks' together, separate from the 'pure' game, and commented appropriately. You can do this by using commands which just modify existing objects, instead of creating new ones, by referring to their tags. As an example, suppose you have the following situation:

    room "Top of Chute" ...;
    room "Bottom of Chute" dir s go down oneway;
    task "Do something weird" tag weird_task;
    ...

Suppose you're at the top of the chute, and that there's some stuff to be done at the bottom, but no immediate way back up. As usual, the game solver balks at taking a one-way trip and will do anything to avoid it. But suppose you know that, as long as you have your giant inflatable cheeseburger, you can get back out again. You can say:

    # Bottom of chute isn't that scary.
    task weird_task need burger safe;

which modifies the task at the bottom of the chute to (a) require the burger (so that you won't go down there without it), and (b) be considered safe by the game solver. So it will happily slide down the chute without getting stuck at the bottom.

This way of modifying previous objects applies all types of object, even links and joins -- these can be tagged too, in the normal way. The single exception is the implicit link created by the room 'dir' clause. These links automatically get tagged when the room does, and with the same name. So the two-level example above could be split into:

    room "Level A" tag LA ...;
    room "Level B" tag LB dir e;

    ...

    # Stop gratuitous travel between levels.
    link LB length 50;

Debugging

Finally... you can gain an insight into what the game solver's up to by using the -debug command-line option. This produces reams of output giving details of the game solver's thoughts before it does anything. It's supposed to be self-explanatory, but my view is slightly biased. Detailed documentation may follow (a) if enough people ask for it, and (b) when I get around to it.


Limitations

Given the wild imaginations of today's IF authors, there are bound to be some game solving situations that can't easily be dealt with using IFM. Some of the things that IFM ignores are:

There are some other limitations that are the result of certain keyword combinations in the current implementation of IFM. These are fixable, and hopefully will be in a later version. They are:


OUTPUT FORMATS

IFM has three different types of output (a map, a list of items, and a list of tasks) and several different output formats. Not all types of output are produced by each output format.


Map Output

Maps are drawn in the classic Infocom style. Rooms are drawn as boxes, and links as are drawn as lines (oneway with an arrow). Joins are marked by adding a 'join number' to the room text of the joined rooms. Non-hidden items can be shown in their initial rooms.

The following output formats produce maps:

PostScript (ps)
Several map sections may be printed per page. Automatic packing is done to try to get a good fit on the page. Also, portrait or landscape is chosen depending on whichever gives the best fit. Fonts of room and item text are scaled to fit them in the room boxes, if required.

To view the PostScript file on-screen, you'll need a PostScript previewer. I recommend Ghostview, which is free and is available for most systems. This can also print the map on your printer, even if the printer doesn't normally print PostScript files.

Tk commands (tk)
This produces commands for input to tkifm, a simple graphical interface to IFM. It isn't very useful to produce this output yourself -- tkifm does that internally to build its map pictures. But you can control its display by setting variables in the usual way.

Raw data (raw)
This produces raw data intended for use by other programs. Each entry consists of a number of data lines, and is separated from other entries by a blank line. Each data line consists of an attribute, a colon, and its value. The attributes should be self-explanatory.


Item List Output

The following output formats produce item lists:

ASCII text (text)
This produces human-readable output. The output should be fairly self-explanatory.

Raw data (raw)
This produces raw data intended for use by other programs. Each entry consists of a number of data lines, and is separated from other entries by a blank line. Each data line consists of an attribute, a colon, and its value. The attributes should be self-explanatory.


Task List Output

The following output formats produce task lists:

ASCII text (text)
This produces human-readable output. The output should be fairly self-explanatory.

Recording commands (rec)
This output produces a list of commands suitable for feeding to IF interpreters in playback mode. All the commands in the output are converted to uppercase.

In order for this to work properly, you have to give commands that the game will understand. The 'cmd' attribute of rooms, links, joins and tasks can help with this. Currently there's no item 'cmd' attribute, so you have to make sure that the item description is recognized by the game (for get and drop commands). Also, if a task is implicitly done in the game without you having to type any commands (e.g. visiting a room), you can indicate this by using 'cmd none'.

Of course, a recording will only play back properly in an interpreter if it provides correct game commands. Random events can't be dealt with by IFM, and will probably cause playback to fail. But you can work around this with an interpreter that is able to fix the random seed at startup (e.g. dumb-frotz). This should eliminate most (but not all) of the problems of randomness.

Raw data (raw)
This produces raw data intended for use by other programs. Each entry consists of a number of data lines, and is separated from other entries by a blank line. Each data line consists of an attribute, a colon, and its value. The attributes should be self-explanatory.


Customization

You can change the appearance of many map features according to your taste. You do this by setting the values of the variables that control those features. This section tells you how to use variables -- for a complete list of the customization variables available, see Output Variables.

As a first example, the background colour of rooms is determined by the variable 'room_colour'. Its default value is ``white''. It can be changed like this:

    room_colour = "beige";

Setting a variable like this will affect all output formats. But in some cases you don't want to do that. A good example is the one above -- if you don't have a colour printer, you may not want to have beige rooms printed (they'll come out greyish). To get around that, you can set variables that are specific to a particular output format:

    tk room_colour = "beige";

This says to set the variable to ``beige'' only if producing Tk output. The default for all other formats is still ``white''.

You can also customize the appearance of individual rooms and links on the map, by using different display styles. A display style is just a group of variable settings with a given name. For example, suppose you're making a map of Colossal Cave and want to mark rooms where you can refill your water bottle. You can define a style called, say, Water, like this:

    style Water;
    room_colour = "light blue";
    endstyle;

The values of variables that are set between the 'style' and 'endstyle' clauses only apply to things drawn in that style. Now, if you declare rooms like this:

    room "At End Of Road";
    room "Inside Building" style Water dir e go in;

then the room ``Inside Building'' will be drawn with a light blue background. You can customize individual links in a similar manner. Note that here you must put the 'style' clause before the 'dir' clause, so that the style applies to the room. If it appeared afterwards, it would apply to the implicit link to the previous room instead.

An alternative way to define a variable in a particular style is to use the 'in style' clause, like this:

    room_colour = "light blue" in style Water;

If a style only changes a single variable, this may be more convenient.

If you assign a style (say, called newstyle) to an object, but don't define it anywhere in your input, then IFM will look for a file called newstyle.ifm using the standard search path. If the file exists, it is expected to define style newstyle. For example, you could put the ``Water'' style definition above into a file called Water.ifm somewhere on the IFM search path, and it would be read automatically. This is useful if, for example, you want to use the same style in several different maps.


Output Variables

There are many variables available for customizing output. At the moment, only map output can be customized.

The names, descriptions and default values of the customization variables are all contained in the IFM initialization file. You can change this file to set global defaults for everybody, or alternatively set your own preferences in your personal init file.

Here's the original initialization file that came bundled with IFM.

    ##################################################################
    # System IFM init file
    ##################################################################
    
    # This file contains the default definitions for all variables
    # used by the output formats.  Changing these values will affect
    # all users.
    
    ##################################################################
    # General notes
    ##################################################################
    
    # At the moment, only map output is controlled by variables.  The
    # default values are designed to give nice results in all
    # formats.  The default for PostScript is to produce maps for
    # monochrome printing on A4 paper.
    
    # The map output formats differ in their treatment of fonts.  In
    # PostScript, the font and font size are specified separately,
    # via the *_font and *_fontsize variables.  In Tk, they are both
    # specified together, via the *_fontdef variables.
    
    # In PostScript output, a font size represents the maximum
    # desired size -- the actual size may be scaled down in order to
    # fit the text into an enclosing space (e.g. a room box).
    
    # In Tk output, all line width values are interpreted as integers.
    
    ##################################################################
    # General variables
    ##################################################################
    
    # Space allocated for each room, in cm.  In PostScript, this is
    # the maximum size -- the actual size may be reduced in order to
    # fit things on the page.
    room_size = 3;
    
    # Proportion of the room space that's taken up by the room
    # dimensions.  These values should be less than 1 or you'll have
    # no space left for link lines.
    room_width = 0.8;
    room_height = 0.65;
    
    ##################################################################
    # Room style variables
    ##################################################################
    
    # Default background colour of rooms.
    room_colour = "white";
    
    # Offset and colour of room 'shadows'.  This is a pseudo-3D
    # effect which makes rooms look raised off the page.  The offsets
    # are a proportion of the allocated room space.  Note that you
    # can change the direction of the room shadow by negating one or
    # both of the offsets.
    room_shadow_xoff = 0.05;
    room_shadow_yoff = 0.05;
    room_shadow_colour = "grey30";
    
    # Width and colour of the room box lines.
    room_border_width = 1;
    room_border_colour = "black";
    
    # Whether to draw dashed room borders (PostScript only).
    room_border_dashed = 0;
    
    # Width and colour of the room exit lines.
    room_exit_width = 1;
    room_exit_colour = "black";
    
    # Colour, font and fontsize of room description text.
    room_text_colour = "black";
    ps room_text_font = "Times-Bold";
    ps room_text_fontsize = 10;
    tk room_text_fontdef = "Times 10 bold";
    
    # Whether to append room tag names to room descriptions.  If so,
    # they are appended in square brackets, like [this].
    show_tags = 0;
    
    # Whether to show non-hidden item descriptions in rooms.
    show_items = 1;
    
    # Colour, font and fontsize of item description text (if shown).
    item_text_colour = "black";
    ps item_text_font = "Times-Italic";
    ps item_text_fontsize = 6;
    tk item_text_fontdef = "Times 8 italic";
    
    ##################################################################
    # Link style variables
    ##################################################################
    
    # Width and colour of link lines.
    link_line_width = 1;
    link_colour = "black";
    
    # Size of oneway link arrows, as a proportion of the allocated
    # room space.
    link_arrow_size = 0.08;
    
    # Whether to draw link lines as splines (Tk only).
    link_spline = 1;
    
    # Whether to draw dashed link lines (PostScript only).
    link_dashed = 0;
    
    # Appearance of text that's associated with link lines.  The Tk
    # default is to colour them differently for readability.
    ps link_text_font = "Times-Roman";
    ps link_text_fontsize = 6;
    ps link_text_colour = "black";
    tk link_text_fontdef = "Times 8 bold";
    tk link_text_colour = "red";
    
    # Text strings indicating up/down/in/out on links.
    link_updown_string = "U/D";
    link_inout_string = "I/O";
    
    ##################################################################
    # PostScript setup variables
    ##################################################################
    
    # Prolog file that gets prepended to all the PostScript output.
    # This defines all the procedures for drawing everything.
    prolog_file = "ifm-pro.ps";
    
    # File of colour definitions, which contains the RGB values of each
    # colour referred to below (and a good many more).  It's just the
    # rgb.txt file found on Unix/X11 systems.  See that file for a list
    # of available colours.  You can use the same colour names for the
    # Tk output, since it uses the standard X colours.
    colour_file = "ifm-rgb.txt";
    
    # Default page size.  Available page sizes are: A3, A4, A, B, C,
    # Legal, Letter.
    page_size = "A4";
    
    # If defined, these set a custom page size which overrides the
    # 'page_size' variable.  Units are in cm.
    page_width = undef;
    page_height = undef;
    
    # Whether to rotate each page to landscape.  If not defined, then
    # rotation is decided on a per-page basis in order to get the best
    # fit.
    page_rotate = undef;
    
    # Margin space to be left on each page, in cm.
    page_margin = 2;
    
    # Whether to show a border around each PostScript page.
    show_border = 0;
    
    # Colour of the page border (if drawn).
    page_border_colour = "black";
    
    # Colour of the page background.
    page_background_colour = "white";
    
    # Whether to show the main title on each page.
    show_title = 1;
    
    # Appearance of the main title (if shown).
    title_font = "Times-Bold";
    title_fontsize = 18;
    title_colour = "black";
    
    # Appearance of the title printed above each map section.
    map_text_font = "Times-Bold";
    map_text_fontsize = 14;
    map_text_colour = "black";
    
    # Scale factor which is applied to all PostScript fonts.  This is
    # a convenience variable to make font adjustments easier.
    font_scale = 1;
    
    ##################################################################
    # Tk setup variables
    ##################################################################
    
    # Maximum width and height of the map canvas window, in rooms.
    canvas_width = 8;
    canvas_height = 6;
    
    # Background colour of the map canvas.
    canvas_background_colour = "wheat";
    
    ##################################################################
    # Aliases
    ##################################################################
    
    # Alternate spellings for colour variables, for U.S. IFM fans.
    canvas_background_color => canvas_background_colour;
    color_file => colour_file;
    item_text_color => item_text_colour;
    link_color => link_colour;
    link_text_color => link_text_colour;
    map_text_color => map_text_colour;
    page_background_color => page_background_colour;
    page_border_color => page_border_colour;
    room_border_color => room_border_colour;
    room_color => room_colour;
    room_exit_color => room_exit_colour;
    room_shadow_color => room_shadow_colour;
    room_text_color => room_text_colour;
    title_color => title_colour;
    
    # Compatibility aliases for old versions of IFM.  If your maps
    # use any of these, you should replace them with the aliased
    # variable -- at some point in the future these aliases may go
    # away.
    exit_colour => room_exit_colour;
    exit_linewidth => room_exit_width;
    item_font => item_text_font;
    item_fontsize => item_text_fontsize;
    label_colour => link_text_colour;
    label_font => link_text_font;
    label_fontsize => link_text_fontsize;
    link_linewidth => link_line_width;
    room_font => room_text_font;
    room_fontsize => room_text_fontsize;
    room_linewidth => room_border_width;

Previous versions of IFM had two object attributes which are now implemented as display styles: the room 'puzzle' attribute, and the link 'special' attribute. Here's the 'puzzle' style:

    # Room 'puzzle' style (was previously the 'puzzle' attribute).
    
    style puzzle;
      room_colour = "grey";
    endstyle puzzle;

And here's the 'special' style:

    # Link 'special' style (was previously the 'special' attribute).
    
    style special;
      ps link_dashed = 1;
      tk link_colour = "blue";
    endstyle special;


LANGUAGE

This section gives a formal description of the IFM input language.


Symbols

In the following sections, these symbols are used:

ID
A name starting with a letter, followed by any combination of upper- or lowercase letters or numbers or an underscore. E.g. Dense_Forest. IDs are not allowed to clash with reserved words (e.g. 'room', or 'tag'). One way to avoid this is to capitalize all tags (reserved words are all in lowercase).

STRING
Any sequence of characters, in double-quotes. E.g. "Black Rod". To get a double-quote inside a string, quote it by using a backslash, like this: "Ground Floor, \"A\" Block". To get a backslash inside a string, quote that too (\\). You can continue strings on several lines -- a newline-and-whitespace sequence is translated into a single space, just like in TADS and Inform.

Note that dollars ($) are special inside strings -- they mark the start of a variable substitution. See Variables for details. If you want a literal dollar symbol in a string, use $$.

NUMBER
An number, or an arithmetic expression (see Expressions). If the context requires an integer, the number is silently rounded to the largest integer not greater than this value.

COMPASS
A compass direction, which can be abbreviated or in full (e.g. 'n', 'se', 'northwest', etc).

OTHERDIR
One of 'up', 'down', 'in', or 'out'.

[...]
An optional part.

thing1 | thing2
A choice of several different things.


Format

IFM generally has a free-format layout -- i.e. whitespace may be placed anywhere to increase readability. The only exception is inside quoted strings, where spaces are significant. Comments may be added, starting with a hash (#) and continuing to the end of the line. All commands are terminated with a semi-colon.


Control

The overall title of the map may be set using the command

    title STRING;

If a map has several sections, you can set the title of each section using the command

    map STRING;

This sets the title of the next map section. Also, any variables set after this command are local to the next map section (see Variables). If you use this command at all, then the number of uses should be the same as the actual number of map sections. But note that the map command doesn't have to be near the room that starts a new map section, so you could (for example) put all the map commands and their local variables at the start of the input.


Tags

All IFM objects may be given tag names, so that you can refer to them in other commands. Tags for different types of object are independent -- for example, you could have a room and an item with the same tag. However, tags for similar objects must be unique.

In many cases, you are allowed to refer to a tag name anywhere, even earlier in the file that you defined it (as long as the tag is defined somewhere!). Exceptions are the room 'from ID' clause (see Rooms), and tags in commands that modify existing objects (see Commands) -- these tags must be defined before being used.

The special tag 'last' may be used to refer to the last object that was defined (depending on the context). Also, within an individual command, the special tag 'it' may be used to refer to the most recent object tag.


Commands

There are 5 different types of object in IFM: rooms, items, links, joins and tasks. Each is created using its own command, the general format of which is:

    <type> <body> [attribute-list];

For rooms, items and tasks, body is just a string description. For links and joins, body specifies two room tags to link or join together.

Many of the attributes of objects accept a list of tags as arguments. All of these, if specified more than once in the same object, concatenate the lists together.

Once an object has been declared with a tag name, its attributes can be modified by later commands referring to that tag, like this:

    <type> ID [attribute-list];

where ID is the tag name of the object. Note that the tag must be defined earlier in the file than it is used.


Rooms

A new room is added using the command

    room STRING [attribute-list];

where STRING is a description of the room. Room attributes can be:

tag ID
Give the room a tag name, so that you can refer to it elsewhere.

dir COMPASS [NUMBER] [COMPASS [NUMBER]...] [from ID]
Specify the position of the room relative to the room with the given tag ID (which must be defined before it is used). If no 'from' clause is specified, the last defined room is used instead. There can be more than one direction given -- the new room is placed relative to the previous one using them. Following a direction with a number means to repeat it that many times.

The 'dir' clause creates an implicit link between this room and the previous one. Some of the room attributes below behave differently depending on whether they appear before or after the 'dir' clause in the attribute list.

If the room is given a tag name, then the implicit link will be given the same tag.

link ID [ID...]
Specify other rooms that this room links to. Note that this creates a link with no special attributes -- use the standalone 'link' command for that (see Links).

join ID [ID...]
Specify rooms on other map sections that this room joins to. Note that this creates a join with no special attributes -- use the standalone 'join' command for that (see Joins).

exit COMPASS [COMPASS...]
Indicate which other directions the room has exits in. Room exits in a particular direction are marked on the map only if there is no link going to or from the room in that direction.

note STRING
Append a note to the room's note list.

score NUMBER
Indicate that you score the specified number of points when visiting this room for the first time.

need ID [ID...]
If this appears before a 'dir' clause, indicate that you can only enter this room after getting the specified items. If it appears afterwards, it applies to the implicit link instead.

after ID [ID...]
If this appears before a 'dir' clause, indicate that you can only enter this room after doing the specified tasks. If it appears afterwards, it applies to the implicit link instead.

before ID [ID...]
If this appears before a 'dir' clause, indicate that you can only enter this room before doing the specified tasks. If it appears afterwards, it applies to the implicit link instead. Those tasks are marked unsafe.

leave ID [ID...]
If this appears before a 'dir' clause, indicate that the specified items, if carried, must be left behind when entering the room. If it appears afterwards, it applies to the implicit link instead.

leave all [except ID [ID...]]
As above, except indicate that all items must be left behind. The 'except' clause can be used to omit specific items.

go OTHERDIR
Indicate that the link to this room is in the specified direction.

cmd STRING
Specify the command you type to move to this room from the previous one. If no 'cmd' clause is given, the command is deduced from the 'go' clause. If that isn't specified, the command will be deduced from the 'dir' clause.

cmd from STRING
As above, but this specifies the command to go in the other direction. This defaults to the 'cmd to' command, if specified.

cmd to STRING
This is identical to 'cmd' on its own, and only exists for symmetry.

oneway
Indicate that the return journey from this room to the previous one is not possible.

length NUMBER
Indicate that the direction link to this room has the specified length (default 1). This only affects the calculation of the 'nearest' task when solving the game -- see Tasks.

start
Indicate that this is the room the player starts in. Default is for the first room mentioned to be the start room.

finish
Indicate that entering this room finishes the game.

nolink
Indicate that this room does not have an implicit link with the previous one via the 'dir' clause.

nopath
Indicate that the implicit link from this room should not be used by the game solver.

style ID [ID...]
Add a list of display styles to the room. See Styles.


Items

An item is introduced using the command

    item STRING [attribute-list];

where STRING is the item description. Item attributes can be:

tag ID
Give the item a tag name, so you can refer to it elsewhere.

in ID
Set the initial location of this item. Default is the last defined room. If there is no last room (i.e. an item was declared before any room was declared), then this item is initially carried by the player.

note STRING
Append a note to the item's note list.

score NUMBER
Indicate that you get points the first time you pick this item up.

hidden
Indicate that this item is not immediately obvious when entering the room.

keep
Indicate that this item shouldn't be dropped (no 'drop' task should be generated).

given
Indicate that this item didn't have to be picked up when it entered the inventory (no 'get' task should be generated). This attribute is obsolete -- you should use the task 'give' clause instead (see Tasks).

lost
Indicate that this item wasn't dropped when it left the inventory (no 'drop' task should be generated). Normally you should use the task 'drop' or 'lose' clauses instead (see Tasks). The only use for this attribute is for items that are left behind due to a 'leave' clause.

need ID [ID...]
Indicate that you can only pick this item up after getting the specified items.

after ID [ID...]
Indicate you can only pick this item up after the specified tasks are done.

before ID [ID...]
Indicate you can only pick this item up before the specified tasks are done.

finish
Indicate that getting this item finishes the game.

style ID [ID...]
Add a list of display styles to the item. See Styles.


Links

You can create extra room links using the command

    link ID to ID [attribute-list];

and the following attributes may be specified:

tag ID
Give the link a tag name, so you can refer to it elsewhere.

dir COMPASS [COMPASS...]
Set the intermediate directions that this link travels in, in the same manner as for rooms. Note that if you omit the final direction to the linked room, it is added automatically.

go OTHERDIR
Indicate that the link is in the specified direction.

cmd STRING
Specify the command you type to go in this direction. If no 'cmd' clause is given, the command is deduced from the 'go' clause. If that isn't specified, the command will be deduced from the 'dir' clause.

cmd from STRING
As above, but this specifies the command to go in the other direction. This defaults to the 'cmd to' command, if specified.

cmd to STRING
This is identical to 'cmd' on its own, and only exists for symmetry.

oneway
Indicate that this is a one-way link, in a similar manner to the room attribute of the same name.

hidden
Indicate that this link should not be drawn on the map. Hidden links are still used when solving the game.

nopath
Indicate that this link should not be used by the game solver.

length NUMBER
Indicate that this link has the specified length (default 1). This only affects the calculation of the 'nearest' task when solving the game -- see Tasks.

need ID [ID...]
Indicate that you can only go in this direction after getting the specified items.

after ID [ID...]
Indicate that you can only go in this direction after doing the specified tasks.

before ID [ID...]
Indicate that you can only go in this direction before doing the specified tasks. These tasks are marked unsafe.

leave ID [ID...]
Indicate that the specified items, if carried, must be left behind when using this connection.

leave all [except ID [ID...]]
As above, except indicate that all items must be left behind. The 'except' clause can be used to omit specific items.

style ID [ID...]
Add a list of display styles to the link. See Styles.


Joins

There is a standalone 'join' command which joins two rooms on different map sections:

    join ID to ID [attribute-list];

The following attributes may be specified:

tag ID
Give the join a tag name, so you can refer to it elsewhere.

go COMPASS | OTHERDIR
Indicate that the join to this room is in the specified direction.

cmd STRING
Specify the command you type to go in this direction. If no 'cmd' clause is given, the command is deduced from the 'go' clause. If that isn't specified, the command will be undefined.

cmd from STRING
As above, but this specifies the command to go in the other direction. This defaults to the 'cmd to' command, if specified.

cmd to STRING
This is identical to 'cmd' on its own, and only exists for symmetry.

oneway
Indicate that this is a one-way join, in a similar manner to the room attribute of the same name.

hidden
Indicate that this join should not be drawn on the map. Hidden joins are still used when solving the game.

nopath
Indicate that this join should not be used by the game solver.

length NUMBER
Indicate that this join has the specified length (default 1). This only affects the calculation of the 'nearest' task when solving the game -- see Tasks.

need ID [ID...]
Indicate that you can only go in this direction after getting the specified items.

after ID [ID...]
Indicate that you can only go in this direction after doing the specified tasks.

before ID [ID...]
Indicate that you can only go in this direction before doing the specified tasks. These tasks are marked unsafe.

leave ID [ID...]
Indicate that the specified items, if carried, must be left behind when using this connection.

leave all [except ID [ID...]]
As above, except indicate that all items must be left behind. The 'except' clause can be used to omit specific items.

style ID [ID...]
Add a list of display styles to the join. See Styles.


Tasks

You can indicate tasks which need to be done in order to solve the game using the 'task' command

    task STRING [attribute-list];

and these are the available attributes:

tag ID
Give the task a tag name, so you can refer to it elsewhere.

in ID
Specify the room the task must be done in. If this clause is omitted, it defaults to the last defined room. You can use the special word 'any' to indicate that the task may be performed anywhere. A task declared before any room is equivalent to saying 'in any'.

need ID [ID...]
Indicate that the specified items are required before you can do this task.

after ID [ID...]
Indicate that this task can only be done after all the specified tasks have been done.

follow ID
Indicate that this task must be done immediately after the specified one. Not even a 'drop item' task is allowed in between.

get ID [ID...]
Indicate that doing this task enables you to get the specified items, and must be done before you can get them.

give ID [ID...]
Indicate that doing this task puts the specified items straight into your inventory, wherever they happen to be.

lose ID [ID...]
Indicate that doing this task causes the specified items to be lost. This implies that all tasks which need these items must be done before this one.

drop ID [ID...] [in ID] [until ID [ID...]]
Indicate that doing this task drops the specified items in the current room (or the room indicated by the 'in' clause) if you're carrying them. No 'drop' message is generated. If there's an 'until' clause, you can't retrieve the items until the specified tasks have been done.

drop all [except ID [ID...]] [in ID] [until ID [ID...]]
As above, but drop everything you're carrying. The 'except' clause can be used to omit specific items.

goto ID
Indicate that you get 'teleported' to the specified room when this task is done. This happens after 'give' and 'drop' actions.

safe
Mark this task as safe -- i.e. one that can't cause the game solver to get stuck.

finish
Indicate that doing this task finishes the game.

score NUMBER
Indicate that you get the specified score for doing this task.

note STRING
Append a note to the task's note list.

cmd STRING [NUMBER]
Specify the exact command you type to do the task. If a number follows the command, do the command that many times. Multiple 'cmd' clauses concatenate into a list of commands.

cmd none
Indicate that no command is required to do this task.

style ID [ID...]
Add a list of display styles to the task. See Styles.


Variables

Various aspects of output are controlled by variables. These are set using the following syntax:

    [FORMAT] IDENT = NUMBER | STRING | undef [in style ID];

FORMAT, if specified, is the name of a specific output format -- the variable then applies only to that output format. IDENT is the name of the variable, and it can take a numeric or string value. Note that setting a variable to the value 'undef' effectively removes it.

If the style clause is present, this means to only set the variable to this value in the specified style (see Styles).

You can use the value of a defined variable anywhere in the input where a number or string is expected, by prefixing it with a dollar ($) symbol.

All strings that are printed by IFM output formats undergo variable substitution -- that is, parts of the string which look like variable references get the value of the variable inserted. If you want a literal $ inside a string, use $$. If you want substitution to be performed on a variable that is cuddled up to other text that looks like part of the variable name, you must surround it with twiddly brackets, like ${this}. Note that variable substitution only happens when the string is output -- this happens after all other processing, so it is the final value of the variable that will be substituted.

Variables can have 'aliases' -- that is, other names. A variable alias is set using the syntax

    alias_name => var_name;

This has the effect that whenever variable 'alias_name' is set or accessed, then variable 'var_name' is actually set or accessed instead. Note that if a variable already exists with name 'alias_name', its value is hidden while the alias is in force. Aliases can be removed by saying

    alias_name => undef;

Some output formats use variables so that you can customize them. They expect certain variables to be defined, and give an error if they aren't. The default values for these variables are set in the initialization file -- see Output Variables.


Styles

Display styles change how individual things are shown when output. Currently, only map output for rooms and links is affected by styles.

IFM keeps track of the currently active list of display styles, and there are two commands which change this list. The command

    style ID;

pushes the specified style onto the style list. This style becomes the current style. Any IFM objects declared while a style list is in force will by default be output in those styles. Any variable setting is by default in the current style (though you can specify a particular style using the 'in style' clause -- see Variables).

The command

    endstyle [ID];

pops the current style from the style list. The previous style on the list (if any) becomes the current style. The ID, if specified, should match the ID in the corresponding 'style' command, or a warning is given.

Each display style has its own set of values for customization variables. On output, when the value of a variable is needed for displaying an object, the style list for that object is searched in reverse order of declaration. The value used is from the first style to define this variable. If no style defines it, then the default value is used.

If a style is referenced by an object but not defined anywhere in the input, then its definition is assumed to be in a separate file, which is searched for using the standard search path. The name of this file is formed by adding an .ifm suffix to the style name. If the file is not found, or it does not define the required style, a warning is given.


Expressions

Whenever a number is expected in IFM input, you can supply an arithmetic expression. An expression can be a number or a variable value, or a combination using the following constructs (in order of precedence):

    ( expr )            parentheses
    expr ^ expr         exponentiation
    + expr              unary plus
    - expr              unary minus
    expr * expr         times
    expr / expr         divide
    expr % expr         remainder
    expr + expr         plus
    expr - expr         minus

Note that in expressions, all variables are treated as numeric. If a variable is set to be a string and is then used in an expression, its numeric value will be read from the string. For most strings, this will be zero. This means that if you want to set a string variable to be exactly equal to another variable, you can't say something like

    string_var = $other_string_var;

because the right hand side is an expression, which is treated as numeric. The way to do it is to use variable substitution in strings:

    string_var = "$other_string_var";


Include Files

Your IFM input can include other input files too, via the 'include' directive. There are two forms:

    include "file"

and

    include <file>

The first form looks for the exact file name -- either absolute or relative to the current directory. The second form does a directory search to find the file, using the IFM search path (which can be set via the -I command line option or the IFMPATH environment variable).

Note that to be recognized as such, the 'include' directive must be the first thing on a line. Note also that no semicolon is required after it.


DIAGNOSTICS

This section describes the possible error and warning messages which might be produced by IFM, and what they mean. Note that individual output formats may print their own errors and/or warnings. This list only covers the standard ones.

Here's the list of error messages. If any errors occur, no output is produced.

error: invalid repeat count
You've given a repeat count of zero or less for a string or direction, which doesn't make much sense.

error: no last room
You've given the very first room a 'dir' clause. See Rooms.

error: no [type] referred to by 'last'
You've said 'last' to refer to the last room, item or task that was defined, but none of that type of object have been defined yet. See Tags.

error: no [type] referred to by 'it'
You've said 'it' to refer to the last room, item or task tag that was mentioned in the current command, but no tags of that type of object have been mentioned. See Tags.

error: [type] tag [name] already defined
You've given two similar objects the same tag name. See Tags.

error: [type] tag [name] not defined
You've referred to a tag name that hasn't been defined anywhere in the input. See Tags.

error: [type] tag [name] not yet defined
You're referring to a tag at a point where it hasn't yet been defined, in a situation where it must be (e.g. the room 'from' clause, or a command that modifies attributes of a previously-defined object). See Tags.

error: can't modify [name] attribute
You're attempting to modify an attribute of an object which can't be changed once it's set (e.g. a tag name). This is because it would create inconsistencies between objects.

error: can't link [name1] and [name2] -- different map sections
The rooms you're trying to link are on different sections of the map, and have no spatial relation to one another. You might have forgotten to link a previous room in the list. Or you meant to use a 'join'.

error: can't link [name] to itself without at least one direction
To link a room to itself, you need to specify at least one direction. Otherwise, the link goes nowhere. See Links.

error: links between [name1] and [name2] have differing lengths
You've defined more than one link or join between the given rooms, but given them different values for the 'length' attribute. This isn't allowed. See Limitations.

error: more than one task needs to follow [task] immediately
You've given two or more tasks an identical 'follow' tag. Only one task can 'follow' a given task. See Tasks.

error: [num] cyclic task dependencies
The game isn't solvable because there's one or more chains of tasks where each must be done before the next, but the last must be done before the first. See Finding a Solution.

error: variable [name] is not defined
A customization variable needed by an output format is not defined. You should only see these errors if you have modified or overridden the system initialization file. The remedy is to define the variable somewhere.

Here's the list of warning messages. If only warnings occur, then output is still produced.

warning: attribute [attr] ignored -- no implicit link
You've given a room with no 'dir' clause an attribute that is associated with that link (e.g. 'oneway', or 'special'). Most likely you're putting the attribute in the wrong place -- if you want, say, a join to have one of these attributes, you must define it using the standalone 'join' command instead. See Rooms.

warning: link from [name1] to [name2] outside grid
The destination room for a link is not in a compass direction from the last specified position.

warning: rooms [name1] and [name2] overlap
The coordinates of the specified rooms are the same. See Conflicts.

warning: room [name] crossed by link line between [name] and [name]
A link line passes through the coordinates of a room. See Conflicts.

warning: room [name] has multiple [dir] links
More than one link connects to the specified room in a particular direction.

warning: can't solve game ([num] tasks not done)
The game is unsolvable according to the current set of tasks. This can be due to part of the map being inaccessible, or IFM stupidly choosing the wrong order of doing things. Hopefully the latter shouldn't happen very often. See Finding a Solution.

warning: no matching style command
You've used 'endstyle' without a matching 'style'. You probably have too many 'endstyle' commands.

warning: unexpected style: [name] (expected [name])
You've used 'endstyle' with an argument that doesn't match the argument of the corresponding 'style'. You might have missed out another 'endstyle' somewhere, or have too many.

warning: style [name] referenced but not defined
An object in your input uses the specified style, but it isn't defined anywhere and the style definition file name.ifm doesn't exist in the search path (or if it does, it doesn't define the required style).

warning: [keyword] is obsolete -- use [phrase] instead
You've used an obsolete keyword. Consult the documentation and then try the suggested alternative instead. Note that multiple uses of an obsolete keyword only result in a single warning.


ENVIRONMENT

These environment variables are used by IFM:

HOME
Pathname of your home directory (used to find personal init file).

IFMPATH
Colon-separated list of directories used to search for library files.


SEE ALSO

tkifm(1), scr2ifm(1)


AUTHOR

Glen Hutchings (zondo@hunting2.demon.co.uk)