--by Joseph R. Poirier and Tod Foley
Several years ago I joined the Information Superhighway and took the on ramp marked MUD, or Multi-User Domain.... What makes these games so enticing is that they are a step up from chat and e-mail. Not only is it possible to assume whatever personality you want, but you can act any way you want. You can kill without ever seeing blood, roar threats or cross swords without ever breaking into a sweat.... With MUDs and other on-line multiplayer games, you can live vicariously in a new universe and even attain godhood, thereby achieving omnipotence.
Kristina Harris, "Godhood: Not All Fun and Games," "My Turn," Newsweek, July 18, 1994, p. 12.
One of the advantages of access to the Internet is the increased ability for people from all over the world to meet, and one of the major meeting places is interactive multi-user virtual realities. Also known as multi-user dungeons, or MUDs, these environments have expanded out of the game-playing niche into educational uses, corporate uses, and research. Some people call them multi-user domains in an attempt to de-emphasize the Dungeons and Dragons role-playing influences and highlight the multi-user side. Nowadays, the acronym MUD has been generalized to lowercase mud.
Muds are a cross between a text adventure game and a chat line. Muds contain a number of rooms, each having a description and possibly containing objects that players can use. Players of the mudknown as muddersmove from room to room around the mud. They can pick up objects and use them, meet and talk to other players, solve puzzles, play games, read mud newspapers, combat monsters, and explore virtual reality.
Muds are becoming more and more mainstream. Once the bastion of male computer science students who also liked a good role-playing game from time to time, they now attract all sorts of people from many walks of life. On a mud, you can bump into someone, male or female, who is a film director in real life, or perhaps an English professor, a high school teacher, a sociologist or an engineerall in the same room! Newsweek magazine, for instance, has mentioned muds several times in its new Cyberscope Department, a page-long collection of short articles related to Cyberspace.
There are some graphical muds being developed, but the vast majority are text-based. Some muds emphasize social aspects; others emphasize combat. On social muds, players tend to use the mud as a communication medium. Players talk to other players, create and play games, and join group discussions. On combat muds, players buy weapons and armor, join guilds, learn skills, and fight monsters to gain experience. With enough experience, a player can become a wizard on the mud. Wizards have the ability to create new objects, rooms, and puzzles on combat muds.
There are several major types of muds, such as MUCKs, MUSHes, MOOs, MUSEs, LP-MUDs, Diku-MUDs, and others. Each type of mud differs in the underlying programming environment. Sometimes you will see the word mud expressed as MU* in newsgroups and research papers. This is due to the various mud types that have those two letters in the beginning of their names.
Which type of mud you end up programming for (also known as building), often depends as much on happenstance as on deliberate decision. After all, one tends to hang out wherever one's friends hang out. But there are distinct differences from one variety of mud to the next, in both the structure of their programming languages and in the kind of players and builders they tend to attract. For what it's worth, just about any kind of virtual environment could be created in any of the mud languages, and yet certain languages have developed genre- or function-based followings, and have evolved further than other, lesser-used types of muds.
This chapter describes how to use and program several different types of muds: MUCKs, MUSHes, MUSEs and MOOs. LP-MUDs and Diku-MUDs, which are also very popular, are not discussed here. Many small examples will be presented throughout the chapter.
If you are totally new to muds, you might want to begin by reading through "Interactive Multi-User Realities: MUDs, MOOs, MUCKs and MUSHes," which can be found in The Internet Unleashed, an earlier SAMS book. That chapter describes mud basics in more detail. It describes several resources you can use on the Internet, such as newsgroups and FTP directories, to obtain more information on muds. It also details mud history and reflects on various social issues involving muds.
Here is a quick outline of mud expertise. It gives a few broad categories of mud programming knowledge so that you can see where your overall skills currently lie.
This chapter deals with beginner, intermediate, and some advanced levels. First, a quick summary of basic mud commands is presented. Following this is a tutorial that talks about intermediate and advanced mud programming issues in a step-by-step manner. Many examples, throughout the tutorial, will help you understand the concepts presented. After the tutorial, there is a detailed reference of mud commands, attributes, functions, and flags.
Throughout this chapter, commands and actions that you actually type will appear in monospaced type, and italicized type for placeholder terms for which you supply the appropriate item.
In addition, examples will use the > character as your prompt on the mud, to denote the commands you type as opposed to the messages the mud sends back to you. In those examples, you don't actually type the > character.
A | (vertical line) indicates two or more options exist. Choose one. Don't type the |.
Terms in [] (brackets) denote optional arguments. Don't type the brackets.
Finally, throughout this chapter, you will be using a character named Speedy.
Each type of mud has its own set of commands, and these commands differ from mud to mud. However, there are several common commands that you can expect no matter which type of mud you use. To refresh your memory, here is a quick summary of some basic commands you should know.
telnet machine-name port-number
However, most mudders use some kind of mud client program, which interprets and displays mud messages to them in a nicely formatted fashion.
To connect to your character after you are on the mud, you type:
> connect character password
Some muds take care of password entryor even the entire login sequencefor you. Just follow the prompts.
On most muds, you have to send e-mail to the mud administrator in order for him or her to create a character for you to play. Most muds provide for a "guest" character also, so that you can explore the mud a bit before deciding to establish a real presence there.
To access the help facilities on your mud, use:
> help
> help topic
to obtain help on just about everything in the mud, such as commands, attributes, functions, and various general topics.
This command allows you to talk to other players, and other players to talk to you. The text you speak is transmitted, publicly, to everyone else in the room with you.
On most muds, the command to speak out loud to other players is the double-quote ("):
> "message
You do not need to put a double-quote at the end of your message. The mud puts that in for you.
> "Hey, what's for dinner?
You say, "Hey, what's for dinner?"
In addition to speaking, you can act out actions on the mud. These actions create a kind of narrative element to the mud environment, and allow you to express yourself in a different manner than merely speaking.
On many muds, the act command is the colon (:):
> :message
This command will append your message to your name and then display that to everyone in the room.
> :ran a marathon yesterday.
Speedy ran a marathon yesterday.
The command to look at an object is:
> look object
When you perform this command, the object's description is shown to you. If you leave off the object portion, the mud assumes you want to see the description of the current room. The look command can be abbreviated to l.
> l book
You see a musty, leather-bound tome.
> l Library
You are in the library of the house. You can see several bookshelves filled with dusty books lining the walls of the room.
To move around from room to room on a mud, you use:
> go direction
To move to a specific place, use:
> go place
You can also move in a particular direction by typing that direction, such as:
> north
There may be several possible directions or places to move from each room. The best muds are very good at clearly denoting the exits from rooms in the mud.
To send a message secretly to another player in the same room, use the whisper command. This command differs from mud to mud:
> whisper player = message (MUSH, MUCK, MUSE) > whisper "message" to player (MOO)
This command has an abbreviation: w.
To send a secret message to another player not in the same room as you, use the page command. This command differs from mud to mud.
> page player = message (MUSH, MUCK, MUSE) > page player "message" (MOO)
This command has an abbreviation: p.
On some muds, the page command costs virtual money, whereas the whisper command is free.
The command to pick up an object is:
> get object
If the object can be taken, it is placed into your inventory.
On some muds, the command is also known as take.
The command to drop an object is:
> drop object
The object will be removed from your inventory and placed in your current room.
On some muds, the command is also known as put.
To see a list of the objects you are carrying, use:
> inventory
This can be abbreviated to i.
> i
You are carrying:
a rose
an envelope containing:
a letter
some chocolates
The command to see who else is on the mud varies from mud to mud:
> WHO (MUSHes, MUCKs, MUSEs) > @who (MOOs)
The command to return to your place of origin in the mud is:
> home
When you first begin playing a mud, your place of origin is usually a standard home room. Later, you can create your own room and reset your home room to it.
The command to quit playing varies from mud to mud:
> QUIT (MUSHes, MUCKs, MUSEs) > @quit (MOOs)
This section discusses most of the key topics you should be familiar with as a MUCK, MUSH or MUSE programmer. Most of the concepts apply to all of the MU* environments; however, there are a few topics that only apply to certain types of muds. These topics will be labeled as such in the text.
MUCK is the most basic of the three types of muds that are discussed in this section. MUSH is more advanced than MUCK, and MUSE can be thought of as MUSH with a few extensions.
Thanks go to Lydia "Amberyl" Leong for the mud help document upon which this section is based.
MUCKs, MUSHes and MUSEs have four basic types of things: rooms, players, exits, and objects. You can think of rooms, players, and exits as special objects that have added capabilities. Rooms are the building blocks of the mud. You move through the rooms in the mud in order to explore it and meet other people. Players are the people that you meet. You, of course, are a player, and there may be many more players, besides yourself, playing the mud at the same time. Exits connect rooms to each other. Finally, objects are everything else in the mud. You can create objects yourself and program them to behave in many ways.
Every object in the mud has a database reference number, known as its DBREF. This number can be used to access properties and attributes on the object.
Most commands will accept either names or DBREF as object specifiers, but some will insist on one or the other. Try it both ways if you get stuck.
Most muds have their own monetary system. As you wander around the mud, from room to room, you may find gold coins or other monetary types. You can then use this money to create new objects, build rooms, and other such activitiessince many of these activities require a small amount of money to be used.
Different muds have different types of money, such as gold coins, drachmas, cookies, pennies, dollars, or a fictional type like "blabooies." This chapter uses credits as the monetary unit in its examples.
Throughout this chapter, you may see the term wizard or official. Wizards are players in the mud who have special abilities. Frequently, the wizard is the person who is actually running the mud on their machine. However, there may be other wizards who help the mud administrator, with such things as character creation, policing the mud, and keeping the environment consistent. On MUSEs, wizards are called officials.
Some commands in the mud are only available to wizards. These commands will be identified as such where appropriate.
Some commands and attributes in the mud start with an "at" symbol (@), such as the command to teleport from one room to another, @teleport. These commands and attributes are collectively known as at-commands.
There are several flags that you can set in a MUSH. The syntax for setting a flag is:
> @set object = flag (To turn the flag ON) > @set object = ! flag (To turn the flag OFF)
Throughout this section, different flags will be mentioned as appropriate.
In addition to speaking and acting, you can write messages directly to other players using the emit command: @emit message. The message you type will be sent to everyone in the room, including you. As an abbreviation, you can type: \\ message. There are a few variants of this command. @pemit player = message will display the message only to the player specified. @oemit player = message displays the message to everyone in the room except the player specified.
An emitted message does not print who emitted it. If you want to always see who caused an emitted message, you can turn on your NOSPOOF flag. Upon doing so, you will be informed of the player or object that causes an emitted message.
Another flag is the HAVEN flag. When this is set on a player, the player will not receive pages or @pemit messages. If you try to page a player with HAVEN set, you will be told that that player is not accepting pages. This flag does not black out @emit messages, however.
To find out where a player is located, use the @whereis player command. This command will display the room where the player is. After the player is located, he or she is given a message that you are looking for him or her. If you do not want to be located, set yourself to DARK.
These commands are not available in MUCK.
To create a new object in the mud, type:
> @create object-name = cost
If you leave off the cost, the object will be created with the default cost, usually 10 credits. After checking to make sure you have enough money to create the object, the mud will respond with the message "Object created as object #DBREF" and the object will be placed into your inventory. You can now refer to this object by using either its object name or it's DBREF. The MUSH performs pattern-matching, so you only need to type enough of the object name to differentiate it from other objects you may be holding.
If you change your mind and you want to rename an object, use:
> @name old-object-name = new-object-name
To change your own name, you can use
> @name me = new-name password
Now that you have created an object, you can set its description. The description of an object is one of the attributes that an object can have. An attribute is simply a property on an object.
There are several types of descriptions. @desc object = description sets the description that is displayed to the player that is looking at the object. @odesc object = description is displayed to everyone except the person looking at the object. The player's name and a space are prepended to the message specified. The command @adesc object = actions sets a list of actions that are executed by the mud when the object is looked at.
At this point, it is useful to take note of the basic forms that attributes take throughout MUSH. Assume there is an attribute with the name @xxx. It is applied to the player that is dealing with the object. In addition to the basic attribute, there are usually additional attributes that correspond to the basic attribute. @oxxx is applied to everyone except the player dealing with the object, and @axxx runs a list of actions when the object is used is some fashion. Familiarizing yourself with the @xxx / @oxxx / @axxx combination of attribute names will help you understand and remember other attribute names, since they are similar.
MUCKs, in general, don't have the @axxx attributes, and usually do not have as many @xxx and @oxxx attributes as MUSHes and MUSEs do.
When an object is picked up, its @succ attribute is triggered"succ" is short for "success". To set the success attribute on an object, use @succ object = message. When the object is picked up, the message will be displayed to the player who took it. The @osucc attribute sets the message displayed to players other than the player who took the object, and the @asucc attribute can be used to set actions that you want performed when the object is taken.
Conversely, the @fail attribute is triggered when a player fails to pick up an object, and the @ofail and @afail attributes correspond appropriately. Usually, a player fails to pick up an object because an object is locked.
It is frequently a good idea to lock an object since unlocked objects can be stolen from you. To lock an object to yourself, use:
> @lock object = me
This will allow only you to pick up the object. In a more general form, the lock command is:
> @lock object = key
The key can be an object name, or a DBREF, or the special words "me" or "here." Boolean expressions are allowed. If you want the key to be a player, you must prefix the player name to an asterisk (*), as in @lock spinach = *Popeye.
Locks are related to the @succ (success) and @fail (failure) attributes in that successfully passing a lock will trigger @succ and failing the lock test will trigger @fail.
To unlock an object, use @unlock object.
The @drop attributes set the messages that are displayed when an object is dropped. Using @drop object = message sets the message displayed to the player who drops the object. @odrop is the message displayed to others, and @adrop are the actions performed when the object is dropped
The @move attributes are triggered when the player or object on which they are set moves from one room to another. The @move, @omove and @amove behave as you would expect.
To destroy an object that you own, use @destroy object-name. You will be paid whatever it cost you to create the object. Destroying objects when you don't need them anymore helps free up space in the mud database. If you want to give anyone the ability to destroy an object that you have created, turn on its DESTROY_OK flag.
If you want other players to be able to see how you have programmed an object, set the object VISUAL.
> @create root beer float root beer float created as object #1765. > inventory You are carrying: root beer float (#1765). You have 921 credits. > @desc root = You see a frosted mug of root beer with a dollop of vanilla ice cream floating in it. Set. > @succ root = You pick up the frosted mug. Set. > @osucc root = picks up the mug of root beer. Set. > @drop root = You put down the frosted mug. Set. > @odrop root = puts down the mug of root beer. Set. > drop root beer float You put down the frosted mug. > look root beer float root beer float (#1765) You see a frosted mug of root beer with a dollop of vanilla ice cream floating in it. > get root beer float You pick up the frosted mug. > @destroy root You get back your 10 credit deposit for root beer float (#1765). Destroyed.
On most muds, you can type enough of the object name to make it distinguishable from any other object in the room. Thus, in the root beer float example, referring to it as root was sufficient.
The @axxx attributes (attributes that start with the letter a) are used to execute action lists. An action list is simply a list of one or more actions, separated by semicolons. For example, you could create a fragile vase. When it is dropped, it does two things: it prints a message describing how it shatters, and then it destroys itself.
> @adrop vase = @emit The fragile vase shatters.;@destroy me Set. > drop vase The fragile vase shatters. fragile vase has left. You get back your 10 credit deposit for fragile vase (#1238).
Actions lists are not available in MUCK.
In addition to the basic look command, you can also examine items in the mud. Examining an object provides you with more detailed information about the object. The command to examine an object (or player) is:
> examine object
If you do not control the object, the examine command will tell you the owner of the object and show any public attributes on the object, such as its description, its success and fail attributes, and so forth.
If you do own the object, or if the object has its VISUAL flag set, you will be able to see all the attributes and how they are programmed.
You will also see any objects that are inside the object you are looking atprovided that the containing object is not set OPAQUE.
If you set an object DARK, it will not show up in a room's contents list. If a player is set DARK, no one will be able to see what that player is carrying.
You can teleport from one room to another by using the teleport command. You can only teleport into rooms that are set JUMP_OK.
> @tel room-number
You can also teleport objects that you own into rooms that are JUMP_OK:
> @tel object = room-number
So, if you know that a meeting place that you want to go to is room #222, you could get there by typing:
> @tel #222
If an undesirable player or object is in a room that you own, you can teleport it out of your room to any other room that is JUMP_OK. You could also teleport it to any other room that is yours. So, if someone barges into your room, where you are having a private conversation with someone, and they won't leave, you can get rid of them!
You can also get rid of them by more violent method of killing them.
Killing, in general, is frowned upon. In fact, some muds don't allow player killing. However, one must remember that muds are only games. So, if someone is being obnoxious, and won't stop, sometimes the only way to drive your displeasure home is to kill them off. The command to do this is:
> kill thing = money
The default cost of killing something is 10 credits. This gives you a 10% chance of success. For each additional credit you spend, your chance of success goes up 1%. Spending 100 credits gives you a 100% chance of success.
Killing something sends it home. The object killed is also paid half the amount of money used to kill it, as a sort of insurance policy.
When an object is killed, the @kill, @okill, and @akill attributes are triggered. They work as you would expect.
Certain things, such as wizards or objects set IMMORTAL, cannot be killed. Only wizards can set things IMMORTAL.
Occasionally, you may want to give another player an object that you have. Or perhaps you want to give them some money. The commands to do these things are:
> give player = object > give player = money
The player who is receiving the object must have their ENTER_OK flag set.
Conversely, you can take an object from another player by using the possessive form of the get command:
> get player's object
The player who is holding the object must be set ENTER_OK, and the object that you are trying to take cannot be locked against you. This is another reason to use locks on objects. If you don't lock an object, someone else can steal it from you!
Sometimes, instead of just giving a player an object, you many want to allow some other player to own an object that you have created. In order to do this, you must first allow your object to have its ownership changed by setting it CHOWN_OK. You can then give the object to someone else. The player who receives the object may then change its owner by using the change ownership command, @chown object = me.
In a mud with lots of rooms and objects, you might end up losing one of your objects and forgetting where it is. The @find string command will scan through the database and pick out all objects and rooms that have the given string in their names. It prints out the object name and DBREF. @find without any arguments displays all the objects and rooms in the mud that you own. However, it can cost a lot because it takes a lot of computations. Here are some examples
@find dog @find Speedy
Related to the find command is the search command, @search. It scans through the mud and prints everything about an object. There are several arguments you can give to this command: @search player will display all the objects owned by that player. @search type = object-type will display objects that have that type. For object-type, you can use the types: rooms, exits, objects, or players. @search string searches for that string. And @search flags = flags-desired will search for those flags. Here are some examples:
@search Speedy @search type = objects @search dog @search flags = D
If you want to make sure you don't lose an object, you can link it to yourself. First, set its home to yourself by using the command @link object = me. Then, turn on the object's STICKY flag. STICKY objects are sent home when they are dropped. Thus, when the object is dropped, it will return to you.
> @link homing pigeon = me > @set homing pigeon = STICKY
If someone then takes the pigeon, and later lets it go by dropping it, it will return to you.
In addition to objects and players, you can also create your own rooms, complete with their own descriptions.
As an example, consider building a tree with a treehouse in it. The tree will be in the middle of a field. The treehouse will have a balcony outside.
The field room already exists. It has been set LINK_OK. The example will describe how to create the tree, the treehous,e and the balcony. It will then describe how to link the three rooms to the existing field.
It helps to form a mental picture of the scene you wish to create. This is expressed in Figure 11.1. From the mental image, you can generalize the scene into objects, rooms, and exits, as seen in Figure 11.2.
Figure 11.1. This is the conceptual idea of the scene.
Figure 11.2. Here are the generalized rooms and exits of the scene.
Now that you have an idea of what you are trying to accomplish, you can begin to create it. To create a room, use the dig command:
> @dig room-name
This creates a room with that name. The mud responds with a message informing you of the DBREF of the room.
Following the example, create the Tree:
> @dig Tree Tree created with room number #1442. > @tel #11442 Tree (#1442R) > @desc #1442=You see a large tree. There seems to be a treehouse up in the branches. Set.
The descriptions used here are simplistic because the focus here is room construction. In a real mud, though, a more eloquent description would better convey the sense of the scenery.
This room will initially be disconnected from everything else. You can think of it as floating in hyperspace. You can then teleport into that room using the @tel command and set its description and other attributes. However, if your room is not linked to other rooms, you may get a message from time to time indicating that you have an unconnected room. To turn off the message, set the room's FLOATING flag.
Rooms are connected to each other using exits. Exits are the links between rooms. You can create exits by using qualifiers with the @dig command, or by creating them directly with the @open command.
The format to create exits with the @dig command at the same time you create rooms is:
> @dig room-name = in1;in2;in3;... , out1;out2;out3;...
In this command, the in1;in2;in3;... names are names of entrances into the new room. Each name in the in-list is an alias for the entrance. The out1;out2;out3;... names are the names of exits from the new room back into your current room. Each name in the out-list is an alias for the exit. The first name in the list of entrances, in1, will be used as the name in the Obvious exits of the current room. The first name in the list of exits, out1, will be used in the Obvious exits of the new room.
You can specify as many of these entrance and exit names as you wish; just remember to separate them with semicolons. Note that the list of entrance names is separated from the list of exit names by a comma.
Two links are created in the @dig command above. One link goes from the current room to the new room, and uses the in-list names. That's the entrance to the new room. The other link goes from the new room back into the current room, and uses the out-list names. That's the exit from the new room.
You can use the @dig command with exit names to create the treehouse and the deck in the example. Assume you are in the Tree room:
> l Tree (#1442R) You see a large tree. There seems to be a treehouse up in the branches. > @dig Treehouse=treehouse;house;up;u,tree;down;d Treehouse created with room number 1443. Opened. Linked. Opened. Linked. > l Tree (#1442R) You see a large tree. There seems to be a treehouse up in the branches. Obvious exits: treehouse
Note how the treehouse exit name now appears in the Obvious exits list. That's because it was the first name in the list of entrances into the new room, treehouse;house;up;u.
You can then move into the treehouse by typing one of the entrance names, such as treehouse, or house, or up, or u. This moves you through the exit, into the new room that you have just created.
> treehouse Treehouse (#1443R) Obvious exits: tree
The Obvious exits in the treehouse specifies tree because tree was the first name in the list of exits, tree;down;d, in the @dig command earlier.
Next, we can describe the treehouse and then create the deck:
> @desc here=You are in a small treehouse near the top of a large tree. A deck can be seen outside. Set. > @dig Deck=deck;out,treehouse;house;in Deck created with room number 1446. Opened. Linked. Opened. Linked. > deck Deck (#1446R) Obvious exits: treehouse > @desc here = You are standing on the deck attached to the treehouse. A field can be seen below. Set.
Now the tree, the treehouse, and the deck have been created. To link the field to the tree and the deck, you can use the @open command to create specific exits
> @open in1;in2;in3;... = #DBREF , out1;out2;out3;...
which will open an exit from the current room to the room specified by the DBREF. The exit will have the entrance names specified in the in-list. Another exit will go from the DBREF room to the current room, and will have the names specified in the out-list.
You can leave off the out-list. In that case, the exit from the DBREF room to the current room is not created.
Using the @open command, we can create the exits from the field to the tree:
> @open tree;up;u=#1442,field,down;d Opened. Linked. Opened. Linked.
The messages for opening and linking are indicating to you that both the incoming and outgoing links from the tree to room #1442 have been created.
Next, we can create the exit from the deck to the field by teleporting to the deck and then using the @open command without an out-list, thereby creating a one-way link from the deck to the field, but not the other way. Assume the field room has a DBREF of 1440.
> @tel #1446 Deck (#1446R) You are standing on the deck attached to the treehouse. A field can be seen below. Obvious exits: treehouse > @open field;jump;down;d=#1440 Opened. Linked. > l Deck (#1446R) You are standing on the deck attached to the treehouse. A field can be seen below. Obvious exits: treehouse field
Now the example is complete!
To set your home to a particular room, use the @link command:
> @link object = #room-number
If you are in the room you want to change to your home, you can use the shorthand keyword here in place of the room-number:
> @link me = here
You can also allow other players to set their homes to one of your rooms by setting that room's ABODE flag. Rooms that are set ABODE may become other players homes.
In addition, rooms may be linked to other rooms using the same @link command
> @link room = #other-room
which will make room a so-called "drop-to" room. Objects that are dropped in room are sent to other-room. For instance, in the tree example, you can set the tree's @link to be the field. Then, any object dropped from the tree will fall down to the field:
> @tel #1442 Tree (#1442R) You see a large tree. There seems to be a treehouse up in the branches. Obvious exits: treehouse field > @link here = #1440 Dropto set. > drop brick Dropped. > l Tree (#1442R) You see a large tree. There seems to be a treehouse up in the branches. Obvious exits: treehouse field > down Field (#1440R) You are standing in the middle of a field. There is a large tree here. Contents: brick Obvious exits: tree north south east west
If a drop-to room is also set to be STICKY, then any objects dropped in it will fall to the next room only when all the players have left the drop-to room. Think of the STICKY flag on a room as a delayed drop-to action.
Exits are really one-way streets from one room to another. To create a path going from one room to another both ways, you actually have to create two exitsone exit going one direction and the other exit going the opposite direction.
Once an exit is created, it shows up if you examine the room from which it leaves. For instance, if we examined the Deck of the example:
> examine here Deck (#1446R) Type: ROOM You are standing on the deck attached to the treehouse. A field can be seen below. Owner: Speedy Key: *UNLOCKED* Contents: Speedy (#5Pc) Field;jump;down;d (#1451E) Treehouse;house;in (#1448E)
Thus, with the examine command, you can see all the exits that are going out of a room.
You can also get a list of all the links going into a room by typing:
> @entrances #room-number
This will also show all the objects whose homes are set to that room.
> @entrances here Treehouse (#1443R) (Deck;out) 1 entrance found.
Exits can have the same types of attributes that other objects have, such as @succ or @fail messages. Here is a summary of attributes as they apply to exits:
@succ
Message displayed when a player goes through the exit successfully. It is displayed to the player going through the link.
@osucc
Message displayed to the other people in the room from which the player is leaving successfully.
@asucc
Actions to be executed when a player leaves a room successfully. Not present in MUCK.
@fail
Message displayed when a player fails to go through an exit. It is displayed to the player who fails to pass through the link.
@ofail
Message displayed when a player fails to go through an exit. It is displayed to the people in the room from which the player is trying to leave.
@afail
Actions to be executed when a player fails to exit a link successfully. Not present in MUCK.
@drop
This attribute does not apply to exits. It is included here for completeness with the other drop attributes.
@odrop
Message displayed to the other people in a room that a player is entering successfully. Not present in MUCK.
@adrop
Actions executed when a player enters a room successfully. Not present in MUCK.
As a rule, you should define the @osucc, @ofail, and @odrop attributes on any exit you create. In this way, a message will be displayed to the people in the room that the player is leaving, and another message will be displayed to the people in the room that the player is entering. You can also define the @desc attribute on a exit, so that something is displayed to a player who tries to look at it.
Most exits from one room lead into another room. However, you can create special NULL exits that display messages to players rather than move them to different rooms. To do this, create an exit on a room, set its lock to #0, and then set its @fail message to the message you want to display when the player types that exit name. You should also set it DARK.
For example, perhaps you want to display the message You wave to the crowd when the player types the command wave. To accomplish this:
> @open wave = here Opened. Linked. > @lock wave = #0 Locked. > @fail wave = You wave to the crowd. Set. > @set wave = DARK Set. > wave You wave to the crowd.
NULL exits can also be used to display a message to the user when he or she tries to move in a direction that does not exist. Normally, when a player tries to move in a non-existent direction, a default Huh? (Type 'help' for help) message is displayed to that player. The Huh? message is displayed for any unrecognized command that the player types. A better, more informative message would be something like You cannot go in that direction. To create this, create a NULL exit as mentioned above. Then, for the exit's name list, specify the directions for which you want the message to appear.
For instance, suppose a room only has a north exit. You want to display the You cannot go in that direction message when the player tries to go in a direction other than north. So, create an exit whose name list has all the non-north directions and abbreviations, and then set up its @lock and @fail:
> @open east;e;west;w;south;s = here Opened. Linked. > @lock east = #0 Locked. > @fail east = You cannot go in that direction. Set. > @set east = DARK Set. > east You cannot go in that direction. > west You cannot go in that direction. > south You cannot go in that direction.
To allow people to be able to teleport into your room, set the room JUMP_OK. To allow people to create links that lead into your room, set your room LINK_OK. Note that other players still can't create links that lead out of your roomyou have to do that.
The TRANSPARENT flag, when set on an exit, causes the description of the next room to be seen when a player looks at that exit. This allows players to see into adjoining rooms.
You can unlink an exit by using the @unlink command:
> @unlink exit
This will unlink the destination of the exit. You can then relink the destination to some other room using the @link command. Exits become owned by whomever links them, so you shouldn't leave an exit unlinked.
You can create objects that can contain other objects by setting the object ENTER_OK. Then, to go inside the object, use the command:
> enter object
and to go out of the object, use the command:
> leave
The normal @desc attribute is the description of the container, and it is displayed to players that look at the object from the outside. The @idesc attribute sets the message that is displayed to anyone inside the container who types the look command.
There are several attributes that are triggered when an object is entered by something, or when something exits an object. The @enter attribute is displayed to the entering object, the @oenter attribute is displayed to anyone else inside the object, the @oxenter message is displayed to people who are in the room that the player leaves, and the @aenter attribute is a list of actions that are to be executed when an object is entered. The @leave, @oleave, @oxleave, and @aleave attribute correspond similarly for objects that are leaving a container.
> @create phone booth phone booth created as object #999. Set. > @desc = You see a telephone booth here. Set. > @idesc = You are inside a phone booth. There is a coin-operated telephone attached to one side of the booth. > @enter phone booth = You enter the phone booth. Set. > @oenter phone booth = :squeezes into the phone booth with you. Set. > @oxenter phone booth = :enters the phone booth. Set. > @leave phone booth = You leave the phone booth. Set. > @oleave phone booth = :departs from you, leaving the phone booth. Set. > @oxleave phone booth = :leaves the phone booth. Set.
Sometimes you want to create sentences in your attribute messages that depend on the gender or name of the player performing some action on an object. The attributes that begin with @a (such as @asucc, @afail, @adrop, and so forth) frequently use special substitutions in their actions.
For instance, perhaps you want to create a cat that meows when it is dropped. You want to display a message to everyone that the cat meows at him when dropped by a male player, and meows at her when dropped by female player. In addition, you want to use the dropping player's name in the message. You can do this using special pronoun substitutions:
> @adrop cat = %N drops the cat, which lands on the ground and meows at %o.
In the preceding message, the player's name is substituted for the %N portion of the message, and the objective form of the player's name is substituted for the %o part of the message. In this manner, a male player named Linus would trigger a message Linus drops the cat, which lands on the ground and meows at him, while a female player named Lucy would trigger the message Lucy drops the cat, which lands on the ground and meows at her. The mud determines which pronoun to substitute by looking at the gender of the player, which is stored in the @sex attribute.
There are several types of pronoun substitutions:
%N, %n
Substitutes the player's name. %N is the capitalized form; %n is not capitalized.
%S, %s
Substitutes the subjective form of the player's name (he/she/it/they). %S capitalizes the first letter; %s does not.
%O, %o
Substitutes the objective form of the player's name (him/her/it/them). %O capitalizes the first letter; %o does not.
%P, %p
Substitutes the possessive form of the player's name (his/her/its/their). %P capitalizes the first letter; %p does not.
%A, %a
Substitutes the absolute possessive form of the player's name (his/hers/its/theirs). %A capitalizes the first letter; %a does not.
This information is enough to get you started on creating objects, setting some of their attributes, building some rooms and linking them up with exits, and making your own little virtual world.
The next few sections will describe some specifics for several types of muds: MUCKs, MUSHes, and MUSEs.
MUCK, which is a pun on the term MUD, is a spin-off of the original TinyMUD. This section describes TinyMUCK version 2.2.
There are many popular MUCKs on the Internet. Some of them were among the first muds to appear on the Internet and, as a result, have a large population of longtime mud players.
Name |
Machine Name |
Internet Address |
Port |
|
AnimeMUCK |
anime.tcp.com |
128.95.10.106 |
2035 |
|
CaveMUCK |
cave.tcp.com |
128.95.44.29 |
2283 |
|
FurryMUCK |
sncils.snc.edu |
138.74.0.10 |
8888 |
|
NAILS |
flounder.rutgers.edu |
128.6.128.5 |
5150 |
FurryMUCK, one of the earliest muds, harbors a large group of devoted players. Its theme is anthropomorphic animalscharacters that are animals with human characteristics.
These addresses are current as of August, 1994. Keep in mind that they may change.
From the player's standpoint, MUCKs operate similarly to other types of mud on the Net. Beneath the visible text, however, programmers will find that they are significantly different. This section indicates some of the principle features of programming in MUCKs.
In order to program more complex objects in MUCK, you have to learn how to program in the MUCK programming language, called Multi-User Forth, or MUF for short.
MUF programs must be entered into the MUCK and then compiled in order for them to function. To enter the editor, you can type:
@program program-name
This will put you into the MUCK editor. You can then enter insert mode by typing
line-number i
which will start inserting what you type before the given line-number. To exit insert mode, type a period (.) on a line by itself.
You can then compile the program by typing:
c
And, finally, you can run the program by typing:
????
A more detailed listing of editor commands can be found in the MUCK Reference Manual section, later in this chapter. This should be enough to get you started.
In MUF programs, comments are enclosed in parentheses:
(This is a comment)
MUF programs consist of words. A word is simply a series of statements. They usually manipulate the stack in some manner. A word begins with a semicolon, followed by the name of the word, the statements of the word, and then ending with a semicolon:
: word-name statements ;
There are three basic types of values in MUF: the integer, the string, and the DBREF. Strings are enclosed in double-quotes. DBREFs are denoted by putting a pound symbol (#) in front of the DBREF number:
7 (This is an integer) "Test" (This is a string) #354 (This is a DBREF)
MUF is a stack-based programming language based on Forth. If you are not familiar with the concept of a stack, it is a place to hold information, and it has a "top" element that can be accessed as necessary. When you "push" an element onto the stack, that element becomes the new "top" of the stack, similar to stacking plates on top of one another. The first element to be removed, or "popped" from the stack is the top element. The next element to be removed is the element under the top element, and so forth.
You can put these basic values onto the stack by simply specifying them in your MUF words:
: stack_test
56
"First string"
#45
"Second string"
;
In this example, the stack now has four values on it. The number 56 is at the bottom of the stack because it was pushed onto the stack first. The Second string is on the top of the stack, and we can access it if we look at the top of the stack or pop the top value from the stack.
The indentation did not matter in the previous example. Indentation is simply used to make MUF programs easier to read. The previous example can also be written:
: stack_test
56 "First string" #45 "Second string"
;
MUF functions operate on the top of the stack. A value may be taken from the top of the stack, processed in some manner, and perhaps put back onto the stack. More commonly, the top two values are taken from the top of the stack and then processed in some manner to result in one new value, which is then placed back onto the top of the stack. The addition function behaves in this manner. It takes the top two values from the stack, adds them together, and then places the result back onto the stack:
: simple_add
5 8 +
;
This simple addition would first place 5 onto the stack, followed by 8. The + operator would then pop these two values off the stack, add them together, and push the result, 13, back onto the stack. The number 13 could then be used in some fashion.
Note: This is the same as RPN (Reverse Polish Notation), also known as postfix. Anyone familiar with the high-end HP calculators will know how to use it already.
Another example is
: more_add
4 5 9 + 8 + +
;
which first puts 4, 5 and 9 on the stack. It then adds the top two values, 5 and 9, and pushed their sum, 14, back onto the stack. It then pushes 8 onto the stack. The next addition adds 14 and 8 together to get 22, and the last addition adds 4 and 22 together to get 26. Thus, at the end, the stack has one value: 26.
Subtraction, multiplication and division work in a similar manner. For subtraction, the top value is subtracted from the value underneath it. For division, the second-from-the-top value is divided by the top value.
The equality function is an equals sign (=). It pops the top two integers from the stack and compares them. If they are equal, 1 is pushed onto the stack. If they are not equal, 0 is pushed onto the stack. Thus:
: equals_test
4 100 = 7 7 =
;
would result in a stack of: 0 1.
Similar to the equality function, the dbcmp function also compares two values on the top of the stack. It compares two DBREFs. If they are equal, 1 is pushed, otherwise 0 is pushed.
: dbcmp_example
#54 #54 dbcmp
;
would result in the stack consisting of the number 1.
As mentioned previously, the top value can be removed from the stack by "popping" it. The pop function accomplishes this in MUF programs, appropriately enough:
: pop_example
5 7 pop 8 9 pop
;
This places 5 and 7 onto the stack, pops 7 back off, then places 8 and 9 onto the stack and pops 9 back off. The stack would thus be 5, 8, with 8 being the top of the stack.
Another basic function is the swap function. It switches the top two values on the stack around:
: swap_example
4 5 swap 7 swap
;
This program would place 4 and 5 onto the stack, then swap them to get 5 and 4. The number 7 is then pushed onto the stack, to get 5, 4 and 7. The last swap would swap 4 and 7. The end result is 5, 7, 4, with 4 being at the top of the stack.
One more frequently used function is the duplicate function, dup. It simply pushes a copy of the top of the stack onto the stack:
: duplicate_example
4 dup 7 dup
;
After this function, the stack would be 4, 4, 7, 7. The second 7 would be on the top of the stack.
You can use variables in MUF code. First, you must declare the variable by putting:
var variable-name
at the top of the program. Once declared, the variable can be used in a variety of ways.
The variable storage function is an exclamation point (!). It places the next-to-the-top value into the variable named on the top of the stack.
var testvar
: variable_storage_example
7 testvar !
;
This puts the value 7 into the variable named testvar.
To retrieve the value of a variable, use the fetch function, which is an "at" symbol (@). It pops the top value on the stack and uses it as a variable name. The contents of that variable are then placed back onto the stack:
var testvar
: variable_fetch_example
7 testvar !
testvar @
;
This first places the value 7 into the variable named testvar. It then accesses testvar and places its contents, 7, onto the stack. The stack, then, would consist only of the number 7.
Three special variables in MUF are me, loc, and trigger. The me variable contains the DBREF of the playerm, and loc contains the DBREF of the player's current room. The trigger variable contains the DBREF of the player who triggered the program. These variables will be used in later examples.
To send a message to an object, you use the notify function. This function sends the string at the top of the stack to the player at the second top element of the stack:
: notify_example
me @ "Hello, there!" notify
;
This example first retrieves your DBREF from the me variable. It then pushes a string onto the stack. Finally, it uses the notify function to send that string to you.
MUF has an IF-THEN construct similar to other programming languages. However, it has a major difference with the IF-THEN statement you may be familiar with from other programming languages. In MUF, the IF-THEN statement is more like a compare and jump statement in assembly language.
if
if-statements
then
following-statements
In the preceding syntax, the if keyword pops the top value from the stack. If the value does not equal 0, the if-statements are executed, and then the following-statements are also executed. The if-statements consist of all statements up to the next then keyword. If the value popped by if is 0, the if-statements are skipped, and only the following-statements are executed.
As an example, consider:
: if_example
4 2 2 + =
if "The values were equal"
then "Top string"
;
First, 4 and the sum of 2 and 2 are compared, using the equality function. Since they are equal, the if statement pushes The values are equal onto the stack. It then pushes the string Top string onto the top of the stack.
The program
: if_example
4 2 3 + =
if "The values were equal"
then "Top string"
;
would only push the string Top string onto the stack because 4 and 5 are not equal.
As a helpful hint, it may be useful to think of the then keyword as an endif.
With these concepts, you can create many MUF programs. In addition, you can use the built-in functions that are available in MUF. For a listing of these, consult the MUCK Reference Manual, later in this chapter.
MUSH, which stands for Multi-User Shared Hallucination, is a spin-off of the original TinyMUD. This section describes TinyMUSH version 2.0.
There are several MUSHes on the Internet. Many of them have certain themes.
|
Name |
Machine Name |
Internet Address |
Port |
|
Deep Seas |
muds.okstate.edu |
139.78.9.1 |
6250 |
|
DuneMUSH |
mellers1.berkeley.edu |
128.32.243.78 |
4201 |
|
NarniaMush |
argo.unm.edu |
129.24.9.24 |
6250 |
|
NeverendingStory |
jove.cs.pdx.edu |
131.252.21.12 |
9999 |
|
PernMUSH |
cesium.clock.org |
130.43.2.43 |
4201 |
|
Shadowrun |
yacht.slip.andrew.cmu.edu |
128.2.116.75 |
4201 |
|
TinyCWRU |
caisr2.caisr.cwru.edu |
129.22.24.22 |
4201 |
DeepSeas, previously known as SpaceMadness, DreamScape, Asylum, Chaos, and TinyHELL, is another long-standing mud with a consistent player base. A lot of dino mudders (long-timers) can be found there.
These addresses are current as of August, 1994. Keep in mind that they may change.
In addition to the commands and objects previously mentioned in the tutorial section, there are a few things that are specific to MUSHes.
You can use a semicolon to possessive-pose. This is the same as posing (acting), except that it adds a possessive s after your character name and before the action message
;message > ;brain hurts. Speedy's brain hurts.
Puppets are special types of objects that seem just like players. Puppets can be controlled by their owners and can see and hear things in a room just like a player would. The controlling player can then use the information returned by the puppet because the puppet echoes the text it sees and hears back to its owner. It does not echo back actions that are performed in the same room as its owner, to prevent message confusion.
To change an object into a puppet, set the object to be a puppet: @set object = puppet. The puppet will announce that it has grown ears and can now hear. After it becomes a puppet, you can use its name or its DBREF to have it perform actions that you want.
You can force an object to perform some action by using @force object = actions. Or, you can use DBREF actions. The object will behave as if it, itself, had typed the action.
> @create a little dog a little dog created as object #333 > @set little dog = puppet a little dog grows ears and can now hear. Set. > inventory You are carrying: a little dog (#333p) You have 930 credits. > @desc little dog = You see a little puppy. Set. > drop little dog a little dog> Speedy's Room a little dog> You are in Speedy's Room. a little dog> Contents: a little dog> Speedy (#5Pc) a little dog> Obvious exits: a little dog> north a little dog> Dropped. Dropped. > l little dog a little dog (#333p) You see a little puppy. > @force little dog = :wags its tail. a little dog wags its tail. > #333 :pants happily. a little dog pants happily. > #333 "Yap yap! a little dog> You say "Yap yap!" a little dog says "Yap yap!" > #333 north a little dog walks north out of Speedy's Room. a little dog has left. a little dog> Hallway a little dog> You are in a short hallway. Speedy's room is to the south. A stairway can be seen to the north. a little dog> Obvious exits: a little dog> north south > #333 south a little dog> You walk south into Speedy's Room a little dog> Speedy's Room a little dog> You are in Speedy's Room. a little dog> Contents: a little dog> Speedy (#5Pc) a little dog> Obvious exits: a little dog> north a little dog walks in from the north. a little dog has arrived. > @set #333 = DESTROY_OK Set. > @destroy #333 a little dog> *NOTHING* You get back your 10 credit deposit for a little dog (#333). Destroyed.
Sometimes it's fun to program an object that listens for certain strings and responds to those strings in some manner. The @listen directive will set the pattern string and the @ahear attribute sets the actions that are executed when the listen string is matched.
> @listen object = string > @ahear object = actions
When using @listen, the string frequently has some word or phrase that the object is listening for, plus some wildcard characters to match whatever else might be in a message. If a matching string is seen, the @ahear actions are then executed. For instance, the command
> @listen me = * movie*
would be looking for a string of anything, followed by a space, followed by the word movie, followed by anything else. This could match something like Speedy went to see a movie today. It could also match What's playing at the movies tonight?.
> @listen me = * movie* Speedy - Set. > @ahear me = :wanna direct! Speedy - Set.
Then, later, maybe someone mentions movies. That triggers the response automatically:
Archimedes went to see a movie last night. Speedy wanna direct! Mak says, "Which movie did you see?" Speedy wanna direct!
In addition to the @ahear attribute, there is also an @amhear and an @aahear attribute. @ahear responds only to messages that the listening object did not generate. @amhear only responds to messages that the object itself generated. @aahear responds to all messages.
Whenever an action occurs, the mud stores information related to that action in a special storage area. These variables can then be accessed using percent substitutions.
Percent substitutions are simply variables that begin with a percent sign. For instance, in the previous section, you learned about pronoun substitutions. Pronoun substitutions are just special kinds of percent substitutions.
One of the items that the mud stores is the name of the enactorthe object that caused an action. As described in the previous section, the name of the enactor is stored in the %N variable. The %N variable is the capitalized name, such as Speedy, and the %n variable is the uncapitalized form, such as speedy. Additionally, the actual DBREF of the enactor can be accessing by using the %# variable.
Another item stored by the mud is the affected objectthe object on which the action is being performed. The DBREF of this object can be accessed by using the %! variable.
For example, if Speedy dropped a bowling ball, Speedy would be the enactor, and the bowling ball would be the affected object. Speedy's name could be accessed using %N or %n, and Speedy's DBREF could be accessed using %#. The DBREF of the bowling ball could be accessed using %!.
The DBREF of the location of the action can be accessed through the %l variable.
There are several special percent substitutions that are used for text formatting. These formatting characters resemble the special formatting characters in the C programming language: %r converts into a carriage return-newline combination, %t converts into a tab character, %b converts into a blank space, and %% converts into an actual percent sign.
That is why you have to type two percent signs when you want to say a line with a percent sign in it. If you say
> "24.6% of all statistics are made up on the spot! Speedy says "24.6 of all statistics are made up on the spot!"
you notice that the percent sign disappears. The mud is trying to convert it to a percent substitution, and a percent sign followed by a blank space converts into a blank. To get a percent sign into your text and actions, type two percent signs
> "24.6%% of all statistics are made up on the spot! Speedy says "24.6% of all statistics are made up on the spot!"
since the %% is the formatting specification for a percent sign.
Another way to get an actual percent sign is to use the escape character, which is a backslash (\):
> "24.6\% of all statistics are made up on the spot! Speedy says "24.6% of all statistics are made up on the spot!"
The escape character causes any of the characters following it to be printed as-is.
In addition to the various percent substitutions just mentioned, there are 26 registers that you can set using @va through @vz and access by using %va through %vz. These registers are like attributes or variables. They can store just about any mud valuenumbers, strings, messages, function results, and so forth. To set a register, use the syntax
> @register object = actions
and then access it using the corresponding percent-substitution, %register.
As an example, consider creating a water balloon that splats when it is dropped. The va register will be used to print out a message when the balloon is dropped.
> @create water balloon water balloon created as object #1765. > @va water balloon = SPLAT! Set. > @drop water balloon = The balloon falls and goes %va. Set > drop water balloon The balloon falls and goes SPLAT!
You can also use the register to perform an action. To do this, you have to trigger the register using the trigger command:
> @trigger object/attribute
The @trigger command can be abbreviated @tr.
In the water balloon example, a more realistic action would be for the water balloon to destroy itself when it is dropped. You can put the destroy command in a register, and then trigger it using the trigger command when the balloon is dropped. So, adding to the water balloon example:
> @vb water balloon = @destroy me Set. > @adrop water balloon = @tr me/vb Set. > drop water balloon The balloon falls and goes SPLAT! water balloon has left. You get back your 10 credit deposit for water balloon (#1765).
When the balloon is dropped, the @drop attribute used the va register as a string, and the @adrop attribute triggered the vb register, which destroyed the balloon.
With 26 registers to play around with, you can perform a lot of complex programming. You can use registers to store temporary information. Registers can trigger other registers, too.
You can also make use of the 10 numbered variables, named %0 through %9. These variables are collectively known as the stack in mud terms, although it isn't really a stack in the computer science sense of pushing and popping objects. The stack is just a storage area for the 10 numbered variables.
These numbered variables are known as positional parameters. They are set consecutively using two methods: the @listen command and the @trigger command.
To illustrate the @listen command and how positional parameters are set, consider a parrot that listens for phrases with the word Parrot in them. The parrot will speak the three phrases that come after the trigger word Parrot:
> @create parrot parrot created as object #1799. > @listen parrot = * says "Parrot * * * * parrot - set. Set. > @ahear parrot = "SQUAWK %1 %2 %3 Set. > drop parrot Dropped. > "Parrot can you hear? You say "Parrot can you hear me?" parrot says "SQUAWK can you hear" > "Parrot tell a story to us. You say "Parrot tell a story to us." parrot says "SQUAWK tell a story"
In this simple example, the asterisks in the @listen command convert to numbered variables %0, %1, %2, %3, and %4. The first asterisk matches the player's name from the spoken text, and this is placed in the variable %0. The three phrases after the string Parrot are matched and placed into numbered variables %1, %2, %3, and %4, respectively. The %1 variable is the first word after Parrot, %2 is the second word, %3 is the third word, and %4 is everything else, including the ending double-quote. The @ahear command then uses some of these variables in its spoken text.
The @trigger command can also be used to set the numbered variables, using the format:
> @trigger object/attribute = item, item, item, ...
This command sets the positional parameters to the given items, consecutively, starting from %0. The attribute that is triggered can then access these numeric variables.
Consider a globe that has its va attribute programmed as follows:
> @va globe = @emit The globe %0, %1, and %2. Set.
Now, to get %0, %1, and %2 to be shimmers, sparkles, and glows eerily, you can use the @trigger command:
> @tr globe/va = shimmers, sparkles, glows eerily. Triggered. The globe shimmers, sparkles, and glows eerily.
Note how the parameters to the @trigger command become the numeric variables for the va attribute.
You can use braces to surround strings so that commas within them do not get interpreted by the mud as separation characters.
Consider the globe in the previous section. What if you wanted the second phrase to be hums, glistens instead of sparkles? If you type
> @tr globe/va = shimmers, hums, glistens, glows eerily.
you get
Triggered. The globe shimmers, hums, and glistens.
This is because the comma separating hums and glistens is taken to be a parameter separator.
In cases like this, you want a comma to be taken as an actual comma, rather than a parameter separator. You can use braces to specify that the string within the braces is to be taken as a whole unit, and any commas within the braces are part of the string:
> @tr globe/va = shimmers, {hums, glistens}, glows eerily.
Triggered.
The globe shimmers, hums, glistens, and glows eerily.
So remember, when you want to get an actual comma into a string, you may need to throw a pair of braces around the string. Braces are also frequently used in function calls and switch statements (both discussed later in this section), since those programming constructs commonly use commas.
You can also trigger an object by using it. To use an object, simply type the use command:
> use object
This command triggers the @use, @ouse, and @ause attributes. The object will respond to the use command only if the @ause attribute has been set on the object.
The format for a function call is:
[ function-name(parameters...) ]
The parameters of the function must be enclosed within parentheses, and the function itself must be enclosed in square brackets.
As an example, the name() function returns the name of an object when given its DBREF. If object #5 was the player Speedy, you could determine the name by using the name() function and the object number:
> @emit [name(#5)] Speedy
Functions can be nested, and nested functions only require one outermost set of square brackets. You can put the name() function, mentioned earlier, and the Ustrlen() function, which returns the length of a string, together to find out the length of the name of object #5 by typing:
> @emit [strlen(name(#5))] 6
Sometimes, you may want to evaluate the value of an attribute as though it were a function. In those cases, you may have to add additional square brackets to denote that you are trying to access a variable as a function.
For instance, the get(object/attribute) function returns the value of the named attribute on a given object. If the va and vb attributes on some test object are defined as
> @va testobject = vb Set. > @vb testobject = blah blah Set.
then by accessing va you see
> @emit [get(testobject/va)] vb
and by accessing vb indirectly through va you see
> @emit [get(testobject/[get(testobject/va)])] blah blah
because [get(testobject/va)] returns vb, which is substituted into the outermost get()function to return blah blah.
There are dozens of predefined functions to choose from. A few important functions are discussed next. For a description of the rest of the functions available, or for quick reference, see the function glossary section, later in this chapter
The v() function is simply another way to access the 26 v-registers. The code [v(va)] is functionally equivalent to %va. However, the percent sign form is evaluated faster than the function call form, so it tends to be used in most cases.
The s() function performs pronoun substitution on a string, and returns that string. The pronouns apply to the object that triggers the function.
Thus, if your character has male gender, and you evaluate
> @emit [s(%P went down the road.)]
you see
He went down the road.
And, if your character had female gender, you would see:
She went down the road.
All the pronoun substitutions, discussed earlier, can be used in the s() function.
The get() function, mentioned earlier, is another frequently used function. It is used to access various attributes from a particular object. Its syntax is
[get(object/attribute)]
This function tends to be used frequently. The syntax is
rand(number)
and it returns a random integer between 0 and (number - 1).
You can create conditional expressions on an object by using the switch command. The switch command is similar to a CASE statement in a programming language. There is a conditional expression that is evaluated and, based on the results of that evaluation, different actions are performed.
The format for the switch command is:
> @switch test = condition1, action1, condition2, action2, ..., conditionN, actionN, default
The switch command tests the result of the test against each condition. If there is a match, the corresponding action is performed.
As an example, consider programming a dog to obey commands you give it. When you tell the dog to sit, it sits down; when you tell the dog to speak, it barks, and so forth. You can accomplish this by using the switch command. First, set up the dog to listen for commands:
> @create Rover Rover created as object #6745. > @listen Rover = *"Rover, *" Set.
Now, use the switch command on the @ahear attribute to perform different actions based on what a player commands Rover to do:
> @ahear Rover = @switch %1 = sit , :sits down. , speak , "RARWK!, jump, :does a backflip., play dead, :lies down on the ground., beg, {:whimpers, cries, and looks utterly heartbroken.}
Set.
Now you can tell Rover what to do:
> drop Rover Dropped. > "Rover, sit You say "Rover, sit" Rover sits down. > "Rover, speak You say, "Rover, speak" Rover says "RARWK!" > "Rover, jump You say "Rover, jump" Rover does a backflip. > "Rover, play dead You say "Rover, play dead" Rover lies down on the ground. > "Rover, beg You say "Rover, beg" Rover whimpers, cries, and looks utterly heartbroken.
Note that the conditions and actions are separated by commas. As mentioned previously, if you want to use a comma or a semicolon in one of your conditions or actions, make sure you surround the statement with braces ({}). Otherwise, a comma signifies a new condition or action, and a semicolon signifies the end of the switch command.
The last action in the switch command for Rover is enclosed in braces because the action contains commas within it.
As another example, you can achieve an IF-THEN-ELSE effect by using a switch statement and testing for either 1 or 0 in the conditionals:
> @create Parrot
Parrot created as object #6746.
> @listen Parrot = * has arrived.
Set.
> @ahear Parrot = @switch [eq("Speedy", %0)] = 1, {:flies around squawking, "Speedy is here! Speedy is here!"}, 0, :ignores %0.
The test portion of the switch command tests to see if Speedy is the name of the player who is entering the room. If so, it flies around and announces Speedy's entrance; if not, it ignores the entering player:
Snowball has arrived. Parrot ignores Snowball. Speedy has arrived. Parrot flies around squawking, "Speedy is here! Speedy is here!" Pooka has arrived. Parrot ignores Pooka.
Again, note the braces around the true part of the command because of the comma within it.
Perhaps you want to create an object and then define some specialized commands on that object. You can create a stuffed bear that growls when you squeeze it. The squeeze command is not a predefined command in the mud. You have to define it. You can create user-defined commands by setting them on a register:
> @attribute object = $command:actions
To define the squeeze command on the bear:
> @va bear = $squeeze:@emit The stuffed bear grrrowls huskily.
Then, when you type
> squeeze
you see
The stuffed bear grrrowls huskily.
You can define multiple commands on an object. Just make sure that you don't pick a command name that is already used, such as page or get, because predefined mud commands take precedence over user-defined commands.
You can allow your user-defined commands to accept arguments, too. You specify these arguments by using asterisks (*) in the command definition. These asterisks are converted into the positional parameters for the attribute that defines the command. For instance, you can change the squeeze command to accept an argument:
> @va bear = $squeeze *:@emit You squeeze the bear's %0. The bear grrrowls huskily. Set. > squeeze tummy You squeeze the bear's tummy. The bear grrrowls huskily.
In addition to user-defined commands on an object, you can also create user-defined attributes on an object. The format is:
> &attribute object = anything
You can also define an attribute using
> @set object = attribute:anything
You can set an attribute on an object to anything you want. You can use it for temporary storage of variables, names or DBREFs of objects, strings, and so forth. You can define an unlimited number of attributes on an object.
Make sure that your user-defined attribute does not conflict with an already-defined attribute. As you create more and more complex objects, the chances increase for you to think of an attribute that you have already defined on that object, and you end up redefining something that you have already written!
You can access these attributes using the get() function or the v() function.
> @va bear = $squeeze:@emit You squeeze the bear.;&lastsqueeze bear = %N Set. > @vb bear = $last:@emit This bear was last squeezed by [get(me/lastsqueeze)]. Set. > squeeze You squeeze the bear. > last This bear was last squeezed by Speedy.
This example sets a user-defined attribute called lastsqueeze whenever the bear is squeezed. The lastsqueeze attribute is set to the squeezing player's name. Then, when the player types last, this attribute is used to recall which player last squeezed the bear.
Each command that the mud executes goes into a queue first. The mud then runs commands out of the queue. Most of the time, the commands are executed immediately. However, some commands delay action for a few rounds, such as @trigger and @force.
To get a listing of the queue, you can use the @ps command. This command displays four queues. The Player Queue shows actions that you have triggered. The Object Queue shows actions that have been triggered by objects that you own. The Wait Queue displays commands that have been queued up and thus will be executed at some future point in time. And the Semaphore Queue shows semaphores, explained next. The @ps command has options you can use to obtain different process displays, such as a verbose listing, a brief listing, or a quick summary. See the Help on your mud for a description of these options to the @ps command.
You can delay commands so that they are executed at a later time by using the wait command:
> @wait #seconds=actions
This queues up the specified actions and runs them after waiting the specified number of seconds. For instance, you can set up a twilight effect by setting up these attributes on a test object:
> @va testobj = @wait 5 = @emit It is starting to get dark.;@tr me/vb Set. > @vb testobj = @wait 10 = @emit It gets darker.;@tr me/vc Set. > @vc testobj = @wait 15 = @emit It's completely dark now. Set.
Then, if you trigger the va attribute, the messages are displayed after each wait delay:
> @tr testobj/va
A delay of 5 seconds and you see:
It is starting to get dark.
Then, 10 seconds later:
It gets darker.
And, finally, 15 seconds after that:
It's completely dark now.
Sometimes, though, the commands that you have queued up will interact with each other in such a way that things get out of control. When these infinite loops occur, you can use the halt command to stop them.
> @halt
clears out your personal queue,
> @halt command
clears out your queue and puts the specified command onto the queue, and
> @halt object=command
clears out the given object's queue and puts the specified command onto the queue.
One of the more advanced mud concepts is the semaphore. A semaphore is a signal that indicates that it is okay for something to perform some action. On many real-life train tracks, semaphores alongside the road indicate that a train has recently passed. The semaphore is initially green, indicating to an approaching train that it is okay for the train to continue. Once the train has passed, the semaphore changes to red. Another approaching train would see the red semaphore and know that another train recently passed through, and it may be necessary for the second train to slow or stop in order to avoid hitting the back end of the first train. A timer on the semaphore eventually changes the red light back to green.
Similarly, a semaphore in a mud is used to signal when certain actions can occur and prevent other actions from occurring until the correct time.
One typical use of a semaphore is to prevent an attribute or object from being triggered until it finishes executing its actions. For instance, you have a vending machine that dispenses magic baseballs. When it is triggered, it starts performing actions to create a new magic baseball and to give that baseball to you. A semaphore can be used to prevent someone from coming along and triggering the machine again while it is in the middle of first action list.
Another common use of semaphores is to force commands to be executed in a specific order. You can create user-defined commands hop, skip, and jump, and force them to be executed in that order through the use of semaphores.
Each semaphore has a count associated with it. The count is the number of actions that are currently blocked, waiting for a notification to unblock them and thus allow them to execute. If the count is 0, an action will occur immediately. A positive number indicates that that many actions are currently blocked. A negative number indicates that that many actions in a row will immediately execute.
The @wait command is used to place an action list on a semaphore and then "block" those actions from occurring until a notification occurs. The syntax is
> @wait object=actions
which will set up a semaphore on the object and enqueue the given actions onto the semaphore's pending action list. If the actions are a list of commands, you have to enclose the list in braces. You can also set up a default execution time by using
> @wait object/#seconds=actions
Such a wait command will behave like a normal wait command, except that after the given number of seconds, the actions execute anyway, regardless of whether an actual notification has occurred. You can use this to make sure a set of actions is eventually executed, even if no notification ever occurs.
The @notify command is used to notify the semaphore. When notified, the semaphore will execute the first action in the action list. The action item will be "unblocked." A second notification will start the second action, and so forth. To notify a semaphore on an object, use
> @notify object
which will execute the first action that is pending on the object's semaphore queue. You can also execute a given number of actions by specifying that number
> @notify object=#notifications
which executes that many actions on the object's semaphore queue.
Finally, the @drain command resets the semaphore count to 0.
Semaphores are usually used on larger, more complex objects, such as vending machines, mud e-mail systems, and so forth. As a simple example of semaphores, consider constructing a trick "ack-in-the-box." The box contains a cat that springs out and coughs up a hairball when the box is wound up. The idea is for the commands relating to the hairball to be executed in a group, without interruption, so a semaphore is used:
> @create ack-in-the-box ack-in-the-box created as object #1515. > @desc ack= This is an ack-in-the-box. Type 'wind' to wind it up. Set.
Now set up the ack-in-the-box to be ready to execute a wait command immediately by setting up a notification for right now, and for when the mud starts up. The startup actions make sure that the ack-in-the-box begins with its semaphore at -1, which means that any action on it will not be blocked.
> @notify ack Notified. > @startup ack=@drain me;@notify me Set.
Now the object is ready to execute an action on it. This next section of code sets up a user-defined command, wind, that performs various actions and then, as its last action, renotifies the ack-in-the-box. This will allow the next wind command to immediately execute:
> &do_wind ack=$wind:@wait me={@emit The box whirrs and humms. Suddenly, the box SPROINGS open, and a scruffy-looking orange cat SPRINGS out of the box!;@create hairball;@desc hairball=You see a slimy cat hairball.;@drop hairball=The slimy cat hairball splats on the floor.;@adrop hairball=@emit A slimy cat hairball splats onto the floor.;@set hairball=DESTROY_OK;@emit The cat says "ACK!" and coughs up a hairball!;drop hairball;@emit The cat is then pulled back into the box, which closes.;@notify me}
Set.
And test it out!
> drop ack-in-the-box
Dropped.
> l
Speedy's Room
You are in Speedy's Room.
Contents:
ack-in-the-box (#1515)
Obvious exits:
out
> l ack
This is an ack-in-the-box. Type 'wind' to wind it up.
> wind
The box whirrs and humms. Suddenly, the box SPROINGS open, and a scruffy-looking orange cat SPRINGS out of the box!
The cat says "ACK!" and coughs up a hairball!
ack-in-the-box dropped hairball.
The cat is then pulled back into the box, which closes.
A slimy cat hairball splats onto the floor.
> l
Speedy's Room
You are in Speedy's Room.
Contents:
hairball (#1530d)
ack-in-the-box (#1515)
Obvious exits:
out
> wind
The box whirrs and humms. Suddenly, the box SPROINGS open, and a scruffy-looking orange cat SPRINGS out of the box!
The cat says "ACK!" and coughs up a hairball!
ack-in-the-box dropped hairball.
The cat is then pulled back into the box, which closes.
A slimy cat hairball splats onto the floor.
> l
Speedy's Room
You are in Speedy's Room.
Contents:
hairball (#1530d)
hairball (#1531d)
ack-in-the-box (#1515)
Obvious exits:
out
As you can see, every time you wind the ack-in-the-box, a slimy hairball is created, which drops onto the floor! If you were holding the ack-in-the-box, it would drop into your hand (and thus into your inventory).
This simple example illustrates the use of semaphores to make sure that the commands to create the hairball describe it, and set attributes on it are not preempted by another wind command, issued as the ack-in-the-box was working on the first wind command.
Putting all the concepts together, this section will go through the programming of an exploding jawbreaker. The jawbreaker will have a command defined on it that will allow a player to eat it. When the player attempts to eat the jawbreaker, various random amusing things happen. The object will also have several user-defined attributes. These attributes store various variables, strings, and messages.
Here goes!
> @create jawbreaker jawbreaker created as object #4545. > @desc jawbreaker = It looks like a giant candy jawbreaker, about the size of a plum. Upon closer inspection, it seems to be made of sugar, nitroglycerin, [name(v(currate))]'s saliva, salt and various corn by-products. Type 'eat jawbreaker' to eat it! Set. > @fail jawbreaker = Type 'eat jawbreaker' to eat it. Set.
This creates the jawbreaker and sets its description. Note the function call within the description. The currate attribute contains the DBREF of the player who last tried to eat the jawbreaker. This attribute is set by other commands later. The name() function uses the value in the currate attribute to generate the name of the player who last ate the jawbreaker.
> &comment jawbreaker = This is a trick jawbreaker that explodes when someone tries to eat it. Set.
The comment attribute is simply a note to future programmers. It describes what this object does. On very complex objects, it is helpful to throw in a few comments to describe how the object works, so that other people can figure out what is going on!
Next, program the eat jawbreaker command:
> &chew jawbreaker = $eat jawbreaker:@emit [name(%#)] picks up the jawbreaker and pops it into %p mouth!;@trigger me/rand[rand(3)];&lastate me=[get(me/currate)];&currate me=%# Set.
This sets the eat jawbreaker command and the actions to be performed when eat jawbreaker is typed by a player. There are four actions in the action list.
The first action displays a message to everyone indicating that the player put the jawbreaker in his or her mouth. Note how the %p pronoun substitution is used to print the appropriate possessive pronoun corresponding to the player.
The second action generates a random number from 0 to 2, using the rand() function. This number is appended to the string rand to generate a random attribute name, which is then triggered. One of three attributes will be triggered: rand0, rand1, or rand2.
The third and fourth actions in the action list set the lastate attribute to the last player who ate the jawbreaker, and the currate attribute to the player who is currently trying to eat the jawbreaker. These two attributes can then be used in later commands and actions.
Now, all there is to do is to define the three random actions that are triggered when the player eats the jawbreaker.
> &rand0 jawbreaker=@emit [name(v(currate))] closes [poss(v(currate))] eyes and smiles from ear to ear as the sugar kick from the jawbreaker sets in. Ahhh. After a few seconds of sucrose satisfaction, [name(v(currate))] removes the jawbreaker from [poss(v(currate))] mouth.
This first random action is fairly harmless. Nothing bad happens to the player. The currate attribute is used to determine both the player's name, using the name() function, and the possessive form of the player (his, her, its), using the poss() function.
> &rand1 jawbreaker=@emit [name(v(currate))]'s eyes get real big as the fire-hot super jalapeno pepper flavor of the jawbreaker takes effect! [poss(v(currate))]'s eyes start to water as [subj(v(currate))] screams, "YOW THAT'S HOT!" Suddenly, [name(v(currate))]'s head EXPLODES with a flash and a smell of hot peppers and burnt sugar!! *BOOM* !!;kill [name(v(currate))]=100
This second random action is a bit more violent. It kills off the player, due to the flavorful effects of the jawbreaker, by performing a kill command on that player. The subj() function returns the subjective pronoun of the player.
>&rand2 jawbreaker=@emit [name(v(currate))] gasps, "I've been poisoned by [name(v(lastate))]'s *gag* spit!" In a last-ditch attempt for revenge, [name(v(currate))] throws the jawbreaker at [name(v(lastate))]! [switch (eq(here, loc(v(lastate))), 1, The jawbreaker BONKs [name(v(lastate))] on the head and the unstable nitroglycerides in the jawbreaker EXPLODE!, 0, Luckily, [name(v(lastate))] is not around, and the jawbreaker merely ricochets off the wall and rolls to a stop nearby.)];kill [name(v(currate))]=100;kill [name(v(lastate))]=100
The last random action is somewhat whimsical. It tries to kill off both the current player and the player who last ate the jawbreaker. Note the switch statement in the action list. It tests to see if the player who last ate the jawbreaker is in the same room. If so, an appropriate message is printed as the jawbreaker hits that player. If not, the jawbreaker just rolls around to a stop. Finally, a few kill commands are performed. If the person who last ate the jawbreaker is not in the current room, that kill command will have no effect.
This exploding jawbreaker is amusing to play around with in a room full of people! Players who lose out and are killed by the jawbreaker can simply teleport back to the room, so there isn't much harm done.
Thanks to Jen "Tourmaline" Kleiman for providing the idea behind this example.
MUSE, a variation and elaboration of MUSH, is written in a language called TinyMUSE, another spin-off of the original TinyMUD. TinyMUSE was first implemented in a popular simulated environment called MicroMUSE. This on-line environment was first conceived in 1990 by programmer Stan Lim ("Jin"), who built the original environment in Larry Foard's TinyMUSH programming language. "Cyberion City," as it came to be called, is a beautiful vision of a utopian society in an orbital station high above the Earth, complete with talking robots, spaceships, and otherworldly adventures. As the years went by and the source code was refined and elaborated upon, the newer language became known as TinyMUSE, and a specialized client program, called TinyFugue (also commonly known as tf) was developed to interface with the MUSE during play. TinyMUSE worlds come in a variety of flavors, but the language has become a favorite of many educators because of its ease of use, and its firm but modifiable "social structure."
Almost all of the capabilities of MUSHes can also be found in MUSEs. MUSEs also have a few idiosyncracies all their own, such as a heirarchical player class system.
There are a few thematic MUSEs out there, but the granddaddy MUSE is MicroMUSE.
Name |
Machine Name |
Internet Address |
Port |
|
MicroMUSE |
chezmoto.ai.mit.edu |
18.43.0.102 |
4201 |
|
Rhostshyl |
rhostshyl.cit.cornell.edu |
128.253.180.15 |
4201 |
|
TimeMuse |
murren.ai.mit.edu |
18.43.0.179 |
4201 |
|
TrekMUSE |
grimmy.cnidr.org |
128.109.179.14 |
1701 |
These addresses are current as of August, 1994. Keep in mind that they may change.
MicroMUSE, a popular simulated environment based at MIT, was first conceived in 1990.
TinyFugue, the TinyMUSE client, is available via anonymous FTP to chezmoto.ai.mit.edu and may be found in the pub/muse directory as tf.33a5.tar.gz.
There are a growing number of TinyMUSE worlds on the Net, of which those listed here represent merely a sample. A number of educational TinyMUSE worlds were incorporated into a single overarching structure known as MuseNet (the Multi-User Science Education Network); any of these worlds may be reached via telnet to any of the affiliated sites.
With the TinyFugue client, you can instantly open bamf portals between any of these worlds, allowing your character to toggle between worlds at a whim. To see a list of available worlds, simply type /listwords from within TinyFugue. To open a connection to another MuseNet world, type /world world-name, and then type ESC-B to bamf through the portal.
The current list of MuseNet worlds includes MicroMuse, BridgeMuse, MariCopa, CyberLib, and EcoMuse. MuseNet worlds can be reached via telnet at any of the following addresses:
musenet.bbn.com micro.bbn.com muse.usmee.maine.edu micro.usmee.maine.edu bridges.usmee.maine.edu pico.usmee.maine.edu femto.usmee.maine.edu cyberion.usmee.maine.edu nano.usmee.maine.edu atto.usmee.maine.edu
The MUSE environment allows for classes of programming capability to be distributed by the MUSE Directors, according to the talent and drive of individual players. The learning curve is therefore broken into a series of plateaus, allowing players to progress at their own speed. The following things are specific to MUSEs.
There are a few common commands that MUSEs have, in addition to those described in the "Overview of Common Commands" section, earlier in this chapter.
The money command shows your current number of credits and the value of all your assets.
The news command displays the current newsfile, which you should check regularly.
In TinyMUSE worlds, programming capabilities (powers) and virtual civic duties are conjoined rather formally in a hierarchic structure of roles: Visitor, Citizen, Guide, Official, Corporation, Administrator, and Director (the specific duties and powers of each class are determined by the Directors of any given MUSE, and may be set to affect all lower-ranked users, all equally ranked users, or all users). This system tends to ensure that those players entrusted with powerful characters act in a responsible and accountable manner.
Moving around in the virtual environment and playing with other people's creations is an enjoyable pastime, but the real action begins when you decide to become a MUSE Citizen. At this point, you receive a real character (having been known as guest up until now), and some building privileges. Your character, provided by the officials of the MUSE, will have a name and an associated password, and probably very little else.
The function
class(player)
can be used to access a player's class in MUSE programs.
You can also use functions that access player's powers:
controls(player1, player2, power)
returns 1 if player1 has the specified power over player2. Otherwise it returns 0.
haspow(object, power)
returns 1 if the object has the specified power. Otherwise it returns 0.
MUSEs have specialized rooms called zones. A zone is a group of rooms acting as if it were one big room. Messages that are sent to a room in the zone can be heard throughout the zone. For instance, you can create a long hallway, composed of three rooms, such as the West End of the hallway, the Middle of the hallway, and the East End of the hallway. Players can be located in any of these three rooms. Then, you can group the three rooms into a zone room. Certain messages that are sent to the zone can be heard by everyone in the hallway rooms. In this manner, you can create large rooms that seem to have a depth to them. You can create a ballroom or a large dance floor using a zone.
To link a room into a zone, use the command:
@zlink room = zone-object
And to emit a message to everyone in the zone, use:
@zemit zone-object = message
There are also a few functions you can use in MUSE code that relate to zones. The function
zone(object)
operates on a given object. If the object is a room, the room's zone is returned. If the object is an exit, thing, or player, the DBREF of the zone that the object is in is returned.
Additionally, the function
zwho(zone-object)
returns a list of the DBREFs of all the players in the zone, and the function
inzone(zone-object)
returns a list of all the rooms that have been z-linked to the zone.
In an object-oriented environment, directing players where you want them to go can be tricky. If done inartistically, linearity can be easily overdone, making things seem like a slow-moving sequence of still images. The trick is to balance the decision to "opt out" of a storyline with various "lures in" to the storyline. It also doesn't hurt to make every room alive with incidental action.
The following environment is excerpted from Tod Foley's "Monster Island," a 1920's pulp adventure MUSE narrative with a fairly strong overarching plotline. The story begins in a dark, secluded bay...
Blackmoor Bay(#248Rpv)
The waters of Blackmoor Bay are a deep indigo. Dark waves lap the sandy shore in a mesmerizing rhythm, and a chilly seabreeze sends a shiver up your spine. Rumors claim that this Cove is home to a host of lost seafaring souls, sailors whose ships were destroyed by the merciless might of The Great Sea...
Contents: ship(#252SeoK) Obvious exits: Shore Sea
A combination of locks were used to keep people from traveling straight out into the sea. The @fail message for this exit describes the biting wind and stinging spray. Although there are a few clues lying around on the shore, the objective is for players to get out to sea.
enter ship ship(#252SeoK)
The ship is stocked and ready to sail, despite the fact that no crew can be found. The hull is old and the sails slightly torn, but she seems seaworthy enough. Care to set sail?
Obvious exits: bay climb mast
Typing climb mast leads you to the crowsnest, where you can see the entire surrounding area (and perhaps discern another clue); it also allows you to jump straight into the cold waters of Blackmoor Bay, just for fun. The desire was to structure this adventure so that one party could come along fairly soon after the previous one, so the ship had to stay here. If you examine the code, you see that that's exactly what it does. The @va register waits to hear the magic words "set sail" to issue you the exit name:
ex here ship(#252SeoK)
This dilapidated old vessel looks like she's weathered a few storms, but she's rigged and ready to sail. You see no movement onboard. Dare you enter?
Owner: Speedy Credits: 1 Type: Thing Flags: sticky enter_ok opaque key Zone: Universal Zone Created: never Modified: never Idesc:The ship is stocked and ready to sail, despite the fact that no crew can be found. The hull is old and the sails slightly torn, but she seems seaworthy enough. Care to set sail? Va:$set sail:@PEmit %N=You take the helm, piloting the ancient vessel northward to the mouth of the Bay. The Sea looks especially rough for this time of year... What's your call - `sea' or `bay'? Contents: Speedy(#5Pevcn) Home: Blackmoor Bay(#248Rpv) Location: Blackmoor Bay(#248Rpv) Exits: bay(#372Ev) sea(#371EDv) climb mast;up;climb(#251Ev)
First we head out to sea...
sea You boldly set forth into the fierce and mysterious Sea... The Great Sea(#250RvJ)
You head out into The Great Sea, fierce winds blowing in your hair, salty spray hitting your face... you begin to fear for your safety as the sky darkens and the waves grow higher... you have no idea which direction you're facing anymore...
Obvious exits: <I>sland <B>ay
After a second or two, we see...
You fear a storm is approaching...
And after another second or two...
The sky begins to draw dark as the clouds above gather in disturbing thickness...
The pressure's really getting to you by now, isn't it? Well, the clue in the crowsnest is a scrawled note reading "Ye better pray, matey." And, in true literal fashion, players who type anything beginning with p-r-a-y will be rewarded by the following line of code (from register @va of The Great Sea):
Va:$pray*:@PEmit %N=Your prayers have been answered, %N. The sky begins brightening as the clouds part, and a lovely rainbow arches overhead. You see a single white dove heading toward an island that wasn't there a minute ago... ;@tr me/vb
All in all, it really hasn't taken very much worktwo rooms and two thingsto get the players fairly immersed in the adventure. They've experienced a little thrill and a small miracle, and feel as though they're far from "home." At this point, we could allow players a chance to move on through other exits and travel to other realms. By linking exits to the rooms of fellow MUSErs, the Sea becomes Great indeed. Once the players commit to debarking upon the island, however, the narrative picks up in full swing:
island River Mouth(#373R)
The river is narrow, the water dark, and the smell is horrid. You push aside some of the hanging vines that obscure your view, and peer ahead. Overhead, several carrion-birds circle slowly, patiently waiting...
Obvious exits: <S>wamp <J>ungle Sea
One easy way to create the feeling of "life" in environments like these is by using @wait commands in the @adesc line, like this:
Adesc:@Wait 5=@Emit A mutant rodent on the north bank is startled by your passage. It freezes,