Mocha is an online creation language designed for and currently in use
on the mud Ishar, a
multi-player, real-time fantasy game. The language allows the
creators of the virtual world to design special effects for their own
sections of the world and for general use by other creators. The
design and original implementation of the language, both in its
interpreted form and in its compilation to C, are the work of Steven S. Lumetta, a
graduate student at the University of California at Berkeley, and any
comments or questions should be directed to him. All aspects of
Mocha, including the source code and all documentation, are covered by
the GNU public license, although the source code is not yet publicly
available (and no date is set for its release).
|
![]() Intermediate material
|
Search for keywords (Cyberverse documents only): |
Each Mocha function is associated with rooms, objects, or mobiles. Once someone writes a function, any creator can (with permission) use the name of the function to mark items of the appropriate type (e.g., rooms for a room function) as having that particular behavior. Mocha functions are generally customizable, so that a single function can manage many similar behaviors.
Mocha works by allowing a creator to define special responses to events in the virtual world. When certain things happen, the MUD signals an event by telling everything that might want to know about the event. For example, when a player types, "say hello," the MUD tells the room, any mobiles in the room, any objects worn or carried by the player, and any objects in the room that the player is trying to say hello. Normally, none of these things care about such an event, and the MUD handles it in the usual fashion, in this case by telling everyone in the room what was said. If, by chance, a nearby mobile responds to being greeted, that mobile might react to the event by preparing to speak. Responding to a specific event is called handling the event, and the section of the function which describes the way an item responds is called a handler for that event.
The MUD signals a vast number of events, including nearly every command in the game as well as a number of events with more context. Each event has a name, used by a creator to mark the corresponding handler in a function. The say command, for example, uses the name command_say. Other command event names have a similar form: they begin with "command_" and end in the name of the command. Many other events begin with "do_."
When a player quaffs a potion, for example, the MUD first signals a command_quaff event to everything nearby. If nothing handles this event, the server figures out which potion is to be quaffed and signals a do_quaff event to the potion. Since a potion is most likely to exhibit any unusual behavior only when quaffed, most creators prefer to provide a handler for the do_quaff event rather than for the command_quaff event when writing a potion function. In the typical case, a handler for command_quaff must first check whether the player intends to quaff the potion or something else, then check whether the player is in a proper position to quaff the potion. By handling do_quaff instead, the creator allows the MUD to take care of these considerations and needs only to specify the actual effects of quaffing the potion.
When the MUD calls a handler, it passes along several pieces of information about the event. Each piece is called a parameter of the handler, and together, along with their order, they form the handler's interface. A handler for a typical command event, for example, receives the item with the special behavior, the person issuing the command, and the arguments passed to the command. If the player Asylenth types "get 2.key bag," a room function's handler for command_get would receive a reference to the room, a reference to Asylenth, and a package of information including the strings "2.key" and "bag." The handler can then use these pieces of information to make decisions and define behavior.
When a handler is finished, it must return a piece of information to the MUD to tell the MUD what to do next. A handler can return one of three distinct values. One value tells the MUD to continue with the usual processing of the event; a second tells the MUD to stop immediately; and the third tells the MUD that a player associated with the event has been killed.
At this point, we are ready to start talking about the actual syntax of Mocha, or how the text of a function must appear.
Each piece of information, whether it be a parameter to a handler, a value returned from a handler, or one of the others we will discuss below, must have a type. The type of a handler's return value, for example, is known as special. The named derives from the common and perhaps antiquated MUD synonym for a function: a special, which in turn is an abbreviation of "a special function." The name of a second type comes from mathematics: an integer represents any whole number, be it positive, negative, or zero. Since Mocha is running on a digital computer, an integer can really only represent numbers up to two billion, but you'll probably never notice that limit. The following list describes the simple types handled by Mocha:
special command_say (person mob, person pc, arguments args)On the first line is the return type--always special for a handler. The second line begins with the name of the handler and continues with the interface, written as a comma-separated list of parameters surrounded by parentheses. Each parameter consists of a type and a name for the piece of information. The name is generally mnemonic--it helps someone reading the function to understand what it represents. In the signature shown above, the first parameter is a reference to the mobile with the behavior defined by the function. If this were instead a room function, this parameter would instead appear with type room and a name more appropriate for a room, perhaps "rm." Similarly, for an object function, we might write object obj for the first parameter. The second parameter refers to the person trying to say something, and the third gives the arguments to the command.
Ignoring the variable declarations for now, let us write an extremely simple example of a handler. Let's say that we want a certain mobile to do twice the normal amount of damage plus one. Forget for now the fact that we might simply change the mobile's damage dice rather than writing a function--we'll make the behavior more complex later. To intervene when the mobile makes an attack, we must catch the do_attack event, which has the following signature:
special do_attack (person mob, person vict, object wpn, integer type, integer amt, integer quality)For this handler, mob refers to the mobile; vict refers to the person being attacked; wpn refers to the weapon being used in the attack and has the value none when the mobile is unarmed; type is the type of damage being done (drawn from a list of values representing different types); amt is the amount of damage, with 0 indicating a miss, and quality is a measure of the success of the attack. Unlike most handler parameters, several of do_attack's parameter are returned to the MUD, allowing a handler to change the amount, type, and quality of damage while allowing the MUD to take care of sending the damage messages and killing the victim if appropriate.
For our purposes, the only important value is the amount of damage, which we intend to double, then add one. The following handler accomplishes the task:
special do_attack (person mob, person vict, object wpn, integer type, integer amt, integer quality) { amt = 2 * amt + 1; return ignored; }The body of our handler consists of two statements and a blank line. Mocha ignores white space (spaces, tabs, and carriage returns) except in strings and names. We could not, for example, have written amt as "a m t."
The first statement is an assignment statement. The expression on the right hand of the equal sign is assigned to the parameter on the left hand side. In this case, Mocha finds the number represented by amt, multiplies it by two, adds one, and pushes it back into the amt parameter. Note that an assignment is irrevocable--we can no longer read the original value of the parameter on the left hand side (although in this case we could recompute it fairly easily using the current value).
The second statement returns a value to the MUD. In this case, we return the value ignored, telling the MUD that it should go ahead and process the attack with the altered damage value. Each statement is followed by a semicolon. If we were to test this handler in the MUD, we would find (perhaps to our surprise) that mobiles using this function never miss an attack. Even when we make the odds of hitting incredibly low, they manage to do a single point of damage every time they try to hit. Why is this so?
Looking back to the definition of the do_attack event, we see that when the mobile misses an attack, the MUD still signals the event, but sends 0 as the amount of damage. Our handler blindly doubles the 0 and adds 1, resulting in a hit for a single point of damage. To prevent these specious hits, we must add a conditional statement:
special do_attack (person mob, person vict, object wpn, integer type, integer amt, integer quality) { if (amt > 0) // This is the new line. amt = 2 * amt + 1; return ignored; }The conditional statement appears in the first line of the handler body; to the right of the condition is a C++-style comment: when Mocha sees two adjacent slashes outside of a string, it ignores the rest of the line. A conditional statement begins with the keyword if, followed by a condition (a piece of information with type boolean), and completed by another statement. Note that no semicolon separates the first two parts of the conditional statement from the statement making up the third part. Also note that the latter statement is indented to indicate that it is only executed when the condition is true.
The condition in this case is that the amount of damage is greater than zero--that is, that the mobile has actually hit the victim. If the mobile misses, amt represents the value zero and the condition evaluates to false. When the condition is false, the statement following the condition is ignored. As we expect, the mobile hits at a normal rate after our change.
The data definition for a function specifies the information used by the function during its operation. A room that remembers the person who last occupied it, for example, would do so by declaring a space for that information in its data definition. The data definition is divided into four sections, each of which has a distinct meaning. The first section defines constant values for use in the handlers and routines. In many cases, functions become more readable when symbolic names such as "attack_delay" replace numeric values, such as 18. The reason is hopefully obvious. The second section defines public values: values which can be overridden by adding extra descriptions to items making use of the function. I might, for example, declare "attack_delay" as a public variable rather than a constant, setting the default value to 18. If someone else uses my function and wants whatever is attacking to attack more quickly, they can add an extra description called ".ATTACK_DELAY" and set the text to a smaller number. One copy of attack_delay exists for each virtual number using the function, so other people's selection of options will not affect my items. The third section defines private data: data which cannot be overridden by extra descriptions, but which also create only one copy per virtual number. A group of mobiles with a shared group of enemies might make use of private information. The fourth section defines the data needed for each instance of an item. Since only one room with a given virtual number exists, this section does not appear in room functions. Instance data might be used for a sac of poison that must remember who drank it (to apply damage over time); clearly such a function with only one place to store the name of the drinker would not work.
With Mocha, a creator can attach individual functionality to any mobile, object, or room. Functions can be further customized through the use of extra descriptions, allowing each function to be used with a variety of items.
Each function in Mocha is given a unique name and is stored in a file with that name. Files are then further grouped into libraries: one library exists for each area in the MUD; other libraries can be added by the gods and might include, for example, a "trap" library for generic trap functions.
A Mocha file consists of a data description and a number of event handlers. An event handler is called whenever that particular event occurs. The "command_kill" event, for example, is called whenever someone attempts to attack another person in the presence of the item using the function in which the handler is defined.
The data description defines and names the variables used for the event handlers in the file. The description can instead name another Mocha file, in which case all of the event handlers and data are inherited from the named file. Individual event handlers can then be overridden, allowing a creator to slightly alter an existing function without rewriting it entirely.
<function type> { int num_tries = 3; public: action[PVd] to_victim = "@n strikes you %d times!\n"; private: integer max_uses = 4; instance: person victim; };The function type defines the type of the function and must be person, object, or room.
The data definition includes four possible sections which must be defined in the correct order. The first is constants, which allows the creator to use symbolic names for values. In the example above, the name "num_tries" has been assigned the integer value 3. Whenever "num_tries" is encountered in the event handlers, it is treated as the integer value 3. The second section, from "public:" to "private:" is a list of variables unique to the item type (e.g., having a single copy for each type of mobile rather than a copy for each instance of the mobile). The value of a public variables is automatically overridden by extra descriptions. If, in the example, the item had an extra description with keyword ".TO_VICTIM," the string used for that item would be taken from the extra description rather than the default shown above. The third section of the data definition, from "private:" to "instance:," is again related to types of items rather than individual items, but cannot be overridden by extra descriptions. The last section, after the "instance:" marker, is a list of variables created for each instance of an item. Were the definition above used with a mobile, each instance of the mobile would have its own victim.
A special construction in the public section allows a creator to define symbolic names for a particular integer variable. The variable should be initialized with "one_of { <value name 1>, <value name 2>, ...}." The names are then defined as constants for the event handlers, and the default value for the variable is the first value mentioned in the one_of construction.
base <base function>This form derives the function type and data definition from the file <base function>. All event handlers are also inherited, but can be overridden by simply redefining them later in the file.
|
|
|
|
|
P, V, and O should appear at the start of the specifier. d and s may appear more than once, but the order must match the order of the "%d" and "%s" escapes in the action string.
Constant lists are specified as a number of items separated by commas and placed in braces (e.g., {1, 2, 3}). Unlike the similar spell constant value, which takes the form {<level>, "<spell name>"}, constant list elements must all have the same type.
The above construction evaluates the boolean expression and then executes either the first statement or the optional statement following the "else" marker.
Also see the room_echo function.
switch (<integer expression>>) { 1, 3 { ... } default { ... } }
special
command_<command_name> (<func_type> item, person pc,
arguments args)
Here the <command_name> might be anything from kneel to follow to kill. The pc is the person who issued the command, the item is the item carrying the function (room, mobile, or object), and the arguments are the additional arguments to the command.
special
do_attack (person mob, person pc, integer type, integer amt)
The mob has attacked the pc and inflicted damage. Type and amt can be changed.