Colin's Way Easy Intro Guide to MOO Programming

Version 2.2
March 1996

by Colin McCormick (a.k.a. Snowfall)
colin@tripod.com


0.0) Silly Foreword

This document is for free distribution. If you have a brilliant idea for how to make it into truly monumental cybernetic scripture (or just have a comment, question or flame) let me know. Personal cheques are also welcome. The Way Easy Guide is intended to teach basic programming concepts and methods, so if you already understand how to code a MOO for loop or build a recursive daemon wanderer, allow me direct you to Pavel Curtis' LambdaMOO Programmer's Guide, available at parcftp.xerox.com/pub/MOO/ProgrammersManual.*. There are a number of elements of MOO programming that I have omitted in the interests of time and personal sanity; Pavel's guide is significantly more complete.

You may also be interested, as an intermediary document, in my Way Easy MOO Programming Examples, a series of building and programming examples spelled out in (I hope) straightforward steps.

0.1) Special Note for HereMOO Programmers

HereMOO is revolutionary among MOOs in its Web-based implementation. All programming techniques on standard MOOs will work on HereMOO, but there are a few non-standard things one can do as well. The most relevant of these is the fact that HTML tags can be written directly into descriptions or even into spoken/emoted text. Thus by typing

	@describe me as "A <B>great</B> guy!" 
other people, when looking at you, will see:

A great guy!

You could also type
	say Melanie, you look <I>marvelous</I> tonight!
and, on the MOO, everyone will see:

Colin says, "Melanie, you look marvelous tonight!
This technique will also work for adding images, links, lists, etc. to your description and the description of your room and objects.

For help with HTML, check out NCSA's Beginner's Guide to HTML.

For more complicated HTML tricks on HereMOO (including how to use a link to send a command to the MOO and how to add images of your own to your description) see below.


1.0) Introduction

This book is for aspiring MOO code programmers and other lower vertebrates, except sheep. MOO code, for all its power and flexibility, is really quite easy to learn (except for sheep.) To read this book you will have to know basic elements of the MOO environment and commands, but not much else, and by the time you finish you should have a good grasp of the elements of MOO programming, how to create and edit verbs, and the basics of the MOO language.

One note before we begin. In order to do most of the examples in this guide, you need to have been made a programmer on your MOO. Most MOO wizards will do this upon request, with a smile and free of charge. If you try some of the following examples and get errors like "E_VERBNF (Verb not found)" or "E_PERM (Permission Denied)" make sure that you are a programmer before proceeding, unless you're a sheep, in which case there's just no hope.

Ovine-less ones, read on!

1.1) Object Orientation

To program on a MOO, one must first understand the concept of an object-oriented data structure. Everything that exists on a MOO is an object that has a unique object number. These objects are organized into a sort of genealogical hierarchy, with the Root Object (#1) at the top and its "children", "grandchildren", etc. branching down from it. The nifty thing about this is that every object automatically inherits all of the characteristics (i.e. properties and verbs) of its parent, and, by extension, of all of its ancestors back to #1.

1.2) Inheritance

Inheritance is useful in programming in that it allows for the creation of classes of objects. If we wished to create (for instance) a refrigerator, a backpack and a kangaroo pouch, we could first make a generic "container" object that could hold other objects and recognize such commands as open, close, and lock. Then we could create three children of this generic container (customizing them to look like a refrigerator, a backpack and a kangaroo pouch) and they would all function as containers that could be opened, closed and locked, without having to reprogram each one individually. Most MOOs are organized around this idea, and there are many generic items available to use as parents. (For a complete list, ask the resident wizards.)

To see the children or parent of an object, type

	@kids object name
	@parent object name 

1.3) Referring to Objects

Objects are refered to in MOO code by their object number, preceded by the number symbol (#). Hence, the Root Object is #1, and the next object made will be #2, etc. One of the best ways to find object numbers is the @contents command. To see the names and object numbers of the objects in your room, for instance, type @contents here. To see your own contents (the items you are holding), type @contents me.

1.4) In-Database Help

Type help objects for more information about objects.


2.0) Properties

Now that we've established the idea of objects and their hierarchy, let's talk about what you can do with them. Objects can have properties and verbs defined on them. Properties are chunks of data that have four possible data types (or "flavours") while verbs are MOO-code programs. Properties are more straightforward, so we'll start with them.

