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.
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):
+--------------+ +--------------+ +--------------+ | | | | | | | 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.
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.
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.
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 "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.
There are several reasons why it might be a good idea to split a game map into different sections:
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.
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.
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.
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.
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.
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.
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;
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.
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.
... 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;
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).
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.
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:
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.
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.
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.
The following output formats produce maps:
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.
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.
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.
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;
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 $$.
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.
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.
<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.
room STRING [attribute-list];
where STRING is a description of the room. Room attributes can be:
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.
item STRING [attribute-list];
where STRING is the item description. Item attributes can be:
link ID to ID [attribute-list];
and the following attributes may be specified:
join ID to ID [attribute-list];
The following attributes may be specified:
task STRING [attribute-list];
and these are the available attributes:
[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.
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.
( 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 "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.
Here's the list of error messages. If any errors occur, no output is produced.
tkifm(1),
scr2ifm(1)