Please Note

This document has been produced automatically from LaTeX source, and has not been edited. It uses different characters to specify various fonts (rather than various uses, as would be ideal), as follows:
Italic Text
Bold text
Typewriter font text
Small capitals
Serif Text
Please be aware of this, and do not try to read these characters as part of the actual text.

Also, no attempt has been made to divide the file into pages. You can, however, navigate by sections; each section starts with the text `Section :', and each appendix with the text `Appendix :', where is the number of the section or appendix, such as `2.2' or `B.1'.

An Introduction to TinyFugue

An Introduction to TinyFugue

Andy Mortimer (

Version 1.0 alpha 1

Table of Contents

1 Introduction
1.1 Things To Do
1.2 Copyright
1.3 Acknowledgements
2 Getting Started
2.1 Starting TinyFugue
2.2 Editing
2.3 TinyFugue commands and MUD commands
2.4 Connecting to an online game
2.5 Managing multiple worlds
2.6 Fine-tuning TinyFugue
2.7 Saving Your Configuration
2.8 Using History and Logging
3 The Building Blocks
3.1 Setting and Viewing Variables
3.2 A Brief Introduction to Macros
3.3 Matching Text
4 Reacting Automatically to MUD Output
4.1 Hiliting MUD output
4.2 Using Triggers
4.3 More Flexible Hilites and Triggers using Regular Expressions
4.4 Changing the Attributes
4.5 Redefining the Keyboard
4.6 Using Hooks and Hiliting TinyFugue Output
4.7 Other Useful Switches to the /def Command
A TinyFugue Supplemental Files
B Some Sample Macros
B.1 Automatic Reading of All Help Pages
B.2 A Complex Kill Trigger

Section 1: Introduction

TinyFugue, or `tf' for short, is a very flexible MUD client for UN*X and OS/2. This flexibility, however, does have it's price, and TinyFugue is often seen as being very difficult to learn. This document is designed as a tutorial for the beginner, to help this first step; the idea is that by the end, you will be able to use TinyFugue to almost the fullest extent possible without going deeply into macro programming, and certainly have some idea of how to go about any given task and where to refer to in the help for more information.

You may notice that, for most of the document, I refer to the game to which you are connected as a `MUD'. This is not to say that TinyFugue is only useful for MUDs---far from it---but that (from a quite-possibly-biased MUSHer's viewpoint) I tend to refer to the whole genre of text-based online games as MUDs.

It is a constantly evolving document, which I hope will grow and change as time (and TinyFugue!) goes on, so if you have any comments, criticisms or contributions, please do email me and let me know. In particular, since I have not used OS/2, most of the information in this document is based around the UN*X version. As far as I am aware, the program behaves the same, but I suspect some of the files are different. If anybody knows of any differences between the two which are not documented here, please let me know and I will incorporate them. Also, I am currently using TinyFugue version 3.5 alpha 19, and so any differences in earlier or later versions which you feel ought to be mentioned would also be appreciated.

And finally, due to the nature of this document, you may see some comments <| like this |> scattered through it. These are reminders and remarks about places where I feel the document is not yet finished: it is probably safe to ignore these, but if you can answer the questions, please do get in touch! A not insignificant number of them are issues which I have no way of solving myself, so your input would be appreciated.

Section 1.1: Things To Do

These are the longer-term things which I still want to put in, or which I am still wondering if I ought to put in. Any suggestions for addition to (or removal from!) this list would be appreciated, as would submissions for sections like `TinyFugue tips and tricks'. Any submissions of code will be assumed to have been placed in the public domain; if this is not the case, please state it on submission.

  • Functions and Expressions

  • Substitutions

  • Control Statements and Conditional Evaluation

  • TinyFugue tips and tricks

  • Quick reference

  • Using history and recalling text

  • Further reading: where to start in /help; the supplemental files;

  • Sample `.tfrc'

  • Index!

Section 1.2: Copyright

This document is copyright 1996 Andrew Mortimer. You are permitted to distribute it freely, either electronically or as hardcopy, either in one of the forms provided or in a different form, so long as the text of the document is left in it's entirety, including this copyright notice, and no significant changes are made to the document structure; you may not charge for the document itself, although you may charge a handling fee.

Any example code contained herein, unless stated otherwise, is free of all copyright and has been placed in the public domain by the author. If the name of an author is specified with the code, this name should be given also in any redistributions of the code.

Section 1.3: Acknowledgements

In this first version of the document, thanks go mainly to Hawkeye for writing TinyFugue!

Section 2: Getting Started

Section 2.1: Starting TinyFugue

If TinyFugue is not already installed on your system, you will probably need to compile it first. I don't intend to go into this in any detail here, as it is well documented in the source package in the `README' file, but I think I did ought to mention that under OS/2, you will require EMX to compile it.