2.1) Properties and their Flavours

Properties can hold any data you wish. Some properties, defined on #1 and thus inherited by all objects, characterize an object in the MOO's virtual reality; among these are the "name", "location" and "contents" properties. Others may be used by you, the programmer, to hold the value of (say) the number of sandwiches left in a sandwich vending machine, or a list of possible phrases your pet parrot might say. There are four valid data types for properties; they are:

Strings must be surrounded by quotes (") and lists by curly brackets ({}) with their elements delimited by commas (,). All elements in a list must be of the same data type. For example, {"this", "is", "a", "list"} and {{1, "hi"}, {2, "there"}, {3, "bob"}} are both valid properties.

2.2) Referring to Properties

Properties are referred to as 'object number.property name' -- i.e., the name property of the Root Object is '#1.name', and its location property is '#1.location'. To see the value of any property, just type its reference (without quotes), preceded by the evaluation operator, ";":

	;#1.name
	=> "Root Class"
The evaluation operator (;) is used to find the value of any reference or MOO code expression. It is a very powerful programming tool, but has the drawback that it can only recognize objects by number, not by name or alias.

2.3) Creating and Removing Properties

To create a new property, use the @property command:

	@property me.nickname "Crazy Caboola"

	@property #123.flowers = {"iris", "rose", "daisy"}
(You may abbreviate this as @prop.) The first command will create a property on you (remember, everything is an object, including you) called "nickname" and give it the value "Crazy Caboola", a string. The second will create a property on object #123 called "flowers" with the value {"iris", "rose", "daisy"}, a list. Property names must not contain spaces (use the underscore "_" for property names like "sandwiches_left".)

It's worth noting at this point that if you don't own the object onto which you are attempting to define a property, the MOO probably won't let you define it. We'll return to the topic of ownership and permissions later; for now try examples on yourself or objects you've created.

If no initial value is specified in @property, it will default to 0, a number. Be careful about this, because if you create a property and give it no initial value, and later try to change the value of that property to a string, a list or an object number, the MOO will become confused and you will have to remove the property and start again. If you are creating a blank property that you know will eventually be a string, list or object number, give it an initial value of "" (the empty string), {} (the empty list) or #-1 (a default "nothing" object.)

To remove a property, use the @rmproperty command:

	@rmproperty me.flowers
(You may also abbreviate this as @rmprop.)

To list all properties explicitly defined on an object (the ones implicitly defined on it because of inheritance are difficult to list -- do the following for each parent on up the object tree) use:

	;properties(#1)

2.4) Changing and Viewing Property Values

Object inheritance includes properties, and the initial values of these properties are the same as those of the object's parent, except for certain key properties such as "name", "location" and "owner". Both inherited and explicity defined properties you own can be changed by the @set command:

	@set me.nickname to "A Dyspeptic Aardvark"
In order to see the value of a given property, you can either use the evaluation operator (;) if you know the object number of the object on which the property is defined, or you can use the @display command:
@display me.nickname
.nickname     Colin (#123)       r c    "A Dyspeptic Aardvark"
The name, owner and value of the property are displayed, as well as its "permission bits", which we will discuss later. @display may be abbreviated as @disp.

2.5) In-Database Help

Type help properties for more information about properties.


3.0) Verbs

Verbs are the best part of a MOO. They are what make people able to do things - hence their name. A verb is a short program, written in MOO code and "defined on" (assigned to) some object. (Remember that this means the verb is implicitly defined on all of this object's children, as well.) Examples of verbs include look, get, and floccinaucinihilipillificate. The greater the quantity and quality of verbs that are defined, the more complex and engaging the MOO's virtual reality becomes.

3.1) Verb Syntax

All MOO verbs not involving the evalulation operator (also claled "eval") are structured in the following way:

(verb) (direct object) (preposition) (indirect object)
The three arguments (direct object, preposition and indirect object) are optional, and can be used in any combination to suit the form of the verb. For instance:
	spill sewage on Yog-Shaggoth
uses all three optional arguments, while
	harass Cobweb
uses only one, the direct object. It is important to figure out how you want a verb to be typed in by players before you write it; make sure that you have all the information typed in that the verb will need (i.e. you need to know on whom to spill the sewage, and also what to spill in the first place!) Be aware that if an object other than a player or a room has a verb defined on it, the name of that object must be one of the arguments. Otherwise, the MOO doesn't know what object to find the verb on!

