For MOO Canada
September 1997
by Neil Fraser
(aka Odo)
This is a tutorial for players who have achieved Programmer status on a MOO, but don't know how to program. It will be necessary to print out a copy of this document (5 pages) so that you can refer to it while on the MOO.
Before starting this tutorial (and programming) you must have mastered the builder commands. Make certain you have successfully used ALL of the following commands:
? (The most useful command; use it before pestering someone.)
@create, @recycle, @rename, @describe
@lock, @addalias, @messages
@mail, @send, @rmm, @notedit
@audit, @dump, @d
@kids, @parents, @locations, @contents
If you are not familiar with all of those commands you will waste a lot of time programming features that are already available through the command line.
The first task in this tutorial is to use the familiar builder commands to make a new object called Odo's mouse (substitute your own player's name), with aliases mouse, and pet. Give it a suitable description, and configure the mouse so that whenever somebody other than you tries to pick it up they fail, and are told Odo's mouse bites you, and you drop the rodent. while everyone else in the room is told Max tries to catch Odo's mouse, but gets bitten. If you can't do all of that unaided, you are not ready to program.
Now create a new verb on your mouse that will make it squeak
when the mouse is pinched. Type: @verb mouse:pinch this none none
You now have the verb, but there is no code to execute.
Type: @edit mouse:pinch You are now in the verb editor,
we will worry about how it works later, right now let's get some
code writen. Type the following commands (including the initial
quote):
"this.location:announce_all("Odo's mouse squeaks!");
"this.location:announce_all("Odo's mouse gnashes its teeth.");
The verb now has two lines of code. Use the compile command to
save the code. If there was an error while it was compiling, type
del 1 or del 2, retype the offending line, and
then compile. If you somehow get into serious trouble, type
abort and start over again. When it has successfuly compiled,
quit back to the command line, and test the verb by typing:
pinch mouse
Now for some theory. Type @list mouse:pinch to see the code for your verb. The word this in a verb is a built-in variable that returns the object that the verb is running on. In your verb, this always returns the object number of your mouse. All objects have a location property that stores the object number of the room, player, or container that they are sitting inside of. Putting these two together this.location returns the mouse's containing object. All rooms have a verb announce_all that tells all objects in the room the text that you give it (@list here:announce_all to see this verb's code). If the mouse is sitting in a room, then the room's announce_all verb is called. If the mouse is being carried by a player, then the player's announce_all verb is called (which passes it to the room).
The verb editor is not very user-friendly, so it deserves some explanations. Type @edit mouse:pinch to get back in and list to show your two lines of code. You can use p and n (previous, next) to move the insertion point (cursor) up and down. Position this point between your two existing lines of code and use " (say) to add suspend (15); as a line of code. Remember, use ? if you run into dificulties, and look while in the editor gives you an overview of the editing commands. Once you have got the line inserted, list the code and make sure that it is between the other two. If it isn't, use move to correct the problem. Now change the 15 to be 2 with substitute. Type s /15/2 and list the code to verify the change. The substitute command will be the most used command because it allows you to fix bugs without deleting the line and restarting. Compile the code (and fix any errors), then quit from the editor.
Pinch your mouse again and note the two second delay between
the two messages. Take a guess as to what suspend (60);
would have done. There is still a problem with the verb; when
someone pinches your mouse, other people hear the mouse's reaction,
but they have no idea what caused it. Edit the verb again and
insert the following two lines before all the others, then compile
(fix errors) and quit.
player:tell("You pinch Odo's mouse.");
this.location:announce(player.name + " pinches Odo's mouse.");
There are four new items in these lines. You already know that this
in a verb returns the object that the verb is running on. The
built-in variable player is simmilar, it always returns
the object number of the player who activated the verb. On every
player is a verb tell that will tell the text to only that
player. The exact opposite is a room's announce verb that
tells the text to everyone in that room, except the player
who activated the verb. You know what the location property
returns, so given the context you should have figured out that
the name property returns.
Find somebody else who is logged on to the MOO and test your mouse with them. Now make a rat with exactly the same attributes as your mouse. There are three methods.
There is a final problem with the pinch verb, when you pinch the rat, one is informed that the mouse was pinched. This is because the name is hard-coded into the text. Edit the mouse's pinch verb, and on all four applicable lines use substitute to change Odo's mouse to this.name with the apropriate quotes and pluses. While you are editing the verb, replace the expression player.name with player:title(). It is customary to use :title() instead of .name when requesting the name of a player. This is so that if the player wants to add Mr/Mrs/Cptn/Dr in front of the name (or otherwise change how their name is seen) they may do so without modifying the name property (which, for players, isn't allowed to have spaces in it). After all the editing, your verb should look like this:
player:tell("You pinch " + this.name + ".");
this.location:announce(player:title() + " pinches " + this.name + ".");
this.location:announce_all(this.name + " squeaks!");
suspend(2);
this.location:announce_all(this.name + " gnashes its teeth.");
One of the most interesting
types of verb is the one that listens to conversation in a room,
and reacts accordingly. This is easilly done. Whenever anything
is said, the room calls the notify verb on every object
in the room, if that verb exists. Type @verb mouse:notify this none this
then edit the new verb so that is contains
the following (try using enter instead of "):Test the verb; remember that objects in your pocket can't hear what is said outside -- you must drop your rodents first. Find somebody else who is logged on and have them test it. You should notice a bug. Occasionally when somebody else says cheese you get the rodent's response BEFORE you hear the triggering text. This 'time travel' effect occurs because the room calls the notifys on all the objects one by one. If it gets to the rodent before it gets to you, then the rodent responds before you hear the trigger. Were you to insert suspend(1); in the mouse's notify verb, the whole proccess would be suspended for one second, but the order of messages would be unchanged. What you need to do is insert the line fork (1) before, and endfork after, the announce_all line. This will cause the normal execution to jump over the announce_all line and continue uninterrupted, and then one second later the announce_all line will be executed. Crude, but it works.
Insert the following lines between the endfork and the first
endif:
elseif (index(args[1], "cat") || index(args[1], "hawk"))
fork (1)
this.location:announce_all(this.name + " looks around nervously.");
endfork
The || symbol means or. Other symbols you should know about are:
When you are ready to continue, add these lines before the very last
endif.
elseif (index(args[1], " squeaks!"))
fork (1)
player:tell(this.name + " glares at you.");
this.location:announce(this.name + " glares at " + player:title() + ".");
endfork
Because of the location of these lines they can never be trigered by
spoken text. Try pinching the mouse. The mouse will squeak and will
thus trigger the glare reaction. It will also trigger the glare reaction
from all other sympathetic rodents in the room.
The second last verb I am giving you is created with
@verb me:whistle none none none and has the following code:
player:tell("You whistle a note so high that it is inaudibe.");
player.location:announce("You hear " + player:title() + " whistle an inaudibly high note.");
rodent_list = {#1234, #2345}; -- substitute the #s of your rodents.
for rodent in (rodent_list)
move(rodent, player.location);
rodent.location:announce_all(rodent.name + " scampers in.");
endfor
Since this code is located on you, and
you would normally be the one executing the code, player
and this are interchangable. There are three new items
in this verb. The simplest is the function move(what, where).
You may use this function to move objects that you own (including
yourself), to locations that aren't locked. The next new item
is the list. Lists are equivalent to the 'arrays' found in other
programming languages. Lists can store groups of strings (text),
numbers, object numbers, other lists, variables, or a combination
of the above. One use for lists is as a control for for
loops, the third new item. The for loop will step through the
controlling list and assign the rodent variable to be equal
to each item in the list in turn, and execute the enclosed statements
once every time. The total number of repetitions equals the length
of the controlling list. If you create more rodents, then just
add their object numbers to the list. When you test the verb
(just whistle) you will discover a bug. If a rodent is
already in the room when you whistle it emits the 'scampers in'
message, and stays where it is. Fix this problem by enclosing
the contents of the for loop with
if (rodent.location != player.location) and endif.
You are already familiar with the location and the name properties found on every object. You can create your own properties on objects you own. Use @prop me.rodent_list to create one on your character. Then set the contents of the new property with @set me.rodent_list to {#1234, #2345} where the numbers are the object numbers of your mouse and rat (and any others you create). The property now contains a list which may be used in any verb. Edit the whistle verb and replace the current list with this.rodent_list, or delete that line completely and replace rodent_list in the for loop line with this.rodent_list. The advantage of this change is that other verbs and other objects now have access to that list as: #<your player's #>.rodent_list
One such item would be a status report of your rodents. Create
an object (based on a $thing) called a rodent report with
aliases rodent report and report. Set the description
to be: The current locations of Odo's rodents are:. Use
@verb report:description this none this to create a new
verb, and add the following lines to it:
desc = {pass(@args)};
for rodent in (#<your player's #>.rodent_list)
line = rodent.name + " is in " + rodent.location.name + ".";
desc = {@desc, line};
endfor
line = "Odo has " + tostr(length(#<your player's #>.rodent_list)) + " rodents.";
desc = {@desc, line};
return desc;
When you look at an object, its description verb is called. The
default description verb simply returns the object's description
which is then printed. The description verb you just wrote calls
the default description verb (using pass(@args)), adds
some extra text to the description, and returns (to the look verb)
the modified description. Thus you never have to call this verb,
you just look at the object. The phrase desc = {@desc, line}
appends the line to the end of the existing description
list. Each entry in this list is a line of text. The length()
function simply returns the number of items on the given list.
Since that value is a number, the tostr() function must
be used to convert it to text. Also note that on line #3 rodent.location
would have returned an object number, not a name.
You now have at least four verbs. When you try to make your own verbs you will find that they will not work unless you have defined their arguments correctly with the @verb command. In the examples I have used this none none, this none this, and none none none. The best way to explain what arguments a verb needs is by example, here are some of the ones you will most likely use.
| Verb: | Arguments: | Usage: |
| Odo:whistle | none none none | whistle |
| mouse:pinch | this none none | pinch mouse |
| Odo:kick | any none none | kick Max |
| pie:throw | this at any | throw pie at Max |
| Odo:apologize | none to any | apologize to Max |
| Odo:title | this none this | May only be called from other verbs. |
If you have actually done this tutorial (as opposed to just reading it), you've got a basic foundation of knowledge regarding the MOO programming language. To explore further, obtain a copy of the excellent LambdaMOO Programmer's Manual, and experiment.