To run TinyFugue, simply type tf from a command prompt[1], if the TinyFugue program is on your path. If not, you will have to find out where it is installed and give the full pathname (under UN*X, this would usually be `/usr/games/tf'). As TinyFugue starts up, it reads in it's standard library file `', a file called `' in the same directory as `' if your system administrator has installed one, and finally your personal configuration file[2]. This file sets up various parameters for the program, which will be explained in more detail as we progress, but if TinyFugue behaves in a way which is very different to what is described here, you might wish to check if you have this file. For now, you don't need to do anything with it.

[1] I have no idea if this is valid under OS/2 as well, but it seems a reasonable guess.

[2] This is the file `.tfrc' in your home directory under UN*X; under OS/2 it can also be called `tfrc' if you use a FAT filesystem, and your home directory is that pointed to by the environment variable HOME

When TinyFugue starts up, there are two different screens which you might see, depending on which version you have. Versions before 3.5 alpha 17 default to what is called non-visual mode, where output scrolls up the screen and you type on the bottom line (see figure 1), and versions 3.5 alpha 17 and above default to visual mode, where TinyFugue sections off the bottom few lines of the screen for you to type in, and uses most of the screen for output only. In visual mode, you will also see a status bar separating the two areas, with various information about the current state of TinyFugue. There is a command to change between these two modes, detailed in section 2.6; in this document, I will give all the examples in non-visual mode as it is easier to represent!

  TinyFugue version 3.5 alpha 19
  Copyright (C) 1993, 1994, 1995, 1996 Ken Keys (
  Type `/help copyright' for more information.
  Regexp package is Copyright (c) 1986 by University of Toronto.
  Type `/help', `/help topics', or `/help intro' for help.
  Type `/quit' to quit tf.
  \% Loading commands from /usr/lib/games/tf/tf-35a19-lib/
  \% Loading commands from /usr/lib/games/tf/tf-35a19-lib/
  ---- No world ----

Figure 1: Sample TinyFugue startup, in non-visual mode

To round off this section, I should probably note that to quit TinyFugue, you can simply type /quit, as it says at startup in figure 1, which will close all open connections and return you to the shell prompt.

Section 2.2: Editing

For those of you who have been using UN*X for a while, especially programs such as Emacs and Bash which use what seems to have become a `standard' set of keys, the basic editing in TinyFugue will be a breeze, although there are some slight differences and many of the more obscure functions have moved totally. For those of you who haven't got used to it, though, and for those of you who use OS/2 and have never had the chance to use UN*X (or, I suppose, never wanted to?), this section will summarise the major editing keys in TinyFugue. <| What's in `'? Does it use all the standard (home, end, etc) keys? |> But first, a quick note about the Meta key: under UN*X <| and OS/2? |>, there is a key called `Meta' or `Alt', which can be used in the same way as the control key. However, it can also be `used' by pressing the `Escape' or `Esc' key before the letter or whatever you wish to type. So if you see a key called `Meta-D', say, this can be entered either by holding down the key labelled `Alt' or `Meta' and then pressing `D', or by pressing `Esc' followed by `D'.

First of all, cursor movement. You can use the cursor keys to get around, so that won't be a problem, but there are some others which will make your life easier. The `Ctrl-A' and `Ctrl-E' keys go to the beginning and end of the line, respectively, and the `Ctrl-F' (forward) and `Ctrl-B' (backward) keys move by words[3]. You can also move about in the `history' of lines you have already typed using the `Ctrl-P' and `Ctrl-N' keys.

[3] Emacs users take note: that was with the control key, not the meta key!

Deletion of single characters can be done with the backspace key or the `Ctrl-D' key, which deletes the character under the cursor. Words can be deleted backwards (ie, normally) with `Ctrl-W', or forwards (the word under the cursor) with `Meta-D', and the line can be deleted from the cursor to the end with `Ctrl-K', or the entire line with `Ctrl-U'. And finally, if the screen gets messed up, you can redraw it[4] using the `Ctrl-L' key.

[4] To some extent; it will look right, but TinyFugue removes all the output currently on the screen.

If you can't remember all that, don't worry; it's there for you to refer to later on. But you should at least have some idea how to edit things in TinyFugue now.

Section 2.3: TinyFugue commands and MUD commands

Before we get onto any specific commands, though, I ought to mention the way commands are processed in TinyFugue. All communication with the program is done by typing in the data entry area (ie, in the bottom window in visual mode, or on the bottom line in non-visual mode), and so TinyFugue needs some way to distinguish it's own commands from those which you wish to send on to the MUD. It does this by starting all it's commands with a slash, `/'. So, for example, to get help about TinyFugue you would type /help, but to get help about your MUD you might type help (obviously, this depends on whether your MUD supports a help command!).

Section 2.4: Connecting to an online game

There are two ways to connect to a remote host on which your MUD is running. The first is almost like the telnet command, in that it simply takes a host name and a port number and connects to the game running there. This is the easiest type to use if you wish to try out a MUD, or indeed TinyFugue, and don't want to go to all the hassle of setting up a world for it (see later). To use this method, type /connect host name port.

This method does have quite a few disadvantages for MUDs you visit often, not least of which is the fact that you have to remember the address of the MUD every time you want to log into it. Wouldn't it be nice if you could give each different game a descriptive name, and get TinyFugue to remember things like the address? And while we're at it, why not take it one step further and store the name of your character as well, and get TinyFugue to connect to that character every time you visit that game? Sound far-fetched? No? Oh, well ...

As you probably guessed, TinyFugue can indeed do this, using the concept of worlds. Worlds are defined using the /addworld command, in one of the following ways:

/addworld name host port
Adds a world called name, on the given host and port. This is the simplest way of using the command, but it doesn't know anything about your character unless you give a default character as well, using the /addworld default command (see later).

/addworld name character password host port
This adds a world exactly as above, but also records information about your character. This information will be used to log you in as the named character, whenever you open a connection to that world.

/addworld default name password
Sets the default character name and password for worlds where they are not specified.

All of these forms take an optional argument to specify the type of the world, by putting ``-Ttype'' after /addworld above. The type field is used to work out how to log you in, and can have one of the following values:

TinyMud login format (sends ``connect name password'' to the game)

LP/Diku login format (sends ``name'' on one line, followed by ``password'' on the next), and looks for prompts from the MUD.

As lp above.

As lp above.

As lp above, but it assumes any prompts sent by the server are terminated by either the END-OF-RECORD or GOAHEAD sequences, which are defined by the telnet protocol[5]. This makes prompt handling nicer if you have it, but I think many places do not.

[5] If this means nothing to you, don't worry about it. You may want to ask somebody on the MUD, but the lp type (or one of the others) will work fine.

You can also define the world type as one of the types given above followed by a period and a subtype, for example tiny.mush or tiny.muck. These worlds will be treated the same as a tiny world by TinyFugue, but you can add your own macros which differentiate between then (see section 3.2 for more details).

Note that, although it is strictly speaking optional to specify a world type, it is a good idea. If you don't, TinyMud login format is assumed, but prompts etc are not necessarily handled correctly. It is also possible to specify a macro file to load when you connect to that world; see /help addworld for more details on this and on the command in general.

Having defined the world, you can connect to it using the /connect world command, in exactly the same way as above.

Once connected to a world, by either method, you can type any commands you like, which TinyFugue will then send to the MUD[6]. In order to disconnect again from the world, you can either tell the game to disconnect using a command such as QUIT on a TinyMUSH, or use the /dc command which hangs up TinyFugue's end. It is recommended that you use the first method if it is available, however[7].

[6] Except, as mentioned earlier, commands which begin with a slash, which TinyFugue interprets itself. If you must send a command like this, look at /help send.

[7] If you disconnect this way, it calls what is known as a disconnect hook, which can be set up to do something when you finish in a world. If you use the /dc command, this hook is not called.

<| /listworlds, /unworld, etc |>

Section 2.5: Managing multiple worlds

Unlike telnet, or many similar programs, TinyFugue allows you to connect to more than one world at the same time, and to switch between them at will. The world which you are displaying and typing to at any point is known as the active or foreground world, and any others are known as background worlds. If you are in visual mode, the name of the foreground world is displayed in the status bar above the input area. When something happens in a background world, then rather than displaying it mixed it with the output from the foreground world, which would be confusing to say the least, TinyFugue displays `% Activity in world name', and lets you go back to it at your leisure. In visual mode, the text `(Active: n)' is also displayed in the status bar, where n is the number of worlds from which have output you have not yet seen.

Connecting to more than one world is easy: you simply give one /connect command for each world, when you want to open it. In order to switch between open worlds, you use the /fg name command, which makes the named world the foreground world and displays anything which has arrived from that world while it was in the background. There is also a /world name command, which combines the functions of the /connect and /fg commands: if you are currently connected to a world, it brings it to the foreground, or if you are not connected it connects you (and then brings it to the foreground).

There is one other command which is useful when you have many worlds open, and that is /listsockets, which prints a table listing all the open worlds, as well as which is the foreground world, how many lines are waiting for you, and some other information.

One of the supplemental files supplied as standard with TinyFugue is also useful here. I will go into these in more detail later, but if you type /require, it will record whenever something arrives in a background world, and add it to a list of active worlds. It will also set up the key Meta-W, otherwise known as Alt-W or ESC-W, to go to the next world on this list, or the previous world you visited if there are no remaining active worlds. I personally find this invaluable.

Section 2.6: Fine-tuning TinyFugue

Having hopefully got TinyFugue up and running, it is time to start fine-tuning our working environment. There are a lot of variables and commands[8] in TinyFugue which change small aspects of the program's behaviour. I have listed here a few of the more important ones, together with examples; more information can be found in various places in the /help if you need it. Note that setting a variable to a given value is done using /set variable=value, so for example to get a 24 hour clock you would type /set clock=24-hour; more details of this are in section 3.1.

[8] In fact, most of these commands simply change the value of an associated variable, but using the commands is usually a better idea. In some circumstances, however (such as the /more command, which also prints a message), you may wish to set the variables yourself; if you get to this stage, I suggest you read the relevant /help pages.

This flag defaults to `off'; if it is set to `on' by typing /cleardone on, the input window in visual mode is cleared whenever return is pressed, rather than scrolling.

If set to `12-hour' (the default), gives a 12 hour clock on the status line in visual mode, or gives a 24 hour clock if set to `24-hour'.

By default, TinyFugue strips ANSI codes from the text it gets sent, converting the colours into it's own internal format and discarding the rest. This variable changes this behaviour; see /help emulation if you run into problems with this. Otherwise, it is probably safe to leave it alone.

Use this command to set the number of lines displayed in the input area, if you want more or less than the default of 3. It is called as /isize n, where n is the number of lines you require.

Use this command to make TinyFugue pause after every screenful of output, to give you time to read it before continuing. Unlike most pagers, TinyFugue uses the TAB key to go onto the next page to let you keep typing; if you really can't cope with this, look at `' in the library directory. You can also use Meta-H to scroll half a screen, Meta-L to scroll a single line, and Meta-J to junk all the remaining text which you haven't seen.

Normally, if you close the last open world, TinyFugue will print `---- No World ----' and wait for you to do something else. If you set this to `on', using /quitdone on, TinyFugue will exit altogether when you close the last world.

This is the command that determines whether you are in visual mode or not. Visual mode, as mentioned above, separates the input from the output and adds a status bar, and is highly recommended. It is enabled by default in versions of TinyFugue after 3.5 alpha 17; if you have an earlier version than that, you can turn it on using /visual on. Note that visual mode is not available on all terminal types.

By default, TinyFugue will attempt to word wrap all incoming text to fit on the screen (use /wrap off to disable this), but not in log files. If you set this variable to `on', it will wrap in logfiles as well.

If you have wrapping enabled, this variable sets the indentation of any wrapped lines, excluding the first. This makes it much easier to separate paragraphs, especially if you hang around with people who tend to type a lot at a time. It defaults to zero; use /wrapspace n to set it to n (I use 2).

<| Anyone else got any favourites? |>

Section 2.7: Saving Your Configuration

Having gone to all the trouble of setting it up, you don't want to have to retype everything whenever you reload TinyFugue. This section tells you how to go about saving all the configuration which you have done so far.

There are two places you can put this sort of information: in your personal configuration file (usually called `.tfrc', or `tfrc' on OS/2 with a FAT filesystem, and stored in your home directory), or in a file which is automatically generated by TinyFugue itself, and the two are not interchangeable. Most of the things which you create, as opposed to changing, can be saved automatically: this includes the worlds you have defined, as well as macro definitions, bindings, hilites, gags, triggers and hooks, more of which later.

First, the automatic ones. Worlds are saved using the command /saveworld, hilites with /savehilite, etc (see table 1); they are loaded again using the command /loadworld, etc. The filenames used depend on whether you are running UN*X or OS/2, by default. If ident is the `filename identifier' given in table 1, then the filename under UN*X is `tiny.ident', and under OS/2 it is `'. These defaults can be changed, however, by redefining the `name macro', which is simply a macro the body of which is the name of the file[9], in your `.tfrc' file. Note that if you decide to use this method, you have to include /loadsuffix commands in your `.tfrc' file for all the types you decide to save; they will not get reloaded automatically.

[9] For example, to store world definitions in a file called `' under UN*X, you would give the command /edit -i This is in fact the preferred way of naming files; this old UN*X way is for backwards compatibility.

Type Command Suffix Filename IdentifierName macro
World Definitionsworldsworld /WORLDFILE
Macro Definitionsdef macros /MACROFILE
Key Bindings bind bind /BINDFILE
Hilites hilitehilite /HILITEFILE
Gags gag gag /GAGFILE
Triggers trig trig /TRIGFILE
Hooks hook hook /HOOKFILE

Table 1: The different automatic save files and their associated commands

One final note about using the /saveworld command on later UN*X-based versions of TinyFugue[10]: since version 3.5 alpha 14, you will be warned whenever a password is loaded from a world-readable file. If you get this message, you will need to execute the command chmod go= filename from the shell prompt, where filename is the file in which your world definitions are saved, which will ensure nobody else will be able to access the file.

[10] Some OS/2 versions also give this message, but as you cannot change file permissions in OS/2, it is irrelevant. Either ignore it, or upgrade your version of TinyFugue.

Now we come on to your personal configuration file, often referred to as simply your `.tfrc' file. You can do just about anything in here, including defining worlds, hilites, etc, although it is recommended that you use the automatically saved files instead if you can, and certainly do not define something in your `.tfrc' file which you are actually using the automatic files for! Other than these, there are a number of things you can do in this file which you cannot do anywhere else, including:

  • Load other TinyFugue source files. If it is a file you wrote yourself, you will probably want to use the /load filename command, but in the more usual case of a library file, the best command to use is /require filename, which loads the named file from the TinyFugue library directory if it is not already loaded[11].

    [11] This doesn't work for your own files, though, unless you've set them up specially.

  • Load the automatic files detailed above.

  • Set global variables and options, as in section 2.6. Just add the commands to the file just the same as when you type them in TinyFugue.

  • If your file gets very long, you may well want to put some comments in to remind you what the different parts do, or simply to make it easier to read. In order to do this, you can begin the line with a semicolon `;', and anything you type after that will be ignored.

Section 2.8: Using History and Logging

TinyFugue stores many different types of text, including what you have typed, the output from each world, and everything output by TinyFugue, in what is called history buffers. You can access all of these buffers using the /recall command, although the history of things you have typed can more easily be accessed using the `Ctrl-P' and `Ctrl-N' keys to move through it.

The /recall command is very powerful; I shall only skim the surface here. It can be called as /recall buffer-switch range, where range is a plain number, in which case it means to recall that many lines, or something of the form `from-to', where from and to are line numbers in history, either or both of which may be omitted. If you leave out the from parameter, you will have to put `--' before it in order to stop it being processed as a switch, for example as /recall Legends -- -30.

The buffer-switch parameter can be any of the following:

`-w' or blank
Recall from the history of output from the current world.

Recall from the history of output from the named world.

Recall from TinyFugue output.

Recall from global output, which includes output from all worlds and from TinyFugue.

Recall from input history, ie the list of things you have typed.

It is also possible to include a pattern after the range, in which case only lines matching the pattern are displayed. See section 3.3 for more details on patterns; if you are using a pattern, you may prefer to give the range as `/n', which means the last n lines which match the pattern.

You can also save the output from a world in a file on disc, using the /log buffer-switch state, where buffer-switch is exactly as for the /recall command, except that you cannot leave it blank, and state is either `ON', to turn logging on, `OFF', to turn logging off, or a filename, to turn logging on and log to that file. Only one file may be open for each buffer-switch. If you switch logging on without specifying a filename, it goes to the file named by the LOGFILE macro, which defaults to `tiny.log'.

<| /relog? |>

Section 3: The Building Blocks

This section introduces some of the concepts which will be useful in the next section, when we go on to defining macros and triggers.

Section 3.1: Setting and Viewing Variables

A variable is, quite simply, a `slot' in which you store information. The sort of variables we will discuss here are what is called global variables, which is to say that setting them inside a macro is exactly the same as setting them directly[12]. The way to set a variable called var to have the value val is to type /set var=val, either directly or as part of a macro (see section 3.2). You can see what the variable is set to by typing /set var on it's own.

[12] The other type are called local variables, and if set inside a macro are not usable outside of it; these are only really useful when you get on to reasonably advanced macro programming. In a lot of ways, though, they are the same, but they use the command /let rather than /set to define them.

There is also another way to access variables which can only usually be used easily from macros[13], which is to use what is called a percent substitution. There are many different types of these, but the one that concerns us here has the format %{var}, which in a macro body would be replaced by the value of the variable named var. For more details of this, type /help substitution, and make sure you have set /more on!

[13] Usually, to use this directly, you would prefix the command by /eval; for instance, /eval /echo % The TinyFugue library directory is %{TFLIBDIR}. would display `% The TinyFugue library directory is /usr/lib/games/tf/tf-35a19/.' on my system.

Section 3.2: A Brief Introduction to Macros

I won't go into the details of writing macros here, but some of the basics are well worth knowing before we start to discuss triggers and other such things. In essence, a macro is a way of defining your own TinyFugue commands, and in fact a large proportion of the standard TinyFugue commands are actually implemented as macros. Macros can have a name, a body, which is the list of commands which it executes, a maximum number of times it can be executed, a chance of happening, a set of conditions under which it will automatically execute, and more besides, but in this section we will look only at simple macros which you have to call yourself, and which don't do anything fancy.

The simplest macro is one whose body is not a TinyFugue command at all, but just some text which is sent to the current world. For example, the following command:

/def insultmary=say Mary is really quite a silly person
would send the text `say Mary is really quite a silly person' to the game whenever you typed /insultmary, presumably resulting in output similar to `Rolo says "Mary is really quite a silly person"'.

This has quite a few drawbacks (not to mention the likelihood of getting frowned at by Mary!), but the most major of these comes if you are very grumpy, and want to be able to insult lots of different people. Of course, you could define commands called /insultfrodo, /insultmortitia, and so on, but it would be much easier and more flexible to be able to type simply /insult Rhubarb and have TinyFugue put the name in the right place for you. This is done, again, through percent substitution, but instead of the name of a variable you put the position where the text is expected to appear after the command, as a number. So in the above example, where we only wanted one word, we could define our /insult command as follows:

/def insult=say %1 is really quite a silly person

This would get boring after a while, though; it would be nice to be able to choose our insult. Given the comments above, we would be tempted to try something like

/def comment=say %1 is %2
in order to allow us to type `/comment Custard the wrong colour' and have it transmit `say Custard is the wrong colour', but unfortunately this doesn't work. The reason? The `%2' would take only the second word, rather than everything that remains, and so we would only say `Custard is the'. Instead, we must replace this with
/def comment=say %1 is %{-1}
where `%{-n}' stands for `all words except the first n'.

The other thing which is worth mentioning at this point is regexp substitution, which is (you guessed it!) another type of percent substitution. It is only really worthwhile when using triggers (see section 4.2), but this would seem the logical place to talk about it. If you define a macro which, at any point, uses a regular expression (this is usually in the pattern of a trigger, but can be for example in the regmatch() function), you can use parentheses to denote different parts of the regexp which can then be extracted later. So, for example, if you define a macro which triggers on the regular expression `(Flopsy|Mopsy|Cottontail) has arrived.', your macro can include `say Hello, %{P1}' in the body in order to automatically greet Flopsy, Mopsy or Cottontail whenever they arrive[14]. This explanation has been deliberately left vague, because of complications in using regexp matching; triggers are explained in much more detail in section 4.2 below.

[14] Although these so-called `auto-greet' macros are popular examples, they quickly become annoying and are not recommended for actual use!

The astute among you will have noticed that so far, I haven't mentioned how to use TinyFugue commands inside a macro. To start with, you are less likely to want to do this, but there will probably come a time when you want to do more in your macros than just output text. The solution is simple, however, as you may have already guessed: enter them just like when you type them in directly! (In other words, TinyFugue macros are always preceded by a slash, which tells TinyFugue to process the command itself rather than sending it to the MUD.) In order to enter more than one command---or to send more than one line to the MUD---you can separate them with the string `%;'[15]:

/def insult=say %1 is really quite a silly person%;/duck
would execute the TinyFugue command /duck whenever you insulted anybody.

[15] Note that this does not work if you enter it directly. If you wish to do this (so you can repeat the command using the Ctrl-P key, for instance), either define a macro to do it or precede the entire line with the /eval command.

It is possible to see the definition of a macro, using the /list name command, which displays the definition of the command name, or all (non-invisible) defined macros if name is not given. You can also give switches to /list, just like to /def, which will make it display only those macros defined using those switches. So, for example, the command /list -t would display all trigger macros. In order to change a macro which you have written, you cannot just use /def[16], but instead you have to use the /edit command. This has exactly the same syntax as /def, but can only be used when the named macro already exists. If you must use /def, though, or if you wish to simply get rid of a macro, the command is /undef name, which removes the definition of the macro name.

[16] In fact you can do this, if you set the variable redef to `on'; this is disabled by default, however, and I would recommend that you leave it like this unless you know what you are doing.

And finally, a note about defining commands with no name. This is perfectly possible to do, and can be quite useful when defining triggers etc to save you having to come up with unique names for them all, but as soon as you try to edit or remove it, you will run into problems. However, if you give a /list command with appropriate arguments, you will be able to find it's macro number (see figure 2), which can be used to refer to it in the commands /undefn num or /edit #num which act just like their named counterparts.

  % 410: /def -p1 -P1BCgreen -mregexp -F -t'([Ff]es(ber)*(ter)*)' 
  % 411: /def -p1 -P1Cyellow -mregexp -F -t'([Ll]ath(en)*)' 
  % 412: /def -p1 -P1Cyellow -mregexp -F -t'([Tt](ie(mar)*|ack))' 
  % 413: /def -p1 -P1Cyellow -mregexp -F -t'([Aa]q(uila)*)' 

Figure 2: Sample output from a /list command, showing some macros without names

Section 3.3: Matching Text

There are a lot of times when you want to compare some text (perhaps received from a MUD, or typed in by the user, or whatever) against a certain pattern, and act differently according to whether it matches or not. TinyFugue has three different types of pattern, which I will explain in this here. What I will not do is to give any examples of code, but all of section 4 is about matching text, and doing various things with it. The three matching styles are known as simple matching (`simple'), glob matching (`glob'), and regular expression matching (`regexp').

Simple Matching

As the name implies, this is the simplest of the lot, and just compares the two strings character-by-character, treating uppercase and lowercase characters as being different. As a quick example, `text' would match `text', but not `Text' or `more text'.

Glob Matching

Those of you who are used to the UN*X shell may have come across the name `glob patterns' before, referring to the way the shell does it's pattern matching, and indeed glob matching in TinyFugue is very similar to this. There are a small number of special characters, listed below, and apart from these few any character stands for itself, uppercase and lowercase being considered to be the same for these purposes. The special characters are:

The asterisk matches any number of characters, including none. So `A*' would match any word beginning with the letter A (uppercase or lowercase), or `* pages: *' would match any line like `Jimbo pages: I'm coming in to land, Chief'.

The question mark matches any single character, for example `c?t' would match `cat' and `cot', but not `coot' or `ct'.

A pair of square brackets containing characters will match a single character if it is the same as any of the characters between the brackets. Ranges can be given using a hyphen, `-', so you could use `[0-9]' instead of `[0123456789]': `[a-z]*' would match any line beginning with a letter, but not something like `10000 bottles of beer on the wall'.

If the first character in the set of square brackets is a caret, the matching is reversed and a character is matched if it is not in the set. A pattern `[^a-z]*' would therefore match `10000 bottles of beer on the wall', but not `ten thousand bottles of beer on the wall'.

A set of patterns in curly braces matches if one of the patterns matches a single, whole word. That is to say, `{soft|loud}ly' would not match either `softly' or `loudly', but `{one|two} more' would match either of `one more' and `two more'. Note that you can have as many different patterns as you wish: zero (ie, the pattern `{}') matches the empty string, and one (eg `{word}') matches just a single word. This is a better way of doing it than putting spaces at either end, which would not match at the beginning or the end of a line. Note also that you cannot put more than one word inside each set of curly braces.

\The backslash can be used to remove the special meaning of a special character, including itself, so in order to match `[\*]' you would use the pattern `\[\\\*\]'.

Note that, unlike regular expressions, the pattern must match the entire string. So to match one word in the middle of a line, you must put an asterisk at either end of your pattern, like `*Dark Avenger*' rather than `Dark Avenger'. To match a word at the beginning of a line only, of course, you would use a pattern such as `Pusspuss *'.

Regular Expression Matching

Regular expressions are even more complicated than that. I will only give a summary here; the TinyFugue help about them is pretty complete, so you can look at /help regexp for more information. I have tried to put in a lot of examples, but if you can't understand it on your first read through I suggest you skip it, and come back later.

Regular expressions can be thought of as a sequence of `things', which we will refer to as atoms. The simplest atom is a single character, which (unless it's a special character, more of which later) matches itself. Atoms can be put one after the other to make the regexp, and so the pattern `abcd' would match `abcd'. Note that a regular expression matches a line of text if it matches anything within the line, as distinct from a glob pattern which must match the whole line, so the regular expression `abcd' above would also match `abcdefg' and `The slug said "abcd"'.

In addition to these, there are some other single-character atoms, which have special meanings inside a regular expression:

A period or full stop matches any single character in the string, very much like the `?' character in a glob pattern; using the same example as above, the regular expression `c.t' would match any line containing `cot' or `cut', but not one containing `coat' or `ct' (unless it also contains a string that matches; for example, the string `I have cut my coat in half' would be matched, but `I have ripped my coat up' would not).

A caret matches the beginning of the line: `^Helen' would match the string `Helen' at the start of the line, such as `Helen waves', but nowhere else.

Similarly, a dollar sign matches the end of the line, so `^Helen$' would match only a line consisting of only the word `Helen'.

To remove the special meaning of these (or any other) characters, precede them with a backslash. So `mat\.$' would match `The cat sat on the mat.', but not `the cat sat on the mat!'.

There are some characters you can add to the end of any atom to change the way it matches. You can only add one of these characters to each atom. They are:

The asterisk matches zero or more successive occurrences of the atom, for example `baaa*' would match `baa' and `baaaaaaaa', or `^My.* valentine\.$' would match any sentence starting with `My' and ending with `valentine.', such as `My funny valentine', `My valentine', `Mycroft's valentine' or `My Irish Wolfhound is your valentine.'.

An atom followed by a plus sign matches one or more successive occurrences of the atom. For example, `baa+' would also match `baa' and `baaaaa' (note there is one less `a' in this version!), or `a+r+g+h+' could be used to tell when people fall off buildings (can you see what it would match?). Note that `^My.+ valentine\.$' would not match `My valentine', although it would match if there were two spaces instead of one.

Finally, the question mark matches one or zero occurrences of the preceeding atom: for example, to match the words `cot' or `coot', you could use `coo?t', where the question mark tells TinyFugue that the second `o' is optional.

And finally, there are some longer atoms which you can use. These behave exactly the same as the single-character atoms given above, but remember that if you add a *, + or ? to the end it applies to the entire atom, not just the last character. These longer atoms are:

As mentioned above, the backslash removes any special meaning from the character c. You can use a backslash on a character which has no special meaning, and it will still count as a single atom; to get a backslash, use `\\'.

[chars] and [^chars]
As in the glob patterns above, a set of characters within square brackets matches any one of those characters, or any character which is not in the set if the first character in the set is a caret. However, in a regular expression this is much more flexible, as you can combine it with the `*', `+' and `?' characters in ways such as `[0-9]+', which matches any number, or `^[^A-Z]*$', which would match any line which contains no uppercase characters.

This construction, in a similar way to the `{pattern1|pattern2|...}' construction in a glob pattern, matches any one of the regular expressions in the list, although it does not stipulate that it should be a whole word. However, it has the same advantage as the `[...]' given above, in that you can use the `*', `+' and `?' characters to build really quite complicated regular expressions. So, for example, the regular expression `Fes(ber|ter)?' would match `Fes', `Fesber' or `Fester', or `((one|(apples?|| and |\.))+' would match a simple shopping list such as `three apples, three oranges and a pear'.<| This probably ought to be checked at some stage ...! |> There is also a completely different use for this same construction, although you can use it for both purposes at the same time, and that is in regexp substitution, as mentioned in section 3.2. Each expression in parentheses like this gets given a number, starting from the left with 1, and then you can reference the text that was in that set later; this can be used to extract the name of the person who has just arrived in order to greet them, for instance, using the regular expression `^([^ ]*) has just arrived'.

And that is---finally---it. The TinyFugue online help (/help regexp) gives a more technical description of regular expressions, and I think a slightly more complete one, as well as a very useful comparison of glob patterns and regular expressions.

Section 4: Reacting Automatically to MUD Output

Having waded through all that, we now come on to the useful bits: how to get TinyFugue to notice what comes from the MUD, or from other places, and to react accordingly. This is done---you guessed it!---using the pattern matching methods which you have already met.

For a lot of the things mentioned below, there is a special command to do it, like the /hilite command which marks the entire line containing the text. However, all of these commands are actually macros, which are exactly equivalent to certain forms of the /def command (/hilite pattern simply does /def -ah -t"pattern", for example), and we will have to use these more complicated forms in order to get different styles of highlighting. For now, though, I will simply give in passing these equivalences, and you can refer back to them later.

There are two global variables which control the way hiliting works: matching and hiliteattr. matching can take one of the values `simple', `glob' or `regexp', and determines the default matching style used by commands such as /hilite; this defaults to `glob', and I shall assume this in all the examples which follow. hiliteattr changes the attribute (effect or colour) used by these commands, and defaults to bold (see /help hiliteattr and /help attributes for details on how to change this variable). Both of these can be overridden using the more complicated /def forms of the commands, which we will see how to do in section 4.4.

Section 4.1: Hiliting MUD output

To start with, when you use TinyFugue, all the output will come up the same colour. This is usually fine, except that when there is a lot of spam, it can be quite difficult to read it all. For this reason, TinyFugue provides a mechanism whereby you can spot certain strings in the text coming from the game, and hilite the entire line which contains them. This is done, as mentioned above, using the /hilite pattern command, which is equivalent to /def -ah -t"pattern". pattern uses the matching style given in the matching variable.

For example, to hilite whenever anybody pages you[17], you would give the command

/hilite {*} pages: *
(or /def -ah -t"{*} pages: *") and to hilite any line which contains `Apeman', often because it is your name, you would use
/hilite *Apeman*

[17] In fact, there is a command /hilite_page which does this for many different paging styles, which you would probably use instead of doing it this way.

Hiliting the entire line is quite limiting, though, especially if you're talking quite a lot or if you want to notice some other people's names being mentioned as well, and so there is an alternative called partial highlighting which highlights only the text matched by the pattern. The command for this is /partial pattern, which is equivalent to /def -Ph -F -t"pattern". Now, you may remember that glob matching and simple matching can only match entire lines, so we can't use them here: what's left is regular expression matching, so the pattern must be a regular expression. The highlighted part is that covered by the entire regular expression.

For example, if as above you were called Apeman, or Ape to your friends, you could give the command

/partial [Aa]pe(man)?
and your name would be hilited whenever it appeared, even if whoever typed it forgot the capital letter. This is also useful for `chat channels' or whatever:
/partial ^KewlKroo>
would hilite the `KewlKroo>' prefix on any line on the Kewl Kroo's chat channel, rather than the whole line like /hilite would, which makes it much less intrusive.

Section 4.2: Using Triggers

The major reason for using hilites, as demonstrated above, is to draw your attention to important information which you might otherwise miss. There are some things in this category, though, which simply require you to do something in return; for example, if somebody tries to kill you, or just if somebody waves and you want to wave back. This is what triggers are for.

A trigger is just like a normal macro (see section 3.2), except that rather than waiting for you to run it, it is executed automatically whenever certain text is received from the MUD. The command to do this is /trig pattern=body (or /def -tpattern=body), where pattern is the text you want to trigger on, in the style given by the matching variable, and body is just like the body of a normal macro. For example, if you are on many games at once and there is a lot of background noise, you need to know immediately if somebody starts trying to kill you. The command

/trig {*} tried to kill you!=/echo !!! Attempted murder !!!
would set up a trigger to warn you, but not very helpfully; better would be the command
/trig {*} tried to kill you!=/eval /echo !!! Attempted murder !!!%; /fg ${world_name}
which would also switch to the offending world[18]. It is not, unfortunately, possible to easily extract and use the name of the person using the /trig command, but it can be done with the more flexible /def command: see section 4.3.

[18] The ${world_name} construction inserts the value of the `special macro' world_name, which contains the name of the current world. You can also use world_character, world_password, world_host, world_port, world_type and world_mfile. I call these `special macros' because the ${...} construction is that used to substitute the body of a macro, but you cannot run these macros.

There are, however, some other forms of the /trig command which allow you to modify it's behaviour somewhat. The first, /trigc chance pattern=body, gives the trigger a certain chance out of 100 of executing when the text which it matches arrives, and is otherwise exactly the same as the /trig command. The /trigc command is equivalent to /def -cchance -tpattern=body.

The other change you can make is to give the trigger a priority over other triggers. This usually defaults to 0, but can be set to any value below about 2147483647! When a piece of text arrives, TinyFugue searches through the macros which match it in order of this priority, and executes the first one it comes across. If there are more than one of the same priority, one is chosen at random. This allows you to exclude certain cases from matching, by defining a (possibly empty) trigger of higher priority, as in the following example which would wave back to anyone who waved, unless it was you:

/trig {*} waves=:waves
/trigp 1 Apeman waves

You can also change both these things at once, using the /trigpc priority chance pattern = body command.

Section 4.3: More Flexible Hilites and Triggers using Regular Expressions

Recall the trigger in section 4.2, which would print up a message and switch worlds whenever somebody tried to kill you, but could not attack them back. There is in fact a way to do this, but unfortunately it requires a regular expression pattern, and so we cannot do it with the simple /trig command (unless we set matching=regexp), but must use /def instead.

The /def command has a switch which will set the matching style for a particular trigger, the `-mstyle' switch, where the styles are the same as those for the matching variable. So, for example, the command

/def -t"^[^ ]+ waves$" -mregexp=:waves
would catch whenever anybody waves and wave back, just like the trigger
/trig {*} waves=:waves
in section 4.2 above, if rather less efficiently (note that both of these still need to be overridden for the case `yourname waves').

The use of regular expressions, in particular, has one major advantage: the ability to extract certain parts of the pattern and re-use them in your macro (see sections 3.3 and 3.2), by enclosing them in parentheses and using the `%{Pn}' substitution, where n is the number of the particular set of parentheses. So, for example, the following trigger would wave back to anyone who waved:

/def -t"^([^ ]+) waves$" -mregexp = :waves to %{P1}
or, finally, we can attack somebody back when they start trying to kill us:
/def -t"^([^ ]+) tried to kill you!$" -mregexp = kill %{P1}
Note that this would only try once for each of their tries, which is still not ideal, but it would give you a bit of time. See appendix ?? for a more sophisticated trigger to deal with this situation.

This sort of thing can also be done with partial highlighting, using a special form of the `-P' switch. Recall that in section 4.1, we said that the /partial command was equivalent to the /def -Ph -F -tregexp command. If, instead of `-Ph', we use `-Pnh', where n is the number of our parenthesised expression, it will hilite only that part of the regular expression[19]. For example, <| there must be a better example? |> on a TinyMUSH game, objects are referenced by dbref numbers, written as `#123', but when you look at some objects it also tells you the flags which are used by the object, as single letters, for example `#123Rh' indicates that object #123 has the ROOM and HALTED flags set on it. The following command will hilite these flags[20]:

/def -F -P1h -t"#[0-9]+([A-Za-z]+)"

[19] Notice the lack of the `-mregexp' switch in the above command; it will not do any harm to include it, but as mentioned in section 4.1, partial hiliting requires a regular expression anyway.

[20] In itself, this is not very useful, but when combined with a command to hilite the dbref in a different colour (see section 4.4) it can make displays much easier to read.

Section 4.4: Changing the Attributes

Code Name Meaning
n normal Do not apply any other attributes to this
g gag Do not display
G norecord Do not record in history
u underlineUnderline this text
r reverse Display this text in reverse video
f flash Make this text flash
d dim Display this text dimmed
B Bold Display this text brightened or made bold
b bell Beep when this text is displayed
h hilite Display this text using the attributes in hiliteattr
CcolourColour Display this text coloured (see table 3)

Table 2: The available attributes and their one-letter codes

black bgblack
red bgred
green bggreen
yellow bgyellow
blue bgblue
magenta bgmagenta
cyan bgcyan
white bgwhite

Table 3: Colour names for use with the `C' attribute

When hiliting text, you are not stuck with just the one way to do it. Of course, if you only have a monochrome display, you will not be able to use colour, but if your terminal supports it you can still gag text (tell TinyFugue not to display it), put it in reverse video, beep when it arrives, underline it, make it flash, and more. A complete list of all available attributes is in table 2.

These attributes are entered using the `-aattr' (for hiliting) or the `-Pattr' and `-Pnattr' (for partial hiliting) switches to /def, using the forms of these commands given in section 4.1. More than one attribute may be entered by simply listing their characters one after the other, except for the normal (`n') attribute, which is rather pointless to combine with any other, and the Colour attribute `Ccolour', which must always be the last named so TinyFugue can read the name of the colour properly.

For example, the following would highlight the name `Anna' in bold red whenever it appeared as a word on it's own:

/def -F -P2BCred -t"([^A-Za-z0-9]|^A-Za-z0-9]|$)"
or to make any whispers show up in reverse video and beep when they are displayed, you might use
/def -arb -t"{*} whispers*"

Section 4.5: Redefining the Keyboard

Although TinyFugue comes with it's own set of built-in key bindings, it is possible to define your own, and to change the existing ones, quite easily, using the /bind sequence = command command. Any legal macro body is valid for the command, so you can use key sequences to send strings to the current world, to execute any existing TinyFugue command or macro, and as a macro themselves to do complex operations, although it is usually better to write the macro seperately and then just bind a key to call that macro. To remove the binding again, just call the /unbind sequence command with the same key sequence.

The key sequence can be anything you can type, although it is strongly advised that it does not begin with a common character such as a letter, since once you type a key which starts a keybinding, nothing is displayed until what you have typed could no longer match the binding. For example, if I bound the key sequence `john' to enter `John_The_Fish' into the input buffer, then while I was typing `johan' I would have to wait until the `a' before anything was displayed! Instead, I should bind a key sequence such as `~john', or `Ctrl-X john', or even just `Ctrl-X j', which I would not want to type under any other circumstances.

The sequence parameter to the /bind command can be any string, but in order to allow you to easily include control characters in the sequence, the following conversions are applied to it:

  • Any pair of characters of the form `^c' is converted to the key combination `Ctrl-c', if possible. (In order to enter a literal `^' character, you can use `\94'; see below.) For example, the escape character could be entered as `^['.

  • A number preceded by a backslash, which does not begin with a zero, is treated as a decimal character code; in this way, the escape character is `\27'.

  • A number preceded by a backslash which does begin with a zero is treated as an octal character code, so `\033' also represents the escape character.

  • The string `\0x' is followed by a hexadecimal number, and again represents a character code, making `\0x1B' represent the escape character.

In order to bind to a character together with the Meta (or Alt) key, precede the character with an escape character.

There are two other TinyFugue commands which are very useful for binding to keys: the /input and /dokey commands. The /input string command simply inserts the string into the input buffer as if you had just typed it; for instance,

/bind ^xj=/input John_The_Fish
would make the key sequence `Ctrl-X j' be the same as typing `John_The_Fish'. The /dokey name command performs the function of the predefined editing key name; see /help dokey for a list of these and some examples. This is mostly for redefining the basic TinyFugue bindings.

Section 4.6: Using Hooks and Hiliting TinyFugue Output

In the course of a session, TinyFugue will do lots of things for you, such as connecting or disconnecting from a world, changing the current world, loading a file, etc. It can sometimes be useful to be able to execute a command whenever one of these events happen, and the mechanism by which you do this is called hooking. TinyFugue provides 30 or so different parts of it's execution which you can hook into and execute your own commands, using the /hook command.

This command has the syntax /hook hook = body, where body is a standard macro body, and hook is the name of the hook to catch, optionally followed by a pattern. This command is equivalent to /def -h"hook" = body. The name of the hook, and the format of the pattern, can be found under /help hooks, but just to whet your appetite, here are some examples:

This hook is called whenever the foreground world (ie, the one you are looking at) changes.

`WORLD FarSide'
A hook with this text would be called whenever the foreground world became equal to `FarSide'.

Called if the game closes the connection (but not if you use the /dc command).

Called whenever something happens in a world. Some people like to /hook ACTIVITY=/fg %1 to automatically switch to any world where something happens.

Hooks have another use, which is perhaps not so well documented, which is to change the attributes of a lot of the messages which TinyFugue displays to you. This can be done quite simply, using the /def form and including a `-a' switch exactly as for hiliting in section 4.4. You do not need to include a body if you are doing this. For instance, to make all the `---- World world ----' messages come out bold, you would use

/def -hWORLD -aB
and to hide all the `% Loading commands from file filename' messages, you could use
/def -hLOAD -ag

Section 4.7: Other Useful Switches to the /def Command

A lot of the switches which the /def command takes, and which we have given above, can be used together; for example, it is quite permissible to define a macro which hilites a piece of text as well as acting on it as a trigger (the macro above which notices when you get killed would seem to be a prime candidate for this). However, there are more switches to /def which we haven't mentioned above, or which we have touched on only briefly, and I would like to tell you about some of them here. As ever, though, read /help def for more information about these, and about the others which I won't mention here.

Makes the macro a multi-shot macro, which will be executed shots times and then deleted. If shots is zero, the default, it will be a permanent macro.

Useful for triggers, hilites and hooks, this switch makes the macro triggerable only by text or events from the named world world.

Just like the `-w' switch, but applies to a world type instead of a single world (see section 2.4).

Again, this is mostly useful for triggers and hooks (and is in fact recommended for most of these), and means that the macro does not mask any with a lower priority.

Appendix A: TinyFugue Supplemental Files

As well as `', the file containing many of the standard TinyFugue commands, TinyFugue comes with a lot of other files which you can load and use. Some of these are useful, and some are just to demonstrate various aspects of coding; it is the useful ones which I have listed here. If you are interested in coding, I suggest you look through your library directory to see what else is in there. Most of the files have instructions or comments in them, to tell you what they do.

Provides a way of calling simple macros without a leading slash, making them appear like MUD commands.

Execute a given command at a given time.

Like filename completion in Bash etc, this allows you to type part of a word, and then press a special key and have TinyFugue fill in the rest for you.

Provides facilities for transferring files between TinyFugue and a MUCK or LP-Mud. See /help putfile and /help getfile.

Just like UN*X finger, allows you to get information about a user on a remote host.

Keyboard bindings like the Bash shell for UN*X.

Keyboard bindings like the Emacs editor.

Defines `ESC-DOWN' and `ESC-UP' to push and pop the contents of the current input line onto a stack. This is useful if you are in the middle of typing a long line and you want to wave to somebody, or similar!

Remembers and replays movement commands, similar to TinTin.

Utilities to quote text from various sources; a simplified interface to the /quote command.

Starts logging to a file, and performs a recall to the file.

Get an RWHO list from a MUDWHO server. Must be loaded after you define your worlds.

Allows you to press space at a `--More--' prompt, rather than tab.

Implements ``speedwalking'' a la tintin: you can type multiple single-letter directions on the commandline, optionally preceeded by a number, and it will execute them seperately for you the required number of times.

Lets you type `ESC-S' and TinyFugue will spell-check the current input line. Requires the spell program on your system.

Sample `.tfrc' file.

`Tick counting', similar to tintin, useful on Diku MUDs. Don't ask me what it is, though...<| anyone? |>

Emulates most of the commands from tintin++, to varying degrees of accuracy.

Various small but useful macros, including /reedit to get the definition of a macro into the input buffer for editing, and /xtitle to change the title of an xterm window. See /help tools.

Tells you when a player logs onto the MUD.

Constructs a queue of worlds which have become active in the background, and defines `ESC-w' to switch to the next one. Very useful.

Appendix B: Some Sample Macros

The aim of this appendix is to give some slightly more comprehensive and useful macros than the ones which are used as examples in the text. I have tried to attach a reasonable commentary to each one, but you may find you have to look at the /help for some of the commands.

<| If anybody has any reasonably simple macros which they think would go nicely in here, please feel free to send them to me and I'll put them in. |>

Appendix B.1: Automatic Reading of All Help Pages

The following macro, still quite a simple one, was written because the TinyMUSH help text is mostly split up into 25-line chunks for those who don't have a MUSH client, but I got bored with having to keep typing more as I read through the help, and it really seemed a shame not to use TinyFugue's paging instead. It looks for lines of the format

`{ 'command' for more }'

and executes the command, so it is not limited to help.
/def -T(tiny\.mush(\..*)?) -ag -mregexp -t'^{ \]+)\for more }' = %{P1}
Notice that it is set to only work for worlds of type `tiny.mush' or subtypes thereof, and that it gags the line so it is not displayed.

Appendix B.2: A Complex Kill Trigger

This is an extension of the examples given in sections 4.2 and 4.3, to notice when somebody is trying to kill you and try to kill them. It is a long way from perfect---in particular, it requires a message from the game before each try, in order to prevent it totally spamming the game and leaving twenty commands in the queue when you get killed, it gives an ugly message about the att_name_retry macro not being found, and it will probably crash and burn horribly if somebody with the same name attacks you in more than one world at once, or if more than one person attacks you in the same world---but it should at least serve as a starting point, and it illustrates a few techniques of macro programming. You may have to change the message format for your server. Note, though, that due to the network transmission not being instantaneous, you should never use this macro in your own home (or wherever you get sent when you die), as there will quite probably be a command still in the queue when one of you dies, and if it is you who dies, the fight will just go on!

Since some of these are long macros, I have split them over several lines, terminating each line with a backslash. You will not be able to type it in like this, but if you put it straight into a `.tf' file, you can use /load and it will be read fine.

  /def -mregexp -ah -t'^([^ ]*) tried to kill you!$' = \
    /if /eval /test $${att_%{P1}_retry} =~ ""%; /then \
      /attack %{P1}%; \
  /def attack=\
    /def -msimple -t"Your murder attempt failed." att_%1_retry=kill %1%; \
    /def -mglob -t"You killed %1!" att_%1_done=/att_%1_stop%; \
    /def -mglob -t"%1 killed you!" att_%1_dead=/att_%1_stop%; \
    /def att_%1_stop=/undef att_%1_retry%%; \
      /undef att_%1_done%%; \
      /undef att_%1_dead%%; \
      /undef att_%1_stop%; \

This is rather more complicated than any you may have met before, so I shall go through it step-by-step. The trigger defined by the first /def simply waits for somebody to try to kill you, and then (unless you're already in the middle of a fight) calls the /attack command with the name of the person who just attacked you. The attack command define a trigger for when you fail to kill somebody, which simply tries again. It also defines triggers for when you kill them, and when they kill you, which both call the final command which removes all the commands the /attack command has just defined. And finally, it starts the ball rolling by trying to kill them (by calling directly the trigger we defined earlier).

As an example, suppose we have two people called Alice and Bob. Alice, being really rather cunning, has installed this set of macros, but Bob has not. Unfortunately for him, Bob then tries to kill Alice, and the following happens:

  1. If Bob manages to kill Alice first time, nothing triggers. Otherwise, Alice's TinyFugue catches the `Bob tried to kill you!' message, and because she isn't already attacking him, executes /attack Bob, which defines macros /att_Bob_retry, /att_Bob_done, /att_Bob_dead, and /att_Bob_stop, and then executes /att_Bob_retry.

  2. The /att_Bob_retry command sends the string `kill Bob' to the MUSH.

  3. If Bob has tried again to kill Alice, and failed, then the original trigger notices that Alice is already attacking Bob, and doesn't do anything. If he succeeds, Alice gets the string `Bob killed you!' from the MUSH, which triggers the /att_Bob_dead command, which calls the /att_Bob_stop command, and she goes to step 6.

  4. If Alice doesn't manage to kill Bob, she gets back the string `Your murder attempt failed.', which then triggers the /att_Bob_retry command again, and we go back to step 2.

  5. Otherwise, she gets the string `You killed Bob!', which triggers the /att_Bob_done command, which in turn calls the /att_Bob_stop command.

  6. The /att_Bob_stop command undefines all the Bob-specific triggers, and then stops. This means that Alice won't keep trying to kill Bob, and also that next time Bob tries to kill her, she won't get a load of messages about commands being already defined.

<| This macro hasn't been particularly well tested. As I said before, it's far from perfect, but if anyone happens to find any major bugs in it, please do let me know, and I'll try to correct them. |>