3.2) Creating Verbs

The first step to writing a verb (after you have figured out its syntax) is to create it on a particular object. Usually this object will be some nifty toy or tool you've already created using @create, but you can also define verbs on yourself and any room you own. Verbs on yourself will always be usable by you, but never by anyone else (unless they copy the verb to themselves.) Verbs on a room are usable when standing in the room, but not anywhere else. Verbs defined on any other object will only be usable if you are holding that object or if it is in the same room as you.

To create an (unprogrammed) verb on an object, use the @verb command:

	@verb object:verb name d.o. prep i.o
You must specify the object on which the verb is to be defined, the name of the verb, and its arguments (if any.) Objects can be specified either by their object number or by their name (or alias) if they are in your possession or in the same room as you.

Argument specifications are of the following form: for the direct object and indirect object, you may choose this (the name or number of this object must be typed in with the command); any (any valid object name or number must be typed in with the command) or none (no [in]direct object name or number is required.) Valid prepositions are pretty straightforward: (on, at, with, over, etc.) A full list can be seen by typing help preposition. Examples of two valid verb creations:

	@verb #231:chuck this at any

	@verb loins:gird this none none
The first command creates the verb chuck on object #231. chuck expects to see a command like chuck #231 at Melia. The second command creates the verb gird on the object loins. gird expects to see a command line like gird loins. The object number, name and alias are interchangeable as long as you are either holding the object or it is in the same room as you.

3.3) Correcting Verb Arguments

If you realize after creating a verb that you have specified the wrong arguments, use the @args command to correct it:

	@args loins:gird this with any
loins:gird now expects to see something like gird loins with magnesium.

3.4) Removing/Renaming Verbs

To delete a verb that you don't want, the command is @rmbverb object number:verb name. To rename a verb, the command is @rename object number:old verb name to new verb name. To see a list of all the verbs defined on an object, type @verbs (object).

Finally, to see all the verbs explicitly defined on an object, type

	;verbs(object number)

3.5) In-Database Help

Type help verbs or help @verb for more information on verbs.


4.0) Permissions

As with most human systems, MOO employs a system of ownership to keep track of who can do what to what. This system is somewhat complex, but the basic rule of thumb is: if you made it, you own it, and you can do anything you want to it. If you're in a hurry to get to programming, skip on ahead, but it's a good idea to understand permissions if you want your verbs to interact with other players.

4.1) Overview

Each object, property and verb has an owner, specified in its "owner" property. When a new object, property or verb is created, the player who creates it is the owner unless otherwise specified. All players have an "owned_objects" property that is a list of the objects they own; unfortunately, no such system exists for keeping track of the properties or verbs one owns, which can occasionally cause problems.

In addition to an owner, objects, properties and verbs all have a set of permission bits that specify what everyone other than their owner may do to them (owners have no restrictions on what they may do to their objects. Calm down, Melanie.) These permissions are similar to those on UNIX filesystems, and are stored as a string of characters. The possible characters, or "bits" for objects, properties and verbs are different, and will be discussed in turn.

The best way of viewing a property or verb's owner and permission bits is the @display command, mentioned above.

	@display me.nickname

	@display me:run
The best way of viewing an object's owner and permission bits is the @examine command:
	@examine me

	@examine #100
We'll start by discussing property permissions.

4.2) Property Permissions

The permission bits for properties are drawn from the following set:

A permission bit string can have zero, one, two or all three of these characters in it. For example, a permission bit string "rc" means that the property is readable by everyone, writable (changable) only by its owner, and the inherited version of the property on any children of the object will be owned by the child object's creator. This latter property is particularly important for wizard-owned objects that become generics. The default permission string for properties created with @property is "rc".

As a side note, it's a good idea to avoid writable properties where possible. There's no reason for other people to be able to change your data, and if you rely on the propety to give information to some of your verbs, you have no guarantee that those verbs will continue to work from day to day.

4.3) Verb Permissions

Verb permission strings are drawn from the following set:

Writable verbs are very dangerous, as they allow any programmer to issue commands as if he/she were you, by editing the verb's code. The default permission string for verbs is "rd", but if a verb is specified with the arguments "this none this" then it will have the permission string "rxd", which is useful when creating verbs that are to be called from within other verbs.

4.4) Object Permissions

