MUCK Manual Version 1.0 Written for TinyMuck version 2.2 fb5.64 and fb6.0 by Jessy @ FurryMUCK INTRODUCTION ABOUT THIS MANUAL 1.0 BASIC COMMANDS AND SETTING UP A CHARACTER 1.1 Some Definitions and Abbreviations 1.2 Connecting to the MUCK 1.3 Getting a Character 1.4 Basic Commands 1.4.1 Looking 1.4.2 Moving 1.4.3 Talking 1.4.4 Seeing Who's Around 1.4.5 Carrying Things 1.5 Setting Up Your Character 1.5.1 Describing Yourself 1.5.2 Setting Your Sex and Species 1.5.3 Locking Yourself 1.5.4 Getting a Home 1.6 Getting Help 2.0 COMMANDS 2.1 Overview: Dbrefs, Names, Flags and Properties 2.1.1 Protected, Restricted, and Wizard Properties 2.1.2 Messages and Message Properties 2.1.3 Data Types and Setting Properties 2.1.4 Triggering from Properties 2.1.5 Registered Names 2.1.6 Pattern Matching, Regular Expressions, and Output Types 2.1.7 Pronoun and Name Substitutions 2.2 Overview: Rooms, Things, and the Environment Tree 2.2.1 Droptos 2.2.2 Looktraps 2.3 Overview: Exits and Locks 2.3.1 Bogus Exits 2.3.2 Unsecured Exits 2.3.3 Exit Priorities 2.3.4 Multiple Links 2.4 User-Created Commands 2.5 Flag Reference 2.6 Server Command Reference 2.7 User-Created Command Reference 3.0 PROGRAMMING 3.1 Overview: MPI 3.1.1 MPI Macros 3.1.2 MPI Examples 3.1.3 MPI Reference 3.2 Overview: MUF 3.2.1 Mucker Levels 3.2.2 MUF Libraries 3.2.3 MUF Macros 3.2.4 MUF Examples 3.2.5 MUF Reference 3.2.6 MUF Library Reference 4.0 TUTORIALS 4.1 Using the List Editor 4.2 Making a Multi-Action 4.3 Making Puppets 4.4 Making Vehicles 4.5 Building Rooms and Areas 4.6 Archiving Your Belongings 5.0 ADMINISTERING A MUCK 5.1 Technical Issues 5.1.1 Selecting a Site 5.1.2 Compiling the Server 5.1.3 Setting Up the Database 5.1.4 Security Concerns 5.2 Non-technical Issues 5.2.1 Conceptualizing the World 5.2.2 Recruiting a Staff 5.2.3 Sharing Responsibility 5.2.4 Privacy Issues 5.2.5 Acceptable Use 5.2.6 Toading 5.2.7 Record Keeping Appendix A: Getting a Client Program Appendix B: UNIX Crash Course Appendix C: Sample Acceptable Use Policy and Programming Policy ***************************************************************************** INTRODUCTION A MUCK is a computer-driven, text-based `world'. Users, or `players', log on to the MUCK computer via the Internet, and may then interact with other players. The program running the MUCK and the computer it's on are both called the `server'. Each player controls a `character', a virtual person or creature inhabiting the world of the MUCK. Your character might be very much like you, the player, or she might be very different. MUCK is one member of a group related servers, including MUSH, MUX, MOO, MUSE, and MUD; collectively, these servers are often referred to as M*'s or M***'s. Everything is in text; there are no graphics. You will `see' things from the perspective of your character: whatever place you're in will be described, in words; people and things around you will also be described, and you can look at them. Succinct commands let you say and do things, interacting with other people on the MUCK (on a small MUCK, this might be six other people; on a large one, it might be 300). Some people view the text-only aspect of MUCKs as a limitation, others as a very positive feature. While a screen full of text lacks the instant impact of well done graphics, the world that comes alive in your imagination can be more vivid and colorful than anything computer graphics can produce. Just as the words on the page `disappear' when one is immersed in a novel -- and one instead imaginatively experiences the world described in the book -- the words on your computer screen can `disappear', and you imaginatively experience the world of the MUCK. Here, though, you are an active participant rather than a passive observer. MUCKs can be constructed to model any sort of world imaginable, on any scale: a droplet populated by microbes, a one-room bar, an undersea kingdom, or a far-flung stellar empire. By (recent) historical accident, many MUCKs are worlds populated by furries. A furry is an anthropomorphic animal, an animal with human characteristics. You might meet an accountant or a college student on a MUCK, but you're more likely to meet a suave wolf who likes to quote poetry, or a tiger with a weakness for chocolate truffles. So, yes, furries are inherently silly. But they are also inherently noble: protean and indestructible, they embody who we might be in a world where anything is possible. ***************************************************************************** ABOUT THIS MANUAL The MUCK Manual comprises five sections. Section 1 is an introduction for new players, covering basic commands and setting up your character. Section 2 deals with server commands and the commands included in the standard start-up database. Section 3 covers programming. Tutorials are presented in Section 4. Section 5 discusses technical and nontechnical issues of administering a MUCK. New players should read Section 1; others may safely begin with later sections. The Overview sections provide relatively thorough discussion of relevant concepts, but are not comprehensive. The Reference sections are comprehensive (or at least attempt to be), but are written in a terse style that assumes the reader has some familiarity with the topic or has read the Overview. The remaining sections discuss aspects of the section topic meriting special attention. ==================================== Examples in the manual are set off by half-lines of equal signs, like this. ==================================== Text enclosed in [square brackets] is optional. Text enclosed in <angle brackets> should be edited as appropriate for you and your character. For example, where the manual says `type <your name>', type your name, rather than typing this literally. Within examples, lines beginning with a > sign indicate material you should enter at your keyboard; the remaining lines are sample output from the MUCK. (In some longer programming entries, the > sign has been omitted so that you may cut and paste text from this file to a MUCK.) The MUCK Manual attempts to provide a single, relatively comprehensive reference for MUCK, as Lydia Leong's MUSH Manual does for MUSH. The organization of the MUCK Manual is in part based on her manual, and any indebtedness is gratefully acknowledged. The MUCK Manual was written with help from Winged(***@***), author of the online documentation for MUCK, version 6.0. Authors and editors of reference materials quoted include Foxen (foxen@belfry.com), Fre'ta (stevenl@netcom.netcom.com), Ioldanach (mortoj@rpi.edu). This version of the manual was written for MUCK version 2.2 fb6.0. Copyright of The MUCK Manual is held by Jessy Dupres. The Manual may be freely copied, distributed, and archived. -- Jessy (scampergal@hotmail.com) ***************************************************************************** 1.0 BASIC COMMANDS AND SETTING UP A CHARACTER 1.1 Some Definitions and Abbreviations Frequently used terms: Player: The person reading this manual; the entity who types things on behalf of a character. Character: The virtual person or creature inhabiting the MUCK, controlled by a player. Players have characters; characters have players. The terms `player' and `character' are often used interchangebly. Puppet: A player-like object controlled by a character. A `virtual character', as it were. A puppet can move and act independently of the character; everything the puppet sees and hears is relayed to the character and player. Zombie: Same thing as a puppet. Robot (or `bot): An object programmed to act like a character, or a character under the control of a program that causes her to perform certain actions automatically. `Bots are sometimes called AI's (from `artificial intelligence'). Flag: A setting on an object that influences or restricts its behavior. Flags are also called `bits'. Flags are discussed in Section 2.1. Object Type: An object may be a PLAYER, THING, ROOM, EXIT, or PROGRAM. A `player' is something that can be connected to via a name and password, can own things, and can act independently. Because this object type is called `player' rather than `character', many help documents (including this manual) will often use this term, though what's meant is the character object, not the living person who controls it. A `thing' is a mobile object that may be picked up, dropped, handed, and otherwise manipulated. A `room' is a place in the world of the MUCK, though it need not be described as an interior room: rooms can also be described to simulate an outdoor area, or described in an abstract way so that moving through the room shows characters useful text (for example, a tutorial on building might be set up as a series of rooms, with the description of each room serving as a `page' in a maunal). An `exit' is a link between two objects on the MUCK. Exits may be created such that they serve as virtual doors between rooms, or they may serve as user-created commands. (`Exits' are also called `actions'.) A `program' is a special instance of class `thing' which contains the code of an executable program. The type of an object is determined when it is created, and indicated by a flag. An object with a P flag is a player; an object with an R flag is a room; an object with an E flag is an exit; an object with an F flag is a program. If an object has none of these flags, it is a thing. The collective term for all of these is is `object': players, rooms, etc. are all objects. Dbref: Every object in the database has a unique identifying number: its `database reference number', or `dbref' for short. (Many objects may have the same name.) A dbref is usually preceeded by an # octothorpe, e.g. #123. Client: A program that replaces Telnet as a means to connect to the MUCK server. Clients have numerous special features specifically applicable to M*'s, such as automated logon, the capacity to switch between several worlds, hiliting certain kinds of text, line-wrapping, separating text being typed from text being displayed by the MUCK, paging, and scrollback. Different platforms require different clients. TinyFugue is a popular UNIX client. MUDDweller is a widely used Macintosh client. SimpleMU, Pueblo, Phoca, and ZMud are common Windows clients. A single client can connect to different servers... that is, you don't need a separate client for MUSH, MUCK, and MUD. Virtually all client programs are distributed as freeware or shareware, and are widely available over the Internet (see Appendix A). Penny: The unit of currency on a MUCK. Money is usually a non-issue on MUCKs, though some commands require an expenditure of pennies. The administrators of a MUCK may set the name of the currency: it might be pennies, or it might be pop-tarts, fleas, or lurid fantasies. You will often find money simply by moving through the MUCK, and will see a message such as `You find a penny!' or `You find a reason for living!' Many MUCKs also have banks or other places where you can get more pennies. Save: A period in which the server backs up the database to disk. At scheduled intervals, the server automatically executes relatively brief saves in which only those objects changed or created since the last save are backed up (`delta saves'), as well as longer saves in which the entire database is backed up (`full saves' or `database saves'). In addition to automated saves, wizards may initiate a save via the @dump command. (`Saves' are also called `dumps'.) During a save, activity on the MUCK is frozen: commands you enter during a save will be queued and executed after the save completes. On a very large MUCK, a full save can take ten or more minutes. Lag: (noun) A perceptible delay between the time you enter a command and the time it is executed. Lag may be caused by an overloaded server (too many people logged on or too many programs running), by an overly large database (the database is larger than available RAM, necessitating frequent swaps to disk), or by problems on the Internet. (verb) To experience lag or a `locked-up' screen. Spam: (noun) Text scrolling by too fast to be read comfortably. (verb) To act or use programs in such a way that those around you are subjected to an excessive amount of frivolous or repetitive text. Idle: To be logged onto the MUCK but not doing anything. Mav: To say aloud something meant to be whispered. By extention, to say aloud something meant to be paged, or to whisper or page to the wrong person. Maving is a potential source of embarrassment or awkwardness. The name derives from a character in the early days of M*s who was chronically prone to this particular faux pas. God: The MUCK's overall controller or administrator, and the player object with dbref #1. God has access to a few commands unavailable to all other players, and may not be @forced. The term `God' is used less frequently on MUCKs than on MUSHes and MUDs. In fact, leadership by a single wizard player is relatively rare. More often, a core group of wizards share responsibility for administering the MUCK, with each having access to the God character's password. Wizard: An administrator of the MUCK, and a player with the W flag. Wizards have control over all objects in the database, and may use restricted commands available only to them. (`Wizard' is often shortened to `wiz'.) Realm Wizard or Realms Wizard: An administrator of a certain area within a MUCK. Realms wizards have partial command over objects and players within their area, but cannot use wiz commands. Use of the realms wizard system is somewhat uncommon. Mortal: A non-wizard character. This term too is used less frequently on MUCKs than on MUSHes and MUDs. While wizards are technically players, people usually refer simply to `wizzes' on the one hand and `players' on the other. (Where the distinction is important, this manual will use the term `mortal'.) Staff: A MUCK administrator, who may or may not be a wizard, and may or may not have access to restricted commands. A common staff position is `helpstaff': someone who agrees to help new players and answer questions, but seldom has access to restricted commands. Most wizards are staff, but occassionally a player will be given a wizbit without staff responsibilities and privileges. In other words, `staff' is an administrative rather than technical term. MPI: An online programming language with a LISP-like syntax. MPI is available to all players. Because it is universally available, MPI includes a number of security features that restrict its power. MPI is covered in Section 3. MUF: An online programming language with a FORTH-like syntax. MUF security is handled through a system of privileges. In order to program in MUF, one must have a `Mucker bit' (an M flag). Mucker bits range from M1 (highly restricted) to M3 (almost as powerful as a Wizard flag). On well established MUCKS, only highly trusted players with demonstrated programming skill are given M3 bits. The power and efficiency of MUF make MUCK readily user-extensible. MUF is covered in Section 3. User-Created Command or User-Created Program: This term is not commonly used on MUCKs, but is often used in this manual, and so is mentioned here. Many of the commands people are accustomed to using on MUCKs are not part of the MUCK server, but rather separate programs created by players and wizards. Soft-coded commands, in other words. Some (such as the `say', `page', and `whisper' commands used on most MUCKs) are enhancements of server commands. Others (such as `ride', `lsedit', and `lookat') are basic utilities that do something the server itself cannot. A large MUCK will also have a great many other user-created commands and programs: some invaluable, some highly specialized, and some frivolous. Control: This term too is used quite frequently in the manual. In almost all cases, your permission to change an object is determined by whether or not you control it. For mortals, control is essentially synonymous with ownership: you control everything you own; you don't control things you don't own, with one exception: anyone can control an unlinked exit. Wizards and realms wizards have extended control: Realms wizards control anything in their realm, including players; wizards control everything. ***** Frequently used abbreviations: VR: Virtual Reality. The MUCK world or worlds. Characters live there. RL: Real Life. The world outside the MUCK. Most players live there. M* or M***: A generic abbreviation for all flavors of text-based, Internet-accessible, interactive programs, including MUD, MUSH, MUX, MOO, MUSE, and MUCK. IC: In Character. Acting or speaking as your character, rather than as you, the player. OOC: Out of Character. Acting or speaking as you, the player, rather than the character. Medieval warriors arguing about Mac vs. Windows are OOC. In some situations, on some MUCKs, being OOC without signalling that you are doing so (by putting something like `(OOC)' or `notes OOC' before your poses and comments) is considered a breach of etiquette. IMHO: In My Humble (or Holy) Opinion, & IMO, In My Opinion LOL: Laughs Out Loud. ROTFL: Rolls on the Floor Laughing. AFK: Away From Keyboard BRB: Be Right Back BBL: Be Back Later TS: TinySex. To make love or have sex on the MUCK, via the gestures and comments of your character. `TS' is both a verb and noun. TP: TinyPlot. A role-played, jointly-authored, predominantly improvised storyline acted out by a group of players. TPs are usually consensual: players agree ahead of time to participate in the TP, though other players may be drawn into the TP by the storyline's development. Usually players will plan the main conflict, premise, or events ahead of time -- at least provisionally -- and improvise their characters' individual contributions and reactions. (The terms `TinySex' and `TinyPlot' derive from `TinyMUD', an early server from which MUCK grew out of.) RP: Role Playing. Acting IC in a way that is consistent with either the overall theme of the MUCK, a TinyPlot in which one is participating, or both. Some MUCKs are predominantly places to RP; some are mostly places to socialize, where RP is sporadic. Sidebar: `MUCK' itelf is not an abbreviation or acronym. The names of other M* servers are: MUD stands for Multi-User Dungeon or Multi-User Domain; MUSH for Multi-User Shared Hallucination; MOO for MUD, Object Oriented. `MUCK' is simply a name with a sound and connotations rather like those of `MUD' and `MUSH'. Purportedly, the name derives from the fact that MUF gives players the ability to `muck around with' the database. 1.2 Connecting to the MUCK To play on a MUCK, you need to be logged onto a character. The most basic way to do this is with Telnet. A client program (see Appendix A) is vastly preferable to Telnet, but not essential. For now, examples will assume you are using Telnet from a UNIX account. If you don't have a character on the MUCK, you will need to log on as a Guest. You will also need to find the addresses and port numbers of some open MUCKs. Lists of such address are frequently posted on the newsgroup rec.games.mud.announce, and are easily found via web searches for keywords such as `muck' or `mud'. The example below use the current address for FurryMUCK (as of Summer, 1997). Logging on involves two steps: connecting to the MUCK, and logging onto a specific character. To connect to the MUCK via Telnet on a UNIX account, type `telnet', followed by a numeric or domain name IP address, followed by a port number. Press enter. ==================================== > telnet furry.org 8888 ==================================== If the connection is successful (i.e., if you typed everything correctly, the Net is not having a Bad Day, and the MUCK itself is running) a login screen will scroll onto your terminal. If you experience a delay, and then a messages such as `connection incomplete: timed out', wait a few minutes and try again: it's possible that the MUCK was undergoing a save while you were trying to connect, or that there were transient connection problems on the Internet. The login screen should show informational text, illustrations composed of ASCII characters, or a combination of these. It is possible to successfully connect to the MUCK, yet see a rather garbled looking login screen. Usually this is due to improper terminal settings. If this happens, continue with the login step. The problem may disappear once you've logged on, and if doesn't, people online may be able to help with terminal settings. (This problem is relatively rare.) Once you've connected to the MUCK, you need to log onto a specific character. If you don't have a character on the MUCK, you can (usually) connect to a Guest character. ==================================== > connect guest guest ==================================== If you have a character, type `connect' followed by the character name and password. ==================================== > connect cashmere flipFlap ==================================== On large MUCKs (including our example, FurryMUCK) it is common to require Guests to page a wizard and ask to be let out of the Guest Room. This is a security measure: it is not unheard of for players who have been banned from the MUCK for causing problems to attempt to log on as a Guest and continue their improper behavior; a wizard can check the site from which a Guest is connecting, and refuse to free someone from a site known to be the home of problem-causers. If this step is required (the text you see after logging on as a Guest will say so), type `wizzes' to get a list of any wizards online, and page one asking him to let you out (paging is discussed below, 1.4.4). If no wizards are online, you'll have to try again later. On most MUCKs, this step is unnecessary. You can get out of the Guest Room simply by typing `out'. 1.3 Getting a Character The MUCK server has a provision for automatic character generation, but usually this is disabled. Instead, you'll need to send email requesting a character. The contents of the email you send will vary from MUCK to MUCK: some simply ask for the character name you want, others require your real name, your age, and your state or country of residence. Help files on the MUCK or people online should give you more specific information. Typing `news registration' or `news join' for a helpfile or `staff' for a list of people responsible for answering such questions are good first steps for finding this information. Once your character is approved and created, you will be sent email containing the character's name (usually the name you asked for) and your password. Sometimes you request a password; sometimes it is randomly assigned. Random passwords may be changed to something memorable once you connect. Passwords are case-sensitive: `flipFlap' and `flipflap' are two different passwords. Character creation by this method usually takes a day or two. On MUCKs that allow automatic character creation, use the `create' command at the log on screen: ==================================== > create Cashmere flipFlap ==================================== 1.4 Basic Commands 1.4.1 Looking The `look' command (abbreviated `l') shows you your surroundings. Typing `look' by itself shows you the room you are in. You automatically see the description of a room upon entering it; typing `look' shows the same description again (it may have scrolled off your screen). Typing `look <object>' or `l <object>' shows you the description of <object>. The `lookat' command does a possessive look, showing something that is held by someone else. The syntax is `lookat <object>'s <thing>', e.g. `lookat Jessy's ring'. 1.4.2 Moving The `go' or `goto' command causes your character to move in a certain direction or use a specific exit. For example, typing `goto north' would cause your character to move one room to the north (assuming there is a room to the north -- or, more accurately, there is an exit named `north' in your current location, leading to another room). The `goto' command is not really necessary, and is almost never used: typing the exit name by itself will also cause you to `go' in that direction, and directions are usually abbreviated to their first letter. Thus, typing `north' or `n' should produce the same result as `goto north'. If you can't go in a particular direction (because there is no exit in that direction, or the exit is locked against you), you will see a fail message such as `You can't go that way.'. A commonly used exit name is `out' or `o': typing either of these in an interior room will often move you outside the room or building. Frequently, the builder of a room makes use of a program that appends the names of `Obvious Exits' to the description of a room (An `obvious' exit is one that is not hidden by being set Dark). The names of these exits will then appear in a list below the room's description. The absence of such a list does not mean that there is no way out of the room. Read the room's description carefully to see if it indicates directions you can go. If you are still unable to determine valid directions, try common-sense directions such as `west', `n', or `out'. If all else fails, you can get out of room by typing `home' or `gohome', which will return you to your home location. 1.4.3 Talking The `say' command (abbreveated as a " quotation mark) causes your character to say something hearable by all other characters in the same room. Thus, if Cara's player types `say Hi there!' or `"Hi there!', people in the same room will see `Cara says, "Hi there!"' Characters can communicate via gestures or `poses' as well as speech. The `pose' command (abbreated as a : colon) causes your character to do something seeable by all other characters in the same room. So, if Cara's player types `pose waves.'. or `:waves.', people in the same room will see `Cara waves.' The pose command handles punctuation intelligently, omitting the space between the character's name and the pose when the text begins with punctuation. Thus, if Cara's player typed `:'s going to go see a friend', characters around her would see `Cara's going to go see a friend.', rather than `Cara `s going to go see a friend.' You can communicate with a specific player, or a group of players, rather than the whole room, via the `whisper' command (abbreviated as `w' or `wh'). The syntax is `w <name or names> = <message>' For example, if Clearsong's player typed `w cashmere = What say we blow this joint?', Cashmere would see `Clearsong whispers, "What say we blow this joint?" (to you)'. If Clearsong's player had typed `w cashmere cara = I thought that was illegal.', both Cashmere and Cara would receive the whisper. To format a whisper as a pose, put a colon before the whispered message. For example, Cashmere could answer Clearsong by typing `w clearsong = :nods.' Whisper will search the room for partial name matches. Thus, if there is no one else named Clear<something> present, typing `w clear = :nods' would send the whisper to Clearsong. The whisper command `remembers' the last person you whispered to, so to continue a whispered conversation, the name may be omitted: `w = <message>'. You can communicate with people in other rooms via the `page' command (abbreviated as `p'). The syntax for pages, page poses, and pages to multiple players is the same as that of whisper: `p <name or names> = <message>' (for a page) and `p <name or names> = : <pose>' (for a page pose). Like whisper, the page `remembers' the last person you paged to and the name may be omitted. (Note: Spaces have been placed around the = equal sign in the above examples for clarity. The spaces are not necessary, but can be included.) 1.4.4 Seeing Who's Around Space on the MUCK is divided into `rooms', which may or not be described to resemble an actual interior room. There may be numereous people online, but not in the same room as you. The names of characters in the same room as you will be appended to the room's description in a list of the room's `Contents'. Some things in the list may not be connected characters, however: some may be things. some may be puppets, and some may be characters who are not online (they're `asleep'). To get a list of online characters in the same room, type `who' in lower case. In rooms set Dark, the `Contents' list will not appear: there may or may not be other players present. Guest Rooms and New Player Start rooms are often set Dark. To see who's online on the MUCK as a whole, type `WHO', all upper case. 1.4.5 Carrying Things The commands for handling things are quite straightforward: `get <object>' causes you to pick up something; `drop <object>' causes you to drop it. When you go somewhere, objects you are carrying will go with you. In order to get something, you must be in the same room, and it must not be locked against you. You can hand things to other players, syntax `hand <object> to <player>'. Being able to hand or be handed to isn't automatic: type `hand #ok' to enable handing. The `inventory' command, abbreviated `i' or `inv', shows a list of everything you are carrying, and a line showing how many pennies you have. 1.5 Setting Up Your Character A new character is essentially a blank slate, having only a name, a dbref, a password, a flag or two, and some pennies. In order to become a fully functioning part of the MUCK world, you will need to do a few things to get your character set up. The following section describes a very basic set up. Most of the things you'll do in this section involve setting `properties'. A property is a named location on a MUCK object where information is stored. Properties are discussed fully in Section 2; you don't need to understand exactly how MUCK handles properties in order to set up your character. Any commands you issue at this stage are reversible, so don't worry about making mistakes. Simply re-enter the command correctly. 1.5.1 Describing Yourself Until you describe yourself, people who look at you will see `You see nothing special'. You can (and should) set what people see when looking you by entering a description, or `desc' as it's usually called. The syntax is `@desc me = <description>'. A `straight desc' such as this is rather limited: the text remains the same at all times, you won't know when someone looks at you, you'll be limited to about twelve lines of text, and you can't divide the text into paragraphs. Later, you can use MPI and the list editor (a program that lets you work with a `list', a series of props which together act like a document) to create highly flexible descs that change in response to the time of day, who's looking at you, what you're wearing or what mood you're in, and so forth. Many MUCKs have a user-created command that allows you to `morph', or change descriptions with a succinct command. You may wish to type `morph #help' to see if such a program is available on your MUCK. Most MUCKs also have an MPI macro that notifies you whenever someone looks at you, called `look-notify'. To use it, put `{look-notify}' -- curly braces included -- anywhere in your description. For example, `@desc me = A nymph with green hair.{look-notify}' 1.5.2 Setting your Sex and Species Your sex does not have to be male or female, but it's recommended. From time to time you'll encounter programs or commands that format their output based on the user's sex (pronoun substitution is the most common instance). No harm will be done if your sex is something other than male or female, but the program output may look a bit strange. To set your sex, type `@set me = sex : <value>'. Your species can be anything appropriate to the MUCK. On MUCKs where everyone is human, the species prop is often used to indicate a profession or social position. To set your species, type `@set me = species : <value>'. Many MUCKs have a user-created command that shows the sex and species of everyone in the room. The most common is `WhoSpecies', or `ws'. Type `ws' to see if it's available on your MUCK. 1.5.3 Locking Yourself On some M* servers, locking yourself is a very worthwhile precaution, preventing other players from taking things from you or picking you up. On MUCKs, locking yourself really isn't crucial. The server won't let you pick up players, and it won't let you take things being held by other players. If you are not locked, people *can* `rob' you, which means that they take one of your pennies. Most players have several thousand more pennies than they need. Nonetheless, you can lock yourself by typing `@lock me = me'. (Locks are discussed in Section 2.) A more practical issue is that of `handing' and `throwing'. You may or may not wish to allow people to hand and throw things to you. Both are convenient, but it is possible to abuse the hand and throw commands. Abuses range from throwing objects to someone who does not wish to be disturbed (a minor annoyance) to handing someone an innocuous looking object that can eavesdrop on their conversations (an offense that merits being banned from the MUCK). Most players do allow handing and throwing. To allow handing, type `hand #ok'. To allow throwing, type `throw #ok' and `@set me = J'. To disallow them, type `hand #!ok' and/or `throw #!ok'. Both are disallowed by default. (`hand' and `throw' are user-created commands, but most MUCKs have them.) 1.5.4 Getting a Home (Note: It is not necessary to get a home immediately, and sometimes the simplest approach is the best one: page a staff member and ask her to set you up with a home or tell you how to do so.) 'Home' has both a technical and non-technical meaning on MUCKs. In the technical sense, all players and things have a `home', a place to which they are `linked', and to which they will return if the MUCK doesn't know where to put them or if they are sent home by a command or program. A player's home must be a room. A thing's home may be a room or player. In the non-technical sense, your `home' is, naturally enough, where you live. Your `non-technical home' (the place where you hang out, keep your belongings, sleep, etc.) does not necessarily have to be your `technical home', but it is more convenient for both to be the same. Getting a home on the MUCK involves both technical and non-technical issues. Read this section completely before typing any of the commands. On the non-technical side, you will need to find a place where you can put a home. There may be places where the builder has installed a command that lets you put in a home: read `news' and any materials in the place where you start off, or ask people online for information about such places. If you don't find such a place, or if you find a public place where you would like a home but such a command is not available, you will need to contact the owner of the room and work with her to set up a home. Type `ex here' to find out the name of the room's owner, then contact her by page or page #mail. On most MUCKs, wizards or staff members work with new players to help set up a home. It's worthwhile asking people online how this matter is usually handled on your MUCK. On the technical side, getting home involves three steps: 1. Creating a home. 2. Linking yourself to the home. 3. Linking the home to the rest of the MUCK. If you are using a program that automates the process, creating a home is simply a matter of following instructions provided on a sign or directory. Usually you just type something like `claim <# or direction>'. To create your own home, make a room with the `@dig' command, syntax `@dig <room name>'. (You must have a `builder bit' -- the B flag -- in order to use @dig. On most MUCKs, all players have a builder bit.) ==================================== > @dig Cashmere's Den Cashmere's Den created with Room #1234. Parent set to Outdoor Environment Room(#101RJA). ==================================== Note the dbref of the newly created room (#1234 in this example). You will need it in order to go there or link the room to the rest of the MUCK. If you forget the room's dbref, you can determine it with the `@find' command. ==================================== > @find cashmere's den Cashmere's Den(#1234RJ) End of list (1 object found) ==================================== (If you see the room's name, but not its dbref, you are set Silent. Type `@set me=!S' to clear the Silent flag set on your character.) A newly created room is said to be `floating': it exists, but is not linked to anything else on the MUCK. In other words, there are no `doors' in and out of the room. If your MUCK has convenient `global exits' (commands that take you to a centralized location from anywhere on the MUCK) it may be feasible to leave the room floating indefinitely. If your MUCK has a convenient global exit, and you would like to set up a home without having to wait for help from someone else, @dig a room as described above, then use the `@link' command (syntax `@link <object> = <home>') to set your home and the `home' command to go there. ==================================== > @link me = #1234 Home set. > home You wake up at home, without your possessions... Cashmere's Den(#1234RJ) ==================================== As the example suggests, you can loose things when using the home command: when you type `home', both you and anything you are carrying go home. If you are carrying things that do not belong to you, or that are linked to somewhere else on the MUCK, they will return to their homes when you use the home command (in other words, the `home' command is recursive). There are two ways to avoid this problem. Teleporting may or may not be allowed on your MUCK (usually players can teleport to rooms they own). If you are able to, teleport to the new room first, then link yourself there. ==================================== > @tel me = #1234 You feel a wrenching sensation.... Cashmere's Den(#1234RJ) > @link me = here Home set. ==================================== The second alternative is to use `gohome' rather than `home'. The `gohome' command takes you to your home without causing the things you're carrying to also go home. However, `gohome' is a user-created command: though it's widely available, there is a possibility that your MUCK does not provide it. ==================================== > @link me = #1234 Home set. > gohome You head home, arriving safely. Cashmere's Den(#1234RJ) ==================================== If you plan to leave your room set floating for a while, and your MUCK has a gohome command, you can just use it whenever you want to go home. If there is no gohome command, it will be worthwhile to create an action that takes you there without causing you to loose things you are carrying. Use the @action command (abbreviated @act, syntax `@act <action name> = <origin or attachment point of acction>'), and the @link command (syntax @link <action> = <destination>) to create the action ==================================== > @act myplace = me Action created with number #1236 and attached. > @link myplace = #1234 Linked to Cashmere's Den(#1234). ==================================== ***** Linking to the Rest of the Muck: Worst-Case Scenario If you want to link your home to the rest of the MUCK, and no programs or staff members are able to do this for you, you will need to work in concert with another player (the owner of the room where you want your home to be). The two of you will create two exits: one leading from your home to the other person's room, and one leading from the other person's room to your home. By working together, you will be able to work within a basic security restriction: a player may not attach an exit in a room owned by someone else. There are a number of ways to accomplish this, and a number of different orders in which the steps could be performed. The method and order in the following example will work fine. Cashmere has dug his den, and he has found a place where he'd like to put his home: The Deep Dark Woods, owned by Fin. Cashmere and Fin go to the Deep Dark Woods together and begin. Both players will set their rooms Link_OK, so that others may make exits that lead to them. Fin will make an exit in the Woods (she owns the Woods, so she can make an exit there, and the Den is set Link_OK, so she can link the exit to the Den). Cashmere will use the new exit to go to his Den. From there, he will make an exit leading out to the Woods (he owns the Den, so he can make an exit there, and the Woods are set Link_OK, so he can link the exit to the Woods) ==================================== C> @set #1234 = L (Cash sets Den Link_OK) Flag set. F> @set here = L (Fin sets Woods Link_OK) Flag set. F> @open Cashmere's Den;cd (Fin makes new exit) Exit created with number #5222 F> "What's the dbref of your room? Fin says, "What's the dbref of your room?" C> "#1234 Cashmere says, "#1234" F> @link cd = #1234 (Fin links exit to Den) Linked to Cashmere's Den(#1234RJL). F> "OK, done. `Cashmere's Den', alias `cd'" Fin says, "OK, done. `Cashmere's Den', alias `cd'" C> "What's the dbref here? Cashmere says, "What's the dbref here?" F> "#3209 Fin says, "#3209" C> :nods... "Thanks. BRB." Cashmere nods... "Thanks. BRB." C> cd (Cash uses new exit to go to Den) Cashmere's Den(#1234RJL) C> @open Out;ou;o (Cash makes a new exit) Exit created with number #5223 C> @link out = #3209 (Cash links exit to Woods) Linked to The Deep Dark Woods(#3209RJL) C> @set here = !L (Cash sets Den not-Link_OK) C> out (Cash uses new exit to go to Woods) The Deep Dark Woods . . . F> @set here = !L (Fin sets the woods not-Link_OK) ==================================== Finally, note that it is quite feasible to simply move in with someone else. All that is necessary is that the other player's home be set 'A' or 'Abode'. This flag lets other players set it as their homes. To do so, simply go there and type `link me = here'. 1.6 Getting Help There are a number of online resources for getting help with questions or problems. The server command `help' (syntax `help <topic>') provides online documentation of many (but not all) server commands and features. Many (but not all) user-created commands follow the convention of including a #help function (syntax `<command> #help'). For example, you can get information on using the page program by typing `page #help'. MUF and MPI both have online documentation: for MUF, type `man <topic>'; for MPI, type `mpi <topic>'. Additional information can be found in the `news' and `info' files. Typing either of these commands should show a list of topics. The news and info files are written and updated by a wizard; their content and quality will vary widely. The staff should also be able to provide help and information: type `staff' or `helpstaff' to get a list of available staff members. Smaller MUCKS may not have a separate helpstaff; in this case type `wizzes'. And, there's always The MUCK Manual. ***************************************************************************** 2.0 COMMANDS Section 2 documents server commands and the user-created commands provided in the standard start-up database. 2.1 Overview: Dbrefs, Names, Flags, Properties and Lists All objects on a MUCK, of whatever type -- Player, Thing, Room, Exit, or Program -- have a unique identifying number, a `database reference number', or `dbref' for short. Multiple items may have the same name. The type and behavior of all database objects are determined by flags and properties. Both are ways of storing information about the object. Of the two, flags control more basic or fundamental aspects of the object. It might be helpful to think of flags as something that determine *what an object is* and properties as something that determine what *features* or *attributes* and object has (a property is in many ways comparable to an `attribute' on MUSH). Multiple properties can be combined to form a `list'... a collection of props that together act much like a file or document. Dbrefs and Names: A dbref is assigned to each object when it is created. In most cases, when specifying an object by its dbref, the number should be preceded by an # octothorpe. (Some user-created programs require that the octothorpe be omitted when the dbref is stored in a property.) The server assigns either the next available number or that of the most recently recycled object. For example, if the database holds 1,000 objects and all are valid (have not been recycled), the next object created will be given dbref #1001. If someone then recycles an object, say #123, the next object created would be given dbref #123. So, dbrefs are not a reliable guide to an object's age. Dbrefs cannot be changed. Unused dbrefs of recycled objects are `garbage'. Multiple objects can have the same name, except for players: there can be several hundred exits named `out' on a MUCK, but only one player named `Ruffin'... and there could (conceivably) be many things, exits, and programs named `Ruffin'. All names can be changed with the `@name' command, syntax `@name <object> = <new name>'. To rename a player, the password must be supplied as well: `@name <'me' or old name> = <new name> <password>'. A player must have control of an object to rename it; programs can rename objects if the object and the program have the same controller, or if the program is set W. (Programs can only name players indirectly: if the player's password is available, a wizbitted program can force the player or a wizard to rename a player.). Player names, unfortunately, cannot include spaces. `Madame_Bovary' is an acceptable player name, but `Madame Bovary' is not. The names of other types of objects can include spaces. When handling or modifying an object in the same vacinity as you, you can specify it by its name or part of its name. Partial names will work, provided that you specify enough characters to distinguish the object from others in the vacinity: `@desc super = <description>' will describe `Superball', provided that `Superman' or something else with a name beginning with `Super-' is not in the same vacinity. When handling or modifying an object that has the same name as something else in the vacinity, or an object not in the same vacinity as your character, the object will need to be specified by dbref. You can determine the dbref of an object controlled by you and in the same vacinity by examining it (ex <object>). You can determine the dbref of objects that you control but are not in the same vacinity with the @find command, syntax `@find <object's name>'. Two substitution strings can be used in place of either a name or dbref: `me' and `here'. `Me' matches your character's dbref; `here' matches the dbref of the room you are in. ==================================== > @name here = Waterloo Station Name set. > @name pen = Bic Four-Color Ballpoint I don't see that here. (You left the pen at home.) > @find pen Nyest Penal Colony, Massage Room(#855RJ) (Includes string `pen'. Ignore.) pen(#1237) End of list 2 objects found. > @name #1237 = Bic Four-Color Ballpoint Name set. > @name me = Ruffin flipFlap You can't give a player that name. (Already a player named Ruffin.) > @name me = Nebuchudnezer flipFlap Name set. (But Nebuchudnezer works.) ==================================== Flags: Flags -- also called `bits' -- provide an economical way to store important information about all objects in the database. Usually you can see both the dbref and flags of any objects you control when they appear in a Contents or Inventory list, or in the case of rooms, simply by doing a look. Whether or not you can see this information is determined by the S flag set on your character: type `@set me =!S' to see dbrefs and flags, or `@set me = S' to hide them (in this context, the S flag means `Silent'). Whether you are set Silent or not, you can see the flags set on an object you control by examining it. The first flag listed after an object's dbref is its `type flag'. The type flag functions differently than any remaining flags. It determines the type of the object; it is set at the time of the object's creation; it cannot be changed; and it determines the meaning or function of remaining flags. If an object is created with the @dig command, it will be a room and will have an R flag in the first position. If it is created with the @action or @open command, it will be an exit and will have an E flag. If it's created with the @program command, it will be a program and will have an F flag. If it's created with the @pcreate command, it will be a player and have a P flag. If it's created with the @create command, it will be a thing, and will have none of these flags. All flags are either `set' or `not set' at all times. The meaning or function of the remaining flags depends on their context... that is, on what type flag the object has. For example, if a program (something with an F flag in the first position) is set D, the D flag means `Debug', and debugging information is shown whenever the program runs. If a room (something with an R flag in the first position) is set D, the D flag means `Dark': the `Contents' list won't appear for a player who doesn't control the room, and no notices are emitted when players connect and disconnect in the room. In short, the same flag won't always mean the same thing. While the context-dependent meanings of flags can be confusing for new users, it provides an elegantly economical way to store important information. The meanings of each flag in relation to the type flags are listed in Section 2.5. The type flag is set at the time of an object's creation. The remaining flags can be toggled with the @set command and the `not operator' (an ! exclamation point). The syntax for setting a flag is `@set <object> = <flag>'. For removing a flag, it's `@set <object> = !<flag>'. Flags are not case sensitive: `@set here = D' and `@set here = d' produce the same result. ==================================== > @set here = D Flag set. > @set here = !D Flag reset. ==================================== Mortals' use of flags is restricted in a few ways. Most importantly, they can only set flags on things they control. Players cannot change the state of the Zombie (Z) or (D) flags on themselves. They cannot set themselves or anything they own Builder (B) or Wizard (W). They must have a Mucker bit (flags M1, M2, or M3) in order to change the Mucker bit of a program. Players can set the Mucker bit of a program they own to a level lower than or equal to their own, but not higher. Wizard's control all objects and may change the state of any flag on any object, with two exceptions: (1) type flags can never be changed; (2) if the MUCK is compiled with the god_priv option (which it usually is), wizards cannot set players W or !W. ***** Properties: A property is a named location on a database object that holds information. Both the server and user-created programs reference properties to obtain the data they need in order to carry out specific operations. For example, when someone looks at you, the server references your description property ( _/de), retrieves the information stored there (your desc), and displays it to the person who's looking at you. Properties are often called just `props'. Props are organized into property directories (often called `propdirs'). The structure and syntax of property directories are very much like those of file directories in the UNIX or DOS operating systems, with props being analogous to files and propdirs being analogous to directories. A directory can contain props or additional directories; names of props and directories are separated by a slash; props and directories are organized in a hiearchical `tree' structure such that each prop has a unique path name. So, the desc prop mentioned above, `_/de', is actually the `de' property in the _ underscore directory. You can view the props on any object you control by examining it, syntax `ex <object> = <path>'. Typing `ex me = /' would show the `first level' or `root directory' of props and directories stored on your character. Typing `ex me = sex' would show your sex property. Typing `ex me = _page/' would show properties stored in the propdir created and modified when you use the page command. Directories will be prefaced by `dir' and will end with a slash. Properties will be prefaced by something different (usually `str' for `string'), and will end with the value stored in the prop. Like flags, properties are set and removed with the @set command, though the syntax is slightly different. The syntax for setting a prop is `@set <object> = <[path/]property>: <value>'. For removing a prop, it's `@set <object> = <[path/]property>: ` (that is, a property name followed by just a colon). To clear a directory, it's `@set <object> = <propdir>/:' You can remove all the properties you have set on an object by typing `@set <object> = :clear'. ==================================== > @set me = obstreperous:yes Property set. > @set me = obstreperous: Property removed. > @set me = personality_traits/obstreperous:yes Property set. > @set me = personality_traits/lascivious:yes Property set > @set me = personality_traits/: Property removed. > @set me = :clear All user owned properties removed. ( oops ) ==================================== It is common practice to separate words in a property name with underscores (@set me = my_favorite_color:blue), but spaces can be used in property names (@set me = my favorite color:blue). However, spaces at the beginning and end of property names are removed when the prop is set. (Spaces at the beginning or end of a property *value* are not stripped: you can store a string beginning or ending with spaces, or even a string consisting of only spaces.) The number, names, and content of properties are not pre-defined as they are in the case of flags. You can't `make up' a new kind of flag and set it on your character (@set me = G or @set me = 9, say), but you can create and set any property you like and store any information there, as long as the syntax is correct and the amount of information stored doesn't exceed certain limits . If you wanted to do `@set me = number_of_pickles_in_my_jar:32', you could, though the information might not be especially useful. (There are some restrictions on what properties you can set, discussed in Section 2.1.1) While you can set virtually any property, the server and user-created commands will expect specific information to be stored in specific, predefined properties. The server will always reference the `_/de' prop when obtaining a desc; the `hand' command will always check your `_hand_ok' prop. So, using a program or configuring an object is often a matter of determining what props are referenced (by reading #help or program documentation, or by asking players or staff) and setting them appropriately. Important and frequently used properties are stored in the correct location by server commands: @desc, @success, @osuccess, @drop, @odrop, @fail, @ofail, and various @lock commands all store information in the _/ directory. (Properties in the _/ directory and their values are often called `messages' and `locks'. See Sections 2.1.2 and 2.3.) ==================================== > @create Feep Feep created with number #1237 > @desc feep = A cute little feep. Description set. > @succ feep = You pick up the feep. It warbles contentedly. Message set. > @osucc feep = picks up the feep. It warbles contentedly. Message set. > @fail feep = You try to pick up the feep, but it scuttles away whimpering! Message set. > @ofail feep = tries to pick up the feep, but it scuttles away whimpering! Message set. > @drop feep = You set the feep down gently. It nuzzles your ankle. Message set. > @odrop feep = sets the feep down gently. It nuzzles %p ankle. > @lock feep = me Locked. > @chlock feep = me Chown lock set. > ex feep = _/ lok /_/chlk:Mistral(#100PWX) str /_/de:A cute little feep. str /_/dr:You set the feep down gently. It nuzzles your ankle. str /_/fl:You try to pick up the feep, but it scuttles away whimpering! lok /_/lok:Mistral(#100PWX) str /_/odr:sets the feep down gently. It nuzzles %p ankle. str /_/ofl:tries to pick up the feep, but it scuttles away whimpering! str /_/osc:picks up the feep. It warbles contentedly. str /_/sc:You pick up the feep. It warbles contentedly. ==================================== The properties in the _/ directory trigger events or messages when the object is used in a certain way. For example, the @success message is displayed to a player who Successfully uses the object. If the object is a thing, `successful use' means picking it up. For a room, `success' means looking at the room. For an exit, it means passing through the exit or using the exit as a command. The @osuccess message uses the same definitions of `success', but in this case the message is shown to Other players present, rather than the triggering player. @Fail works similarly to @success, but in this case the message is shown when a player *fails* to use the object successfully, usually because it is locked against him (locks are discussed in Section 2.3), and @ofail has a similar relationship to @osuccess. On a thing, a @drop message is shown to a player who drops the object; the @odrop message is shown to other players present when the object is dropped. When a @drop message is set on an exit, the message is shown to the player when he arrives in the destination room. The @odrop message is shown to other players in the destination room. Lists: In addition to directories, props can also be organized in `lists'. A list is a group of properties which are handled together in such a way that they emulate a document or computer file. Lists can be created and edited with the list editor, command `lsedit', syntax `lsedit <object> = <list name>'. This is useful for descriptions that need formatting such as paragraph breaks, indentations, and so forth. Complex MPI strings can be stored in a list rather than a property as well, in which case indentation and other whitespace can be used to make the code more readable than it would be as one long uninterrupted string. There are other uses. ==================================== > lsedit here = maindesc < Welcome to the list editor. You can get help by entering `.h' > < `.end' will exit and save the list. `.abort' will abort any changes. > < To save changes to the list, and continue editing, use `.save' > < Insert at line 1 > <you type type type some text text text> > .end < Editor exited. > < list saved. > ==================================== Lists are stored as a set of properties sharing the same path name, and ending with `#/<line number>'. ==================================== > ex here = maindesc#/ str /maindesc#/1: (a line of text you entered... ) str /maindesc#/2: (another line... ) str /maindesc#/3: (another line... ) (etc) ==================================== The list name can be a path name that includes propdirs. For example, you could store multiple descs in propdir `_descs'. ==================================== > lsedit me = _descs/snazzy <enter your `snazzy' desc> > .end > lsedit me = _descs/grungy <enter your `grungy' desc> > .end ==================================== (The server, recall, always references the `_/de' property for a description. If you write a description with lsedit, you will need to put an MPI string in this property that tells the server where to find the description and how to display it. ==================================== > lsedit me = _descs/snazzy <enter `snazzy' desc... > > look me You see nothing special. > @desc me = {list:_descs/snazzy} Description set. > look me Ooo la la! Today Mistral is modeling the latest from the Spiegal Catalogue: a fetching two-piece... (etc etc) ==================================== The MPI string stored in `_/de' can be made considerably more elaborate and flexible than the one shown here. (See Section 3 for a more complete discussion of MPI.) The syntax for removing a list is `@set <object> = <[path/]list name>#/:' ==================================== > @set me = descs/snazzy#/: Property removed. ==================================== 2.1.1 Protected, Restricted, and Wizard Props Property handling is governed by a system of privileges, though in most cases this will be transparent to the user: you will usually be able to set the properties you want without even being aware that the server is checking to see if you're allowed to do so. Besides `normal' props, there are three classes of priviledged properties: protected, restricted, and wizard. The class of a property or propdir is determined by the first character in its name. A prop or propdir beginning with an _ underscore, % percent sign, or . period is `protected'. A prop or propdir beginning with a ~ tilde is `restricted'. A prop or propdir beginning with an @ at-mark is `wizard'. If the prop or propdir begins with any other character, it is `normal' or `unprotected'. (Note: the `first character' restriction applies to any property or propdir. So, `@email', `@/email', and `data/personal/@email' are all wizard props: each includes a prop or propdir that begins with an @ at-mark.) Properties beginning with a an _ underscore can only be written to by the owner of the object on which the property is stored, or by programs owned by the owner, or by programs running at M3 or W. Properties beginning with a . period can only be written to *or read* by the owner of the object on which the property is stored, or by programs owned by the owner, or by programs running at M3 or W. Properties beginning with a % percent mark, like _ underscore properties, can only be written to by the owner or by programs owned by the owner. The % percent mark properties have the additional function of over-riding pronoun and name substitutions. For example, if Jessy does @set me = %n:the Scamper Gal, the prop will be protected, and will serve the additional function of causing `the Scamper Gal' instead of `Jessy' to be substituted in messages that use the `%n' substitution string. (See Section 2.1.6 for additional information on substitution strings.) A restricted property ( ~ ) may be read like an unprotected prop, however it may only be modified by a wizard or a wizbitted program. A common use of restricted props is appointing non-wiz staff members. Staff commands, exits to administrative areas, and so forth, can be locked so that they may only be used by characters with specific restricted prop, such as `~staff'. A wizard property ( @ ) may only be read or modified by a wizard or a wizbitted program. The server records connection data in players' propdir @/. On some MUCKs, wizard props and propdirs are used to record administrative information such as players' email addresses. Some wizbitted programs record potentially sensitive data such as mail in a wizard propdir stored on players. Locks can read any property -- including restricted and wizard properties -- but may only check for matches with a specific value. (See section 2.3.) 2.1.2 Messages and Message Properties As indicated earlier, a set of properties in the _/ directory is given special handling by the server. They can be set with a group of server commands such as @succ, @ofail, etc., and they cause strings to be parsed and displayed automatically whenever certain events happen. Collectively, this group of commands, properties, and their values are called `messages'. Command/Message Type Property @desc _/de @succ _/sc @osucc _/osc @fail _/fl @ofail _/ofl @drop _/dr @odrop _/odr @pecho _/pecho @oecho _/oecho (There does not seem to be a definitive pronunciation for messages: when speaking in RL, some people would say `at-fail' for @fail, and others would say simply `fail'.) The @desc message is evaluated and displayed to any user who looks at an object of any type. ==================================== > @desc me = A nymph with green hair.{look-notify} Message set. > l me [ Kiri looked at you. ] A nymph with green hair. ==================================== The @succ message is evaluated and displayed to any user who successfully uses an object. The meaning of `success' varies depending on the object type. To successfully use a thing or a program means to pick it up. To successfully use a room means to look at it. To successfully use an exit means to pass through it or use it as a command that triggers a program. To successfully use a player means to steal one of her pennies with the `rob' command. ==================================== > @succ out = You step out back. Message set. > out You step out back. ==================================== The @osucc message is evaluated and displayed to all players in a room where a player successfully uses the object, *other than* the player in question. The @osucc message (that is, the string stored in the object's _/osc property) is prefaced with the user's name when it is displayed. ==================================== > @osucc out = steps out back. Message set. (Kiri types `out'... others see...) Kiri steps out back. ==================================== The @fail is evaluated and displayed to a user who fails to successfully use an object. The normal reason for failure is that the object is locked against the player. The terms for success or failure are those indicated above, for @succ. ==================================== > @fail vault = Ahem. Only authorized bank employees may open the vault. Various alarms begin to sound. Message set. > @lock vault = ~banker:yes Locked. > vault Ahem. Only authorized bank employees may open the vault. ==================================== The @ofail message is evaluated and displayed to all players in a room where a player fails to successfully use the object, *except* the player in question. The @ofail message is prefaced with the user's name when it is displayed. ==================================== > @ofail vault = tried to open the vault! Shrill alarms begin ringing! Message set. (Kiri types `vault'... others see...) Kiri tried to open the vault! Shrill alarms begin ringing! ==================================== The @drop message is evaluated and displayed to a player who triggers a drop. For a thing or program, a drop is triggered when the object is dropped. For a room, a drop is triggered whenever an object is dropped in the room. For an exit, a drop is triggered when a player (or other object type) passes through the exit (using an exit/action linked to a program does not trigger a drop). For a player, a drop is triggered when he or she is killed. ==================================== > @drop grenade = BANG! Message set. > drop grenade BANG! ==================================== The @odrop message is is evaluated and displayed to all players in a room where a player drops an object, except for the triggering player, or all other players in the room a player arrives in when she passes through an exit. The @odrop message is prefaced with the user's name when it is displayed. ==================================== > @odrop out = comes out of the house. Message set. (Kiri types `drop out'... the players in the outside room see...) Kiri comes out of the house. Kiri has arrived. > @odrop grenade = drops a grenade! Run! {null:{delay:3,{lit: {null:{tell:BANG!}}}}} Message set. (Kiri types `drop grenade'... others see...) Kiri drops a grenade! Run! BANG! ==================================== @Pecho and @oecho are somewhat different than the preceding messages, having to do with the format of messages transmitted (or `broadcast') by puppets and vehicles. By default, output broadcast from a puppet is prefaced by the puppet's name and a > greater than sign. Typing `@pecho <puppet name> = <new preface string> sets the puppet objects _/pecho property, which will become the new preface string. (See Section 4.3 for more information on creating puppets.) ==================================== > z look Squiggy> Amberside Inn, Tavern Squiggy> At first glance the tavern seems little changed from days when Squiggy> pirate sloops sought haven in the protected coves of Amberside's Squiggy> cliffs: the beams are still low and smoke-stained... > @pecho squiggly = * Message set. > ex squig = _/ str /_/pecho:* 1 property listed. > z look * Amberside Inn, Tavern * At first glance the tavern seems little changed from days when pirate * sloops sought haven in the protected coves of Amberside's cliffs: the * beams are still low and smoke-stained... ==================================== By default, messages broadcast from the exterior of a vehicle object to its interior will be prefaced by the string `Outside> `. Typing `@oecho <vehicle object> = <new preface string>' will set the vehicle's _/oecho property, which will become the new preface string. (See Section 4.5 for more information on creating vehicles.) ==================================== > @create 1967 Corvette Sting Ray 1967 Corvette Sting Ray created with number #558. > @set 1967 = V Flag set. > @act getin = 1967 Action created with number #559 and attached. > @link getin = 1967 Linked to 1967 Corvette Sting Ray(#558V) > drop 1967 Dropped. > getin 1967 Corvette Sting Ray(#558V) > z :raps sharply on the window... "Can you hear me in there, mistress?" Outside> Squiqqy raps sharply on the window... "Can you hear me in there, mistress?" > @oecho here = >>> Message set. > z :raps again. "What about now?" >>> Squiggy raps again. "What about now?" ==================================== 2.1.3 Data Types and Setting Properties In the vast majority of cases, props can be correctly set by players and wizards with the @set command, as described above. However, in rare cases the `data type' of a property needs special handling, and a different command is needed: @propset. Data handled by the MUCK server is `typed', as it is in many computer languages, including C (the language the server program is written in) and MUF (one of the two programming languages available on MUCKs). The server handles different types of data, and most operations require a specific type of data. In this context, the relevant types are `string', `dbref', `integer', and `float'. (There is also a fifth data type: `lock', which is discussed in secton 2.3. Type `float' is only available on MUCK versions 6.0 or higher.) A string is a series of zero or more characters (a zero-length string is called a `null string'). A dbref is a database reference number. An integer is a whole number. A float is a floating point decimal number. These values can *look* the same but have different *types*. The type of a datum is determined when it is stored. The string of numeral characters "123", the #dbref 123, and the integer 123, and the float 123.0 are *four different values*. The @set command stores property data as a string. The following three commands store information in three different properties, but it's the same value in each case: a string composed of the characters `1', `2', and `3'. ==================================== > @set me = my_favorite_dbref:123 Propery set. > @set me = my_favorite_integer:123 Property set. > @set me = my_favorite_string:123 Property set. ==================================== If you typed these commands, and then did `ex me = /', you would see the three properties, each prefaced by `str', meaning `this data is stored as a string'. ==================================== > ex me = / str /my_favorite_dbref:123 str /my_favorite_integer:123 str /my_favorite_string:123 ==================================== (Note: on some versions of MUCK, the format of this output will be somewhat different, such as `my_favorite_string: (String) 123') Programs and commands often store data as dbrefs or integers rather than strings; occasionally, players will want or need to do so as well. The command for doing this is `@propset', syntax `@propset <object> = <data type> : <[path/]property> : <value>'. The following three commands store information in three different properties, and although they look similar, it's a different value in each case: a dbref, an integer, and a string respectively. ==================================== > @propset me = dbref:my_favorite_dbref:#123 Property set. > @propset me = int:my_favorite_integer:123 Property set. > @propset me = str:my_favorite_string:123 Property set. ==================================== (Type `float' has been omitted from this example: at the time of this writing, @propset does not handle floating point numbers.) If you typed these commands, and then did `ex me = /', you would see the three properties, prefaced by `ref', `int', and `str' respectively, with each preface showing the type of the data stored in the string. ==================================== > ex me = / ref /my_favorite_dbref:Hortense(#123PBJ) int /my_favorite_integer:123 str /my_favorite_string:123 ==================================== There is also a server shortcut for setting properties with values of type integer: put a ^ carot before the number. ==================================== > @set me = lucky_number:^5 Property set. > ex me = lucky_number int /lucky_number:5 ==================================== 2.1.4 Triggering From Properties In most cases, programs and server operations are triggered (i.e. caused to run or execute) by typing a command. However, they can also be triggered from a number of protected properties: all message props (_/de, _/sc, etc.), plus _connect, _oconnect, _disconnect, _odisconnect, _arrive, _oarrive, _depart, _odepart, and _listen. Performing any of the actions implied by these property names sends information to the server and (if the property is set correctly) causes messages to be displayed, an MPI string to be parsed, or a program to run. For example, if you set a _connect prop on your character to trigger a certain program, the program will run each time you connect; if you put an MPI string in your _/de prop, it will be parsed each time someone looks at you. The server searches up the environment tree (see Section 2.2) for triggering props. For example, if _connect prop that triggers a program is set on your character, the program will run each time you connect. If it's set on a room, the program will run each time someone connects in that room. If it's set on the global parent room (#0), the program will run each time someone connects anywhere on the MUCK. The manner in which the props are set differs depending on which prop it is (the _/ directory is handled differently than the others, such a _connect or _listen) and on what the intended result is (messages are handled differently than MPI, which is handled differently than program calls). To cause a message to be displayed by a _/ prop (_/de, _/sc, etc), simply set the message as a string, with @set or the specific server command. `@o- ` messages such as @osucc, @ofail, and @odrop are prepended with the name of the triggering player (or other object type). ==================================== > @desc out=A simple wooden door. Message set. > look out A simple wooden door. > @succ out=You decide to go outside for a bit.... Message set. > out You decide to go outsides for a bit... > @set out=_/sc:You head outside. Property set. > out You head outside. ==================================== MPI strings in _/ props will be parsed automatically. ==================================== > @desc watch=You glance at your watch. The time is {time}. look watch You glance at your watch. The time is 01:44:31. ==================================== To trigger a MUF program from a _/ prop, preceed the dbref of the program with an @ at-mark. For example, let's assume the dbref of the `Obvious Exits' program is #123: ==================================== > @succ here=@#123 Message set. > look here Messy Room(#545RJ) Boy, this place is a mess! Obvious Exits: Out <O> ==================================== The other triggering props (_arrive, _oconnect, etc) are handled slightly differently. Simple strings cause no result, MPI must be preceeded with an & ampersand in order to be parsed, and MUF programs can be called with just a string indicating the dbref. ==================================== > @set me=_connect:555 Property set. > @set me=_arrive:&{null:{otell:waltzes in.}} Property set. ==================================== These triggers can be set up as a propdir rather than a single prop, in order to trigger multiple results from the same action. For example, the following settings would trigger both programs #581 and #555 each time someone connects in the room. The propdirs are evaluated in alphabetical order, so #581 would execute first. (Other than determining alphabetical order, the prop names following the first / slash mark have no effect: they can be whatever you like.) ==================================== > @set here=_connect/desc-check:#581 Property set. > @set here=_connect/my-wwf:#555 Property set. ==================================== The _listen is triggered by *any* activity. As such, it is both very useful and very easily abused. Permissions safeguards are coded into the server for _listen: the only result that can be triggered is execution of a program; the program must be set Link_OK and have a Mucker level equal to or higher than a level set by the MUCK administrators. Usually this parameter is set to 3 or 4 (M4 is `wizard'). `Bot programs and automatic `noise' or `event' programs are common examples. The prop is set simply by putting the dbref of the program to run in the property value. ==================================== > @find noises noises.muf(#812FLM3) ***End of List*** 1 objects found. > @set here=_listen/noise:812 Property set. ==================================== 2.1.5 Registered Names Objects can be specified by `registered names' as well as by names and dbrefs. A registered name is an alias that can (like a dbref) be used regardless of the object's location, but (like a name) consisting of a memorable string. The primary use is to provide a convenient, memorable way of specifying an item, regardless of its location and ownership. For example, a player on a large MUCK might have a puppet with a long, difficult to remember dbref, such as #128629. If the player frequently wanted to teleport the puppet to her from somewhere else, she would need to either memorize the dbref or repeatedly retrieve it with the @find command. As an alternative, she could give the puppet an easy-to-remember registered name such as `pup'. From that point on, the puppet could be specified with the name `pup' preceeded by a $ dollar sign, rather than by dbref. Players can create registered names -- usuable only by the player -- with the @register command, syntax `@reg #me <object> = <registered name>'. (The information is stored in the player's `_reg/' directory.) ==================================== > @find Squiggy Squiggy(#128629XZ) ***End of List*** 1 objects found. > @reg #me #128629 = pup Now registered as _reg/pup: Squiggy(#128629XZ) on Jessy(#2PWQX) > @tel $pup = me Teleported. > i You are carrying: Squiggy(#128629XZ) You have 5086 pennies. =================================== Individual registered names may also be set when an object is created. The standard creation commands -- @create, @dig, @action, and @open -- each take two optional argument fields, separated by = equals signs. The first of these fields is specific to each command; the second field for all four may be used to specify a registered name. @create <name> = <cost in pennies> = <reg name> @dig <name> = <parent room> = <reg name> @action <name> = <source> = <reg name> @open <name> = <link> = <reg name> ==================================== > @create Mary Poppins Umbrella == umbi Mary Poppins Umbrella created with number 226. Registered as $umbi > ex $umbi Mary Poppins Umbrella(#226) Owner: Mistral Type: THING Created: Fri May 09 14:33:47 1997 Modified: Fri May 09 14:33:47 1997 Last used: Fri May 09 14:33:47 1997 Usecount: 0 > ex me=_reg/ ref /_reg/umbi:Mary Poppins Umbrella(#226) > @dig OOCafe = #143 = cafe OOCafe created with room number 225. Trying to set parent... Parent set to OOC Environment(#143RL). Room registered as $cafe > @open Enter Garden = $garden = gogard Exit opened with number 224. Trying to link... Linked to Secret Garden(#455R). Registered as $gogard ==================================== Wizards can set global registered names -- usuable by all players -- syntax `@reg <object> = <registered name>. A frequent and convenient use of global registered names is to provide an alias for publicly available programs such as `do-nothing' and `obvious-exits'. Without global registered names, players would need to find the dbrefs of these programs each time they needed them for building purposes. Since the players do not control these programs, finding the dbrefs can be difficult. ==================================== > @find obv gen-Obvexits(#2002FLM3) ***End of List*** 1 objects found. > @reg #2002 = exits Now registered as _reg/exits: gen-Obvexits(#2002) on The Void(#0R) > @succ here = @$exits Message set. ==================================== Global registered names are stored on room #0, in propdir `_reg/'. 2.1.6 Pattern Matching and Output Types As indicated, the @find command can be used to locate items with a given name. For more flexible searches, you can use pattern matching (a limited version of regular expressions) to find objects whose name matches a certain pattern, rather than matching the name string literally. A pattern consists of logical and grouping operators, wildcard characters, and/or literal characters. @Find -- and the related search commands @owned, @contents, and @entrances -- can be used with a subset of standard regular expression conventions, and additional parameters that specify `output types' (parameters for values such as memory used, time since used or modified, etc.) Suppose that you have created a Coral Reef area, with interesting scenery and games: players try to avoid sharks, wrestle octopi, and find sunken treasure before they run out of breath and are forced to return to the surface. To use the rooms and games, a player must have a snorkle. You have set up a `make snorkle' action linked to an M2 program that changes an object into a snorkle (it sets all the properties used by your games). And, you have a Snorkle Rental Booth: a room where can people rent snorkles or read `The Complete Book of Snorkles' and `Field Guide to the Lesser Coral Reef' (help on how to use the area). The reef rooms have names like `Coral Reef, Sandy Wash' and `Coral Reef, Moray's Lair'. Occassionally, you need to find these objects... to recall and update your snorkles or track down your wandering shark-bot, for example. You can use literal matches to find objects whose name includes the string you are trying to match. Matching is not case-sensitive. ==================================== > @find complete book The Complete Book of Snorkles(#811SJ) 1 objects found. > @find coral reef Field Guide to the Lesser Coral Reef(#810SJ) Coral Reef, Sandy Wash(#802RJ) Coral Reef, Moray's Lair(#805RJ) Coral Reef, Near the Surface(#809RJ) Coral Reef, Weed-Shrouded Cave(#812RJ) Coral Reef, Sunken Hull(#815RJ) Coral Reef, Dark Cave(#817RJ) <etc.> <etc.> ***End of List*** 16 objects found. ==================================== The * and ? wildcard characters allow you to search for names that match a pattern, rather than a literal string. The * asterix wildcard (sometimes called a `glob') matches any zero or more characters; the ? question mark matches any single character. You could find your two cave rooms in the coral reef area (and leave out other cave rooms you might have) by beginning putting a glob between `Coral' and `Cave'. ==================================== > @find coral*cave Coral Reef, Weed-Shrouded Cave(#812RJ) Coral Reef, Dark Cave(#817RJ) ***End of List*** 2 objects found. ==================================== You could find all the reef rooms, leaving out the book `Field Guide to the Lesser Coral Reef', by searching for `reef' followed by a ? question mark, which would require that there be at least one character after the string `reef'. ==================================== > @find reef? Coral Reef, Sandy Wash(#802RJ) Coral Reef, Moray's Lair(#805RJ) Coral Reef, Near the Surface(#809RJ) <etc.> <etc.> ***End of List*** 15 objects found. ==================================== The {curly braces} grouping operators delimit word patterns. To find all your snorkle objects and the `make snorkle' action, but omit `The Complete Book of Snorkles', you could delimit `snorkle' as a word, and not simply a string. ==================================== > @find {snorkle} Snorkle Rental Booth(#856RJ) make snorkle(#857ED) Snorkle 1(#854) Snorkle 2(#859) Snorkle 3(#881) Snorkle 4(#882) Snorkle 5(#883) Snorkle 6(#884) Snorkle 7(#885) Snorkle 8(#886) Snorkle 9(#887) Snorkle 10(#888) Snorkle 11(#889) Snorkle 12(#890) ***End of List*** 13 objects found. ==================================== One can search for objects whose names include words from a group of valid words by separating the words with the `or' operator, a | vertical bar. For example, you could find all objects that include the words `sunken' or `surface'. ==================================== > @find {sunken|surface} Coral Reef, Near the Surface(#809RJ) Coral Reef, Sunken Hull(#815RJ) ***End of List*** 2 objects found. ==================================== The [square brackets] grouping operators delimit character groups or ranges or characters. A group of characters in square brackets are treated as valid single characters. A find for `coral reef, [wdn]' would find the coral reef rooms with either `w', `d', or `n' following the string `coral reef, `. ==================================== > @find coral reef, [wdn] Coral Reef, Near the Surface(#809RJ) Coral Reef, Weed-Shrouded Cave(#812RJ) Coral Reef, Dark Cave(#817RJ) ***End of List*** 3 objects found. ==================================== Instead of typing each valid character, you can also designate a range of valid characters, such as [0-9], [a-z], or [A-Z]. You could find all your snorkle objects, which all have a numeric character following the string `Snorkle `, by using [0-9] as the range of characters. ==================================== > @find snorkle [0-9] Snorkle 1(#854) Snorkle 2(#859) Snorkle 3(#881) Snorkle 4(#882) Snorkle 5(#883) Snorkle 6(#884) Snorkle 7(#885) Snorkle 8(#886) Snorkle 9(#887) Snorkle 10(#888) Snorkle 11(#889) Snorkle 12(#890) ***End of List*** 12 objects found. ==================================== Note that the [square brackets] delimit *character ranges*, not *numeric ranges*. A find for `snorkles [1-12]' won't work... or won't work as one might intend. It finds all objects with either characters in the range of `1 to 1' or the character `2' following the string `snorkles `. ==================================== > @find snorkle [1-12] Snorkle 1(#8454) Snorkle 2(#8459) snorkle 10(#8488) snorkle 11(#8489) snorkle 12(#8490) ***End of List*** 5 objects found. ==================================== Output Types: Searches with @find and the related commands (@owned, @contents, and @entrances) may also be narrowed by object type and several other values, and may return additional information such as the objects' owners and locations. The extended syntax is: @find <name or regex pattern> = <search parameter> = <output type> One or several search parameters may be used. Valid search parameters may be a type flag, a Mucker level, or the following special values: U = Show only unlinked objects @ = Show only old and unused objects ~<size> = Show only objects with current memory used greater than <size> ^<size> = Show only objects with total memory used greater than <size> (wizard only) An output type parameter must be typed in full; only one output type may be used per search. Valid types include: owners = List owners along with objects links = List links along with objects size = Show memory size count = Don't list objects: just show total found location = Show objects' locations ==================================== This search would list any unlinked exits you control... > @find = EU South;sout;sou;so;s(#528E) North;nort;nor;no;n(#533E) ***End of List*** 2 objects found. ==================================== ==================================== This search would list any old and unused objects you control, along with their locations... > @find = @ = location Faded rose(#761) Mistral(#100) ***End of List*** 1 objects found. ==================================== Objects become `old and unused' if none of their `created', `modified', or `last used' timestamps is more recent than the `aging_time' system parameter. ==================================== This search will find all your M1 programs... > @find = 1 HelloWorld.muf(#976FM1) TrainingWheels.muf(#978FDM1) WhatsMyName.muf(#979FM1) CountMyBellyButton.muf(#980FM1) ***End of List*** 4 objects found. ==================================== ==================================== This search will find any objects you control which use more than 2000 bytes of memory, along with the current memory size... > @find = ~2000 = size Manhattan Phonebook(#1301) 1932373346 bytes. ***End of List*** 1 objects found. ==================================== For additional information on output types and regular expressions, see the entry for @find in the Server Command Reference (Section 2.6) and the entry for SMATCH in the MUF Reference (Section 3.2.5). 2.1.7 Pronoun and Name Substitution Messages returned by fields such as @success, @drop, etc., and also the formatting of a number of commands and programs such as page, may be dynamically formatted for a player's name and gender: substitution strings (a % percent mark followed by a key character) are replaced by the appropriate name or pronoun. Standard substitution strings are: %a (absolute) = Name's, his, hers, its. %s (subjective) = Name, he, she, it. %o (objective) = Name, him, her, it. %p (possessive) = Name's, his, her, its. %r (reflexive) = Name, himself, herself, itself. %n (player's name) = Name. Capitalizing the substitution string -- such as %S or %R -- causes the substitute value to be capitalized as well. The server examines the `sex' property of the triggering player (or other object type) and substitutes as needed. Supported values for the sex property are `male', `female', and `neuter'. If the property is not set, the player's name is used instead. ==================================== > @set $pup = sex:male Property set. > @osucc bonk = bonks %r on the head. %S exclaims, "I could have had a V8!" Message set. > pp bonk Squiggy bonks himself on the head. He exclaims, "I could have had a V8!" > @set $pup = sex:neuter Property set. > pp bonk Squiggy bonks itself on the head. It exclaims, "I could have had a V8!" ==================================== The values for these substitutions may be over-ridden by setting a property with the same name as the substitution string. ==================================== These settings give Squiggy a nickname and some way PC pronouns... > @set $pup = %n:The Squigmeister Property set. > @set $pup = %a:hes Property set. > @set $pup = %s:s/he Property set. > @set $pup = %o:hem Property set. > @set $pup = %p:hes Property set. > @set $pup = %r:hemself Property set. > pp bonk Squiggy bonks hemself on the head. S/he exclaims, "I could have had a V8!" ==================================== 2.2 Overview: Rooms, Things, and the Environment Tree. Rooms are objects created with the @dig command and having the type flag R. Things are objects created with the @create command and having no type flag. Both rooms and things can contain other objects. Rooms: The syntax for the @dig command is @dig <room> [=<parent> [=<regname>]] The position of rooms -- and the resulting `geography' of the MUCK -- is determined in two ways. In addition to named exits creating the illusion of spatial relationships (e.g. having a room called `The Village Green' that can be reached by travelling West from `The Cove'), rooms exist in a hierarchical tree structure known as the `environment tree'. One can lead a rich VR life without ever needing to know about the MUCK's environment tree, but builders and administrators will profit from an understanding of how it works. As an analogy, one might think of the rooms on a MUCK as numerous nested boxes. Room #0, the `global parent', would in our analogy be a large box containing all the other boxes... all the other rooms. Rooms inside #0 can also contain rooms: the boxes can contain other boxes, in an unending series. The boxes (rooms) can contain items (players, things, etc) as well as other boxes. A room that contains another room is said to be a `parent room'; a room contained in another room is said to be a `daughter room'. A given room can be contained in another and at the same time contain other rooms: in this case, the room is both a parent and daughter room. Intermediate rooms of this type are often called `environment rooms'. (Or, another analogy: rooms are like directories in a computer file system: the root directory is analogous to Room #0; environment rooms and rooms are analogous to directories and subdirectories within the root directory; players and objects in rooms are analogous to files in these directories.) Environment rooms are used to define areas of a MUCK and to provide commands or features that should only be available in certain areas (more on this below). You can view the series of rooms containing the room you are located in by typing `@trace here'. ==================================== > @trace here Sinshe Village, by the Pier(#687RJ) Sinshe Parent Room(#635RA) Environment: Lowlands(#285RA) Rainforest Parent Room(#121RWA) Rainforest: Main Prarent(#118RA) Master Environment(#101RA) **Missing** ==================================== In this example, the administrators of the MUCK have carefully laid out a consistent, hierarchical environment tree. In addition to the `geographical position' of Sinshe Village, each room on the MUCK has a specific and meaningful place in the environment tree. The village pier is nested inside -- or `parented to' -- the Sinshe Parent Room (#635), which presumably contains all the rooms that make up the village of Sinshe. This room is in turn parented to Environment: Lowlands (#285), which would contain the parent rooms for all areas in the lowlands. The series continues up through rooms #121, #118, #101, and finally, room #0, which appears on this list as `**Missing**' (to mortals, rooms not set Abode and not controlled by them appear as **Missing** on a @trace; for security reasons, the global parent of a MUCK is usually not set A, and as a result the last item on the list will be **Missing**). In fact, not only rooms but all objects on a MUCK have a position in the environment tree. Exits are considered to be located in or on the object to which they are attached. Players and things are always located in a specific room or thing... but, unlike rooms and exits, they move around. The @trace command works on any object. If a player were holding an object called `paper sack', `@trace paper sack' would show the sack's current position in the environment tree. ==================================== > @trace paper sack paper sack(#5474) Jessy(#2WQJ) Sinshe Village, by the Pier(#687RJ) Sinshe Parent Room(#635RA) Environment: Lowlands(#285RA) Rainforest Parent Room(#121RWA) Rainforest: Main Prarent(#118RA) Master Environment(#101RA) **Missing** ==================================== A newly created room is parented to the first room above it that is set Abode, or the first room controlled by the player issuing the @dig command. If no rooms in the environment path meet one of these criteria, it is parented to Room #0. Keeping parent rooms set Abode will insure that new rooms are correctly parented: if Jessy stood on the pier and typed `@dig Under the Pier', the new room would be correctly parented to Sinshe Parent Room(#635RA), the same parent as that of the pier proper. The position of a room within the environment tree can be changed (that is, it can be `re-parented') with the @teleport command: teleport the daughter room to its new parent room. For example, if another player wanted to begin a new area -- an African village say -- she could start off from the pier in Sinshe, and use the @teleport command to position the new rooms correctly. ==================================== > @dig Environment: Afurica Environment: Afurica created with room number #5266. Parent set to Sinshe Parent Room(#635RA). > @tel #5266 = #101 Parent set. > @dig Kapiti Plain Environment Room Kapiti Plain Environment Room created with room number #5435. Parent set to Sinshe Parent Room(#635RA). > @tel #5435 = #5266 Parent set. > @dig Kapiti Plain -- By the Watering Hole Kapiti Plain -- By the Watering Hole created with room number #5433. Parent set to Sinshe Parent Room(#635RA). > @tel #5433 = #5435 Parent set. > @act kap = me Action created with number #5435 and attached. > @link kap = #5433 > kap Kapiti Plain -- By the Watering Hole(#5433R) > @trace here Kapiti Plain -- By the Watering Hole(#5433R) Kapiti Plain Environment Room(#5435R) Environment: Afurica(#5266R) Master Environment(#101RA) **Missing** ***End of List*** ==================================== The new environment rooms do not have an Abode flag. In order to make #5435 and #5266 appear for other players in a @trace, and in order for player-created rooms to be correctly parented, they will need to be set A. Once Kenya has moved to Kapiti Plain -- By the Watering Hole(#5433), any new rooms she digs from there will be correctly parented to Kapiti Plain Environment Room(#5435). Alternately, the parent room can be declared at the time the room is created. ==================================== > @dig Environment: Afurica = #101 Environment: Afurica created with room number #5266. Trying to set parent... Parent set to Master Environment(#101RA). ==================================== Registered names also can be declared when the room is created. ==================================== > @dig Kapiti Plain -- By the Watering Hole = #5435 = kap Kapiti Plain -- By the Watering Hole created with room number #5433. Trying to set parent... Parent set to Sinshe Parent Room(#635RA). Room registered as $kap or... > @dig Kapiti Plain -- By the Watering Hole == kap Kapiti Plain -- By the Watering Hole created with room number #5433. Parent set to Sinshe Parent Room(#635RA). Room registered as $kap ==================================== Environment rooms have numerous uses, most deriving from the fact that the MUCK server uses something very much like the UNIX and DOS operating systems' `search path' when locating executable commands. When a player issues a command, the server looks for a command of that name on rooms up the environment path as well, and will execute the first match it finds, or the command with the highest priority. (The search order and rules for determining exit priority are somewhat involved. See Section 2.3.3., Exit Priorities.) One result is that commands can be made available for a whole area (and only that area) by placing them in the appropriate parent room. In our above example, Kenya could make a command called `map' that shows an ASCII map of Kapiti Plain, and attach it to the Kapiti Plain Environment Room. Then, when any player in the Kapiti area types `map', he would see the map. Because each room has a unique path within the environment tree, there is no danger that a map for some other area will be shown, even if other builders make similar commands with the same name. Environment rooms can also be used to *disable* commands within a certain area. If, for some reason, Kenya decided that spoofing should not be allowed in Kapiti, she could create a command with the same name as that of the global spoof command, attach it to Kapiti Plain Environment Room, and link it to $nothing. The server's search path starts with one's present location and moves toward the global parent. As a result, the newly created do-nothing spoof will be found and executed before the server reaches the global spoof command in #0: spoofs in Kapiti will be intercepted at the area parent room, and produce no result. Programs can search for properties as well as command names in the same way, a fact that has numerous applications for builders and programmers. Keeping all one's rooms and belongings within a parent room also facilitates archiving. If a player parents all her rooms to a personal parent room, she could then archive all her belongings by going to the parent room and typing `@archive here'. Everything controlled by the player and contained in the parent -- daughter rooms, the contents of daughter rooms, and the player herself -- would be archived. (For more information on archiving, see Section 4.5.) The parent room and everything contained by it and controlled by the player -- daughter rooms, the contents of the daughter rooms, and the player herself -- will be archived. The Realms Wizard system also makes use of the the environment tree. A Realms Wizard is a player who owns a room set W, on a MUCK where realms control is enabled. Such a player has wizard-like powers in that room and any rooms below it in the environment tree. This explains a seemingly unnecessary duplication in the environment path from the previous example, which contains `Rainforest Parent Room(#121RWA)' and `Rainforest: Main Parent(#118RA)'. There seem to be two `main' parent rooms for the Rainforest: an additional environment room has been slotted into the path, giving the owner of room #121 (which is set W) realms-wiz control over the Rainforest. This player can attach exits, examine objects, and so forth anywhere in the Rainforest, but not in other areas of MUCK. ==================================== > @trace here Sinshe Village, by the Pier(#687RJ) Sinshe Parent Room(#635RA) Environment: Lowlands(#285RA) Rainforest Parent Room(#121RWA) <-- Owner of this room Rainforest: Main Parent(#118RA) is a realms wizard Master Environment(#101RA) **Missing** ==================================== The same result could be achieved by @chown'ing #118 to the player, and setting it W. Often, though, there are reasons to slot in an extra room in this way. In this example, the realms wiz owning #121 would have control over the Rainforest, but not over Rainforest-specific commands attached to #118. The organizing logic of a MUCK's environment tree does not necessarily have to be spatial or geographical. Another workable system is to create environment rooms that each serve as the parent for rooms of a given type. For example, the administrators of a MUCK might create a series of environment rooms such as `Outdoors Environment Room', `Indoors Environment Room', and `Vehicle Environment Room'. Additional more specific environment rooms might be created as well: the Outdoors Environment Room could contain rooms `Mountain Environment Room', `Swamp Environment Room', `Plains Environment Room', and so forth. The advantage of this system is that commands and default messages can be set up in a consistent, realistic, and economical way. The disadvantage is that -- since newly created rooms have the same parent as the room from which one digs -- players often end up with incorrectly parented rooms: if a player owns an indoors room called `The Den', and then digs an adjacent outdoors room called `The Backyard', the backyard will be located in the Indoors Environment Room unless the player moves it. Many players won't. Things: Things are created with the @create command: @create <object> [=<cost> [=<regname>]] The standard start-up database includes the user-created commands `put' and `fetch'. `Put' allows you to put an item you are carrying inside another item (syntax `put <object> in <object>'). `Fetch' allows you to retrieve an item from inside another (syntax `fetch <object> from <object>'). Partial names may be used for <object>. ==================================== > put kitty snacks in backpack Putting Kitty Snacks in Backpack. > fetch kitty from back Fetching Kitty Snacks from Backpack. ==================================== Things can also be set up as vehicles, by setting their V flag and creating an exit that is both attached and linked to the thing. For vehicles, and occassionally for other objects, you would want to describe the interior of the thing. The interior of a thing can be given a description with the @idescribe command, syntax `@idesc <object> = <interior description>'. ==================================== > @create tRanSMogriFIER tRanSMogriFIER created with number 5489. > @desc trans = A large cardboard box with tRanSMogriFIER written on the side in marker, and an arrow that says ----> IN Description set. > @idesc tRanSMogriFIER = An enthusiastic artist has made lots of buttons and monsters with a marker on the sides. Description set. > l trans A large cardboard box with tRanSMogriFIER written on the side in marker, and an arrow that says ----> IN > @act get in;in;enter = trans Action created with number #2543 and attached to tRanSMogriFIER(#5489) > @link get in = trans Linked to gen-nothing.muf(#363FLM2) > get in tRanSMogriFIER(#5489) An enthusiastic artist has made lots of buttons and monsters with a marker on the sides. ==================================== See Section 4.4 for more information on making vehicles. 2.2.1 Droptos If a room is linked to another room or to a thing, the object linked to will serve as the room's `dropto'. A dropto is a location to which dropped objects will be moved. ==================================== > @dig Lost and Found = #102 = lnf Lost and Found created with room number 198. Trying to set parent... Parent set to OOC Environment(#102RA). Room registered as $lnf > @link here = #102 Dropto set. > Drop bic Dropped. > @contents #198 Bic Four-Color Ballpoint(#1237) ***End of List*** 1 objects found. ==================================== If a room's Sticky flag is set, the drop-to is delayed until all players have left the room. To remove a dropto, @unlink the room. To remove a Sticky bit, type `@set here = !S'. 2.2.2 Looktraps 'Looktraps' are details or `fake objects' in a room. For example, rather than creating a `Sign' object with instructions on how to use some local commands and placing it in a room, you could mention the sign in the room's desc and create it as a looktrap. There will be no separated dbref'd object named `Sign', but players will still be able to do `look sign' and see it. The primary value of looktraps is efficiency: because no separate object is created, overall database size is somewhat smaller, and commands that must search the database have one fewer objects to examine. Looktraps are stored as properties in the _details/ directory. Names of looktraps follow the aliasing conventions of exits: strings delimited by ; semicolons serve as alias names. ==================================== > @set here = _details/sign;plaque;notice:To see who lives here, type `look mailboxes'. To get a home here, type `claim #', using an unclaimed home number for `#'. Property set. > l plaque To see who lives here, type `look mailboxes'. To get a home here, type `claim #', using an unclaimed home number for `#'. ==================================== Looktraps are automatically parsed for MPI. ==================================== This looktrap puts the sign text in a list, allowing better control over formatting... > @set here = _details/sign;plaque;notice:{list:signtext} Property set. > lsedit here = signtext < Welcome to the list editor. You can get help by entering `.h' > < `.end' will exit and save the list. `.abort' will abort any changes. > < To save changes to the list, and continue editing, use `.save' > < Insert at line 1 > > ... text text text ... > ... blah blah blah ... > ... etc etc etc ... < Editor exited. > < list saved. > ==================================== ==================================== This looktrap creates a `delayed effect'. Players get a little message nine seconds after they look at the portrait. > @set here=_details/painting;picture;portrait:A somber portrait in oils, depicting Baron Von Hoofenstaffen, former owner of this mansion.{null: {delay:9,{lit:{null:{tell:You're not quite sure: it seems, perhaps, that the eyes of the portrait are following your moves.}}}}} ==================================== Note: Many MUCKs have soft-coded `look' commands that handle setting and removing looktraps, with syntaxes such as `look #add <detail> = <desc>'. Type `look #help' to determine if such a system is available on your MUCK. 2.3 Overview: Exits and Locks An `exit' is a link between two objects on a MUCK. When the two objects are rooms, the exit creates a virtual `door' between them. When the destination is a program, the exit serves as a user-created command. Other combinations are possible. Exits are also called `actions'. Use of exits (as well as objects of other types) is controlled by `locks': an expression that evaluates as either `true' (in which case the exit/object can be used) or `false' (in which case it cannot). An exit is characterized by having a starting point (an object to which it is `attached') and a destination point (an object to which it is `linked'). ***** Exits are created with either the @open or @action command. Both create an exit (an object with type flag E), but the syntax and defaults are slightly different. The basic syntax of the @open command is `@open <exit name>'. An exit created in this way will be attached to (i.e., start from) the room in which one issues the command, and it will not be linked to anything (it won't lead anywhere). The exit can be linked to another object on the MUCK with the @link command, syntax `@link <exit name> = <destination>'. Since the destination will usually be somewhere else on the MUCK, it will need to be specified by dbref rather than name. ==================================== Hooking up an exit in two steps... > @open out Exit opened with number #1766. > @find hallway Ansley Inn, Hallway(#198R) 1 objects found. ***End of List*** > @link out = #98 Linked to Ansley Inn, Hallway(#98R) Hooking up an exit in one step... > @open out = #98 Exit opened with #1766. Linked to Ansley Inn, Hallway(#198R) ==================================== An exit does not have to be attached to a room, however: an exit can be attached to anything except a program or another exit. The `@action' command (abbreviated as `@act') creates an action/exit attached to an object specified at the time of creation: ==================================== > @act myplace = me Action created with number #1236 and attached. > @link myplace = #1234 Linked to Cashmere's Den(#1234). Many MUCKs have a soft-coded @action command, that allows you to specify both the source and destination at the time of the exit's creation: > @act myplace = me,#1234 Action created with number #1236 and attached. Trying to link... Linked ot Cashmere's Den(#1234) ==================================== The attachments and links of exits can be changed. To relink an exit, issue the @unlink command and then @link the exit to the new destination. ==================================== > @unlink myplace Unlinked. > @link myplace = #1768 Linked to Cashmere's Bachelor Pad(#5784R). ==================================== To change an exit's point of attachment, use the @attach command, syntax `@attach <exit> = <new attachment point>. ==================================== > @attach refrigerator = here Action re-attached. ==================================== (Obvious-exit programs generally list exits in the order of first-attached to last-attached, or the reverse. Therefore, the order in which exits appear on the list can be changed by using @attach to re-attach exits to the room: the exit will then become the last-attached exit, and move to either the first or last position in the list.) To reiterate, exits have a source (the object to which they are attached) and a destination (the object to which they are linked). This means that they are *one way*. This point often causes confusion for new builders: in order to create a `door' between two rooms, one needs to create two exits, one leading in each direction. The following example illustrates this: Cashmere's Bachelor Pad has dbref #5784. From the Bachelor Pad, he will create a Bedroom, and then create two exits that make a door between the Pad and the Bedroom. ==================================== > @dig Cashmere's Bedroom Cashmere's Bedroom created with #5792. Parent set to BD's Environment Room(#4989RA). > @open Bedroom = #5792 Exit opened with number #5793. > bedroom Cashmere's Bedroom(#5792) > @open Out = #5784 Exit opened with number #5784. Linked to Cashmere's Bachelor Pad(#5784R). ==================================== Exits linked to things move the the thing to the point of attachment when used (rather than moving the user to the thing). ==================================== > @act getpup = me Action created with number #4684 and attached. > @link getpup = $pup Linked to Squiggy(#128629XZ). > getpup done > i You are carrying: Squiggy(#128629XZ) You have 10664 wet cats. ==================================== Exits' names can include `aliases', other names that can be used as the exit name. An existing exit can be renamed to include aliases with the @name command, or the aliases can be specified at the time of the exit's creation. ==================================== Renaming an existing exit... > @name bedroom = Bedroom <B>;bedroom;bed;b Name set. Creating an exit with aliases... > @open Out <O>;out;ou;o Exit opened with number #5785. ==================================== Each string separated by a ; semi-colon is an alias for the exit. In the `out' example above, typing either `out <o>', `out', `ou', or `o' would cause the player to use the out exit. Only the first name or alias is shown a list of obvious exits. The above examples follow the common and useful convention of supplying a `full' exit name along with a simple abbreviation in the first alias. ==================================== > look Cashmere's Bachelor Pad(#5784R) Obvious Exits: Bedroom <B> Out <O> ==================================== ***** Locks: Locks are expressions used to control the use of objects. The most common applications are to lock exits so that only some people can use them, and to `lock down' things that you need to leave lying about in rooms (a sign or bulletin board, for example). Just what constitutes `use' depends on the object type. To `use' a thing means to pick it up. To `use' an exit means to pass through it to another room, or to use it as a command. To `use' a room means to look at it. If an object is locked it may be used if and only if the lock expression is `true' for the player (or other object type) attempting to use the object. `True' in this context and in rather nontechnical terms means `if the triggering player/object "is" or "has" or "is owned by" a result of the lock expression'. A simple example... ==================================== > @lock closet = me Locked. > ex out = _/ lok /_/lok:Kenya(#75PBJM1) ==================================== A lock expression is stored as data type `lock' (see also section 2.1.2), as indicated indicated by the prefix `lok', and meaning that the server will evaluate the expression for `truth' in reference to the triggering player (or other triggering object type). The lock in this example evaluates to `database object #75'. If the player or other object trying to use the exit `is' #75 (that is, if it is Kenya), then the lock `passes': Kenya will be able to use the exit. Further, if the the triggering player/object `has' Kenya, the lock would pass: if Kenya were inside a vehicle object, the vehicle would be able to use the exit. And, if the triggering object `is owned by' Kenya, the lock would pass... for example, a puppet owned by Kenya would be able to use the exit. Property values as well as dbrefs can serve as lock expressions. The syntax for locking an object to a property value is `@lock <object> = <property> : <value>. ==================================== > @lock Powder Room = sex:female Locked. > ex out = _/ lok /_/lok:sex:female ==================================== If the triggering player/object `has' the property value `female' in her `sex' property, the lock passes: in other words, only females can use this exit. (When the system parameter `lock_envcheck' is tuned to `yes', the server searches rooms in the environment tree path for property matches on locks, as well as the triggering player/object. In this case, if room #0 were set sex:female, the lock in the above example would always pass.) Wildcard characters (see Section 2.1.5) may be used in locks for property values: ==================================== This exit may be used by males and females, but not fluffs... lyve wyth yt. > @lock Normal Folks Bar and Grill = sex:*ale Locked. ==================================== Lock expressions may contain or evaluate to multiple elements. The elements must be separated by an `or' operator (a | vertical bar) or by an `and' operator (an & ampersand), and may optionally be preceded by a `not' operator (an ! exclamation point). Player names may be used, provided that they are preceded by an * asterix pointer. ==================================== This exit may be used by either of two players, Passiflora or Kenya... > @lock Den of Iniquity = *passiflora|*kenya Locked. This exit may only be used by female staff members... > @lock Wizards' GunPowder Room = ~staff:yes&sex:female This exit may be used by anyone (or anything) who is *not* Stinker... > @lock Jessy's House = !*stinker This exit may only be used by someone who *is* Jessy and who *is not* Jessy. In other words, this lock alway fails... > @lock chair = *jessy&!*jessy ==================================== The operators have the following order of precedence: 1) ! not 2) | or 3) & and The order of precedence can be over-ridden by enclosing sub-expressions within (parentheses). ==================================== This exit may be used by all females and all others who are *not* Stinker... > @lock bar = !*stinker|sex:female This exit may be used by all who are *not* female and *not* Stinker... > @lock bar = !(*stinker|sex:female) ==================================== An object can be locked to a MUF program, in which case the program runs when someone uses or attempts to use the object, and the lock passes if the program returns a true *MUF* value (that is, MUF's truth conditions over-ride the truth conditions for locks). In MUF, a "" null string, the integer 0, the floating point value 0.00000, and the dbref for `nothing', #-1, are false. If the MUF program returns any of these values, the lock fails; if it returns any other value, the lock passes. A lock cannot include MPI (or rather, will invariably fail if set to an MPI string), but an object can be locked to a property value, and the property value on a relevant object can be set to an MPI string. ==================================== This exit may only be used on the stroke of midnight (or by someone who has figured out the lock and set his `time' property to `00:00:00')... > @lock time machine = time:00:00:00 Locked. > @set time machine = time:{time} Property set. ==================================== Locks may be removed with the @unlock command, syntax `@unlock <object>'. 2.3.1 Bogus Exits It is (or was) a relatively common practice to create lookable details and realism-enhancing actions in rooms by means of `bogus exits'... exits that do not lead anywhere. The exits can be given a description, so that doing `look <exit>' shows some detail of the room, and realistic messages can be put in the exit's @fail/@ofail or @succ/@osucc. ==================================== > @open Grandma's Rocker;grandmas rocker;rocker;chair;sit Exit opened with number #5797. > @lock chair = me&!me Locked. > @desc chair = An old, old rocker that has been in the family for generations. Description set. > @fail chair = You take a seat in the old rocker. Message set. > @ofail chair = takes a seat in the old rocker. Message set. ==================================== The above example creates a `virtual chair'. Though it will not appear in the room's Contents list, people can look at it, and can sit in it by typing `sit' (or any of its other aliases). Bogus exits have their place, but builders should be aware that there are other ways of accomplishing the same goals without creating a separate exit for each item (an approach that quickly leads to dbase bloat). Lookable details can created with `looktraps' (see section 2.2.2) and many events like the `sit' message in the above example can be handled by a single action (See section 3.1.2, MPI Examples). 2.3.2 Unsecured Exits An exit that is not linked to anything and not thoroughly locked is insecure, in that its ownership transfers to anyone who uses the exit and anyone can link it. This creates a minor security risk: someone could take control of an exit attached to one of your rooms and make it do something annoying or harmful. So, always secure exits. An exit can be secured by locking it to a condition that always fails -- such as `me&!me' -- or by linking it to something. All established MUCKS provide a `do-nothing' program, a program that produces no result, and thus serves as a convenient linking point for exits. Usually such a program is given the registered name `$nothing' or `$do-nothing'. ==================================== > @link sit = $nothing Linked to gen-nothing.muf(#363FLM2). ==================================== 2.3.3 Exit Priorities In our discussion of the environment tree, it was noted that the server searches up the environment tree for commands matching users' input. If more than one command with the same name is found, the server must resolve which command to execute. This is determined by the `priority' of the exits, and the order of the search path. Both are affected by the system parameter `compatible_priorities'. Wizards can set Mucker bits on exits as well as on programs and players. An exit with a higher Mucker bit runs at higher priority than an exit with a lower Mucker bit, or one with no Mucker bit. For example, suppose a MUCK has a global exit named `bank' linked to a program that gives players 100 pennies, and a player has an exit in his room named `bank' linked to a program that gives players 500 pennies. If neither exit has a Mucker bit set, both are considered `Priority 0' (zero). The *first* exit found in the search path would be executed: a player standing in the room with the `local' exit would receive 500 pennies; elsewhere, the global `bank' command would run, and the player would receive 100 pennies. However, if a wizard set the global `bank' exit M1, the global exit would now have higher priority. Even in the room with the `local' exit, typing `bank' would execute the global exit, and players would receive 100 pennies. As indicated, if there are two exits with the same name and the same priority, the server executes the *first* exit found. But the order of the search path changes depending on whether the system parameter `compatible_priorities' is set to `no' or `yes'. (Wizards may set system parameters with the @tune command.) If compatible_priorities is set to `no', all non-prioritied exits (i.e., exits with no Mucker bit set) are considered `priority 0', and the server uses the following search order: 1) On the room the player is located in 2) On objects the player's inventory 3) On objects in the room's inventory 4) On the player 5) Environment rooms containing the present room, beginning with the `closest' room... the room furthest from room #0 6) Room #0 7) The server If compatible_priorities is set to `yes', all non-prioritied exits are considered `priority 1', and the server uses the following search order: 1) On the room the player is located in 2) On the player 3) Environment rooms containing the present room, beginning with the `closest' room... the room furthest from room #0 4) Room #0 5) Objects in the player's inventory 6) Objects in the room's inventory 7) The server In our example, the wizard had just set the global `bank' exit M1, so it had a higher priority than the local M0 exit. If the wizard then did `@tune compatible_priorities = yes', both exits would now be considered `priority 1': the global is priority 1 because it is set M1, and the local is considered priority 1 because the system parameter is set to run all unprioritied exits at priority 1. The search order for players and inventories has changed, but in both cases the local room is checked before the global parent #0. So, now the local exit would run when player's type `bank' in the room with the local exit. If the wizard then set the global exit M2, it would again have higher priority than the local exit, and would run regardless of where a player is standing when typing `bank'. If you have difficulty getting a local or personal exit to run in preference to a global of the same name, contact a wizard and discuss modifying priorities, either by raising your exit's priority, or by changing the system parameter. 2.4 User-Created Commands We have used the term `user-created commands' throughout the manual to indicate soft-coded commands created by the wizards or players of a MUCK. Most other issues affecting such commands -- the command search path, exit priorities, locks, etc. -- have been discussed at various points above. Because efficient soft-coded commands can easily be added to a MUCK, the platform is highly customizable. In general, this is a Really Good Thing: MUCKs can be continually improved and tailored to their population's needs, without hacking the server code, which often results in bugs and invariably results in innumerable versions and patch levels of the program. On the downside, you can't assume that something you've learned on one MUCK will work exactly the same way on a different MUCK. And, the MUCK Manual cannot provide a complete reference for any given MUCK. Most MUCKs do share a core set of standard programs, libraries, and commands, distrubuted as the `standard database'. Discussion of the standard commands is provided in Section 2.7, User-Created Command Reference. The programming libaries are discussed in Section 3.2.4, MUF Library Reference. 2.5 Flag Reference A (Abode, Abate, Autostart) - On a Room: Anyone can set their home or the home of objects to the room. - On an Exit: The exit is lower priority than an exit without the ABATE flag. If compatible_priorties is tuned to `no', an abated exit's priority is `less than 0'; If compatible_priorites is tuned to `yes', an abated exit's priority is `less than 1'. - On a Program: The program will automatically be loaded into memory and executed when the MUCK starts or restarts. B (Builder, Bound or Block) - On a Player: Player can create and modify objects with the @create, @dig, @link, and @open. On most MUCKs, players start off with a B flag. A B flag on players is also called a `builder bit'. - On a Room: Personal exits (exits attached to a player) cannot be used in the room. - On a Program: Any functions within the program run in pre-empt mode. If the program set B is called by another program, multi-tasking status returns to that of the calling program when execution exits from the called program. (Only wizards can set and remove B flags.) C (Chown_OK, Color) - On any object except Players: Anyone can take control of the object with the @chown command (`change ownership'). - On a Player: MUCK output will be formatted with ASCII color, provided that the player's client handles color and that the text has been formatted with color (version 6.0+ only) D (Dark, Debug) - On a Room: Wizards and the owner of the room see all objects normally, but other players see only objects they own. If no objects would be seen, a `Contents' list is not appended to the description of the room. - On a Thing: The object does not appear in the room's `Contents' list. - On a Player: The player does not appear in the `Contents' list of rooms or in the WHO list. Only wizards may set players dark. - On a Program: A stack trace of internal program operations is printed out to anyone who uses the program. E (Exit) Type Flag: The object is an Exit/Action. F (MUCK Forth Program) Type Flag: The object is a program. H (Haven, HardUID) - On a Room: The `kill' command may not be used in that room. - On a Player: The player cannot be paged. - On a Program: The program runs with the permissions of the owner of the trigger, rather than with the permissions of the user of the program. When this is set in conjunction with the STICKY (SETUID, below) flag on a program, and the program is owned by a wizard, then it will run with the effective mucker level and permissions of the calling program. If the caller was not a program, or the current program is NOT owned by a wizard, then it runs with SETUID. J (Jump_OK) - On a Room: Players can teleport to and from the room (assuming other conditions for teleporting are met). If the MUCK is configured with SECURE_TELEPORTING, J indicates that exits attached to players and objects can be used to leave to leave the room, and !J indicates that they cannot. - On a Thing: The object can be moved by a program running at any Mucker level. - On a Player: The player can teleport to and from rooms (assuming other conditions for teleporting are met). K (Kill_OK) - On a Player: The player can be killed with the `kill' command. A player who is `killed' is simply sent home. L (Link_OK) - On a Room: Anyone can link exits to the room. - On a Program: The program can be called by any program, and can be triggered by actions and propqueues not owned by the owner of the program. M1 (Mucker Level 1) (See also section 3.2.1) - On a Player: The player is an `apprentice' Mucker. He can use the MUF editor and create M1 programs. - On an Exit: The exit runs at priority 1. - On a Program: The program runs with Mucker level 1 permissions. The program cannot get information about or send information to any object that is not in the same room. Some MUF primitives cannot be used. Program output to anyone except the triggering player is prepended with the triggering player's name. Instruction count is limited to about 20,000 instructions. The program follows permissions for protected props (see section 2.1.1). M2 (Mucker Level 2) (See also section 3.2.1) - On a Player: The player is a `journeyman' Mucker. She can use the MUF editor and create M2 programs. She can set the Mucker level of any program she controls to M1 or M2. - On an Exit: The exit runs at priority 2. - On a Program: The program runs with Mucker level 2 permissions. Some MUF primitives cannot be used. Instruction count is limited to about 80,000 instructions. The program follows permissions for protected props (see section 2.1.1). M3 (Mucker Level 3) (See also section 3.2.1) - On a Player: The player is a `master' Mucker. He can use the MUF editor and create M3 programs. He can set the Mucker level of any program he controls to M1, M2, or M3. - On an Exit: The exit runs at priority 3. - On a Program: The program runs with Mucker level 3 permissions. Almost all MUF primitives can be used. There is no absolute limit to instruction count, unless the program is running in PREEMPT mode. The program may over-ride the permissions for protected props. P (Player) Type Flag: The object is a Player. Q (Quell) - On a Player or Room: The Quell flag cancels the effects of a wizard flag. A wizard player set Q is effectively a normal player. A Q flag on a wizbitted room will cancel the realms-wiz powers of the room's owner. R (Room) Type Flag: The object is a Room. S (Silent, Sticky, SetUID) - On a Thing: The object will return to its home when dropped. - On a Room: The room's drop-to is delayed until all players have left the room. - On an Exit: If the exit is attached to a thing and linked to another thing <<<check>>> - On a Player: The player will not see dbrefs on things she owns, and will not see objects in a Dark room. Control is unchanged however. - On a Program: The program runs with the permissions of the owner of the program, and not those of the user. W(izard) - On a Room: The room's owner has Realms Wiz powers in that room and any rooms parented to it, provided that the MUCK's realms_control parameter is set to `yes'. - On an Exit: The exit runs at priority 4. - On a Player: The player is a wizard. Wizards have control over all objects in the database (although with some restrictions in their control over God and other wizards). Wizards can use restricted, wiz-only commands, and can set programs, rooms, and things W and B. Some wizard powers are enabled or disabled by the system parameter `god_priv'. - On a Program: The program is effectively Mucker level 4. All MUF primitives may be used, and do not have a maximum instruction count unless the program is running in pre-empt mode. X(forcicble) - On a Player or Thing: The player or thing may be forced by a player (or other object type) to which it is force_locked. V(ehicle) - On a Thing: The object is a vehicle. - On a Room: Vehicles may not enter the room. - On an Exit: Vehicles may not use the exit. Z(ombie) - On a Thing: The object is a Zombie: all output the Zombie sees or hears will be related to the controlling player. - On a Room: Zombies may not enter the room or be forced in the room. - On an Exit: Zombies may not use the exit. 2.6 Server Command Reference @ACTION | @ACT @action <name>=<source> [=<regname>] Creates a new action and attaches it to the thing, room, or player specified. If a <regname> is specified, then the _reg/<regname> property on the player is set to the dbref of the new object. This lets players refer to the object as $<regname> (ie: $mybutton) in @locks, @sets, etc. You may only attach actions you control to things you control. Creating an action costs 1 penny. The action can then be linked with the command @LINK. ~ @ARMEGEDDON @armedeggon Shuts down the MUCK without first doing a save. The primary purpose is to avoid over-writing the saved database if it becomes aware the current database is corrupt. (Wizard only) ~ @ATTACH | @ATT @attach <action> = <new source> Removes the action from where it was and attaches it to the new source. You must control the action in question. ~ @BOOT @boot <player> Disconnects a player from the game. If a player is connected more than once it affects the most recent connection. (Wizard only) ~ @CHOWN @chown <object> [=<player>] Changes the ownership of <object> to <player>, or if no player is given, to yourself. If the MUCK is compiled with PLAYER_CHOWN, all players are allowed to take possession of objects, rooms, and actions, provided the CHOWN_OK flag is set, with the following exception: mortals may @chown an exit if they own the object it is attached to, or an object it is linked to. Mortals cannot take ownership of a room unless they are standing in it, and may not take ownership of an object unless they are holding it. Wizards have absolute power over all ownership. ~ @CHOWN_LOCK | @CHLOCK @chown_lock <object> = <lock expression> Locks <object> such that it may only be chowned by players for whom <lock expression> is true. The object's CHOWN_OK flag must be set as well. Wizards may chown any item, regardless of its chown_lock. ~ @CONLOCK @conlock <object> = <lock expression> Locks <object> such that only those for whom <lock expression> is true may place things in or remove things from <object>. ~ @CONTENTS @contents [<object>] [= <flags/types> = [<output type>]] Searches the given object for items & exits that match the given flag string. For an explanation of the flags/types modifiers and the output types, see the help entry for @FIND. Example: `@contents here = DE = owner' will list all Dark Exits attached to your current location, giving the owner of each one. See also @FIND, @OWNED, @ENTRANCES ~ @CREATE @create <object> [=<cost> [=<regname>]] Creates a new object and places it in your inventory. This costs at least ten pennies. If <cost> is specified, you are charged that many pennies, and in return, the object is endowed with a value according to the formula: ((cost / 5) - 1). Usually the maximum value of an object is 100 pennies, which would cost 505 pennies to create. If a <regname> is specified, then the _reg/<regname> property on the player is set to the dbref of the new object. This lets players refer to the object as $<regname> (ie: $mybutton) in @locks, @sets, etc. Only a builder may use this command. ~ @CREDITS @credits Displays a screen listing the names of people and places who have contributed to TinyMUCK's development. ~ @DESCRIBE | @DESC @describe <object> [=<text>] Sets the description field of <object> to <text>. If <text> is not specified, the description field is cleared. This is the same as `@set <object> = _/de:[text]'. A description is what is seen when a player looks at something. ~ @DIG @dig <room> [=<parent> [=<regname>]] Creates a new room, sets its parent, and gives it a personal registered name. If no parent is given, it defaults to the first ABODE room down the environment tree from the current room. If it fails to find one, it sets the parent to the global environment, which is typically room #0. If no regname is given, then it doesn't register the object. If one is given, then the object's dbref is recorded in the player's _reg/<regname> property, so that they can refer to the object later as $<regname>. Digging a room costs 10 pennies, and you must be able to link to the parent room if specified. Only a builder may use this command. ~ DROP drop <object> Drops the <object> if you are holding it. It moves the object to the room you are in, unless its STICKY flag is set (in which case the object will go to its home), or the room has a drop-to (in which case the object will go to the room's drop-to). Programs are much like objects but are not affected by room droptos or STICKY flags. A `drop' message can be set, which will be shown to the player dropping the object, and an `odrop', which will be shown to the other players in the room. See @drop, @odrop. ~ @DROP @drop <object> [=<text>] Sets the drop field of <object> to <text>. If <text> is not specified, the drop field is cleared. The drop message on an object is displayed when you drop it. On an exit, it is displayed upon entering the destination room. On a player it is displayed to whoever kills them. On a room, it is displayed when an object is dropped there. This is the same as `@set <object> = _/dr:[text]' ~ @DUMP @dump [filename] Saves the database from memory to disk. Automatically occurs every three hours, and when @shutdown is used. It does slow down the server, so only use if you fear a server crash is iminent. If a filename is given, it will save the db to that file, and save any subsequent dumps to it as well. (Wizard only) ~ @EDIT @edit <program> Searches for a program and if a match is found, puts the player into edit mode. Programs must be created with @PROGRAM. ~ @ENTRANCES | @ENT @entrances [<object>] [= <flags/types> = [<output type>]] Searches through the database for items that you control linked to <object>. For an explanation of the flags/types modifiers and the output types, see the help entry for @FIND. Example: `@entrances here = ED = location' will list all Dark Exits that are linked to your current location, giving the location of each one. See also @FIND, @OWNED, @CONTENTS. ~ EXAMINE | EX examine <object> [=propdir] If you control <object>, examine will give you a complete breakdown of all fields, flags, etc., that are associated with the object. If the optional propdir field is supplied, then it instead lists out all the properties directly under that propdir. To list the base propdir of an object, use `ex <object>=/'. Program-executing fields are displayed as their true text, rather than executing the program in question. If you do not control <object>, however, it prints the owner of the object in question, and, again, displays the true text of the description. ~ @EXAMINE <<< check >>> ~ @FAIL @fail <object> [=<message>] <Object> can be a thing, player, exit, or room, specified as <name> or #<number> or `me' or `here'. Sets the fail message for <object>. The message is displayed when a player fails to use <object>. Without a message argument, it clears the message. This is the same as: `@set <object>=_/fl:[text]' See also @OFAIL, and @DESC. ~ @FIND @find [<name>] [= <flags/types> = [<output type>]] Searches through the database for items that you control matching <name>. Players control only objects they own; wizards control all objects, so @find searches the entire database when they use it. Flags or types can be specified, to specify that you only want to list objects that have that flag set, or that are of that type. You can also specify to list objects that are NOT of that specific type, or that do NOT have that flag. (A ! not operator before the modifier indicates that it is to be inverted.) The flags that you can specify are: (use the initial capitalized letter only) Abode, Builder/Block, Chown_ok, Dark/Debug, Haven, Interactive, Jump_ok, Kill_ok, Link_ok, Mucker, Quell, Sticky/Silent, Vehicle, Wizard, Xforcible, and Zombie. You can also specify Mucker Levels by the level number: 1, 2, 3, or 4. The types that you can specify are: (use the capitalized letter only) Exit, muF program, Garbage, Player, Room, and Thing. There are a few other modifiers you can specify: (use only initial character) Unlinked will specify that you want to list only unlinked objects. @ specifies to list objects longer than about 90 days old. ~size will match all objs whose current memory usage is greater than or equal to size bytes. This must be the last modifier in the list of modifiers. ^size will match all objs whose total memory usage, when fully loaded, is greater than size bytes. To do this, it loads the entire object into memory from disk. This modifier is only available to wizards. For regular players, this acts like ~size. This must be the last modifier in the list of modifiers. The output types that can be given are owners, links, size, count, & location. (You use the whole name for output type, and you can use only one at a time.) owners lists who owns each object. links shows what each object is linked to, or *UNLINKED*, or, for exits linked to multiple things, *METALINK* size displays how much memory is currently being used by an object. If this option is used with the ^ modifier, (see above) then this will display the true full size of the object, and not just how much is currently being used. count causes nothing to be shown but how many objects the @find/etc would match. ie: it doesn't display any of the matched objects. location shows where the object is located at. The matching on names is as follows: Individual words can be matched as {word1|word2|...} Individual characters can be matched as [abc...] A ? matches any character. A * matches any number of characters, including none. Any of these special charcters can be matched by putting a \ before it. Examples of use: "@find north = EU = location" will find all of your unlinked exits named "north" and print them along with their locations. "@find {big|little} = R!L" finds all your rooms whose names contain "big" or "little" and are not LINK_OK. "@find w[ei]ll" will find everything you control whose name contains "will" or "well." "@find =E=links" will list all exits that you control, and display where they are linked to. "@find button==locations" will list all objects you control with `button' in the name, and it will display where thay are located at. "@find =~2000=size" will list all your objects whose current memory usage is 2000 bytes or more, and it will display their size. "@find =^2000=size" will, for a wizard, find all objects in the db that are 2000 or more bytes in total size, when fully loaded, and it will show their sizes. Note that this will load all of each object into memory to make the size determination. On some systems this can take a while, and on all systems this is an abuse to the diskbasing cache. Only Wizards may use this search feature. See also @OWNED, @ENTRANCES, @CONTENTS ~ @FORCE @force <player>=<command> Causes the game to process <command> as if typed by <player>. With the compile option GOD_PRIV, God cannot be forced by his/her sub-wizards. ~ @FORCE_LOCK @force_lock <object> = <lock expression> Locks <object> such that it may only be @forced by players or other object types for whom <lock expression> is true, provided that the XFORCIBLE flag is set. Wizards may force any object, except God. ~ GET get <object> Attempts to pick up <object>. The lock on <object> is checked for a success (true), and the normal path of success/fail is then taken. On success the object is placed in your inventory. Another variation on this is `get <container>=<object>', which attempts to get <object> from the given container. The _/clk lock property on <container> is tested, and if it is true, when it checks to see if the standard _/lok lock property on <object> tests true. If both locks pass, then <object> is moved into the player's inventory. If there is no _/clk property on <container> it defaults to failing. The _/lok property, on <object>, on the other hand, defaults to passing. @succ/@fail messages are not displayed, when fetching something from a container. ~ GOTO, GO go[to] <direction>; go[to] home Goes in the specified direction. `go home' returns you to your starting location. The word `goto' or `go' may be (and usually is) omitted. `Move' is the same as `go'. ~ GIVE give <player> = <# pennies> Gives <# pennies> to <player>. For mortals, <# pennies> must be a positive number less than or equal to the number of pennies they have. <# pennies> is subtracted from the giving player's inventory. Wizards may give any amount from positive MAX_INTEGER to negative MAX_INTEGER. Giving does not affect the wizard's own inventory of pennies. ~ GRIPE gripe <message> Sends <message> to the system maintainer. Gripes are logged for later reference; also, if the system maintainer is connected he will receive the gripe real-time when the gripe is made. ~ HELP help <topic> Shows an online help document for <topic>. `Help' with no argument shows a brief list of basic commands. `Help help' shows a list of index topics. ~ HOME home Sends you home, no matter where you are. You retain your pennies, but any objects you are carrying leave your inventory and return to their own homes. ~ @IDESCRIBE @idescribe <object> [=<text>] Sets the idescription field of <object> to <text>. If <text> is not specified, the description field is cleared. This is the same as `@set <object>=_/ide:[text]' An idescription is what is seen on the inside of a vehicle, when a player inside it looks around. ~ INFORMATION information <topic> Shows an online document about <topic>. If no topic is supplied, a list of available topics will be shown. ~ INVENTORY | INV | I inventory Lists what you are carrying. This can be abbreviated to inv or i. ~ KILL kill <player> [=<cost>] A successful kill sends the player home, sends all objects in the player's inventory to their respective homes. The probability of killing the player is <cost> percent. Spending 100 pennies always works except against Wizards who cannot be killed. Players cannot be killed in rooms which have the HAVEN flag set. On systems where the KILL_OK flag is used, you cannot kill someone unless both you and they are set Kill_OK. ~ @KILL @kill <processid|playername|programdbref|"all"> If passed a processid (a number without a `#' preceeding it), it will kill the given process, if the player controls it. If passed a player name, it will kill all the processes controlled by that player. If passed a program dbref, it will kill all processes that that program is running in. If the argument passed is "all", and the player is a wizard, it will kill all processes on the timequeue. ~ LEAVE leave Leave the vehicle you are currently inside. ~ @LINK @link <object1>=<object2> [; <object3>; ... <objectn> ]. Links <object1> to <object2>, provided you control <object1>, and <object2> is either controlled by you or linkable. Actions may be linked to more than one thing, specified in a list separated by semi-colons. ~ @LIST @list <program> [=[line1] [-] [line2]]. Lists lines in a program, provided you control it or it is LINK_OK. Zero, one, or two line numbers may be specified, denoting the range of lines to list. If no lines are given, the entire program is listed. ~ @LOCK @lock <object> = <key> Locks <object> to a specific key(s). <Object> can be specified as <name> or #<number>, or as `me' or `here'. Boolean expressions are allowed, using `&' (and), `|' (or), `!' (not), and parentheses (`(` and `)') for grouping. To lock to a player, prefix their name with `*' (ex. `*Igor'). A key may be a player, an object, or `property:value'. ~ LOOK look <object> Looks around at the current room, or at <object> if specified. For players, displays their description and inventory, for things, their description, and for rooms, their name, description, succ/fail message, and contents. Also triggers osucc/ofail messages on rooms. Programs are triggered accordingly on desc/succ/fail fields. ~ MAN man [<subject>] Displays the programmer's manual or a quick reference for MUF. ~ MOVE See GOTO. ~ MPI [<subject>] Displays the programmer's manual or a quick reference for MPI. ~ @NAME @name <object>=<name> [<password>] Sets the name field of <object> to <name>. <Name> cannot be empty; a null name is illegal. <Password> must be supplied to rename a player. Wizards can rename any player but still must include the password. ~ @NEWPASSWORD @newpassword <player> [=<password>] Only wizards may use this command. Changes <player>'s password, informing <player> that you changed it. Must be typed in full. If GOD_PRIV was defined, nobody can change God's password. ~ NEWS news [<topic>] Displays the current news file for the game. Must be typed in full. If a topic is given, then it displays the information on that specific topic. ~ @ODROP @odrop <object> [=<text>] Sets the odrop field of <object> to <text>. If <text> is not specified, the odrop field is cleared. Odrop on an object is displayed prefixed by the player's name when s/he drops that object. On an exit, it is displayed upon a player's arrival to the destination room (or the location of the destination player). On a player, it is displayed after the `name killed victim!' message. On a room, it is displayed when an object is dropped there, prefixed by the object's name. This is the same as: `@set <object>=_/odr:[text]' See also @DROP. ~ @OECHO @oecho <vehicle object> = <echo prepend string> Sets a string to prepend to messages relayed to the interior of a vehicle in the vehicle's _/oecho property. By default, the name of the vehicle object, followed by a > greater than character is used to prepend such messages. ~ @OFAIL @ofail <object> [=<message>] The @ofail message, prefixed by the player's name, is shown to others when the player fails to use <object>. Without a message argument, it clears the message. <object> can be specified as <name> or #<number>, or as `me' or `here'. This is the same as: `@set <object>=_/ofl:[text]'. See also @FAIL. ~ @OPEN @open <exit> [=<object> [; <object2>; ... <objectn> ] [=<regname>]] Opens an exit in the current room, optionally attempting to link it simultaneously. If a <regname> is specified, then the _reg/<regname> property on the player is set to the dbref of the new object. This lets players refer to the object as $<regname> (ie: $mybutton) in @locks, @sets, etc. Opening an exit costs a penny, and an extra penny to link it, and you must control the room where it is being opened. ~ OUTPUTPREFIX OUTPUTPREFIX [string] Must be in all capitals, and typed in full. Prints the given line before the output of every command, setting them apart from other messages. ~ OUTPUTSUFFIX OUTPUTSUFFIX [string] Must be in all capitals, and typed in full. Prints the given line after the output of every command, setting them apart from other messages. ~ @OSUCCESS @osuccess <object> [=<message>] The @osuccess message, prefixed by the player's name, is shown to others when the player successfully uses <object>. Without a message argument, it clears the @osuccess message. It can be abbreviated @osucc. <Object> can be specified as <name> or #<number>, or as `me' or `here'. This is the same as `@set <object>=_/osc:[text]' See also @SUCCESS. ~ @OWNED @owned <name> [= <flags/types> = [<output type>]] Searches through the database for items that <name> controls. For an explanation of the flags/types modifiers and the output types, see the entry for @FIND. Example: `@owned Revar=F!L3=location' will list all Mucker Level 3 (3) programs (F) owned by Revar, that are NOT set Link_OK (!L), and it will show the location of each one. Note that only wizards can do an @owned on other people. See also @ENTRANCES, @FIND, @CONTENTS. ~ PAGE page <player> [= <message>] This tells a player that you are looking for them. They will get a message in the form of `You sense <pager> is looking for you in <location>.' A <message> is optional, and is delivered in the form of `<pager> pages: <message>.' Your location is not revealed in message pages. If a player is set HAVEN, you cannot page them, and they will not be notified that you tried. You will instead be told, `That player does not wish to be disturbed.' (Note: Most systems use a user-created program with a global `page' action, which takes the place of the built-in `page' command, and has more features.) ~ @PASSWORD @password <old password> = <new password> This changes your password. ~ @PCREATE @pcreate <player> = <password> Only wizards can use this command. This command creates a new player. It may only be used if REGISTRATION is enabled. ~ @PECHO @pecho <puppet object> = <echo prepend string> Sets a string to prepend to messages relayed from a puppet in the puppet's _/pecho property. By default, the name of the puppet object, followed by a > greater than character is used to prepend such messages. ~ POSE pose <message> or :<message> Causes <message> to be displayed to the room, prepended by your name. ~ @PROGRAM @program <program> Create a new program, or enter edit mode on an existing one. See also @EDIT. ~ @PROPSET @propset <object> = <data type> : <property> : <value> Sets <object's> <property> to <value>, with <value> stored as data type <data type>. You must control <object>. ~ @PS @ps Lists the status of the currently running MUF program processes. This lists all processes for a Wizard. Non-Wizards only see the muf processes that they can @kill. See @KILL. ~ PUT server: put <object> user-created: put <object> in <container> The server version of `put' is an alias of `drop'. The standard start-up database supplies a user created command that moves <object> into <container>, provided that you are holding both <object> and <container> and that <container> is not @conlocked against you. ~ QUIT QUIT Must be in all capitals, and typed in full. Logs out of your character and leaves the game. Your character remains at the location you are in when you log out, although it might be moved elsewhere while you are `asleep.' ~ READ read [<object>] An alias for `look'. The standard start-up dbase supplies a bulletin board program for which `read' is a command to read bulletins. ~ @RECYCLE @recycle <object> Destroy an object and remove all references to it within the database. The object is then added to a free list, and newly created objects are assigned from the pool of recycled objects first. You *must* own the object being recycled, even wizards must use the @chown command to recycle someone else's belongings. ~ @RESTRICT @restrict <on|off> Turning restriction on allows only wizards to log onto the MUCK. Turning it off returns to unrestricted access. ~ ROB rob <player> Attempts to steal one penny from <player>. The only thing you can steal are pennies. ~ @sanchange <<< check >>> ~ @sanity <<< check >>> ~ @sanfix <<< check >>> ~ SAY say <message> or "<message> Says <message> out loud. See also POSE, PAGE, and WHISPER. ~ SCORE score Displays how many pennies you are carrying. ~ @SET @set <object> = [!] <flag> -or- @set <object> = <property> : [ <string> ] -or- @set <object> = : @set does one of three things on TinyMUCK, it can modify flags, add properties to an object, or remove properties from an object. Using the first format, you may set flags, which are: WIZARD, LINK_OK, DARK [DEBUG], FILTER, STICKY [SETUID], JUMP_OK, BUILDER [BOUND], QUELL, CHOWN_OK, HAVEN [HARDUID], ABODE [AUTOSTART], VEHICLE, ZOMBIE, or Mucker. You can also set the Mucker (or Priority) Level of an object by using 0, 1, 2, or 3 as the flag name. An optional flag which may or may not be on a given site is KILL_OK. The second format sets <property> on <object> to <string>, or if <string> is not given, removes <property>. The third format removes all properties from an object. ~ @SHUTDOWN @shutdown Only wizards may use this command. Shuts down the game. Must be typed in full. ~ @STATS @stats [<player>] For mortal players, returns the highest number in the database, which includes garbage that has been generated with @recycle. For Wizards, gives this number as well as a breakdown of each type of object: rooms, exits, things, programs, players, and garbage. Wizards may also specify <player> which returns a similar display limited to the possessions of <player>. ~ @SUCCESS @success <object> [=<message>] Sets the success message for <object>. The message is displayed when a player successfully uses <object>. Without a message argument, it clears the message. It can be abbreviated @succ. <object> can be specified as <name> or #<number>, or as `me' or `here'. This is the same as `@set <object>=_/dr:[text]' See also @OSUCCESS. ~ @SWEEP @sweep [<object>] Returns a list of objects that are listening to <object>. <Object> defaults to `here'. TAKE See GET. ~ @TELEPORT @teleport <arg1> [=<destination>] Moves <arg1> to <destination>, if <destination> is not given, moves you to <arg1>. Wizards may teleport anything to anywhere, provided it makes sense, and mortals are allowed to do two things: teleport rooms to change their parent fields, and the may teleport things to a room they can link to, provided they control either the thing or its location. ~ THROW server: put <object> user-created: throw <object> to <player> The server version of `throw' is an alias of `drop'. Most MUCKs have a user-created program that moves <object> to <player>, announcing the move, provided that you are holding <object> and that you, <object>, and <player> are each configured in such a way as to allow the throw. ~ @TOAD @toad <player1> = <player2> Only wizards may use this command. Turns <player1> into a slimy toad, destroying their character. All possessions of <player1> are @chowned to <player2>. Must be typed in full. ~ @TRACE @trace <object> [=<depth>] Starts with <object> and traces all location fields, until the global-environment room is reached or the optional <depth> is specified. This is generally useful for finding which rooms are parents in your heirarchy. If you cannot link to a particular location its name is replaced by stars ***. ~ @UNCOMPILE @uncompile <program> Uncompiles all programs in the database. (Wizard only) <<<more>>> ~ @UNLINK @unlink <exit>; @unlink here Removes the link on the exit in the specified direction, or removes the drop-to on the room. Unlinked exits may be picked up and dropped elsewhere. Be careful, anyone can relink an unlinked exit, becoming its new owner (but you will be reimbursed your 1 penny). See @LINK. ~ @UNLOCK @unlock <object> Removes the lock on <object>. See @LOCK. ~ @USAGE @usage A Wizard only command that gives system resource usage stats for the muck server process. ~ @WALL @wall <message> Only wizards may use this command. Shouts something to every player connected. Must be typed in full. ~ WHISPER whisper <player>=<message> Whispers the message to the named person, if they are in the same room as you. No one else can see the message. Wizards can whisper *<player>=<message> to whisper to players in other rooms. (Note: Some systems use a user-created program in place of the built in whisper command. These programs generally provide many more useful features.) ~ WHO WHO [<player>] Must be in all capitals, and typed in full. Lists the name of every player currently logged in, and how long they have been inactive. If given a player name, it displays only the matching names and idle times. Wizards also get a display of the host the player is connected from. 2.7 User-Created Command Reference This section provides a quick reference for user-created commands supplied in the standard start-up database (std-db.db). On many MUCKS, several or many of these programs will have been modified or replaced with versions that are either more recent or more specifically attuned to the needs of the MUCK. So syntax may very slightly, but you can reasonably expect to find a command that offers similar functionality. An established MUCK will have many other user-created commands besides these. Most user-created commands follow the convention of a #help argument: typing the command name followed by `#help' will display a help screen. ~ 3WHO 3who Displays the names, online times, and idle times of all players online, formatted to three columns. A less spammy alternative to WHO. ~ @ARCHIVE | @ARC @archive <object> The archive command essentially `decompiles' an object, outputting all commands and settings that would be necessary to recreate the object. The output is simply printed to your screen: it is not saved in some archive on the MUCK. In order to use an @archive dump, you will need to capture the output, by cutting and pasting, logging with a client, or some other means, and save it to a file on your own computer. The archived object can then be recreated by quoting or pasting the file into a MUCK window. The most common uses of archive dumps are porting objects between MUCKs or keeping a back-up copy offline to safeguard against loss or corruption of the MUCK's dbase. The @archive command is recursive: <object> and anything contained by <object> will be archived. Thus, for example, an entire area can be dumped to an archive file by archiving the parent room: all rooms, exits, things, and programs in the area that are owned by the archiving player will become part of the dump. For non-wizzes, @ wiz props will not be part of the dump. Restricted props (props that begin with a ~ tilde) will be included in the dump, but will not be recreated when the archive is quoted, unless the player is a wizard at that time. Also, exits leading to and from archived rooms or areas cannot be linked when the archive is quoted (though exits within an archived area can). It is normal to see various `Huh?' and `Permission denied' messages when recreating an object from an archive dump. MPI and MUF that contains hard-coded dbrefs create another portability concern. A dbref, naturally, refers to a specific object on a specific MUCK. The corresponding object on a different MUCK will have a different dbref. To some extent, this problem can be reduced by using pointers and registered names when building, describing, coding, etc. For example, if you (player #123) used a personal version of {look-notify} ... {null:{tell:[ {name,me} looked at you. ],#123}} ... the code would result in a `Permission denied' error if you recreated yourself on another MUCK via an archive dump. This could be avoided with either of the following versions: {null:{tell:[ {name,me} looked at you. ],this}} {null:{tell:[ {name,me} looked at you. ],*Jessy}} Recreating objects from archive dumps usually sets temporary registered name values on your character, in propdir _reg/tmp. It is not necessary to do anything with this directory, but to conserve dbase space, you might want to remove it after doing a large dump: @set me = _reg/tmp: ~ @BANSITE @bansite sitename Prevents logins from the given site. @bansite !sitename Re-allows logins from the given site. @bansite #list Lists all the sites that are banned. This command prevents anyone from logging onto the MUCK from a banned site. Along with @toad, it is used to prevent problematic players from connecting to the MUCK. For example, a player might be toaded for violations of the MUCK's acceptable use policy, but continue to log on via guest and alternate characters. @Bansite prevents this by locking out connections from a given site regardless of the character. Sites may be specified by host name or numeric address. Wildcards may be used in the site name and address. Wildcards are frequently necessary, since many ISP's use dynamic host names for customer's connected via SLIP or PPP connections. For example, an examination of a problematic player's @/ directory might show that the last host used was: user12.vnn.luser.com Doing `@bansite user12,vnn.luser.com' would be ineffective: in all likelihood, the first one or two sections of the hostname would different the next time the player logged on. In lieu of @bansite user12.vnn.luser.com ... one should do @bansite *vnn.luser.com or, @bansite *luser.com @Bansite has two disadvantages: it locks out other players who connect from the same site, and it offers no protection against players who you want to ban but can log on from alternate sites. (Wizard only) ~ BOOTALL bootall Boots all but your lowest numbered connection. This program does not work well. Cmd-@bootme, which is widely available, is a better choice. ~ CHANGE change <object>=<propname>:/<old>/<new> or change <object>=<mesgtype>;/<old>/<new> Edits a property or message, replacing string <old> with string <new>. The syntax varies slightly -- that is, it uses either a : colon or ; semi-colon delimiter -- for `messages' and properties. `Messages' are `name', `desc', `succ', `osucc', `fail', `ofail', `drop', and `odrop'. Examples: change here=_arrive/check:/444/445 change me=desc;/top hat/beret ~ @CHECK @check Checks all objects in your location for completeness, with the definition of `complete' being adjusted as appropriate for the object type. For example, exits are checked for links, descs, succs, osuccs, and odrops. Locked exits are checked for fails and ofails. The command prints a list of objects that need further work. ~ @DOING @doing <msg> Sets your `sticky do' string... that is, the message shown with your name and times with commands `WHO' and `whodo'. The original purpose of doing strings was to provide a way for people to let others know what they are doing at the moment. Rather than frequently updating their doing string, most players set it to some (presumably) witty or interesting `bumpersticker' type remark, and leave it. ~ EDIT edit <object> = <prop> or, edit <object> = @<mesgtype> This unusual and useful command is works only with the TinyFugue UNIX client. Entering the command and parameters pulls the value of <prop> or <mesgtype> into your client window, with the cursor positioned for editing. You can then use arrow keys, backspace, etc., to edit the property value. Pressing `enter' stores the new property value. <Mesgtype> can be `name', `desc', `succ', `osucc', `fail', `ofail', `drop', or `odrop'. Before using `edit', you must define the following TinyFugue macro: /def -fg -p100 -t"##edit> *" = /grab %-1 Like most TinyFugue settings, this setting is `volatile'... It is not retained between TinyFugue sessions. To avoid having to redefine it each time, put the definition line in TinyFugue's resource file, .tfrc, so that it will be defined each time you start the client. ~ FETCH | RETRIEVE | GRAB fetch <object 1> from <object 2> Removes <object 1> from <object 2>. You must be carrying <object 2> and it must not be locked or container_locked against you. The object names do not have to be typed completely: partial strings sufficient to distinguish the object from others you are carrying will work. See also `put', below. ~ LOOKAT lookat <player's> <object> Does a `possessive look', showing you the description of an object being carried by another player. Example: `lookat tarka's linux club pin' ~ LSEDIT | LSE lsedit <object> = <list name> Puts you in an interactive list editor. See sections 2.1 and 4.1 for more information. ~ LSPROP | LSP lsprop <object> [= <path>] Lists all properties on object and their values. If a path is specified, it will list only properties on that path. Examples: lsp me ............ Lists all properties on your character lsp me = _descs ... Lists all properties in your _descs/ directory ~ PROPCP | CP cp <object 1>=<prop 1>,<object 2>=<prop 2> Copies <prop 1> and its value from <object 1> to <prop 2> on <object 2>. Example: cp out=_/sc,#1344=_/sc (Copies the @succ of exit `out' to another exit) Propcp handles partial input well. If <object 1> is omitted, it assumes <prop 1> is on the user. If <object 2> is omitted, it assumes the destination object is the same as <object 1>... i.e., that you are copying from one prop to another on the same object. If <prop 2> is omitted, is assumes that it is the same as <prop 1>... i.e., that you are copying a property on one object to the same property on a different object. If more information is omitted, the program will ask for it. Rather than providing all -- or even any -- of the information on the command line, one can simply to `cp', and respond to prompts. You must control both objects. See also `propmv', below. ~ PROPMV | MV mv <object 1>=<prop 1>,<object 2>=<prop 2> Moves <prop 1> and its value from <object 1> to <prop 2> on <object 2>, erasing <prop 1>. Example: mv out=_/fl,out=_/ofl (Moves the fail message on `out' to the ofail) Propmv handles partial input well. If <object 1> is omitted, it assumes <prop 1> is on the user. If <object 2> is omitted, it assumes the destination object is the same as <object 1>... i.e., that you are moving a value from one prop to another on the same object. If <prop 2> is omitted, is assumes that it is the same as <prop 1>... i.e., that you are moving a property on one object to the same property on a different object. If more information is omitted, the program will ask for it. Rather than providing all -- or even any -- of the information on the command line, one can simply to `mv', and respond to prompts. You must control both objects. See also `propcp', above. ~ @PURGE @purge <player> = yes Recycles all of <player's> belongings. Most often, this will be used before @toad. It is a good idea to do `@owned <player>' first, and make suitable arrangements. Exits leading to rooms owned by the player will become unlinked, and should either be relinked or recycled. Public building -- or rooms that are jointly used by the player and others -- should be chowned to someone else. Mortals my purge only themselves; wizards may purge any player. ~ PUT | REPLACE | STUFF put <object 1> in <object 2> Moves <object 1> from your inventory into the contents of <object 2>. You must be holding both objects, and <object 2> must not be locked or container_locked against you. Partial names for both objects will work. See also `fetch', above. ~ READ | WRITE | EDIT | ERASE | PROTECT read Show the headers of all posted messages. read new Show headers of all mesgs less than 2 days old. read recent Show headers of all mesgs after last read mesg. read KEYWORD Show headers of all mesgs with matching KEYWORD. read -DAYS Show headers of all mesgs fewer than DAYS old. read MESGNUM Read the mesg referred to by the given mesg number. read - Read the next mesg, after the last one you read. read -recent Read all mesgs after last read mesg, in one long dump. write Post a message. Prompts for subject and keywords. write SUBJECT Post a mesg with given SUBJECT. Prompts for keywords. write SUBJECT=KEYWRDS Post a message with given SUBJECT and KEYWRDS. erase MESGNUM Lets message owner erase a previously written mesg. editmesg MESGNUM Lets message owner edit a previously written mesg. protect MESGNUM Lets a wizard protect a mesg from auto-expiration. These commands operate bulletin boards that run from the standard bulletin board program, gen-mesgboard. `Read' is also an in-server alias for `look'. So, in a room that contains a bulletin board, `read' is a bulletin board command; elsewhere, it does a look. @REGISTER | @REG @reg <object> = <registered name> @reg #me <object> = <registered name> @reg #prop <target object>:<propdir> <object> = <registered name> Sets a registered name for <object>. (See section 2.1.4 for more information on registered names). The default format, @reg <object> = <registered name>, creates a global registered name by setting property _reg/<reg name> on room #0 with the value of <object's> dbref. Because a property is being set on room #0, this will result in a `Permission denied' error for a mortal player (unless she happens to own room #0). The following example, which would be typed by a wizard, gives the gen-nothing program the registered name of `nothing'; players would then be able to link actions to it by typing `@link <action> = $nothing' @reg gen-nothing = nothing Registered names can be set on your character rather than on room #0, in which case they will be be meaningful only for you, by using the #me argument. The following example, which could be typed by a mortal, or by a wizard who wants to create a personal registered name, gives a puppet object the registered name `pup'. It could then be specified by `$pup', rather than its dbref, as in `@tel $pup = me'. @reg #me squiggy = pup You can over-ride the target propdir, from _reg/ to anything else, by using the #prop argument. If the target propdir does not begin with _reg/ , the setting will not be usable as a registered name; this would simply be a way to set a property with data type dbref rather than string (which could also be accomplished with @propset). The following example sets property _disconnect/cleanup in the current room with the dbref of program gen-sweeproom. @register #prop here:_disconnect gen-sweeproom=cleanup The same thing could be accomplished by typing: @propset here=dbref:_disconnect/cleanup:<#dbref of gen-sweeproom> This example makes a setting in sub-propdir _reg/lib, giving the lib-strings library program the registered name $lib/strings. @register #prop #0:_reg/lib lib-strings = strings The same thing could be accomplished by typing: @register lib-strings = lib/strings or, @propset #0 = dbref:_reg/lib/strings:<#dbref of lib-strings> @RESTART @SIZE SWEEP UPTIME @VIEW @WHEN who ***************************************************************************** 3.0 PROGRAMMING MUCK supports two online programming languages, MPI and Muck FORTH, or MUF. MPI is an interpretted language (you don't need to compile it; the server reads the code directly) available to all players. MUF is a compiled, stack-based language, available to players who have a Mucker bit (an M flag). In general, MPI is better for one-off programming tasks and customizing messages (descriptions, exit @osucc's, etc.). MUF is better for public utilities and tasks that involve modifying the database. 3.1 Overview: MPI MPI is an interpretted language with a LISP-like syntax available to all players. Because it is universally available, MPI includes security features that restrict its power. In general, MPI can only read props on the triggering object and on objects controlled by the controller of the object on which the MPI string is stored, and can only set props on the latter... on objects controlled by the owner of the MPI object. Other than setting props as mentioned, MPI cannot modify the database to any significant extent, but is ideally suited for message-handling. And because it is interpretted, it is well-suited for one-off programming tasks: no separate compiling and linking operations are needed, nor is a separate program object for holding the code. MPI's syntax consists of nested functions enlcosed in curly braces that are evaluated left-to-right and `from the inside out'. For example... {if:{eq:{ref:me},#1},Hey it's God!,Hello world!} The MPI parser will first evaluate the innermost function, {ref:me}. The {ref} function returns the dbref of its single argument -- which in this case is `me' -- so {ref:me} returns the user's dbref. The returned expression `replaces', as it were, the function. So, if the user's dbref were #123, the MPI string would in effect and at this moment be... {if:{eq:#123,#1},Hey it's God!,Hello world!.} Then the next-innermost expression, effectively {eq:#123,#1}, would be evaluated. The {equals} function returns true if the two arguments supplied are the same; otherwise it returns false. In this case, the two arguments are not the same, so {equals} will return false. At this point, the MPI value for false -- the string "0" -- will replace the function. (A "" null string is also false in MPI. Any value other than "" or "0" is considered true.) So, at this point the string would in effect be... {if:"0",Hey it's God!,Hello world!} Finally, this, the outermost function will be evaluated. The {if} function takes three arguments. If the first argument is true, it returns the second argument. If the first argument is false, it returns the third argument. In this example, the first argument is false, so the the third argument will be returned: MPI will return the string "Hello world!" to player #123. If God had triggered the string, the {if} test would have been true, and the string "Hey it's God!" would have been returned instead. The {debug} function displays the progress of the parser. Enclosing the whole of our example in a {debug} function will show the process described above. ==================================== > @succ testmpi = {debug:{if:{eq:{ref:me},#1},Hey it's God!,Hello world!}} Message set. > testmpi (@Succ) {IF:"{eq:{ref:me},#1}", "Hey it's God!", "Hello world!"} (@Succ) {EQ:"{ref:me}", "#1"} (@Succ) {REF:"me"} (@Succ) "me" (@Succ) {REF:"me"} = "#123" (@Succ) "#1" (@Succ) {EQ:"#123", "#1"} = "0" (@Succ) "Hello world!" (@Succ) {IF:"{eq:{ref:me},#1}", "Hey it's God!", "Hello world!"} = "Hello world!" Hello world! ==================================== In the lines from the first half of the debugging output -- where indentation is moving to the right -- the parser is essentially finding the innermost, left-most function to be evaluated. The remaining portion, with lines ending in `= <value>' and indentation moving back to the left, depicts the series of returned expressions described above. * The keywords `me' and `here' can be used as normal. In addition, MPI supports the keyword `this', which will be replaced by the dbref of the object on which the MPI is stored. * The variable functions {&cmd}, {&arg}, and {&how} may be used to retrive information about how MPI was triggered. {&cmd} stores the command name typed to trigger the MPI. {&arg} stores any arguments typed along with the command. {&how} stores the method by which MPI was triggered, and will have values such as "(@desc)", "(@succ)", "(@osucc)", "(@lock)", etc. * Functions can be nested up to 26 levels deep. Loops may iterate a maximum of 256 times, at which point the automatically exit. Lists may have a maximum of 256 lines, or 4096 characters, whichever is less. * An MPI string that appears in a _/ prop (a @succ message, a @desc, and so forth) will be parsed automatically. For other triggering props, the parser must be called by an & ambersand at the beginning of the prop string. ==================================== > @set me=_oarrive/aaa:&{null:{otell:pads in quietly.}} Property set. ==================================== * The arguments of functions are separated by commas. Commas appearing in argument strings will confuse the parser: functions will seem -- to it -- to have too many arguments. So, commas in argument strings must be `escaped'... i.e., preceded with a \ backslash escape character, which tells the parser to treat the next character literally, rather than as an MPI instruction. For example, if we wanted our first example to say "Hey, it's God!" or "Hello, world!", the commas would need to be escaped with a backslash character. {if:{eq:{ref:me},#1},Hey\, it's God!,Hello\, world!} * Complex or very long MPI instructions are often better stored in a list, where whitespace can be used to make the code more readable, rather than in a single prop where all will run together in an opaque mass of characters. A simple pointing string using the {lexec} (`execute list') function can then be placed in the triggering prop. ==================================== > lsedit harley = bikecode < Welcome to the list editor. You can get help by entering `.h' > < `.end' will exit and save the list. `.abort' will abort any changes. > < To save changes to the list, and continue editing, use `.save' > > {null: > {if: > { > (lots of complicated really cool motorcycle code goes here) } > } > } > .end < Editor exited. > < list saved. > > @succ ride harley;ride motorcycle;ride cycle = {lexec:bikecode} Message set. > ride harley (The Harley does something amazing.) ==================================== The {lexec} function executes MPI in a list. The {exec} function executes MPI in a property, and thus provides another way to break code up into managable pieces. MUSH coders especially might find this method more intuitive. 3.1.1 MPI Macros 3.1.2 MPI Examples 3.1.3 MPI Reference DB Functions: ref name fullname owner location flags controls nearby money type istype contents exits links force dbequals locked testlock holds contains Connection Functions: online awake ontime idle List-Handling Functions: count mklist sublist lrand lunique lunion lcommon lremove lmember lsort commas Loop Functions: for while foreach filter parse fold Logical Functions: if equals notequals gt ge lt le not or and xor Property Handling: prop prop! exec exec! index index! store delprop list concat lexec rand select timesub propdir listprops Math Functions: increment decrement addition subtraction multiply divide modulo dice minimum maximum absolute sign distance Miscellaneous Functions: isnum isdbref version muckname muf debug debugif macros func delay kill fox String Functions: nl subst strlen smatch strip tolower toupper right left center instr midstr literal eval null pronouns tell otell Time Functions: time timesub lastused usecount modified created convsecs convtime date delay ftime ltimestr stimestr timestr secs tzoffset Variable-Handling Functions: variable &how &cmd &arg set with Definitions: A trigger object is the object that the MPI script is evaluated from. A list is a string containing several individual substring items, seperated by carriage return characters. A property based list is a set of consecutively numbered properties that each contain one string in a list of strings. Property based lists are often numbered like: listname1, listname2, listname3, listname4, etc. Another popular format is listname#/1, listname#/2, listname#/3, etc. MPI can read in either of those formats, and several more, for that matter. For logical constructs, a string value of "0", or a null string ("") are both considered false. Any other value is considered true. ABS|ABSOLUTE {abs:expr} Returns the absolute value of expr. ADD|ADDITION {add:expr1,expr2} {add:expr1,expr2,expr3...} Returns the sum of the values of expr1 and expr2. If more than two args are given, then this will add all the args together and return the result. AND|&& {and:expr1,expr2...} Returns true if expr1 and expr2 evaluate as true. Otherwise, this returns false. If expr1 was false, this doesn't bother to evaluate expr2, as it does C-style shortcutting. If there are more than two arguments, then this will evaluate all of them until either one returns false, in which case this function returns false, or until it has evaluated all of the arguments. This function returns true only if all the arguments evaluate as true. ARG|&ARG {&arg} The {&arg} variable contains a string with the value of the command line arguments the user entered. This is so that you can have stuff like MPI in the fail of an exit, and when the user triggers the exit, and has some extra text on the line they entered, other than the exitname, the MPI can take that extra stuff as arguments for use. Note that you need to set an action HAVEN to get it to accept command line arguments. AWAKE {awake:player} Returns how many times player is connected. This means that it will returns 0 if the player is not connected. If the given object is NOT a player, it will return 0. In all other cases, it will return a positive number, being how many times the given player is connected to the server. CENTER {center:string} {center:string,fieldwidth} {center:string,fieldwidth,padstring} Takes a string and pads it to fit the given fieldwidth, with the string center justified. If no padstring is given, it assumes that it will pad the string with spaces. If no fieldwidth is given, it assumes that the field width is 78 characters. Example: {center:Hello,10,1234567890} would return the string "123Hello12" COMMAS {commas:list} {commas:list,lastsep} {commas:list,lastsep,var,expr} Takes a list and returns a plain english comma delimited string with the items in it. For example, {commas:{mklist:Tom,Dick,Harry}} will return "Tom, Dick and Harry". If you specify the lastsep argument, you can replace the "and" with something else, such as "or" to get a result like "a, b or c". Note: You need to be careful to include spaces around the "or" or else you might get a result like "a, borc". Example: {commas:{mklist:a,b,c}, or } If the var and expr arguments are passed, then for every item in the list, it will set the value of the given variable name (which it will declare) to the item, then evaluate expr, and use the result in the string it outputs. Example: {commas:{contents:here},\, or ,v,{name:{&v}}} will return the name of every object in the room in a comma separated list, using ", or " as the final conjunction. ie: "Tom, Can of SPAM, Dick, or Harry." CMD|&CMD {&cmd} The {&cmd} variable contains the command name the user used, that caused the MPI to run. This is generally the exit name that the player triggered. For example, if the player typed `east', and triggered the exit named `east;e;out', which ran some MPI commands, the {&cmd} variable would have a value of "east". CONCAT {concat:listname} {concat:listname,obj} Returns a string, containing the concatenated lines of a property based list. It concatenates the list semi-intelligently, putting a single space between lines normally, and two spaces between lines when the previous one ended with a period, exclamation mark, or question mark. A property based list is a series of properties that are consecutively numbered. The server understands several different formats, and can also read in property lists in either the propnameX format, or the propname#/X format. It does NOT evaluate the contents of the list for embedded MPI commands. If no obj argument is supplied, then it looks for the list somewhere down the environment from the trigger object. Otherwise, it looks for the list down the environment from the given object. CONTAINS {contains:obj1} {contains:obj1,obj2} Returns true if obj1 is within obj2, or within anything it contains, or within anything they contain. If obj2 is not given, then it checks to see is obj1 is held by the player, or by anything they hold, etc. Basically, this just sees if obj1 is within the locational environment of obj2. CONTENTS {contents:obj} {contents:obj,type} Returns a list of the contents of the given object. If a second argument is passed to it, it restricts the listing to only those objects that are of the given type. Either the object must be nearby the trigger object, or else the owner of the trigger object must control the object. Otherwise this will error out with a Permission Denied error. The valid object type values are Room, Thing, Exit, Player, Program, and Bad. HINT: If you need to get a list of two types of objects from the room, just concatenate the lists from two calls to this function, with each object type you want. ie: {mklist:{contents:here,player},{contents:here,thing}} or {contents:here,player}{nl}{contents:here,thing} CONTROLS {controls:obj} {controls:obj,player} If one argument is given, then this returns true ("1") if the trigger object's owner controls the given object. If two arguments are given, then it returns true if the given player controls the given object. Otherwise, this returns false. ("0") Wizards control everything. CONVSECS {convsecs} Converts systime seconds into a readable time string. CONVTIME {convtime:string} Converts "HH:MM:SS Mo/Dy/Yr" format time string to systime seconds. COUNT {count:list} {count:list,sep} This counts the number of \r delimited items that are in the given list. This is effectively a list item count. If the sep argument if given, then it counts the number of sep delimited substrings in list. ie: The default sep is \r. (A carriage return.) CREATED {created:obj} Returns the systime when obj was created. DATE {date} {date:timezone} Returns a date string in the form mm/dd/yy. If the timezone argument is given, then it offsets the date returned by that number of hours. DBEQ|DBEQUALS {dbeq:obj1,obj2} Returns true if obj1 and obj2 refer to the same object. This does name matching, so {dbeq:*Wizard,#1} will return true if #1 is named Wizard. DEBUG {debug:expr} This will show MPI debugging information for all the commands within the given expression. This is useful for seeing why something isn't working. This returns the result of the evaluation of expr. DEBUGIF {debugif:condition,statement} If condition evals true, use debug mode is used when evaluating statement. Otherwise the statement is evaluated in regular mode. DEC|DECREMENT {dec:var} {dec:var,val} Decrements the value of the given variable by one, returning the result. If a value argument is given, then it will subtract that from the variable, instead of the value 1. DELAY {delay:secs,expr} Evaluates the given expression, then puts the result of that on the timequeue, to execute after the given number of seconds. At that time, the string is evaluated again, and displayed to the user, or to the room, depending on whether it was run from a regular message such as @succ, or from an omessage such as @osucc. Since the expression is evaluated both before and after being delayed, you need to put MPI code that is to run after the delay within a {lit:expr} command. If a {delay} evaluation is a null string, then the notify or notify_except will not be done. {Delay} will return the process ID of the event it puts on the timequeue. DELPROP {delprop:propname} {delprop:propname,object} This function will remove a property and all of its subsidiary properties in the case that it is a propdir. This will delete the property on the trigger object, unless an object argument is specified. If one is, then it will delete the property on that given object. This function returns a null string. If you specify a propname that is protected, you will get an error of Permission Denied. You are only allowed to delete properties from objects that are owned by the owner of the trigger object. DICE {dice:X} {dice:X,Y} {dice:X,Y,Z} Given one parameter, picks a random number between 1 and X. (1dX) Given two parameters, it randomly generates Y numbers between 1 and X, and adds them together. (YdX) A third parameter, if given, is just added to this sum as a modifier. (YdX+Z) DIST|DISTANCE {dist:x,y} Returns distance from 0,0 that x,y is. {dist:x,y,z} Returns distance from 0,0,0 that x,y,z is. {dist:x,y,x2,y2} Returns distance between x,y and x2,y2. {dist:x,y,z,x2,y2,z2} Returns distance between x,y,z and x2,y2,z2. Given two arguments, this calculates the distance of a 2D point from the origin. Given three arguments, this calculates the distance of a 3D point from the origin. Given four arguments, this calculates the distance between a pair of 2D points. Given six arguments, this calculates the distance between a pair of 3D points. DIV|DIVIDE {div:expr1,expr2} {div:expr1,expr2,expr3...} Returns the value of expr1 divided by expr2. Division by zero will return zero. If more than two arguments are given, then the first argument is divided by the second, and the result is divided by the third, etc, for all of the arguments. For example: {div:180,6,3,5} would be read like 180 / 6 / 3 / 5, and a result of 2 would be returned. EQ|EQUALS|== {eq:expr1,expr2} If expr1 and expr2 evaluate out to the same value, then this returns true. Otherwise, this returns false. If both expressions evaluate out to numbers, then this compares them numerically. EVAL {eval:string} Sort of the exact opposite of {lit:}. This takes a string, and evaluates it for MPI commands embedded within it. This can be used on the output of {list:}, for example. EXEC {exec:propname} {exec:propname,obj} Returns the string value of the given property, after having evaluated any embedded MPI commands that it contained. If no object parameter is passed to it, it looks for the property somewhere down the environment from the trigger object. Otherwise, it looks down the environment from the object specified. If the property is not found, this returns an empty string. If the property that it tries to access is read restricted and the owner of the trigger object does not own the object that the property is found on, then the MPI script stops with a Permission denied error. EXEC! {exec!:propname} {exec!:propname,obj} Returns the string value of the given property, after having evaluated any embedded MPI commands that it contained. If no object parameter is passed to it, it looks for the property on the trigger. Otherwise, it looks for the property on the object specified. If the property is not found, this returns an empty string. If the property that it tries to access is read restricted and the owner of the trigger object does not own the object that the property is found on, then the MPI script stops with a Permission denied error. EXITS {exits:obj} Returns a list of all the exits on the given object. The owner of the trigger object has to control obj, or else this errors out with Permission Denied. Programs and exits never have exits attached to them. FILTER {filter:var,list,expr} {filter:var,list,exp,sep} {filter:var,lst,exp,sep,s2} This evaluates expr for each and every item in the given list. On each evaluation, the temporary variable var will contain the value of the item under scrutiny. This function returns a list containing all of the items from the input list, for which expr evaluated true. Var will only be defined for the duration of expr, and will be undefined after the {filter} construct finishes. If sep is given, then it uses that string as the item seperator in the input list, instead of the usual carriage return character. If s2 is defined, then it will use that string to seperate the items in the list it returns, instead of the normal carriage return. Sep and s2 can be multiple characters long. FLAGS {flags:obj} Returns a flaglist string from obj. ie: PM2J. The object must either be in the vicinity, or it must be controlled by the owner of the trigger object. FOLD {fold:var1,var2,list,expr} {fold:var1,var2,lst,expr,sep} This takes a list and stores the first two items in var1 and var2, then evaluates expr. The value returned by expr is then put in var1, and the next list item is put in var2. Expr keeps being evaluated in this way until there are no more list items left. This returns the last value returned by expr. If a sep argument is given, the input list is assumed to have its individual items delimited by that string, otherwise it assumes a carriage return. FOR {for:varname,start,end,increment,command} Acts as a counting loop, like BASIC's for loops. The varname is the name of the variable that it will create and use to store the count value. The start value will be the initial value of the variable, and the end value will be the value that the variable is working towards. The increment is how much the variable will be incremented by in each loop. The command will be evaluated for each value of the variable between the beginning and ending values. For example: {null:{for:i,10,1,-1,{tell:{&i}}}} will echo a countdown from ten to one, inclusive, to the user. FORCE {force:object,command} Forces the given player or thing to do the given command. The thing forced must be @flock'ed to the trigger object, or the trigger object's owner, and it must be set XFORCIBLE, or else this function will get a Permission Denied error. This function returns a null string. {Force} cannot force a thing-object to do something, if it is set Dark, is in a room set Zombie, if it has the same name as a player, or is owned by a player set Zombie. FOREACH {foreach:var,list,expr} {foreach:var,list,expr,sep} This evaluates expr for each and every item in the given list. On each evaluation, the temporary variable var will contain the value of the item under scrutiny. var will only be defined for the duration of expr, and will be undefined after the {foreach} construct finishes. If sep is given, then it uses that string as the item seperator in list, instead of the usual carriage return character. sep can be multiple characters long. This structure returns the result of the last evaluation of expr. Example: {foreach:thing,{contents:here},{store:1,_seen}} FOX {fox} Returns the string YARF! FTIME {ftime:format} {ftime:format,tz} {ftime:format,tz,secs} Returns a time string in the format you specify. See `man timefmt' for the %subs that you can use in the format string. If specified, tz is the number of hours offset from GMT. If specified, secs is the systime to use, instead of the current time. {ftime:%x %X %Y,8,0} will return the date and time for systime 0, for the Pacific time zone. FULLNAME {fullname:obj} Returns the name of the given object. In the case where the object is an exit, then the full name of the exit is given, including all the ; aliases. The object must be in the immediate vicinity, or be controlled by the owner of the trigger object. GE|>= {ge:expr1,expr2} Evals expr1 and expr2, then returns true if expr1 was larger or equal. GT|GREATERTHAN|> {gt:expr1,expr2} Evaluates expr1 and expr2, then returns true if expr1 was larger. HOLDS {holds:obj1} {holds:obj1,obj2} Returns true if the location of obj1 is obj2. If no obj2 argument is given, then this will return true if the location of obj1 is the player. HOW|&HOW {&how} The {&how} variable is a short string telling what ran the MPI command. It can have the values "(@desc)", "(@succ)", "(@osucc)", etc. for when it is run from an @desc, an @succ, an @osucc, or whatever. It can also have the value "(@lock)" for when it is run from a lock test. IDLE {idle:player} Returns player idle time in seconds. If the given player is not connected, or is not a player object at all, then this will return -1. This returns the idle time for the most recently connected connection, if there are multiple connects. IF {if:check,true} {if:check,true,false} This is a simple conditional command. It evaluates the `check' argument and if it is true, then it evaluates the `true' argument and returns its result. If `check' does not evaluate as true, then it will evaluate the `false' argument, if there is one, and returns its result. If there is no false argument, and `check' evaluated false, then it returns a null string. Example: Your computer is {if:{eq:2,3},broken!,All right.} INC|INCREMENT {inc:var} {inc:var,val} Increments the value of the given variable by one, returning the result. If a value argument is given, then it will add that value to the variable, instead of the value 1. INDEX {index:propname} {index:propname,obj} Returns the string value of the property whose name is stored in the given property. This sounds confusing, but it's basically just the same as {prop:{prop:propname}}. If no object parameter is passed to it, it looks for both the index property and the referenced property somewhere down the environment from the trigger object. Otherwise, it looks down the environment from the object specified for both of them. If either property is not found, this returns an empty string. If the property that it tries to access is read restricted, and the owner of the trigger object does not own the object that the properties are found on, then the MPI script stops with a Permission denied error. INDEX! {index!:propname} {index!:propname,obj} Returns the string value of the property whose name is stored in the given property. This sounds confusing, but it's basically just the same as {prop!:{prop!:propname}}. If no object parameter is passed to it, it looks for both the index property and the referenced property on the trigger object. Otherwise, it looks on the specified object for both of them. If either property is not found, this returns an empty string. If the property that it tries to access is read restricted, and the owner of the trigger object does not own the object that the properties are found on, then the MPI script stops with a Permission denied error. INSTR {instr:str1,str2} Lists the position of the first substring within str1 that matches str2. If no such substring exists, then this returns a 0. ISDBREF {isdbref:dbref} Returns true if the string passed to it is a valid dbref. ISNUM {isnum:number} Returns true if the string passed to it is a valid number. ISTYPE {istype:obj,typ} Returns true if the given object if of the given type. Valid types are: Bad, Room, Exit, Thing, Player, and Program. KILL {kill:0} {kill:processID} Kills a process on the timequeue, that was possibly created by {DELAY}. If the process ID it is given is 0, then it will kill all processes done by that trigger object. If the process to be killed was not set off by that trigger, and was not set off by any object that the owner of the trigger owns, then this will error out with Permission denied. If no process is found, this returns 0. If a process was found, and the permissions were okay, then the process is killed, and {kill} returns the number of processes killed. Usually one. LASTUSED {lastused:obj} Returns the systime when obj was last used. LCOMMON {lcommon:list1,list2} Creates a list containing every item that appears in BOTH list1 and list2. Any duplicate items in the resulting list are removed. LE|<= {le:expr1,expr2} Evals expr1 and expr2, then returns true if expr1 was smaller or equal. LEFT {left:string} {left:string,fieldwidth} {left:string,fieldwidth,padstring} Takes a string and pads it to fit the given fieldwidth, with the string left justified. If no padstring is given, it assumes that it will pad the string with spaces. If no fieldwidth is given, it assumes that the field width is 78 characters. Example: {left:Hello,10,_.} would return the string "Hello_._._" LEXEC {lexec:listname} {lexec:listname,obj} This takes a property based list, and concatenates all its lines together, stripping spaces from the beginning and end of each one. It then evaluates the result for MPI commands, and returns the resulting string. A property based list is a series of properties that are consecutively numbered. The server understands several different formats, and can also read in property lists in either the propnameX format, or the propname#/X format. If no obj argument is supplied, then it looks for the list somewhere down the environment from the trigger object. Otherwise, it looks for the list down the environment from the given object. LINKS {links:obj} Returns the object reference of what the given object if linked to. Since exits can be meta-links, linked to multiple exits, if there is more than one link, then this function returns a list of all the destinations, seperated by carriage return characters. (\r) LIST {list:listname} {list:listname,obj} Returns a string, containing a carriage-return delimited list of individual lines from a property based list. A property based list is a series of properties that are consecutively numbered. The server understands several different formats, and can also read in property lists in either the propnameX format, or the propname#/X format. It does NOT evaluate the contents of the list for embedded MPI commands. If no obj argument is supplied, then it looks for the list somewhere down the environment from the trigger object. Otherwise, it looks for the list down the environment from the given object. LISTPROPS {listprops:propdir} {listprops:propdir,object} {listprops:propdir,object,pattern} This function will return a list that contains the full names of all the sub-properties contained by the given propdir. If not given, object defaults to the trigger object. If a pattern is given, the sub-properties in the propdir are each compared against the smatch wildcard pattern, and only those that match are returned in the list. This comparison is only done on the last part of the property name after the last /. See also PROPDIR and SMATCH. LIT|LITERAL {lit:string} Returns the literal string given as its parameter. This means you can have things that look like MPI commands within it, and it will not evaluate them, but will rather just treat them as a string. LMEMBER {lmember:list,item} {lmember:list,item,delimiter} Returns 0 if the given item is NOT in the given list, otherwise, it returns the item's position in the list. The first list item in the list would return 1, and the third would return 3, for example. If the delimiter argument is given, then it treats the list as if it were delimited by that string, instead of by carriage returns. (\r's) Example: {lmember:{mklist:a,b,c,d,e,f},d} would return 4. LOC|LOCATION {loc:obj} Returns the location of the given object. The object must either be in the vicinity, or it must be controlled by the owner of the trigger object. LOCKED {locked:player,obj} Tests the _/lok (@lock) standard lock property on obj against the given player. Returns true if the lock is locked against the player. LRAND {lrand:list} {lrand:list,seperator} Returns a randomly picked stringlist item from the given list. If the seperator argument is given, then it will assume that the stringlist has its items delimited by the given seperator string, instead of by carriage returns. LUNION {lunion:list1,list2} Combines the contents of list1 and list2, removing any duplicates. LUNIQUE {lunique:list} Returns list with all duplicate items removed. LREMOVE {lremove:list1,list2} Returns the contents of list1, with any items that match an item in list2 removed. The resulting list has all duplicate items removed. LSORT {lsort:list} {lsort:list,var1,var2,expr} Returns the sorted contents of list. If 4 arguments are given, then it evaluates expr with a pair of values, in var1 and var2. If expr returns true, then it will swap the positions of the two values in the list. It runs this comparison on every pair of items in the list, so it will be evaluated N*N times, where N is the number of items in the list. This method can also be used to randomize a list. Example: {lsort:{&list},v1,v2,{gt:{dice:100},50}} LT|LESSTHAN|< {lt:expr1,expr2} Evaluates expr1 and expr2, then returns true if expr1 was smaller. LTIMESTR {ltimestr:secs} Given a time period, in seconds, this will return a string, including a breakdown of all the time units of that period. For example, given a number of seconds, it might return "1 week, 2 days, 10 mins, 52 secs". MAX|MAXIMUM {max:expr1,expr2} Returns the greater of the values of expr1 and expr2. MKLIST {mklist:value...} Returns a list with all the given values as list items, seperated by carriage returns. (`\r's) Example: {mklist:Tom,Dick,Harry} returns "Tom\rDick\rHarry". Note: A maximum of nine items can be passed to the {mklist} function. If you need more, you can chain {mklist}s together. Example: {mklist:{mklist:a,b,c,d,e,f,g,h,i},j,k,l,m,n,o,p} MIDSTR {midstr:str,pos} {midstr:str,pos1,pos2} Returns the substring that starts at pos1 within str. If no pos2 is given, then the returned string is only the character at the given pos1 position. if a pos2 position is given, then it returns the substring beginning at pos1 and ending at pos2, inclusive. If pos1 or pos2 are negative, then they represent the position that is that absolute number of characters from the end of the string. The first character in str is 1, and the last one can always be referenced by -1. If a position would be before the beginning of the string, it is assumed to be at the beginning of the string. If it would be beyond the end of the string, it is assumed to be at the last character. If the starting position is later in the string than the ending position, then the returned string has the characters in reverse order. If either pos1 or pos2 are 0, then this returns a null string. ("") MIN|MINIMUM {min:expr1,expr2} Returns the lesser of the values of expr1 and expr2. MODIFIED {modified:obj} Returns the systime when obj was last modified. MOD|MODULO {mod:expr1,expr2} Returns the leftover remainder of expr1 divided by expr2. If more than two arguments are given, then the first arguments is modded by the second, then the result of that would be modded by the third, and so on and so forth. For example: {mod:91,20,3} would be read as 91 % 20 % 3, and a result of 2 would be returned. MONEY {money:obj} This returns the value of an object of TYPE_THING, or returns how many pennies a player has. MUCKNAME {muckname} Returns the muck name string. Example: FurryMUCK MUF {muf:prog,arg} Runs the given MUF prog with the string arg on the stack. This returns the top stack value when the prog exits. If the MPI code was run from a propqueue like _listen, or _connect, then {muf} cannot run a MUF program with a Mucker level of less than 3 MULT|MULTIPLY {mult:expr1,expr2} {mult:expr1,expr2,expr3...} Returns the product of the values expr1 and expr2. If more than two args are given, then they are all multiplied together to get the result. NAME {name:obj} Returns the name of the given object. If the object is an exit, the name returned is the first exit name it has before the first `;'. The object must be in the vicinity, or controlled by the owner of the trigger object. NE|NOTEQUALS|!=|<> {ne:expr1,expr2} If expr1 and expr2 evaluate out to the same value, then this returns false. Otherwise, this returns true. If both expressions evaluate out to numbers, then this compares them numerically. NEARBY {nearby:obj} {nearby:obj,obj2} If one argument is given, then this returns true ("1") if the given object is nearby to the trigger object. If two arguments are given, then it returns true if the two objects are nearby one another. Otherwise, this returns false. ("0") Nearby is defined as: 1) The two objects are in the same location, or 2) One object contains the other, or 3) the two objects are in fact the same object. NL|\r {nl} or \r Returns a carriage return character. This can be used to seperate items in a list, or can split the string at that point, starting a new line. Example: the string: This is\ran example{nl}of using newlines. would print out like: This is an example of using newlines. NOT|! {not:expr} Returns the logical NOT of expr. If expr was true, this returns false. If expr was false, this returns true. NULL {null:expr...} Returns a null string, no matter what the expressions within it return. This can take up to nine arguments, though you could pass the output of several commands as one argument. ONLINE {online} Returns a list of players who are online. This function can only be executed by wizbitted objects. ONTIME {ontime:player} Returns player online time in seconds. If the given player is not connected, or is not a player object at all, then this will return -1. This returns the online time for the most recently connected connection, if there are multiple connects. OR {or:expr1,expr2...} Returns true if expr1 or expr2 evaluate as true. Otherwise, this returns false. If expr1 was true, this doesn't bother to exaluate expr2, as it does C-style shortcutting. If there are more than two arguments, then this will evaluate them until either one returns true, or until it has evaluated all the expressions. This returns false only if all of the expressions return false. OTELL {otell:string} {otell:string,room} {otell:string,room,player} This will tell the given string to all the players in the room, except for the given player. If no room argument is given, it is assumed to be the room that the triggering player is in. If no player is given, then it assumes that you want to skip sending the message to the triggering player. If you pass it a player of #-1, it will send the message to all the players in the room. This returns the message that was sent. If the trigger isn't a room, or an exit on a room, and if the message doesn't already begin with the user's name, then the user's name will be prepended to the message. OWNER {owner:obj} Returns the owner of the given object. The object must be in the vicinity, or be controlled by the trigger object's owner. PARSE {parse:var,list,expr} {parse:var,list,expr,sep} {parse:var,list,expr,sep,s2} This evaluates expr for each and every item in the given list. On each evaluation, the temporary variable var will contain the value of the item under scrutiny. This function returns a list containing the output of expr for each item within the list. This lets you do direct translation of a list of dbrefs, for example, into a list of names. var will only be defined for the duration of expr, and will be undefined after the {filter} construct finishes. If sep is given, then it uses that string as the item seperator in the input list, instead of the usual carriage return character. If s2 is defined, then it will use that string to seperate the items in the list it returns, instead of the normal carriage return. sep and s2 can be multiple characters long. PRONOUNS {pronouns:string} {pronouns:string,object} If passed one argument, evaluates the string and does pronoun substitution with regards to the using player. If given two args, it does the pronoun substitution with regards to the given object. PROP {prop:propname} {prop:propname,obj} Returns the literal string value of the given property. If no object parameter is passed to it, it looks for the property somewhere down the environment from the trigger object. Otherwise, it looks down the environment from the object specified. If the property is not found, this returns an empty string. If the property that it tries to access is read restricted and the owner of the trigger object does not own the object that the property is found on, then the MPI script stops with a Permission denied error. PROP! {prop!:propname} {prop!:propname,obj} Returns the literal string value of the given property. If no object parameter is passed to it, it looks for the property on the trigger. Otherwise, it looks for the property on the object specified. If the property is not found, this returns an empty string. If the property that it tries to access is read restricted and the owner of the trigger object does not own the object that the property is found on, then the MPI script stops with a Permission denied error. PROPDIR {PROPDIR:propname,obj} If the given property on the given object is a propdir, containing sub-properties, then this returns true. Otherwise it returns false. Object will default to the trigger object, if not given. RAND {rand:listname} {rand:listname,obj} Returns the value of a randomly picked list item from a property based list. If no obj parameter is given, then it looks down the environment from the trigger object for the list. Otherwise, it looks down the environment from the given object. REF {ref:obj} Returns the dbref of the given object in the form #xxxx. The object must be in the vicinity, or controlled by the owner of the trigger object. RIGHT {right:string} {right:string,fieldwidth} {right:string,fieldwidth,padstring} Takes a string and pads it to fit the given fieldwidth, with the string right justified. If no padstring is given, it assumes that it will pad the string with spaces. If no fieldwidth is given, it assumes that the field width is 78 characters. Example: {right:Hello,10,_.} would return the string "_._._Hello" SECS {secs} Returns system time: the number of second since midnight 1/1/70 GMT SELECT {select:value,listname} {select:value,listname,object} Returns the value of a single list item from a sparse property list. The item chosen is the one who's line number is the largest one that is less than or equal to the given value. If the list is missing any items, then {select} will return the item in the list with the highest line number that is less than or equal to the given value. ie: If the list has the following entries: _junk#/1:one _junk#/5:two _junk#/16:three _junk#/20:four Then {select:9,_junk} will return "two", {select:16,_junk} will return "three", and {select:25,_junk} will return "four". SET {set:var,value} This sets the value of the given named variable to the given value. If no variable with that given name is currently defined, then this gives an error message complaining about that. SIGN {sign:expr} Returns -1 if expr is negative. Returns 1 if expr is positive. If expr is 0, then it returns 0. SMATCH {smatch:str,pattern} Matches `str' against the wildcard pattern. If there is a match, this returns true, or "1". If it doesn't match, this returns a value of "0", or false. In wildcard patterns, the following characters have the following meanings: * matches any number of any character. ? matches one character, of any type. [abcde] matches one char, if it is a, b, c, d, or e. [a-z] matches one char, if it is between a and z, inclusive. [^abd-z] matches one char is it is NOT a, b, or between d and z. {word1|word2} matches one word, if it is word1, or word2. {^word1|word2} matches one word, if it is NOT word1 or word2. \ escapes any of the prev chars, making it not special. STIMESTR {stimestr:secs} Given a time period, in seconds, this will return the most significant time unit of that time period. For example, a number of seconds, that is equivalent to 9 days, 23 hours, 10 minutes, and 52 seconds, will be have the value "9d" returned, as the abbreviated most significant time unit. STORE {store:val,prop} {store:val,prop,obj} Stores a string value in a given property. If no obj parameter is given, then it stores the property on the trigger object. Otherwise, it will store it on the given object. If you specify a propname that is protected, you will get a Permission Denied error. You are only allowed to store properties on objects controlled by the owner of the trigger object. The trigger object is the object that triggered the evaluation of the MPI commands. This function returns the string that is stored as the prop value. If you store a null value in the property, then it will remove the property if it is not a propdir. It will clear the value of the prop if it IS a propdir. STRIP {strip:string} Returns a copy of string with all the spaces stripped from the beginning and the end. SUBLIST {sublist:list,pos1} {sublist:list,pos1,pos2} {sublist:list,pos1,pos2,sep} Takes a list, and returns a subset of the list items within it. The subset is all the list items between list item pos1, and list item pos2, inclusive. If the pos2 argument is omitted, it assumes that pos2 is the same as pos1. If pos2 is less than pos1, then all the list items between pos2 and pos1 are returned, in reversed order. If pos1 or pos2 are negative, it counts that many list items back from the end of the list, so -1 is the last list item, and -5 would be the fifth from last list item. The input list is assumed to be delimited by carriage returns (\r) unless the sep argument is given. SUBST {subst:str,old,new} Returns a copy of `str' with all substring instances of `old' replaced by the text specified by `new'. Basically just substitutes the new text for the old text in str. example: {subst:Hello World!,l,r} would return "Herro Worrd!" SUBT|SUBTRACTION {subt:expr1,expr2} {subt:expr1,expr2,expr3...} Returns the difference of the values expr1 and expr2. If more than two args are given, all values are subtracted from the first value in sequence. For example: {subt:10,3,2,4} would be read as 10 - 3 - 2 - 4, and it would return a result of 1. TELL {tell:string} {tell:string,player} If passed only a string, tells the user that string. If passed both a string, and a player dbref, it will tell the given player the message. This returns the message that was sent. If the trigger isn't a room, or an exit on a room, and if the message doesn't already begin with the user's name, then the user's name will be prepended to the message. The two exceptions to this are that if the messages is being sent to the owner of the trigger, or to the user, then the user's name will not be prepended. TESTLOCK {testlock:obj,prop} {testlock:obj,prop,who} {testlock:obj,prop,who,def} Tests the lock property `prop', on `obj' against the given player `who'. If no `who' argument is given, then it checks the lock against the using player. If a def argument is given, then the lock will default to that value, if there is no lock property of the given name on the given object. Returns true if the lock is locked against the player. TIME {time} {time:timezone} Returns a time string in the 24hr form hh:mm:ss. If the timezone argument is given, then it offsets the time returned by that number of hours. TIMESTR {timestr:secs} Given a time period in seconds, this will return a concise abbreviated string representation of how long that time was. This might return a value like "9d 12:56" for 9 days, 12 hours, and 56 minutes. TIMESUB {timesub:period,offset,listname} {timesub:period,offset,listname,object} This is sort of like {list}, except that it will only return one line of the given named property list. The line it chooses depends on the time. The period is the length of time, in seconds, that it takes for {timesub} to cycle through the entire list. The offset is the number of seconds to offset into the time period, if you actually need to synchronize the {timesub} with something. The offset usually is just left at zero. If the object argument is not passed, it looks for the list on the trigger. What this all means, is that if you have, for example, a period of 3600 (one hour), an offset of zero, and a property list that has six items in it, then {timesub} will return the first line of the property list during the first ten minutes of the hour, the second line during the next ten minutes, and so on, until it returns the last line during the last ten minutes of the hour. Then it returns the first line for the beginning of the next hour. Here's an example: {timesub:86400,0,_sunmoon} This example will show different property list lines, depending on the time of day. The period is 86400 seconds, which is one day. If the property list has 24 items in it, then a different line will be returned for each hour of the day. TOLOWER {tolower:string} Returns a copy of string, with all uppercase chars converted to lowercase. TOUPPER {toupper:string} Returns a copy of string, with all lowercase chars converted to uppercase. TYPE {type:obj} Returns the type of an object. The possible values are: Bad, Room, Exit, Thing, Player, and Program. TZOFFSET {tzoffset} Returns local time zone offset from GMT in seconds. USECOUNT {usecount:obj} Returns the usecount of obj. V|VARIABLE|& {v:var} {&var} These are two ways of trying to do the same thing. They return the value of the named variable var. If there is no variable with the given name currently defined, then this gives an error stating as much. Variables can be defined either with the {with:} function or within a looping command. VERSION {version} Returns the version string for the server. WHILE {while:check,expr} This is a looping structure. It evaluates the `check' argument, and if it evaluates true, then it evaluates the expr argument, and repeats the process. If `check' evaluates false, then the loop is exited. This returns the result of the last evaluation of expr. WITH {with:var,val,expr..} This defines a new variable with the given name, and sets its value to the given val. Up to 7 expr's are allowed, but the only value returned to {with}'s caller, is the value returned by the evaluation of the last expr. If there is already a variable of the same name, then this command will override that variable, for the duration of the {with:} command. The new variable is only valid to use within the confines of the {with:} command, and it will go away after the command completes. This provides scoped variables quite effectively. NOTE: There can be no more than 32 variables defined at any one time, total. This includes variables that are defined within macros, or properties or lists that are executed with {exec:} or {lexec:}. Here's an example to illustrate the scope of variables inside of {with:} commands: {prop:_mydesc} <- {&people} not defined. {with:people,{contents:here,players}, <- Defining. Not available yet. {if:{count:{&people}}, <- It's usable now. The players awake here are {lit: } <- just puts in a space. {commas:{&people},{lit: and }, who,{name:{&who}} <- uses {&who} as temp var. } <- {&who} no longer defined. } } <- {&people} no longer defined. XOR|EXCLUSIVEOR {xor:expr1,expr2} Returns true if expr1 or expr2 evaluate as true, but false if both do. Otherwise, this returns false. * MACROS If the MPI interpreter comes across a function name that it does not recognize, it will look in the _msgmacs/ propdirs down the environment from the trigger object, for a property with the name of the function. If it does find it there, then it takes the value of that property, and substitutes it in for the function as a macro. The arguments to the function replace the {:1} through {:9} markers in the macro definition. For example, if there were a property set on #0, defined as: _msgmacs/div_rand:{add:{div:{:2},10},{dice:{:1}}} And you had some MPI code that looked like: {div_rand:22,160} Then the macro would expand out to: {add:{div:160,10},{dice:22}} After the macro argment substitution is complete, it is then evaluated. FUNC {func:name,vars...,def} This effectively defines a function in MPI, with the given name, that takes the given named variables. The function is not immediately evaluated, so it needs to be invoked later, to do anything. Here's an example: {func:sqr,val,{mult:{&val},{&val}}} This defines the function `sqr', that takes a single argument. That argument is stored in the `val' variable. The function will multiply the value of the number passed to it, by itself, returning the result. It's invoked like: {sqr:10} Effectively, the above {func} declaration is the same as the following macro, and in fact, it's internally handled the same way: _msgmacs/sqr:{with:val,{:1},{mult:{&val},{&val}}} You can define a function that takes more than one argument, but the maximum number of args you can pass to the function is seven. Example of multiple arguments: {func:names,list,numsp,flagsp, {parse:v,{&list}, {name:{&v}} {if:{or:{&numsp},{&flagsp}}, {lit:(} {if:{&numsp},{ref:{&v}}} {if:{&flagsp},{flags:{&v}}} {lit:)} } } } {names:{contents:here},1,1} ***** 3.2 Overview: MUF MUF -- a dialect of FORTH -- is one of two programming languages implemented on all MUCKs, the other being MPI. The speed and efficiency of MUF make MUCKs readily user-extensible: powerful new commands and programs can be soft-coded into the database. Although the only place you'll be able to use it is on MUCKs, MUF is a real programming language: once you've learned it, you can truthfully say you know how to program computers, and concepts and habits of thought you pick up as a Mucker will be useful in learning languages with widespread RL applications. MUF is an extensible, structured, stack-based, compiled language. Extensible: If there isn't a command to do what you want, you can make one. Structured: A MUF program consists of `functions' or `words'... blocks of code that are executed as a unit and `call' each other as needed. It doesn't matter (to the computer) whether you put all your code on one long line or every word on a new line (it matters a lot to people who are trying to read your program). White space (any combination of spaces, tabs, or new lines) separates words, and the order of the words and their positioning between the symbols that start and end a function are what matter. The program is composed of `functions' or `words' that each perform a specific task. Stack-based: You do everything by manipulating a stack, a set of `last-in-first-out' values like HP calculators use. Compiled: You write a program that people can read, with semi-normal words like `pop' and `rot' and `remove_prop' (this is your source code) and then a part of the server, the MUF compiler, turns it into arcane stuff that computers can read (this is your object code). You won't ever see the object code. Before You Begin: In order to program in MUF, you'll need a Mucker bit, a flag that lets you use the MUF editor. A wiz will need to set this, so page one and ask. There are three levels of mucker bits, M1 (apprentice), M2 (journeyman), and M3 (master) (wizards are considered M4). As a new Mucker, you'll get an M1 bit. M1's have access to most of the functions, but not all; output from an M1 program to anyone other than the owner of the program is prefaced by the user's name; and the program won't be able to send messages to or retrieve information from remote locations. An M1 bit is essentially MUF with training wheels. Once you've written a couple M1 programs that work, you can show them to a wiz and ask for an M2 bit. M2's can't use all the functions, but you can make truly useful programs at the M2 level: bulletin board or book programs, specialized exits and locks, morphing programs, etc. M3 bits are dandy to have, but they are hard to come by on large mucks, and for good reason. An M3 bit gives its owner considerable power over the data base, approaching that of a wizard: an inept or malintentioned M3 coder could cause serious problems. To get an M3 bit on a well established MUCK, you will need to write some very good programs and have shown yourself to be a trustworthy player over time. In general, it's easier to get an M3 on newer, smaller MUCKs. There are two commands that are good to know Before you Begin. One is `man', the online manual command. Typing `man pop' would tell you about the MUF primitive `pop'. The other is @q. This makes a foreground program you have running quit. If you find yourself in a run-away or locked-up program, type @q to get out of it. Actually Beginning: We'll get to MUF code itself soon, but a few words on the mechanics of making programs and their trigger actions may be in order first. The command to make a program is `@program <name of program>', or just `@prog <name of program>'. This does two things. It creates the program object (although there is no code in it yet), and it puts you into the MUF editor. The program is essentially a thing. You can pick it up, hand it to people, and so on, but it has the type flag F and so is treated as a different kind of object. The editor lets you enter and manipulate the text of your program, much like lsedit lets you enter and manipulate the text of a list (though the commands are different). Type `q' to get out of the editor (if you're in `insert' mode, you will need to type a . period and then `q'). Once the program is created, you can edit or add to the code with the @edit command, syntax `@edit <program>'. The editor commands are: <number> i Insert text before line <number>. If you don't have any code in the program, you can just type `i' by itself to enter insert mode. . Exit insert mode. (That's a period, btw) <number> <number> l List lines <number> to <number>. <number> <number> d Delete lines <number> to <number>. a Show macros (abridged version). s Show macros (long version). <program#> v List program's public functions. n Toggle line numbering on and off. c Compile the program you're editing. u Uncompile it. h Get help on the editor. q Quit editor mode. The editor is cumbersome. You may find it easier to work on your program in a text editor on your own computer or in your UNIX account. The following technique works well. Begin the file with: @edit <program name or #dbref> 1 9999 d i And end it with: . c q Put the code of your program between those. Then, by pasting the whole file into your mucking window, or by using your client to upload the file onto the MUCK, you automatically delete all old code, insert new code, and recompile. (To upload a file using TinyFugue, type /quote -0 `<filename> . The apostrophe in front of <filename> is required.) You will also need to make something that triggers the program. The most straightforward method is to open an action and link it to the program. You can also trigger it from a prop (see Section 2.1.3). If a program is set with the L flag, it is `public': anyone can link to it or list it. Without the L flag, only you or a wizard can list and link to the program. A First Program: Let's make a "Hello, world!" program, to practice with the mechanics of making a program, and because it's something of a rite of passage. Make a program. Let's call it Tinker.muf, because we'll be tinkering around with it. Type `@prog Tinker.muf'. The program is created and you go into the editor. At this point, either type `i' to enter `insert' mode and type the code below, or type `q' to get our of the editor and quote/paste it into the MUCK from your host computer, then compile the program and exit the editor (type `c', then `q' while in the editor). ==================================== > @prog Tinker.muf Program Tinker.muf created with number 1230. Entering editor. > i Entering insert mode. > (Tinker.muf. A practice program) > > : main > me @ "Hello, world!" notify > ; > . > c Compiler done. > q Editor exited. ==================================== Create an action attached to yourself and linked to the program. ==================================== > @act test = me Action created with number #1231 and attached. > @link test = tinker.muf Linked to Tinker.muf(#1230FM1) ==================================== Check your program by typing `test'. ==================================== > test Hello, world! ==================================== The Parts of a MUF Program: 1 ( Tinker.muf. A practice program ) 2 3 : main 4 me @ "Hello, world!" notify 5 ; Line 1 is a comment. In MUF, anything in parentheses is a comment. The compiler ignores it, and it doesn't add to the compiled size of your program. Comments are used to tell people what the program does, and they are important. Tinker.muf is a very simple program at this point, but you should go ahead and get into the habit of including comments. They explain what's going on to people who are trying to maintain your code, including you. Code that makes perfect sense to you when you write it will look like gobbledygook in a couple days, and you'll thank yourself for including frequent, detailed comments. The comment here is a header comment. In a full-blown program, we would put notes here about who wrote the program, when, how to install it, how to use it, and permissions for porting the program to other MUCKs. Lines 3-5 compose a function, or `word'... a block of code that is executed as one piece. Functions are the building blocks of your program. You make a MUF program by making different functions that do different things, and calling the appropriate function when needed (more on this later). This function is called `main', and it's our only function so far. A function begins with a colon, a space, and the name of the function (the space between the colon and the name is essential). It ends with a semi-colon. Unlike C/C++, there's nothing special about the function name `main' in MUF. We could call the function anything we want. A MUF program begins with the *last* function in the program, no matter what it's called (when we add more functions, they'll need to go above main). In this case, main is the only function, so it's the last function, so the program begins execution there. Line 4 is the heart of our program, and it includes four different kinds of MUF stuff. 'me' is a variable. It defines a space in the computer's memory that holds some information... holds a value. In this case, it's a predefined variable (MUF has already staked out this variable name and declared what it's going to hold). The variable `me' holds the dbref of whoever's using the program. When you trigger Tinker.muf by typing `test', `me' holds your dbref. If someone else typed `test', it would hold his. You can also add your own variables. '@' is an operator. Operators do stuff to (operate upon) other things. + and - are some other operators. They add and subtract, and they only work on numbers. @ is the `fetch' operator, and it only works on variables. The @ fetches the value out the variable named right before it, and puts the information that was in the variable onto the top of the stack (we'll cover the stack next). Here, the @ is getting a dbref out of the variable `me'. "Hello, world!" is a string. A string is a set of characters. Here the characters are the letters that form the words - Hello, world -. The quotation marks are important. They tell MUF that this is a string. Anything inside double quote marks is treated as a string, a set of characters. The word - me - by itself is a variable, but "me" is a string. 4 by itself is a number, an integer, but "4" is a string holding one character, the numeral 4. #123 is a dbref, but "#123" is a string of the corresponding characters. These differences -- variable vs. string vs. integer vs. dbref-- are called `data types', and they're important in MUF (MUF is `strongly typed'). Most operations can only be performed on one type of data. We have an example of that coming up... NOTIFY is a primitive, a function that has been defined in MUF. NOTIFY needs two things to do its job, a dbref and a string. It takes the string and displays it to the character defined by the dbref (much like a {tell} in MPI). If it doesn't have a dbref and a string on top of the stack to work with, the program won't work. Incidently, the collective term for all these different kinds of MUF stuff (variables, integers, strings, and dbrefs... operators and primitives... function names and the symbols that mark their beginning and end) is `statement'. NOTIFY, @, 123, : , and `main' are all statements. The Stack: Everything in MUF is done by manipulating the stack. The stack is the area in the computer's memory that holds the data for your program, the information it uses as it does its job. So try thinking of it as a RAM disk, a storage device. In this case, the information is stored by putting pieces of information `on top of each other' instead of in tracks or sectors. Each time the program is used (each `process'), it gets its own fresh new stack. The MUF stack is a LIFO stack: `Last in, first out.' When you add something to the stack, it goes on top of the stack, and it's the only thing that's immediately accessible. Previous items are still there, in the computer's memory, but they're `underneath' whatever you just added, and you'd need to use some stack handling functions to get them so your program can use them. A good analogy is a stack of plates. If you put a red plate on top of a blue plate, and then a white plate on top of the red one, you'd have a stack: white plate red plate blue plate All three plates are there, but the only one you can pick up without rearranging the stack is the white one. Within a function, MUF reads the code left to right, top to bottom. We only have one line in our little program, so it reads that line left to right, coming first to the variable `me'. We haven't yet told MUF to do anything with the variable; we've just supplied it. So it puts that variable on the stack. Here it is: me Our stack is pretty short right now, just one item. Notice that our one item right now is the variable `me', as opposed to the information stored in that variable (a dbref). Then MUF reads along our line of code and comes to the @ operator. The operator is not data. It's an instruction to do something with the data that's present, in this case the variable `me'. So the @ doesn't go on the stack. Instead, it does its little operation: it fetches the value that's stored inside `me'. When it does so, it `uses up' the variable `me'. Imagine that the fetch operator picks up the `me', opens it like a box, pulls out what's inside, and throws the box away. So, let's say your dbref is #123. Now the stack looks like this: #123 Still pretty short. Then, reading along, MUF comes to the string "Hello, world!". We haven't told MUF what to do with this string, we've just supplied it. So it goes on top of the stack: "Hello, world!" #123 Now we have a stack that actually looks like a stack. The string "Hello, world!" is stacked on top of the dbref #123. Next MUF comes to the primitive NOTIFY. Like operators, primitives are instructions to do things with data, rather than data to be put on the stack. NOTIFY is a predefined set of instructions that say `Take the string that's on top of the stack, go find the player with the dbref that's right underneath that string, and tell her the string. And forget you ever heard of this string and this dbref'. The program does what it's told, and the player with dbref #123 suddenly sees Hello, world! on her screen (she won't see the quotation marks: they're what define the string as a string, and not part of the string itself). In the process, NOTIFY `uses up' the two pieces of data that it requires, and the stack is now empty. Since the program ends at this point, that's fine. But if we did something else that required data (like tack another NOTIFY on the end of our line), then we'd get `stack underflow', and the program would crash. Watching the Stack with the Debug Mode: You can see exactly what's going on as your program runs by putting it in debug mode. A D flag set on a program means `turn on debugging.' ==================================== > @set tinker.muf = D Flag set. > test Debug> 1 ("") (main) Debug> 2 ("") V0 Debug> 2 ("", V0) @ Debug> 2 ("", #123) "Hello, world!" Debug> 2 ("", #123, "Hello, world!") NOTIFY Hello, world! Debug> 3 ("") EXIT ==================================== The first bit of these lines, `Debug>', simply tells you that what follows is output from the debugger (as opposed to stuff like the line - Hello, world!' - , which is output from the program). The number immediately following is the line number of the program that's currently executing. The items in parentheses, separated by commas, are the stack: the left end of the line is the `bottom' of the stack; the right end is the `top'. The last item (after the parentheses) is what MUF is reading at this exact point in the program's execution. On the first line, our stack is "", an empty string (this is called a "null string"). So our discussion above was slightly inaccurate. When a program is called, the argument to the trigger action (whatever was typed after the trigger command) is pushed onto the stack. We typed just `test' by itself, so nothing was pushed onto the stack. In this case, `nothing' looks like "", a string with nothing in it. Run the program again, with an argument this time, to see the difference. Type `test pickle'. The first line of debugging will now look like... Debug> 1 ("pickle") (main) That `V0' is our `me'. We call it `me'; MUF calls it `V0'... `V' for `variable', followed by a zero because it's the first variable it defined and computers start counting at zero. Study what happens to the stack as each successive item is read by MUF to get an idea of how the stack works. You might try adding another line to Tinker.muf to see the stack at work a bit more clearly. Change Tinker.muf so that now it reads... ( Tinker.muf. A practice program. ) : main me @ "Hello, world!" notify "mink" "otter" "linsang" pop pop pop ; ==================================== > @edit tinker.muf > 1 99 d > i > ( Tinker.muf. A practice program. ) > > : main > > me @ "Hello, world!" notify > > "mink" "otter" "linsang" > > pop pop pop > ; > . > c > q > test Debug> 1 ("") (main) Debug> 2 ("") V0 Debug> 2 ("", V0) @ Debug> 2 ("", #123) "Hello, world!" Debug> 2 ("", #123, "Hello, world!") NOTIFY Hello, world! Debug> 3 ("") "mink" Debug> 3 ("", "mink") "otter" Debug> 3 ("", "mink", "otter") "linsang" Debug> 3 ("", "mink", "otter", "linsang") POP Debug> 3 ("", "mink", "otter") POP Debug> 3 ("", "mink") POP Debug> 4 ("") EXIT ==================================== The POP primitive takes whatever's on top of the stack and gets rid of it. POP pops it off the top, into oblivion. If we added one more POP, the null string would be popped off the stack right at the end of the program, and it would exit with nothing on the stack. If we added yet another one, it would try to pop something that isn't there, and crash. Organizing Your Code: Recall that MUF is a `structured' language: as long as the statements in a function are in the same order, reading top to bottom and left to right, and as long as they are separated by whitespace, it doesn't matter how you place them on the page. `Whitespace' is any combination of spaces, tabs, or line breaks. Instead of... : main me @ "Hello, world!" notify "mink" "otter" "linsang" pop pop pop ; We could have written... : main me @ "Hello, world!" notify "mink" "otter" "lingsang" pop pop pop ; Or even... : main me @ "Hello, world!" notify "mink" "otter" "linsang" pop pop pop ; All three versions would mean the same thing to the compiler, would use the same amount of memory, and would be executed in exactly the same way. To most people, however, the first version would be much more clear. Arrange your code in such a way that its format illustrates or reinforces logical relationships (that is, combine things that go together and separate those that don't). MUF is not case sensitve: `POP', `Pop', and `pop' would all be treated the same way. Capitalization, therefore, can be used as another way to format code. For example, you might choose to capitalize all function names and begin all variable names with lower case to concisely indicate their difference. Some coders use all capitals for primitives. This does clearly indicate their difference, but many people find all caps less readable than upper-lower case. Choose your own style, but remain consistent within a program. Now we'll (completely) rewrite Tinker.muf several times, with each version introducing an important aspect of MUF programming. The > signs and other formatting information have been omitted from the remaining examples, so you can cut and paste from this file into the one you're working on. Conditional Statements: A conditional statement causes the program to do something only if a certain condition is true. The main form of a conditional statement in MUF is the IF-THEN statement. IF-THEN works a bit differently in MUF than in some other programming languages. In BASIC, for example, the meaning is something like `IF A is true, THEN go do B'. In MUF, the meaning is more like `IF A is true, do this bit here... B. THEN do all this other stuff, the rest of the program.' 'True', as far as MUF is concerned, means `any value other than the integer 0, or a null string ( "" ), or the dbref for "nothing", which is #-1'. 0 <---- False "" <---- False #-1 <---- False 1 <---- True "Hiya!" <---- True #123 <---- True An IF checks (and uses up) the top value on the stack. When this value is true, any code between the IF and its matching THEN will execute (every IF must have a matching THEN). When the value is false, the program skips over everything until it gets to the matching THEN, and resumes execution at that point. Here's an example... ==================================== : main random 1000000 > if me @ "Yes, the number is greater than one million." notify then me @ "OK, we're done with the IF-THEN stuff." notify ; ==================================== RANDOM puts a random number between one and the largest integer it can generate (about 2.1 billion) on the stack. The > greater than operator tests the two numbers right before it, and returns true (that is, puts a `1' on the stack) if the first number is greater than the second. If the first number is less than the second, it returns false (it puts a `0' on the stack). The two numbers being compared are used up in the process. So, when our little program gets to IF in the first line, there will either be a `1' or a `0' on the stack. If the value on the stack is 1 (which it will be the vast majority of the time), the IF test will be true, and the next line will execute: the program will notify the user with `Yes, the number is greater than one million.' Then it will continue past the THEN and do the next line, notifying the user `OK, we're done with the IF-THEN stuff.' If the random number were less than than one million, the IF test would be false, and the program would skip straight to the last line. You can also specify what should happen when the IF test is false by placing an ELSE between the IF and the THEN. The following version will notify whether the random number is greater or less than one million. ==================================== : main random 1000000 > if me @ "Yes, the number is greater than one million." notify else me @ "No, the number is less than one million." notify then me @ "OK, we're done with the IF-ELSE-THEN stuff." notify ; ==================================== Intenting one level for each condition, as in these examples, is a common and helpful convention. Stack Effects and Keeping Your Data: Recall that when we did `me @ "Hello, world!" notify', the NOTIFY `used up' the dbref and the string. This is formally expressed in NOTIFY's `stack effect comment'. Type `man notify' to see the manual entry for NOTIFY. The entry, like all entries for primitives, includes a stack effect comment: ( d s -- ). The stack effect comment provides a `before and after synopsis' of the primitive's effect on the stack: the items before the dash are what the primitive requires; the items after are what it leaves. (`d' means `dbref'; `s' means `string'; `i' means integer; `v' means a variable'; `x' means an item that can have various types.) In this case, NOTIFY needs a dbref and a string -- in that order -- and leaves nothing in their place. Most primitives `use up' their data in this fashion, and this is a Good Thing. If they did not, programmers would need to put one or several POPs after each primitive to keep the stack from growing unmanageable, which would greatly increase the size of programs. However, this also means that when you are working with data that you will need again later in the program, you will need to store it, either by putting a copy of it on the stack or by storing it in a variable. The DUP primitive makes a copy of the top item on the stack; its stack effect comment is ( x -- x x ). The following version of our program uses DUP to make an extra copy of the random number, and after the IF-ELSE-THEN section it tells the user what the random number is. ==================================== : main random dup 1000000 > if me @ "Yes, the number is greater than one million." notify else me @ "No, the number is less than one million." notify then intostr me @ swap notify ; ==================================== This time, because of the DUP in the first line, there are two copies of the random number beneath the 1000000 when the greater-than test is executed. The test will use one of them, but the other will remain on the stack. The IF-ELSE-THEN part will then execute, leaving this number unaffected. The last line tells the user what the random number was. When the program gets to this line, the only thing on the stack is the random number, in integer form. The stack effect of NOTIFY is ( d s -- ), so we need to do a little rearranging: we need to use some `stack handling' primitives to convert the integer into a string and place the stack items in the correct order. INTOSTR converts the random integer into a string. If the random integer were 23231874, INTOSTR would remove this number from the stack and leave the string "23231874" in its place. `me @' puts the user's dbref on the stack. But now the two stack items are in the wrong order. The dbref needs to be in front of (or `below') the string. SWAP, one of several stack handling primitives, reverses the order of the top two items on the stack ( x y -- y x ). So, if our stack were `"23231874" #123', SWAP would make it `#123 "23231874"', which will work for NOTIFY. Instead of duplicating a datum on the stack, you can also store it in a variable. This is especially useful when the datum will be needed much later in the program, or when the program will need the same datum at different places. There are two steps to storing data in this way: declaring the variable, and storing the data. Declaring the variable is done simply by including the word `var' or `lvar' followed by the name of the variable in the program. The variable can be declared anywhere in the program, so long as it's somewhere *before* the variable is used in the code, and *outside* the definition of a function. So, it makes good sense to declare all your variables at the top of the program. `Var' declares a global variable (all programs can use it); `lvar' declares a local variable (only this program can use it). *Use local variables.* The data type of a variable does not have to be declared: once you define a variable, it can hold any type of data. Once a variable has been declared, you can store data in it with the `store' operator, an ! exclamation point ( x v -- ). The following version of our program does the same thing as the previous one, but this time stores the random number as a variable instead of keeping it on the stack. ==================================== lvar ourNumber : main random ourNumber ! ourNumber @ 1000000 > if me @ "Yes, the number is greater than one million." notify else me @ "No, the number is less than one million." notify then me @ ourNumber @ intostr notify ; ==================================== Calling Functions: So far, all versions of our program have only included one function, but programs can have as many functions as you want. Functions (also called `words') are blocks of code executed as a single unit. They begin with a colon followed by the name of the function, and end with a semi-colon. The name of a function can be pretty much anything you want, but should include at least one non-numeric character. You should also avoid using function names already defined in MUF (type `man <word>' to see if the function name is already being used.) Once they have been defined, functions can be called simply by including their name in the program's code. The following version of Tinker.muf does the same thing as the previous two, but this time the `greater' or `less than' notifications are handled by separate functions: ==================================== lvar ourNumber : TellTrue me @ "Yes, the number is greater than one million." notify ; : TellFalse me @ "No, the number is less than one million." notify ; : main random ourNumber ! ourNumber @ 1000000 > if TellTrue else TellFalse then me @ ourNumber @ intostr notify ; ==================================== Loops: A loop is a section of code that executes repeatedly, until a certain condition is met. The programmer defines the condition. If no condition is defined (or if the condition is one that will never be met), the loop will continue to execute indefinitely. This is called an `infinite loop'. The code for a loop begins with the primitive BEGIN and ends with either REPEAT or UNTIL. A common way to define the exit condition of a loop is to use a variable to store the number of times the loop is to execute. With each repetition (or `iteration') of the loop, the variable is increased (`incremented') or decreased (`decremented') by one. When the value stored in the variable matches a predefined limit (often `0'), the loop exits. The following version of Tinker.muf uses a loop controlled in this manner to do the `random number test' from the previous version three times. ==================================== lvar ourNumber : TellTrue me @ "Yes, the number is greater than one million." notify ; : TellFalse me @ "No, the number is less than one million." notify ; : main 3 counter ! begin counter @ 0 = if break then random ourNumber ! ourNumber @ 1000000 > if TellTrue else TellFalse then me @ ourNumber @ intostr notify counter @ 1 - counter ! repeat me @ "OK, we're done with the loop." notify ; ==================================== At the very top of the program, we declared the local variable `counter'. In the first line in main, we stored the integer 3 in our variable `counter'. The next line is BEGIN, which starts the loop. The first thing MUF does with each pass through the loop is check to see if the value stored in `counter' is 0. If it is, execution BREAKs out of the loop, and does whatever comes next (which in this case is to tell us "OK, we're done.") In the first iteration of the loop, the IF test will be false, because we just put a 3 in `counter', not a 0. So the loop will continue, executing our random number test. Then, at the bottom of the loop, we fetch the number out of `counter', subtract 1 from it, and then store the new number back in counter. Then the loop REPEATs, jumping back to the top of the loop. This time, the value in counter is 2... still not 0, so the loop exectutes again. The next time the loop repeats, the value is 1: the random number test has executed three times. Then execution will jump back to the top of the loop and begin for a *forth* iteration. This time, however, the value stored in `counter' has reached 0. The IF test will be true, so the BREAK will be executed. The program jumps out of the loop and does whatever comes after the REPEAT (tells us we're done). Here we are using IF to see if it is time to break out of the loop. MUF provides another conditional statement that does the same thing in shortened form. WHILE tests the top value on the stack for truth, just like IF does. When an IF test is true, code between the IF and its matching THEN is executed; otherwise, it skips to whatever follows the THEN. When a WHILE test is true, code between the WHILE and the next REPEAT or UNTIL is executed; otherwise, it skips to whatever follows the REPEAT or UNTIL. In other words, the loop continues to execute WHILE the condition is true. In the above version of Tinker.muf, we used... counter @ 0 = if break then to exit the loop. We could accomplish the same thing with... counter @ while As long as `counter' is not 0, it will be true, and the rest of the loop will execute. When it gets to 0, it becomes false, and the WHILE will cause execution to break out of the loop. UNTIL also provides a way to exit from a loop. The UNTIL marks the `bottom' of the loop, like a REPEAT, but also serves as an exit-condition check like WHILE. If the value on top of the stack is true when the program gets to UNTIL, execution `falls through' the bottom of the loop and continues from there. For example, the following loop repeats until RANDOM generates an integer greater than 1.5 billion. ==================================== begin random dup intostr me @ swap notify 1500000000 > until ==================================== With each iteration, the loop generates a random number, converts it into a string and notifies the user, then tests to see if it's greater than 1.5 billion. If so, the top value on the stack at UNTIL will be true, and the program will exit the loop. Otherwise, execution jumps back to BEGIN, and the loop repeats. ***** You make a MUF program do what it is supposed to by defining functions and directing program flow with conditional statements and loops. There are a number of fine points, but these are the heart of MUF programming. Once you have a good grasp of these, you are past the hard part. Now we will tinker with Tinker.muf until it becomes a useful program, along the way covering most of the issues you will face in MUF. We will make Tinker.muf into a program that checks all the exits in a room and makes sure that they have @succ, @osucc, @odrop, and @desc properties. First we'll do a rudimentary version with no error checking to concentrate on new topics, then a final version. Enter the following code in your program. ==================================== @edit tinker.muf 1 999 d i lvar ourExit : DescTell dup unparseobj " needs a description." .tell ; : SuccTell dup unparseobj " needs a success message." .tell ; : OsuccTell dup unparseobj " needs an osuccess message." .tell ; : OdropTell dup unparseobj " needs an odrop message." .tell ; : main loc @ exits ourExit ! begin ourExit @ while ourExit @ dup "_/de" getpropstr not if DescTell then dup "_/sc" getpropstr not if SuccTell then dup "_/osc" getpropstr not if OsuccTell then dup "_/odr" getpropstr not if OdropTell then ourExit @ next ourExit ! repeat ;