Object permission strings are in fact not exactly that -- they are individual properties called "r", "w", and "f", and are either 1 or 0 (true or false). Still, you can think of them as a string, as the @chmod command (see below) will accept them as a string. Their meanings are:

Again, writable objects are to be avoided unless there's a good reason for them. Since no concise list of the properties and verbs you own is kept, it's easy to lose track of verbs and properties stored on objects you don't own.

4.5) Changing Permissions

To change the permission bit string of a property or object, use the @chmod command:

	@chmod me.nickname "rwc"

	@chmod me:run "rxd"

	@chmod me "rw"
The first of these commands means that anyone on the MOO (who is a programmer) can read and change your nickname property; plus, if anyone creates a child of you, they will own the inherited version of the nickname property. The second command means that anyone can list the code of the "run" verb (which we assume for the moment is a verb defined on you); it can be called from within another verb, and it will print traceback error messages if something goes wrong during its execution. Finally, the third command means that anyone can see what properties and verbs you have defined on you, and anyone can define a new property or verb on you (which they will own.)

As a final emphasis, beware of writable propeties, verbs and objects. If there's no good reason for them to be writable, don't make them so.

4.6) In-Database Help

Type help property_info, help verb_info and help @chown for more information on object, property and verb permissions.


5.0) Programming Verbs

Once a verb has been created and all its arguments are correct, all that remains is to program it. No sweat! The command to program a verb is:

	@edit object:verb
This will move you to the Verb Editor, a special room where you can edit your verb line by line. Since the Verb Editor is every programmer's best friend, it behooves us to briefly mention the commands therein.

5.1) The Verb Editor

When you arrive in the Verb Editor, you will see a list of commands. Typing look at any time will also give you this list. The important ones are:

Two other non-editing commands are important: N.B. -- you must compile your verb in order to it to work. Compile after each version of the verb is completed. If you don't compile, the next time you use @edit, the Verb Editor will ask if you want to continue to work on the last (uncompiled) verb or throw it away.

5.2) Programming Verbs (for real)

MOO verb code looks a lot like C (don't panic!) It is easy and practical to learn, and may even serve as a good introduction to learning other computer languages. A sample bit of MOO code looks like:

	player:tell("You take a cookie from the jar.");
	player.location:announce(player.name, " takes a cookie from
the cookie jar and eyes it hungrily.");
	if (player.name == "Cookie Monster")
	     player:tell("You wolf down the cookie with gusto.");
	     player.location:announce(player.name, " gobbles up the
cookie with no regard for decorum.  You are splattered with crumbs.");
	else
	     player:tell("You delicately nibble at the cookie.");
	     player.location:announce(player.name, " demurely nibbles
at the cookie and politely cleans up the crumbs.");
	endif
The code is executed line by line, except in the event of flow control commands, which will be discussed later. Every line (except the aforementioned flow control commands) must be followed by a semi-colon (;). While simple verbs are easy to keep track of, you may want to make flow charts or other sketches of more complicated verbs before leaping right into writing them.

5.3) The First Verb

The first verb to try, of course, is a simple one. Without barely more knowledge than what we already have we can write a simple verb, that will say something back to us when we type it in. The two most important MOO code commands you will ever know are the following:

	player:tell("Immanuel Kant was a real pissant...");
	player.location:announce("...who was very rarely stable...");
The former tells the player who has called the verb (typed the verb name as a command) whatever is in the quotes. The latter tells every other player in the calling player's room whatever is in its quotes. The above two lines, if written and compiled as a verb named "bruce", would do the following: by typing bruce, the player would see the words:
Immanuel Kant was a real pissant...
and the other players in room would see:
...who was very rarely stable...
This may not seem like a big deal, but considering that you can make the words in the parentheses anything you want, it can get fairly interesting. Also, you needn't have just text in the parentheses: you may also place property references or variables in the parentheses. For example:
	player:tell("Your name is ", player.name, " and the value of
the variable \"count\" is ", count, ".");
will display to the calling player something like:
Your name is Griselde, and the value of the variable "count" is
1293.
Note that the various components of the sentence are separated by commas. The expression '\"' prints out a quote sign in the sentence (a single quote " will be interpreted as the end of the text string!)

(Technical Note: the lines player:tell(...) and player.location:announce(..) aren't actually MOO commands. They instruct the MOO to call other verbs, either tell or announce, that are defined on the objects player and player.location, i.e. a room. These particular verbs derive from the LambdaCore database distribution, and a very few MOOs will not have them. If these lines fail to work on your MOO, contact your wizards for assistance.)

5.4) Congratulations...

...you can now write simple MOO verbs! Okay, so they're not anything to get hot and bothered about, but they're a start. With some general refinements, we can start writing interesting and complicated verbs that will be the envy of other players for miles around.


6.0) (Slightly) More Complicated Verb Programming

The most interesting and complicated verbs are ones that respond differently to different conditions: who is calling it, where that player is, what time it is, etc. This can be accomplished with variables and flow control statements. We've already used one variable unwittingly (player, above), so we're halfway there already.

6.1) Variables

Variables are changeable bits of data that are used only while the verb is being executed. Each variable has a name and a value. All verbs begin with certain pre-defined variables that can be used without any initializing statements. The more useful of these are:

Any variable (including the predefined ones) can be created and given a value or just given a new value by the equal sign ("="):
	count = 3;
	my_object = #143;
	old_character_name = "Rumplestiltskin";
Variables may also be set equal to other variables in this way, although a variable can never be set to a value of a different data type than its original value.

6.2) Flow Control

Variables are most useful when they are used within a more complex structure of execution than just one line after the next. The three basic flow control statements allow for lines to be executed in a special order, depending on certain tests. These statements are:

	if (condition 1)
		...
	elseif (condition 2)
		...
	else
		...
	endif


	while (condition A)
		...
	endwhile


	for variable in (list or numerical interval)
		...
	endfor
The ellipses stand for an arbitrary number of lines, including zero.

6.2.1) The If Statement

You can have as many elseifs as you want; they are tested in order (after the first if) from top to bottom. Once one is judged true, the commands between it and the next are executed, and the whole if statement is over. You may have one or zero elses, and you must have one and only one endif.

6.2.2) The While Statement

The while statement is simpler. condition A is tested, and if it is true all lines between while and endwhile are executed. condition A is then tested again, and the same occurs. Only once condition A is false will the while statement be over. Note that this can result in an infinite loop. Make sure that at least one of the lines after while affects condition A so that it will eventually be false.

6.2.3) The For Statement

for looks at the list or numerical interval it has been given, and sets variable equal to the first element or number in the list or interval. It then executes all lines until it gets to the endfor, sets variable equal to the next element or number in the list or interval, and repeats the process until it has run through the entire list or interval. Note: to specify a numerical interval, the syntax is:

	for variable in [number1..number2]
with square brackets "[]" and two periods between the numbers specifying the range.

6.2.4) Examples

Some examples seem in order:

	snork = random(10);
	badsnork = 4;
	while (snork != badsnork)
	     player:tell("Snork number is still not ", badsnork, "!");
	     snork = random(10);
	endwhile

	for count in [1..10]
	     player:tell("The count is now ", count, ".");
	endfor

	if (player.name == "Dirtbag")
	     player:tell("You are a dirtbag.");
	else
	     player:tell("You are not a dirtbag.");
	endif
The command random(x) generates a random integer from 1 to x.

6.3) Evaluation

Conditions in flow loops are evaluated in reasonably straightforward ways. The following operators are important:

The following tests if a value is in a given list:
	variable name in (list)
The list or list name must be surrounded by parentheses. Finally, the logical operators are as follows:
	&& - and	|| - or 
Thus (3 <= 1 && 10 == 2+8 is false, while 3 <= 1 || 10 == 2+8 is true.

The MOO also considers the following to be true:

and the following to be false:

6.4) A Note on Lists

Lists are very important to programming, and you will probably want to use them a lot (unless you're a sheep.) To refer to a single element of a list, use the list's name followed by the index of the element you want in square brackets:

You can also refer to a single letter in a string in this manner: If you attempt to refer to an element of a list or a letter of a string that does not exist (for instance, the list or string is not that long) the MOO will give you an error message. Similarly, if the variable to which you are attaching an index reference is not a list or a string, the MOO will get upset.


7.0) Where To Go From Here

Well, that's it folks! From this point on, basic programming is really just experimentation and logic. If you write a verb and upon trying to compile it in the Verb Editor are given some sort of error message that contains Line #: syntax error (which will almost certainly happen at some point), look over line # very carefully, check that you have all the endifs, endwhiles, endfors and semicolons that you need, and then (in case of dire need) ask for help from a more experienced programmer or a wizard. The best way to learn is to play. :)

If you'd like more detailed information on data structures, interesting MOO code commands not mentioned here, or just general MOO erudition, there are three other sources you might try. The first is the ostensible sequel to this Guide, LambdaMOO Programmer's Manual, which is long and somewhat abstract, but very thorough and understandable after having read the Way Easy Guide.

Finally, those interested in learning about the cutting edge of MOO technology might enjoy the MOO-Cows mailing list, run by Pavel Curtis. Send a message to moo-cows-request@parc.xerox.com with the words subscribe moo-cows. You'll want to lurk on the list and read all the documentation you can find before posting questions to it.

Happy programming!


Appendices

A.0) More HereMOO-Specific HTML Tricks

There are a number of hip HTML-derived tricks that one can employ to make HereMOO, as cool as it currently is, even cooler.

A.1) Sending MOO Commands Via Links

On certain occasions, you may want a link to send a command to the MOO. For instance, a bouncy ball that has the verb bounce on it might have the following description:

	This shiny red ball makes you happy just looking 
	at it.  You could probably bounce it.
Selecting the bounce link should have the same effect as typing the command bounce ball. So how do you do this? Use the ?Command= syntax in the bounce link HREF. Type the following to give the ball its description including the bounce link:

@describe ball as "This shiny red ball makes you happy 
just looking at it.  You could probably 
<A HREF="http://trickle.tripod.com/cgi/nph-moo?Command=bounce+ball">
bounce it." 
Note first of all that the true URL of HereMOO is http://trickle.tripod.com/cgi/nph-moo. The bounce link as formed above will ask for a new page from the MOO, but give it the command bounce ball first. The plus sign (+) between "bounce" and "ball" is necessary to indicate where spaces would be in a typed command, as URLs cannot contain spaces.

As another example, let's say you have a Coke machine with the verb buy; its syntax is buy something from machine, where something has to be either Sprite, Coke or Dr. Pepper (the drink of all true MOO programmers.) To give the Coke machine a description which allows people to select their drink of choice just by selecting a link, type:


@describe coke machine as "It's big and rectangular, and you know what
it looks like.  You can buy <A
HREF="http://trickle.tripod.com/cgi/nph-moo?Command=buy+sprite+from+machine">
a Sprite</A>, <A
HREF="http://trickle.tripod.com/cgi/nph-moo?Command=buy+coke+from+machine">
a Coke</A>, or <A
HREF="http://trickle.tripod.com/cgi/nph-moo?Command=buy+dr.+pepper+from+machine>a
Dr. Pepper</A> (what the gods enjoy.)

This looks pretty terrible to type in, but it's really just a set of HTML tags interspersed with the text of the Coke machine's description. When people look at the Coke machine, they will see:
It's big and rectangular, and you know what it
looks like.  You can buy a Sprite,
a Coke, or a Dr. Pepper (what the gods enjoy.)
By selecting the various drink names, people can buy a Sprite, Coke or Dr. Pepper (the One True Beverage) -- assuming you've programmed the Coke machine's buy verb correctly!

A.2) Putting Your Own Images on the MOO

To truly personalize your MOO character, room and objects, you will probably want to give them images you yourself have drawn and/or scanned in. You can, of course, put into your descriptions any image anywhere on the World Wide Web: simply include an IMG tag in your description that refers to the image's URL (see NCSA's Beginner's Guide to HTML on how to do this.)

But if you have an image of your own that's not on the Web, the best thing to do is to FTP it to your Tripod account -- check out Tripod's Anonymous FTP Server FAQ for help with this. Once you've saved the image in your account on Tripod, all you have to do is use the following HREF in the IMG tag in your (or your room's, or your objects') description:

https://www.tripod.com/~your_name/image_file_name
Remember that your_name is your username, not your real name.

A.3) Miscellany

For more information on how to structure command syntax, see the Programming Verbs section.

If you have questions about how to do other hip Web things on HereMOO, don't hesitate to ask Melanie or Colin. Remember that all HTML tags you include in descriptions will be interpreted; this means that you can include graphics (and sound!) anywhere text is displayed to MOO users.


colin@tripod.com

This MOOring site owned by Colin McCormick
Click for Next | Next 5 | Skip | Previous
Or click here to learn more about the
MOOring