& help This is the helpfile format of version 2.008 of Amberyl's MUSH Manual. Some very tiny edits have been made to correct information that is now egregiously out of date. This manual is divided into five sections (labeled with Roman numerals), but there are around fifteen subsections total. This helpfile is divided by subsection. The Roman numerals themselves are only tables of contents and introductions to each major section. Sections are accessed as follows: man 1 This shows the name of chapter 1 and any notes the author included. man 2.5 This accesses page 1 of what's in section 2, subsection 5. man 3.4.2 This shows the second page of section 3, subsection 4. For a list of sections, type 'man list'. For some caveats, type 'man caveats'. For credit information, type 'man credits'. & caveats This manual was written back in 1995. Over four years later, the information within it is not entirely current. Though most of the information, particularly the basic information, is still relevant and more or less correct, details and common practices have changed over the years with the evolution of the MUSH servers and trends in MUSH administration and code systems. Indeed, the specific slant of topics in the manual may seem a little odd to current readers, due to the change in MUSH culture over those years. Read at your own risk. The author is presently at work on a from-scratch rewrite of the manual, reflecting TinyMUSH 3.0 and current MUSH coding and administration practices. & credits This manual was written by Lydia Leong (lwl@godlike.com), and converted to helpfile format by Alierak and sTiLe. The author is deeply grateful for their hard work. & list The following main topics are available for viewing: README, Introduction, I, II, III, IV, V, wizeth, index1, index2, index3 README is the readme file that comes along with the manual, describing copyright information and whatnot. Introduction contains a main table of contents and a description of the manual. Wizeth contains an expanded version of the author's speech on wiz ethics. The index# sections have an index of examples. For a table of contents, you should read man intro. Partial tables of contents can be viewed in the I, II, III, IV, and V sections. There should be a man topic for each sub-number listed in the tables of contents. (e.g. 2.3) & I MUSH Manual Version 2.008: Copyright 1993, 1994, 1995, Lydia Leong (lwl@godlike.com / Amberyl) Last revised 5/22/95. Section I: Getting Started. Basic commands and objects. Table of Contents: Introduction: A brief history of MUSH. Configurations and server differences. 1. The Basics 1.1 Some information Common terms used on MUSHes. The format of this manual. 1.2 Getting started Looking around: look, examine, @decompile, TERSE, MYOPIC, @grep Communications: ", :, @emits, page, whisper, basic flags and @set, HAVEN, GAGGED, NOSPOOF Going places: JUMP_OK, @tel, @whereis, UNFINDABLE (Continued in 'man I1'.) & introduction (To skip the TOC, type 'man intro8'.) MUSH Manual Version 2.008 Written for TinyMUSH version 2.2.1 TinyMUSH version 2.0.10 patchlevel 5 PennMUSH version 1.50 patchlevel 12 MudCore Starter Database version 1.0 Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, Lydia Leong (lwl@godlike.com / Amberyl) Section I: Getting Started. Basic commands and objects. Introduction: A brief history of MUSH. Configurations and server differences. 1. The Basics 1.1 Some information Common terms used on MUSHes. The format of this manual. 1.2 Basic commands Looking around: look, examine, @decompile, TERSE, MYOPIC, @grep Communications: ", :, @emits, page, whisper, basic flags and @set, HAVEN, GAGGED, NOSPOOF (Continued in 'man intro2'.) & intro2 Going places: JUMP_OK, @tel, @whereis, UNFINDABLE Objects: get, drop, give, ENTER_OK, inventory Command syntax, @set and flags, special commands, @list 1.3 Attributes: @desc, @succ, @fail, @drop, @kill, IMMORTAL, @move, @sex 1.4 Editing: @edit, @mvattr, @cpattr 1.5 Do it! -- basic character set-up 1.6 A parser discussion 2. Objects 2.1 Basic object commands: @create, @destroy, @force, @name, VISUAL 2.2 Puppets - the PUPPET flag. ROBOTs. 2.3 A Note about Privacy: @sweep 2.4 Attributes on Objects: Action Lists 2.5 Machines: @cost, @pay, and @clone, QUIET and DESTROY_OK flags, @parent 2.6 Limiting object uses: @charges and @runout 2.7 Reacting to the environment: @listen and @ahear, ^, LISTENER/MONITOR 2.8 Keeping your objects safe: @lock, @unlock, @chown, CHOWN_OK, STICKY, @search, @find, @stats 2.9 Do it! -- falcon (Continued in 'man intro3'.) & intro3 Section II: Building and an introduction to programming 3. Building. 3.1 Creating a room: @dig, FLOATING flag 3.2 Connecting rooms: @open, @link and @unlink, LINK_OK flag, @entrances 3.3 Refinements: ABODE and JUMP_OK flags, drop-tos and STICKY flag on rooms 3.4 Exit conventions and the TRANSPARENT flag 3.5 Do it! -- home 4. Introduction to Programming 4.1 The stack and string substitutions - % substitutions 4.2 General attributes and action lists - @trigger and GET() 4.3 The queue: @ps, @wait, and @halt. HALT flag 4.4 Decision making: @switch 4.5 Other ways of triggering: @use and @startup 4.6 User-defined commands: $command, @scan, @verb 4.7 Introduction to functions: ADD() and other math, RAND(), TIME() 4.8 Do it! -- falcon control (Continued in 'man intro4'.) & intro4 Section III: Programming applications 5. Vehicle programming 5.1 Entering and leaving objects: @enter, @aenter, @oenter, @oxenter, @leave, @aleave, @oleave, @oxleave 5.2 Inside vehicles: @idesc, @listen, relays, and commands, @remit, AUDIBLE, @prefix, @filter, @forwardlist 5.3 Do it! -- Wagon controller 6. Complex programming 6.1 Useful functions: General use: NUM(), LOC(), NEARBY() List creation: LCON(), LEXITS(), LWHO() Parsing: POS(), FIRST(), REST(), STRLEN(), MID(), WORDS() Matching: EXTRACT(), MATCH(), MEMBER(), UCSTR(), LCSTR(), CAPSTR() 6.2 Non-standard attributes: & and attribute flags 6.3 Introduction to @dolist 6.4 Time synchronization: semaphores, complex @wait, @notify, @drain 6.5 Security problems in programming, INHERIT, ESCAPE(), and SECURE(), @fsay, @femit, @fpose (Continued in 'man intro5'.) & intro5 6.6 Debugging: PUPPET and VERBOSE 6.7 On the New Programming Style 7. MUSH Programming: QuickStart Guide 7.1 Basic syntax 7.2 Conditional and loop constructs 7.3 Functions Section IV: A Guide for Administrators 8. Owning and seeing everything 8.1 Special privileges, ROYALTY, and Powers 8.2 Attribute ownership - @atrchown and @atrlock 8.3 Object ownership - @chown and @chownall 8.4 The queue - @ps and @allhalt, @kick 8.5 The database - @entrances, @find, @search, and @stats 8.6 A note about security and privacy 8.7 The Master Room and command processing 9. Wiz-specific commands (Continued in 'man intro6'.) & intro6 9.1 Talking to other wizards: @wizwall and @wizemit 9.2 Messages: @wall, @listmotd, @motd, and @wizmotd 9.3 Locking out players: @login and @rejectmotd 9.4 Dealing with players: @newpassword, @pcreate, @boot, @toad, and @destroy 9.5 Dumping the database: @dump and @shutdown 10. Database control 10.1 Quota system: limited object creation - @allquota, @quota, and @squota 10.2 Money control: give and @poor 10.3 Consistency checking: @dbck and @purge, @dump/paranoid, @cut, @fixdb 11. Zones 11.1 Object zones 11.2 Parent room zones 11.3 ZoneMaster players 11.4 Some notes on local wizard control 12. The Fundamental Laws of Wizarding 12.1 Wizard Etiquette 12.2 The Five-Step Method of player management warning - nasty warning - gag - boot - newpassword 12.3 Wizard commands and when they ought to be used (Continued in 'man intro7'.) & intro7 Section V: The Art of Psychocoding 13. Basic Concepts 13.1 Attribute naming and coding style 13.2 String concatenation and the switchless style 13.3 What the parser really does 13.4 Zones in TinyMUSH 2.2 14. List manipulation 14.1 Adding to, removing from, and comparing lists 14.2 Function building blocks: U() and SWITCH(), @function 14.3 Formatting strings: SPACE(), REPEAT(), LJUST(), and RJUST() 14.4 Lists instead of @dolists: ITER() and FILTER() 14.5 Recursion: theory and practice, FOLD() 15. Efficiency 15.1 Parameter passing: U(), ULOCAL(), local registers, SETQ(), and R() 15.2 How the queue works 15.3 Pipelining 15.4 Queue cycles vs. CPU cycles (Continued in 'man intro8'.) & intro8 This is the table of contents for Amberyl's MUSH manual. She can be found on PernMUSH, or at lwl@godlike.com. This manual should be current through TinyMUSH version 2.2.1, and PennMUSH version 1.50 patchlevel 12. Much of it should also apply to TinyMUSH version 2.0.10 patchlevel 5 (from which TinyMUSH 2.2 is derived). Differences between the two versions will be noted where necessary; in general, 2.0 will refer to features common to both 2.0 and 2.2, and 2.2 will denote features specific to 2.2. Do note that because 2.0.10p5 is now an obsolete version, and 2.0.10p6 is not much more advanced, that there may be sections of this manual which simply do not function under those code versions, or which require modification (for example, 2.0.10p5 does not have the %q-substitution, though it does have the R() function). This manual version also covers commands that are softcoded in MUSH, found in the MUSHcode starter database called MudCore. MudCore global commands are found on a variety of MUSHes, most notably PernMUSH and AmberMUSH; it is convenient to mention them in this manual, since many tasks can be simplified by use of these globals, and even those MUSHes that don't use MudCore or the equivalent do have globals which perform similar functions. (Continued in 'man intro9'.) & intro9 The 2.0x manual series is a complete rewrite using the 1.15 manual as a starting base. Unlike the previous manual, this one is intended to replace anything that has come before, including Croaker's manual, which is pretty much obsolete for this version of code. Those programming MUSE should probably look at the 1.15 manual release instead, or use Falryx's MUSE Manual, which is based upon this manual (with permission). The MUSE parser is quite close to the original 1.x parser, and the basic examples in this manual should work without any problems. The MUSE powers system and related issues are beyond the scope of this manual (and the author's knowledge, for the most part); the helptext for MUSE and consultation with a MUSE programmer are probably the best ways to figure out how to program MUSE. (Continued in 'man intro10'.) & intro10 TinyTIM players may still find Croaker's manual helpful. There is also a TinyTIM supplemental manual, since TIM has diverged quite a bit from the "mainstream" MUSH. The contents of the PernMUSH 1.15 manual and this manual may or may not apply to TinyTIM. The parser on TIM, however, is fairly similar to the standard 1.x parser. There is, however, probably little of interest in this manual for the TinyTIM player, since most of the features mentioned here do not exist on TIM. This is the tenth revision of this manual. There have been some major revisions in the servers since the prior revision, though these primarily affect advanced programming techniques and administrative commands. This manual only documents the most important of these; for the rest, the helptext should suffice. (Continued in 'man intro11'.) & intro11 I have also corrected a number of previous errors, and further expanded the notes on advanced programming techniques, which seem to be in great demand. There are also a number of formatting changes, which, in my opinion, make the text easier to read. A fair amount of the text has been expanded (and hopefully clarified); I've also chosen to introduce more "advanced" programming techniques earlier in the manual. Finally, I've taken the opportunity to explicitly debunk a few myths about MUSH programming, as well as provide specific methods for things that I often see programmed incorrectly or inefficiently. There are a lot of small changes, and a few sections added and removed. The structure of this manual is slowly becoming obsolete, due to the changing methods of MUSH programming, and, specifically, learning to program MUSH; it is likely that future revisions of the manual will restructure substantial portions of the text. (Continued in 'man intro12'.) & intro12 The tenth revision is also the first version of the manual to document a softcoded package of MUSHcode, in this case, the MudCore Starter Database. I regard the standardization of certain core pieces of softcode as something which will ultimately make the transition from one MUSH to another much easier, and thus I'm going to attempt to support a standard by placing it in the manual. MudCore is slated for an early-summer 1995 release; portions of it can be found on games such as PernMUSH and AmberMUSH. The ninth revision of the manual was relatively minor, correcting previous mistakes, expanding various bits of material, attempting to clarify the somewhat confused earlier explanation of semaphores, and adding additional material on advanced programming topics. (Continued in 'man intro13'.) & intro13 The eighth revision of the manual was the first to cover advanced programming, in a newly added section. The rest of the manual, however, is not meant to be a guide to advanced programming, nor does it necessarily show the "best" way to do something. It is intended to show the most intuitive and basic way to get something done in MUSH. It is also not a complete guide to MUSH; for that, you need to look at the helptext. This manual is meant to act as a tutorial and as a pointer to some of the more useful things available in MUSH, as well as to clarify some of the things that are not easily covered in the helptext. For the precise syntactic details on a command, look up the help text; especially in the latter sections, this manual attempts to avoid duplicating material found in the help, concentrating instead on techniques and details. (Continued in 'man intro14'.) & intro14 The final "advanced programming" section of the manual convers the "new" style of MUSH programming, which relies heavily on list manipulation functions and string concatenation to produce the desired result. This is commonly referred to as the "switchless style". While I believe this is something best learned on one's own, an overview of the common techniques is presented here. It also includes a discussion of how to write programs that are efficient in their use of queue cycles, without overburdening the server itself with very large, complex function evaluations. If you spot any errors in the manual, PLEASE send me email. You should mention the specific MUSH version you are using, and mention as specifically as possible where the error is. Finally, by way of thanks to the various people who have, in the past, contributed examples/typo reports/etc. to this manual, I've thrown their names in as player examples in various places. I hope they enjoy their brief moment of fame. (Continued in 'man intro15'.) & intro15 A "README.mushman" file accompanies this manual distribution, and must be retained with any electronically-retrievable version of this manual. To summarize the copyright notice contained therein: This manual was written by Lydia Leong (lwl@godlike.com), and any rights and privileges of reproduction are hers, in accordance with United States or international Copyright Laws. Users are granted permission to print and reproduce this manual in its unmodified entirety, as long as this copyright notice is retained and due credit given, and the material remains free of charge. It can be quoted, with credit, within the bounds set by standard academic protocol. It may NOT be used within another major document (such as another manual), nor published in print form (whether or not the publisher makes a profit from such publication), nor may it be hypertextified or otherwise changed in format, without the explicit permission of the author. -- Amberyl, May 22nd, 1995 & I1 Objects: get, drop, give, ENTER_OK, inventory Command syntax, @set and flags, special commands, @list 1.3 Attributes: @desc, @succ, @fail, @drop, @kill, IMMORTAL, @move, @sex 1.4 Editing: @edit, @mvattr, @cpattr 1.5 Do it! -- basic character set-up 1.6 A parser discussion 2. Objects 2.1 Basic object commands: @create, @destroy, @force, @name, VISUAL 2.2 Puppets - the PUPPET flag. ROBOTs. 2.3 A Note about Privacy: @sweep 2.4 Attributes on Objects: Action Lists, @decompile, the parser 2.5 Machines: @cost, @pay, and @clone, QUIET and DESTROY_OK flags, @parent 2.6 Limiting object uses: @charges and @runout 2.7 Reacting to the environment: @listen and @ahear, ^, LISTENER/MONITOR 2.8 Keeping your objects safe: @lock, @unlock, @chown, CHOWN_OK, STICKY, @search, @find, @stats 2.9 Do it! -- falcon (Continued in 'man I2'.) & I2 INTRODUCTION: MUSH is a derivative of the original TinyMUD. In general, a MUSH is a place where players can hang out, socialize, program, and build. There are nearly four dozen public MUSHes running at the time of this writing. Most MUSHes use variants of either the PennMUSH 1.50 or TinyMUSH 2.0 servers (TinyMUSH 2.2 is derived from TinyMUSH 2.0); although behavior for all MUSH versions should be similar, code patches and unofficial modifications are very common, so what works on one MUSH won't necessarily work on another. This manual describes the "standard" behaviors for the official MUSH versions. MUSH looks very similar to the text adventure games that were popular in the mid-1980s. The difference between a MUSH and an Infocom game is the crudity of the parser, the user-extendibility, and the multi-player capability of the former. The programming language of MUSH is most similar to LISP, in its emphasis on lists and the way functions are evaluated. (Continued in 'man I3'.) & I3 The MUSH code dates back to spring of 1990, or so. The only presently surviving MUSH from that era is TinyTIM. Somewhat later came MicroMUSH, and TinyCWRU. All three were based off the original TinyMUSH, which was a heavily modified TinyMUD, done by Lawrence Foard. In January of 1991, PernMUSH was started, using MicroMUSH code as a base. Moonchilde began to distribute PernMUSH code a little while later, while MicroMUSH became MicroMUSE. In the spring, the 2.0 rewrite project was started. MUSH had become a messy collection of hacks, and most of them were non-standard; JT Traub (Moonchilde of PernMUSH), Glenn Crocker (Wizard of TinyCWRU, Dave Peterson (Evinar of TinyDungeon), and some other programmers got together to completely rewrite the MUSH code. (Continued in 'man I4'.) & I4 "Vanilla" MUSH code - the unadulterated original by Larry Foard - is a relic of the past; the MicroMUSH code off which PernMUSH is based has also essentially disappeared. TinyTIM has never distributed its code, and is significantly different from the other existing MUSH codes. These three MUSH versions, therefore, are not covered in this manual. PernMUSH switched to 2.0 code in November of 1991. The 1.x code fragmented into three versions - Spellbound, based off PernMUSH 1.13, SouCon, based off PernMUSH 1.14, and PennMUSH, based off PernMUSH 1.15, Moonchilde's final distribution. Only the last still exists, and it was the only one of the three versions to be publicly distributed; the name was changed to "Penn" to avoid confusion, since PernMUSH itself switched to using 2.0 (and now, 2.2). (Continued in 'man I5'.) & I5 In the fall of 1994, a number of wizards from large MUSHes got together to discuss the difficulties of TinyMUSH 2.0. TinyMUSH 2.0 releases over the past year had been, by and large, fairly buggy, due to lack of testing in a "production MUSH" environment (one open to large numbers of players). Furthermore, gripes about the bugs introduced with new "features" resulted in the virtual end of development of new features. It was decided to begin work on a server development project, called TinyMUSH 2.2 (there had been an aborted TinyMUSH 2.1 project which attempted to use the UnterMUD OIF format for a database). TinyMUSH 2.2 is more a continuing of TinyMUSH 2.0 development than an actual new server. It is the pet project of Jean Marie Diaz (Ambar of PernMUSH), Devin Hooker (Tyleet of Two Moons), and Lydia Leong (Amberyl of PernMUSH), in conjunction with a number of other helpful people. Every reference to TinyMUSH 2.0 in this manual also applies to TinyMUSH 2.2, unless specifically stated otherwise. (Continued in 'man I6'.) & I6 This manual is being written specifically for the PennMUSH 1.50 and TinyMUSH 2.0 / TinyMUSH 2.2 code releases, currently available via mellers1.psych.berkeley.edu, caisr2.caisr.cwru.edu, and ftp.cis.upenn.edu, respectively. Should you wonder at the references to dragons and similar objects throughout this manual, realize that the examples were originally written to fit the world of PernMUSH, presently located at astral.magic.ca 4201; as this manual has evolved, other examples from other worlds, such as The Belgariad (once at csa.bu.edu 4201) were thrown in, together with more generic objects. If you're trying to program any flavor of MUSH besides TinyMUSH 2.x, or a MUSH version earlier than 1.16, you are better off getting the version 1.15 of the Pern manual (mushman.1.15.shar.Z, probably located at the same place you got this manual). This manual is more detailed than that one; it is intended to cover MUSH programming extensively. I have tried to make the examples useful; if you work through the manual's Do It! sections, whether or not you completely understand them, you should have some rooms built for a house, a falcon, a vehicle, and some assorted other useful objects. (Continued in 'man I7'.) & I7 Please note that the 2.0 examples in this manual assume that your MUSH has a certain set of "common" aliases and command options. TinyMUSH 2.0 is highly configurable. If something doesn't work, or works differently from the way it is described in this manual, chances are that the configuration is different. The command "@list options" lists many of these options. Differences between 1.50 configurations are generally less visible, and generally only affect the way things are displayed, or the internals of the server, instead of directly changing the behavior of a command (for example, whether @switch matches all cases or just the first one). -- Amberyl (lwl@godlike.com) & 1 1. The Basics & 1.1 1.1 Some information These are terms which are in common use and which you should know: Player: the person who is reading this manual. Character: the virtual-person who wanders around the MUSH under the control of the player. Puppet: an object that is under the control of a character, who can see and perform actions of its own. Robot: something which appears to be a character, but is actually an external program, usually written in C or LISP, which, when run, connects to a remote computer and pretends to be a player. Some are quite sophisticated. This word is also used to refer to objects which are programmed, using MUSH code, to behave like players, or to perform a specific function, like deliver mail. Sometimes robots will be referred to as 'bots. Machine: Objects which are programmed to do certain things, like dispense mugs of klah, or repeat the word "Die" every five minutes, or any one of a thousand different possibilities. The second type of robot mentioned above is a type of machine. (Continued in 'man 1.1.2'.) & 1.1.2 Flag: Something which controls behavior of an individual object. It can be either on or off, and usually affects the kind of output the object sees/gives. The '@set' command is used to turn a flag on or off on a particular object. A frequently used terminology in this manual is "<object> is [not] set <flagname>". "Joshua is set OPAQUE" simply means that 'Joshua' has the 'OPAQUE' flag set on himself (i.e. it is turned on). Object Type: Objects are classified into one of four types: PLAYER, THING, ROOM, or EXIT. A 'player' is something which can be connected to via a name and password, who can own things and functions independently of anything else in the game. A 'thing' is a mobile object, which can be picked up, dropped, and manipulated in various ways. An 'exit' is a link which connects two locations in the game. These locations are normally known as 'rooms'. (You can be inside a thing, too, but a 'room' is the basic unit of building). In this manual, 'object' and 'thing' are sometimes used generically to refer to objects of any type. (Continued in 'man 1.1.3'.) & 1.1.3 Dbref: Every object in the database is assigned a number, called a "database reference number", or "dbref" for short. A hash sign normally prefixes a dbref, i.e., "#100". The dbref is the object's unique identifier (unlike its name, which could be the same as that of many other objects). Client: a program which replaces telnet. Clients are designed specifically to connect to MUDs, and frequently provide many features that are not available with ordinary telnet, such as macros, logging, hiliting, and the ability to switch between several worlds very quickly. Two popular clients are TinyTalk and TinyFugue. The latter is an improved version of the former. Both run under UNIX. Mark: the unit of currency on Pern. Other places use Pennies, Credits, Dollars, or even Cookies. The cost of an action varies from nothing to 100 Marks, for commands which are computationally expensive. Players normally get a certain amount of money every day. God: the person who is, overall, in charge of the MUSH. He is generally responsible for code maintenance and general order. He has powers not available to anyone else, and may not be @forced by anything. (Continued in 'man 1.1.4'.) & 1.1.4 Wizard: a game administrator, picked by God to help run the game. Wizards control all objects, and have special commands available to them. Wizards cannot hear things that ordinary players cannot. Royalty: a game administrator, a kind of "sub-wizard". Royalty can see all objects as if they controlled them, but cannot change them. Mortal: a non-wizard, non-royalty player. Control: Every object in MUSH is controlled by one or more things. Players always control the objects they own. Wizards control everything. There are other ways to control objects; these will be explained later. Commonly used abbreviations: IMHO: In My Humble (or Holy) Opinion RL: Real Life. The world outside MUDs. VR: Virtual Reality. The MUD world or worlds. OOC: Out of Character. Used when the player rather than the character is the one speaking, such as when a dragonrider character on PernMUSH wishes to comment on the features of the latest SPARC station. The opposite of this is 'IC' -- 'In Character'. (Continued in 'man 1.1.5'.) & 1.1.5 LOL: Laughs Out Loud. ROTFL: Rolls On the Floor Laughing. Now that the definitions are out of the way: Examples in this manual will be set off by [ Example Number ] at the beginning and = signs around the example. Words that appear in < > aren't supposed to be typed in literally. For example, if I use "<your name>", don't type that, but type your name instead. In examples, lines that begin with "> " indicate something that I've typed in. Words that appear in [ ] are optional; for example, if I use "examine[/brief]", you could either type "examine" or "examine/brief". Everything else is the MUSH's response. The 'main character' used in the examples is Elsa, on PernMUSH. Various other characters are used as props throughout the manual; most of them are PernMUSH players who have contributed in some way to this manual and are thus being immortalized by way of thanks. & 1.2 1.2 Basic Commands Looking around: The "look" command (abbreviated "l") allows you to look at your surroundings. "Look" by itself shows you the description of the room you are in. "Look <object or exit>" shows you the description of that object or exit. You may specify a thing by its name, its dbref number, as *player, or as "me" or "here". "Read" is identical to look in all respects. Possessive "look" allows you to look at something that someone or something is carrying. The syntax is just: "look <object>'s <thing>". You cannot look at someone's inventory if they are set OPAQUE or DARK. (OPAQUE and DARK are flags, and will be explained in more detail later.) (Continued in 'man 1.2.2'.) & 1.2.2 You automatically "look" at every room you enter. If you are trying to move rapidly through familiar areas, you may want to set yourself TERSE. This flag suppresses room descriptions and room success/fail messages; the MUSH will display only the location name, its contents, and the obvious exits. The MYOPIC flag prevents you from seeing the dbref numbers and flags on objects that you control. If you like your output "pure", you may wish to set yourself MYOPIC. * * * * * The "examine" command (abbreviated "ex") provides more detailed information about an object. If you do not control the object, this command will tell you who the object is owned by, and (in some configurations) show you any public attributes on that object, such as the desc, sex, and "last" connect time. If you are in the same room with the object, and the object is neither DARK nor OPAQUE, you are also shown the list of all its non-DARK contents, and any non-DARK exits. You can also use "examine/brief" to see just the owner's name. (Continued in 'man 1.2.3'.) & 1.2.3 1.50 has the "brief" command. This is like "examine", except that brief shows all object information EXCEPT for attributes. In 2.0, if the config directive "examine_public_attrs" is on, normal players will trigger the looked-at object's adesc when they examine/full the object, if the object is in the same room. Wizards, however, do not trigger this. If the object is not in the same room, you do not control it, and the "examine_public_attrs" config directive is off, you will see <Too far away to get a good look> instead of the description, and cannot get a contents list, although you will still see other public attributes and the owner's name. If you own the object or the object is set VISUAL, examine will show you the full list of attributes on the object, and other properties on it. To see a specific attribute, "examine object/attribute". (Continued in 'man 1.2.4'.) & 1.2.4 You may use wildcards - * and ? - in examine, to match a group of attributes. The '?' wildcard matches any single character (but must match at least one character); the '*' wildcard matches any number of characters (including "none"). For example, "examine object/v*" would show all attributes on an object whose names begin with v. "examine object/???" would match those attributes whose names are exactly three letters long. "examine object/a?*" would match those attributes whose names are at least two letters long and begin with 'a'. In 1.50, if you are set ANSI, the names of attributes and the dbref of their owners will be hilited in boldface. This makes the boundaries between attributes clearer, preventing large MUSHcode objects from looking like several pages of random characters. * * * * * (Continued in 'man 1.2.5'.) & 1.2.5 In 1.50, the @grep command can be used to search for a certain pattern on an object. Doing a "@grep <object>/<attributes> = <pattern>" prints a list of all attributes on <object> which contain text matching <pattern>. This <pattern> may contain wildcards. <attributes> is a possibly wildcarded pattern of attributes to match (like that for "examine" above). If you merely do "@grep <object>=<pattern>", the game assumes that you want to search all the attributes on <object>. @grep can take one of two switches, "list" or "print". The default is "list", which just returns the list of attributes that match the pattern. The "print" option can only be used if you are set ANSI. It prints out all the attributes which contain the pattern, hiliting the text which matches the pattern in boldface. MudCore provides a similar command, called +grep. This performs a similar task, though it only prints out the list of attribute names that contain the pattern, rather than printing out the attributes themselves. The syntax is, "+grep <object>[/<attributes>] = <pattern>". (Continued in 'man 1.2.6'.) & 1.2.6 MudCore also provides another useful command for listing the attributes on an object, called +lattr; it displays the attribute names, and the first line of text in that attribute. The syntax for this is, "+lattr <object>[<attributes>]". The command also takes several switches, which restrict the attributes displayed to those of certain types (attributes which are $commands, ^monitors, globally-defined, or none of the above). * * * * * The "@decompile <object>" command, when executed on an object that you own, outputs the sequence of commands you would need to type in order to duplicate that object. It may be used on any type of object. @decompile is very useful for creating files which can be uploaded onto another MUSH. To combine @decompile with TinyFugue, use the following sequence of commands (presuming you want to save an object called "Test" to a file called "testfile", and then upload it to another MUSH called "NewWorld"). (Continued in 'man 1.2.7'.) & 1.2.7 /wrap off [ in TinyFugue 3.0 and later: /set wraplog 0 ] /log testfile @decompile Test /nolog /world NewWorld /quote 'testfile /wrap on [ in TinyFugue 3.0 and later: /set wraplog 1 ] This sequence does the following: 1. Turn off wordwrap so extra newlines aren't inserted into the output. 2. Save MUSH output to a logfile. 3. Decompile the object. 4. End saving of MUSH output to a logfile. 5. Log onto a new MUSH. 6. Upload the object to the new MUSH. 7. Turn wordwrap back on. (Continued in 'man 1.2.8'.) & 1.2.8 This method allows you to easily transport objects across MUSHes. Also, if the game should unexpectedly crash, or the database gets corrupted, or some other disaster occurs which causes you to lose the original copy of the object, all you have to do is upload it again. You should always try to @decompile any objects that are especially valuable to you, and store them off-line, in case of such a disaster. ---------- Communications: You can speak using "say <message>", or by typing a double quote followed by the message (with no space after the "). Your name and the word "says" will be appended to the beginning of the message. A trailing " will be automatically added to the end of your message; don't type one in. (Continued in 'man 1.2.9'.) & 1.2.9 You can pose (emote) using "pose <action", or by typing a colon followed by the message (i.e., ":<action>"). Your name and a space will be automatically added. If you want to avoid the added space, use "pose/nospace <action>", or a semi-colon followed by the message (i.e., ";<action>"), or a colon followed by a space and then the message (i.e., ": <action>"). This is useful for doing possessives and similar things. In 1.50, most messages like "<person> <speech action> <message>" will include a comma - i.e, 'Annalyn says, "Hi."' or 'Annalyn whispers, "Hi."' In 2.0, this comma is missing, for historical reasons. * * * * * In 1.50, if you want to send a private message to yourself, you can "think <expression>". Pronoun substitution and function evaluation is performed. The equivalent command for 2.0 is "@pemit me=<expression>"; you can achieve a similar effect with "@pemit/silent me=<expression>" in 1.50, but "think" is easier to type. (Continued in 'man 1.2.10'.) & 1.2.10 There will be many times when you wish to communicate privately. The commands "page" and "whisper" (abbreviated "p" and "w") are used for this, as in, "page <person>=<message>" or "whisper <person>=<message>". Whisper sends a private message to a person who is in the same room as you. In certain configurations, people in the same room also see that you whispered something to that person; most MUSHes, however, do not use this "noisy" form of whisper. The page command sends a private message to a person on the MUSH; you can use it to talk to someone in a different room. The cost of page varies from MUSH to MUSH. You can whisper and page poses to people, by using a ":" in front of your message. If you whisper a pose to someone, you are told, "<Person you posed> senses "<Your name> <pose>". The person sees, "You sense <Person who posed> <pose>". If you page a pose to someone, you are told, "Long distance to <Person you paged>: <Your name> <pose>" The person sees, "From afar, <Person who paged> <pose>". Partial name-matching is performed on "page". You only have to type as many letters of the name as needed to distinguish it from the other players connected (for page) or the things in the room (for whisper). (Continued in 'man 1.2.11'.) & 1.2.11 "Lastpage" is supported in 1.50; to send a message to the person that you last paged, type "p =<message>" or "p <message>". "p" by itself will tell you the name of the person whom you paged last. Try to be careful with this, since it's easy to mis-type when trying to page someone different and accidentally sending the message to the wrong person (this happens a lot when people substitute '-' for the '=', accidentally). There are two ways to block pages in 1.50. The HAVEN flag, when set on a person, prevents them from receiving pages or @pemit *player messages. Alternatively, the use of a page lock prevents those who do not pass the lock from paging the player. Anyone who pages a HAVEN player is told, "That player is not accepting any pages." Anyone who fails the page lock will be told, "That player is not accepting your pages." If the target player has a @haven attribute set, the message in that attribute is sent to the paging player. The HAVEN player does not receive any notification that someone tried to page him. See the section on locks for instructions on how to set a page lock. (Continued in 'man 1.2.12'.) & 1.2.12 In 2.0, only page locks can be used to block pages. Players who fail the page lock will be told, "That player is not accepting pages.", or, if the target player has a @reject attribute set, the message in that attribute. Specifically, to prevent a single person from paging you, use, "@lock/page me=!*<player>". To prevent everyone except a single player from paging you, "@lock/page me=*<player>". To prevent everyone from paging you, "@lock/page me=#0". If you set an @idle attribute on yourself, anytime someone pages you, they will have that idle-message sent to them, and you will be notified that they have received it, together with a timestamp. This is very useful if you are unexpectedly called away from the keyboard and want to know when people paged you, while politely notifying them that you aren't there. (Continued in 'man 1.2.13'.) & 1.2.13 If you sent an @away attribute on yourself, if someone tries to page you when you aren't connected, they will receive that message. Customarily, this is used to tell people where you might be, when you will be back, and where you can be contacted. * * * * * The GAGGED flag, when set on a person, prevents him from doing anything but moving and looking. This flag may only be set by wizards, and is used as a punishment for players who are being particularly obnoxious. Related to GAGGED is the SLAVE flag. It prevents the player from using any commands that change the database. It may only be set by wizards. Flags will be explained in more detail later. * * * * * (Continued in 'man 1.2.14'.) & 1.2.14 The "emit" family of commands prints things without any name prepended. This is handled by switches (the basic command name is followed by a slash and the switch name) added to the commands @emit, @oemit, and @pemit. In 1.50, and in many configurations of 2.0, these command-switch combinations are aliased to shorter commands. The basic command is "@emit <message>". The message will appear exactly as you have typed it. Everyone in the room, including you, will see the message. In 2.0, normal "@emit" is shorthand for "@emit/here". An alternative way of doing emits is "\\<message>". There should be no space between the "\\" and the message. Also, two "\"s are necessary because \ is used as an escape character. (Continued in 'man 1.2.15'.) & 1.2.15 @emit has another switch, "room". "@emit/room" is often aliased to "@lemit". It shows a message to the outermost room you are in (the "absolute room"). For example, if you are in a chair inside a wagon inside a room, and you perform this command, the message is shown to the room, not to any of the intermediate objects. There is a limit of twenty intermediate layers. Related to the @emit command are @pemit and @oemit, and their variations. "@pemit <player>=<message>" will emit a message to a single person. If the person is in a different room, you must use "@pemit *<player>=<message>". In 2.0, this normal @pemit is shorthand for "@pemit/object". In 2.0, you are not told when you @pemit to someone; certain configurations of 1.50 will notify you that you @pemit'ed the message. In those configurations, using @pemit/silent will eliminate the notification. (Continued in 'man 1.2.16'.) & 1.2.16 "@oemit <player>=<message>" will emit the message to everyone in the room the @oemit'ing object is in, except the named player. 1.50 also permits "@oemit <room>/<player>=<message>", which emits the message to everyone in <room> but <player>; the @oemit'ing person must control or be in <room>. This latter form of the command is somewhat unnecessary, and thus non-existent, in 2.0; if it can locate where the named player is, the message will automatically go to that room. "@pemit/contents <object>=<message>" will emit a message to everyone in a room or container object, as well as to the container itself. @pemit/contents is generally aliased to @remit. The <object> is generally a dbref number or *player; if the object is a player, @remit to that player will show the message to all that the player is carrying, as well as the player himself. Note that @pemit without the /contents switch does not show a message to anything a player is carrying, unless the player is set with a @listen attribute of "*". (Continued in 'man 1.2.17'.) & 1.2.17 "@pemit/list <list of dbrefs>=<message>" will emit a message to a list of objects. These objects must be specified by their dbref numbers. This version of the command may also be combined with the /contents list, to send a message to several rooms. @emit, @pemit, @oemit, and @emit/room do not prepend the name of the player using the command. If you want to see where these @emit messages are coming from, you should set yourself NOSPOOF. In 2.0, NOSPOOF tells you the origin of any output that did not originate with you, directly or indirectly (i.e. you or an object that you @forced). If the origin is a player, "[PlayerName(#dbref player)]" will be prepended to the message. If the origin is an object, "[ObjectName(#dbref object){PlayerName}<-(#dbref forcing object)]" will be prepended by the NOSPOOF indicator. The forcing object may be a controller object, the player himself, or a wizard. It is the object which caused the command to be executed. (Continued in 'man 1.2.18'.) & 1.2.18 In 1.50, NOSPOOF tells you the origin of any type of @emit, whether or not you or one of your objects is responsible for it. It prepends "[<object name>]" to the output (or, if the configuration directive "PARANOID" is on, "[<object name>(<dbref number>)]") In 2.0, the HAVEN flag does not block out any type of @emit, though page locks will block @pemit and its relatives @pemit/contents (@remit) and @pemit/list; in 1.50, the HAVEN flag blocks @pemit and @remit to players. 1.50 page locks do not block emits of any sort. ---------- Going places: The @teleport command allows you to move to a different location instantaneously. Two criterion have to be passed in order for you to be able to teleport to a location: you must pass the room's teleport lock, and the room must either be JUMP_OK or controlled by you. You can teleport to a location by using "@tel <room number>". (Continued in 'man 1.2.19'.) & 1.2.19 You can teleport objects that you own, using "@tel <object>=<room number>". If you have an unwelcome guest or object in a room that you own, you may teleport that thing to any JUMP_OK location. You may also teleport the thing to another room that you own or through an exit that you own. In 1.50, and older versions of 2.0, you can find out where a player is with the command "@whereis <player>". The player will be told, "<Your name> has just located your position." @whereis will give you the name of the player's location, and, if the room is JUMP_OK or is controlled by you, the number of the location. If you can teleport to that room, you can go to the player by using "@tel loc(*player)". Some people find being "@whereis"'ed annoying. Generally, it is rude to "@whereis" people that you don't know, or to "@whereis" them without following up with a page message of some sort, explaining why you wanted to know their location. (Continued in 'man 1.2.20'.) & 1.2.20 Players can prevent others from finding out where they are by setting themselves UNFINDABLE. In this case, the locating player is told, "That player wishes to have some privacy." and the player who is set UNFINDABLE is notified, "<Your name> tried to locate you and failed." The UNFINDABLE flag can also be set on rooms. Anything inside an UNFINDABLE room cannot be located by mortals. Paranoid players and those having super-secret meetings may like this flag. Sometimes, there are objects in a room which you may wish to enter, such as a dragon. You can use "enter <object> to enter an object, provided that you pass the enter lock on the object, and it is ENTER_OK or owned by you. In order to get out of the object, use "leave". In 2.0, you must pass the leave lock in order to do this. (Continued in 'man 1.2.21'.) & 1.2.21 You can put enter and leave aliases on objects. The words stored in the @ealias and @lalias attributes on an object may be used in place of "enter <object>" and "leave". For example, if a chair has the @ealias "sit" and the @lalias "stand", "sit" may be typed in place of "enter chair" and "stand" may be typed in place of "leave". You can use multiple aliases by making them semi-colon separated, like exits are: "@ealias chair = sit down;sit;sit on chair" allows one to type "sit down", "sit", or "sit on chair" to enter the chair. ---------- Objects: Picking up and dropping objects is easy - simply "get" or "drop" the appropriate object. Possessive "get" also works - you can take from something using "get <thing>'s <object>". The thing that you are taking the object from must be ENTER_OK, and the object must not be locked against you. Also note that this applies to taking from non-room objects in general; if you are inside a thing, you will not be able to pick up obejcts inside of it unless it is ENTER_OK or you control it. (Continued in 'man 1.2.22'.) & 1.2.22 You can give someone an object or money by using the "give" command, i.e., "give <person>=<object>" or "give <person>=<amount of money>". In 1.50, the person must be set ENTER_OK in order for them to receive things. In 2.0, the giver must also pass the recipient's ReceiveLock, and the giver must pass the objects' GiveLock. To get a list of objects you are carrying, do an "inventory" (abbreviated "i"). This will show everything you are currently holding, including DARK objects owned by other players. ---------- Summary of command syntax: The basic commands for looking, moving, etc. are few in number and easy to remember. More complex commands, which generally are used to change the database, begin with the "@" symbol. (Continued in 'man 1.2.23'.) & 1.2.23 Many commands have "switches" which change the meaning of the command somewhat. "Examine" is one such command, with /brief and /full switches. All commands with switches have a default. In 2.0, if a command has more than one switch which are not mutually exclusive, you may use several switches at once by doing command/switch1/switch2/etc. 1.50 also allows for switches, but you can only give one switch to a command at a time. Flags, mentioned repeatedly above, are used for a wide variety of purposes. They are either on or off, with "@set <object>=<flag name>" to turn one on and "@set <object>=!<flag name>" to turn one off. Finally, three commands are extremely useful: WHO, LOGOUT, and QUIT. The WHO command, for ordinary players, shows the list of all non-DARK connected players, the time they have been on, the time since they last typed a command, and their @doing, which is a short string set by "@doing <message>". LOGOUT, which only exists in 2.0, disconnects a player from a character, returning him to the login screen. QUIT disconnects the player from the MUSH completely. (Continued in 'man 1.2.24'.) & 1.2.24 There is an inactivity timeout. If you idle too long, the MUSH will disconnect you. The idle time allowed is generally one hour. It can be found by doing a "@list options" in 2.0, and an "@config" in 1.50. In 2.0, Wizards may change this value on themselves by setting their @timeout attribute. Wizards who exceed the inactivity limit for mortals are automatically set DARK. (In 1.50, this only occurs if the Wizard is already set UNFINDABLE). 2.0's "@list" command can be used to find lists of all the commands, flags, attributes, configuration parameters, options, command switches, and other useful things. Typing "@list" by itself will give the options. The closest 1.50 equivalent to "@list" is @config, which outputs the important MUSH configuration parameters. =============================================== (Continued in 'man 1.2.25'.) & 1.2.25 [Example 1.2: Demonstration of Basic Commands] > "Hi, everyone. Elsa says "Hi, everyone." > :waves. Elsa waves. F'jon waves back. > ex/brief f'jon [ 1.50: just plain "ex F'jon" ] F'jon is owned by F'jon > @emit The sun shines. The sun shines. > w f'jon=How are you? You whisper "How are you?" to F'jon. F'jon whispers "Fine." > p jyrax=Hello. You paged Jyrax with 'Hello.'. Jyrax pages: Hello! > @whereis Jyrax Jyrax is at: Ramp of Ruatha Hold. =============================================== & 1.3 1.3 Attributes Attributes are places in an object where information can be stored. The most common are the desc, succ, fail, and drop attributes. Each of these have corresponding o- and a- attributes. For example, in addition to succ, there are osucc and asucc. O stands for Other, and A stands for Action. The name of the person who performed the action, and a space are automatically prepended to the beginning of every O message. This person is sometimes called the "enactor"; you will read more about enactors later on in this manual. The syntax for setting these and other standard attributes is "@<attribute> <object>=<message>", as in "@desc me=A young woman." "Desc" stands for Description. It is the message someone sees when they look at something. An "odesc" is seen by everyone but the person looking and the object being looked at. An "adesc" is the list of actions executed by an object when it is looked at. (Continued in 'man 1.3.2'.) & 1.3.2 ====================== [ Example 1.3a: Desc ] > @desc me=Elsa is a slim young woman. Set. > @odesc me=looks at Elsa. Set. > @adesc me=:smiles. Set. [Now, if F'jon looks at me, he'll see: Elsa is a slim young woman. When he does that, Jyrax, who is also in the room, will see: F'jon looks at Elsa.] Elsa smiles. [Elsa's adesc forces her to do a pose, which is displayed to everyone, just as if she had typed :smiles. as usual.] ======================= (Continued in 'man 1.3.3'.) & 1.3.3 Note that many people find @odescs and @adescs rather irritating. In general, one should never use both (the above is for demonstration purposes only). If you want to be notified that a person is looking at you, one useful thing is: "@adesc me=@pemit me=%N just looked at you." (or, in 1.50, "@adesc me=think %N just looked at you.") The abbreviation %N is explained in detail later; it means "eNactor Name", and is the name of the person who triggered off the action. Succ is short for success, and is the message seen by someone when he successfully use something. Succ on an object is displayed when that object is picked up. Succ on an exit is displayed when something walks through it successfully. Succ on a room is displayed when someone looks at it, under normal circumstances. An osucc is shown to everyone else in the room, and an asucc is the action list taken when something is successfully used. (Continued in 'man 1.3.4'.) & 1.3.4 ====================== [ Example 1.3b: Succ ] > @succ me=You lift up Elsa. Set. [ Now, when F'jon does a 'get Elsa', he sees, 'You lift up Elsa.' Elsa is now in F'jon's inventory, being carried around. ] ====================== Fail is short for failure, and is the message seen by someone when he fails to successfully use something. Usually something fails to work because it is locked. (Locks will be explained in detail, later). Fail basically works like succ does. Drop is seen by the person who drops an object. Odrop and adrop have two uses: normally, they are performed when someone drops something, but an odrop on an exit is shown to the people in the room that a person enters (as opposed to an osucc, which is shown to the people in the room that a person leaves). (Continued in 'man 1.3.5'.) & 1.3.5 =============================== [ Example 1.3c: Fail and Drop ] [In the previous example, Elsa was picked up by F'jon. She's decided that she didn't much care for that, so she's going to take some action:] > @lock me=me Locked. [This prevents anyone from picking Elsa up in the future.] > @fail me=Elsa glares at you. Set. > @odrop me=drops Elsa, who looks rather irritated. Set. [Now, when F'jon types 'drop Elsa' he just sees "Dropped." since Elsa did not set a drop message on herself. Jyrax, however, sees: "F'jon drops Elsa, who looks rather irritated." Elsa doesn't get any message, except for the description of the room she's dropped into.] [F'jon tries to pick up Elsa again, but this time, she's locked, and 'get Elsa' shows him the message, "Elsa glares at you."] =============================== (Continued in 'man 1.3.6'.) & 1.3.6 The @succ and @drop family of attributes is triggered in one more case - when objects are given. The giver triggers off the @drop family of attributes; the recipient triggers off the @succ family. * * * * * There is another attribute, kill, which is used when something is killed. To kill something, use "kill <thing>=<money>". For every Mark you spend, you have an additional 1% chance to kill the object. The default cost of killing something is 10 marks. Spending 100 Marks always works. Killing something halts it and sends it home, and pays it half the amount of money that was used to kill it. It also triggers the @kill, @okill, and @akill attributes (@death, @odeath, and @adeath in 1.50). Killing is usually strongly discouraged. Certain things, like wizards, cannot be killed. Objects with the IMMORTAL flag (or, in 1.50, with the Immortal power) cannot be killed, but this privilege can only be granted by a wizard. * * * * * (Continued in 'man 1.3.7'.) & 1.3.7 The "move" set of attributes are used when something moves from one location to another, no matter what the means of moving - going through an exit, entering/leaving an object, going home, or teleporting. The @move, @omove, and @amove attributes can only be set on players and objects. There is also a set of attributes specifically for messages sent when a @teleport is done. The @tport, @otport, and @atport attributes are used when you arrive at your new destination. The @oxtport works like the @otport does, except the message is shown to the others in the room that you just left. There is no @xtport or @axtport. * * * * * In 2.0, there are a large number of other attributes related to succeeding or failing in attempts at various commands (usually related to passing the appropriate lock). Only the "basic" form of the attribute is listed in the table below; prepend "O" or "A" to it to get the other forms. (Continued in 'man 1.3.8'.) & 1.3.8 @tfail: failure in teleportation @dfail: failure to drop an object @gfail: failure to give an object @rfail: failure to receive an object when it is given The only one of these which can easily be simulated in 1.50 is @tfail; to get the effect of @tfail, use the "@efail" set of attributes. * * * * * The final attribute is not like the others. It's the sex attribute, which is used for pronoun substitution, and lets you declare your gender. The syntax is "@sex me=<male or female>". You don't have to make yourself male or female, but it's strongly suggested. The gender "plural" is also supported in 2.2, and modifies the grammar of messages appropriately. (Continued in 'man 1.3.9'.) & 1.3.9 There are more attributes, but these are the standard ones. Certain MUSHes may add their own global attributes, such as "EMAIL". It is also possible to make up attributes with arbitrary names, using "&" as the beginning of the name rather than "@". These user-named ("non-standard") attributes are described in greater detail later in this manual; for now, just note that attribute sets of the format "&<attribute name> <object>=<value>" are valid and set <attribute name> on <object> with some text string <value>. & 1.4 1.4 Editing One rather useful command is @edit. This allows you to perform a substitution on an attribute, so you can correct typos without having to retype the entire thing. The syntax for this command is: @edit <thing>/<attribute>=<old string>,<new string> If <old string> is the character ^ by itself, <new string> will be prepended to the contents of <attribute>. If <old string> is the character $ by itself, <new string> will be appended to the contents of <attribute>. If commas, brackets, parentheses, percent signs, and other 'special characters' appear in either string, curly braces { } must be used to set each string off. If <attribute> is a wildcard pattern, the @edit command does a global replace for all attributes that match the pattern; for example, @edit object/v*=a,b would replace the letter a with the letter b in all attributes whose names begin with v, in the object. (Continued in 'man 1.4.2'.) & 1.4.2 Certain types of patterns are extremely difficult to edit; these are usually patterns which include the { or } characters, which are "special" to @edit. Several methods exist to get around this problem; one is to generate the string using edit(); the other is to take advantage of some features of a client such as TinyFugue. In TinyFugue, type, exactly as printed: /def redit = /grab $(/recall %{0-1}) This command allows you to pull into your input buffer any number of lines which could normally be obtained via the /recall command in TinyFugue. This allows you to edit text that has already been displayed to you. Therefore, to directly edit an attribute within TinyFugue, one can type, "examine <object>/<attribute>" and then "/redit 1" to edit the text which is then displayed. * * * * * (Continued in 'man 1.4.3'.) & 1.4.3 To copy an attribute from one object to another, you can use: @force me=&attrib1 object1=get(object2/attrib2) It is also possible to copy attributes via a version of the @set command: @set <new obj>=<new attrib>:_<old obj>/<old attrib> This copies <old attrib> from <old obj>, setting it into <new attrib> on <new obj>. This somewhat clumsy method can be worked around by using a built-in command -- @mvattr, and, in 1.50, @cpattr. "@mvattr <object>=<old name>,<new 1>,<new 2>,<etc.>" will move an attribute. Moving an attribute always wipes it out. If you want to copy the attribute, you must move it to itself as well, using "@mvattr <object>=<old name>,<old name>,<new name 1>,<new name 2>,<etc.>" (Continued in 'man 1.4.4'.) & 1.4.4 In 1.50, there is, in addition to the @mvattr, a copy command: @cpattr <obj>/<attr>=<obj1>/<attr1>,<obj2>/<attr2>,<etc.> This copies <attr> from <obj> to <attrN> on <objN>. You can specify a large number of object-attribute pairs (limited only by the MUSH configuration, which is generally 100). The original object-attribute pair is NOT wiped out. Also, even if one copy fails (you specified a non-existent object, or one that you don't control, etc.), the game continues to try to finish copying to the other object-attribute pairs. MudCore provides two alternatives: +cpattr and +mvattr. "+cpattr <obj>/<attr> = <obj1>/<attr1> <obj2>/<attr2> <etc.>" is more or less identical to the 1.50 @cpattr save in syntax. There are two forms of the +mvattr command, one which moves one attribute on an object, to another, and one which is used to move from one object, mass groups of attributes which match a certain wildcard pattern, to another object, keeping the attribute names the same: "+mvattr <obj1>/<attr1> = <obj2>/<attr2>" does the first, and "+mvattr <obj1>/<wildcard pattern> = <obj2>" does the second. & 1.5 1.5 Do it! Issue the following commands: @desc me=<description> @lock me=me @adesc me=@pemit %N=<Your name> smiles and returns your glance. @fail me=<message to show when someone tries to pick you up> @sex me=<male or female> @set me=enter_ok You are now described, safe from robbery, and have a proper gender. The adesc also allows you to see who is looking at you. (Don't worry about the %N; it will be explained later). Also, since you are set ENTER_OK, people can give you objects and money. One additional command you may wish to issue is "@lock/enter me=me", which prevents people from entering you. & 1.6 1.6 A parser discussion If you are completely new to MUSH, you may wish to skip this section and come back to it later. Also, if you're programming 1.50 or 2.0 exclusively, you do not need to read this section, which explains parser and game output differences between 1.50 and 2.0. The command parsers for 2.0 and 1.50 are no longer significantly different. Nested brackets, evaluation of the S() and U() functions, and evaluation order should now be consistent. There are some differences in messages between 1.50 and 2.0, particularly in the building commands. Some of the more obvious ones: @create: 1.50 Created: Object <number>. 2.0 <Object> created as object <number> @clone: 1.50 Cloned: Object <number>. 2.0 <Object> cloned, new copy is object <number>. (Continued in 'man 1.6.2'.) & 1.6.2 @destroy: 1.50 Halted. You get your 10 pennies deposit back for <object>. Destroyed. 2.0 You get your 10 pennies deposit back for <object>. Destroyed. <Object> has left. Errors in syntax are handled differently by the two parsers. Neither parser reacts well to missing curly braces, brackets, and parentheses. 2.0 in particular will often produce extremely garbled output if matching is not correctly done; if you are getting output from a 2.0 expression that begins with a large number of spaces and ends with a group of right braces, brackets, or parentheses, you almost certainly have a mismatch. By contrast, 1.50 will generally return the string up the point of the mismatch. This difference in error handling should be noted by those attempting to debug code under two different versions. (Continued in 'man 1.6.3'.) & 1.6.3 These differences, and some other minor cosmetic ones, will be encountered frequently in the text of this manual. If you are using 1.50, please do not be alarmed at them. & 2 2. Objects Objects, for the purposes of this discussion, are anything which are not rooms or exits. Players are very special kinds of objects, which have rules and restrictions in addition to those normally imposed on objects. Players cannot create other players (save for the wizard-only @pcreate command, discussed later). & 2.1 2.1 Basic object commands: @create, @destroy, @force, and @name The syntax for object creation is "@create <object name>=<cost>". If you do not specify a cost, the default is 10 marks. If you have enough money, the MUSH will respond "<Name> created as object <#dbref>" and the object will be placed in your inventory. You may refer to an object either by its name, or by its object number. The MUSH parser performs name matching, so after you create a Blue Tunic, for example, you can refer to it simply as "Blue," unless you have a Blue fire-lizard with you. In that case, you could use "Blue T" instead, or "Tunic." You can change the name of an object with "@name <old name>=<new name>". (This applies to things, exits, and rooms, not just things). Your name can be changed by "@name me=<new name>" (1.50 requires, for player name changes, "@name me=<new name> <password>"). (Continued in 'man 2.1.2'.) & 2.1.2 The syntax for object destruction is "@destroy <objectname>". @destroy may be abbreviated to @dest. In general, you may only destroy objects that you own. It is important that you destroy objects once you no longer need them, or they will clutter the database. MUDs have a history of going down due to database bloat, so it is important to try to conserve space. You will also get back whatever amount of money it cost you to create the object. If you want anyone to be able to destroy your object, you should set it DESTROY_OK. This is useful for objects which are meant to be temporary, such as messages. If you want to avoid accidental destruction of an object, you should set the object SAFE. This flag prevents the @destroy command from being used on the object; instead, you must use @destroy/override (@nuke). This overrides any protections on the objects, which include the SAFE flag, wizard ability to destroy objects they don't own, and wizard ability to destroy players. (Continued in 'man 2.1.3'.) & 2.1.3 You can make an object do something by using "@force <object>=<action>" or "<dbref> <action>". The object behaves as if it had typed the command. There are some restrictions on this, involving the INHERIT flag, which will be explained later. Note that since the semi-colon character is used to delimit commands, use of it in a @force will be considered to separate different commands you want to force the object to do; if this is not what you desire, you must surround the string with { braces }. If you want someone else to be able to see the programming on one of your objects, set it VISUAL. The other person can then "examine" it. ======================================= [ Example 2.1: Basic object commands ] > @create Test Object Test Object created as object #8032 > i You are carrying: Test Object(#8032) You have 168 Marks. (Continued in 'man 2.1.4'.) & 2.1.4 > drop test Test Object has left. Dropped. > @force test=:bounces. Test Object bounces. > #8032 :rattles. Test Object rattles. > @set test=destroy_ok Set. > @destroy #8032 You get your 10 Mark deposit back for Test Object. Destroyed. Test Object has left. ======================================= & 2.2 2.2 Puppets Puppets are special objects which can hear and behave almost like normal players. They are under the control of a player, and relay back everything they see and do to that player. Objects that have the KEY flag set cannot be picked up by puppets. Exits set KEY cannot be used by puppets. In fact, more precisely, puppets are treated as if they cannot pass any @locks that might be set on those objects. This forces players to solve puzzles themselves, instead of having their puppets do all the dirty work. In 1.50, the KEY flag also causes objects to go home when the person carrying them goes home or teleports. (Continued in 'man 2.2.2'.) & 2.2.2 ======================= [ Example 2.2: Puppet ] > @create Test Puppet Test Puppet created as object #8051 > @set test=puppet Test Puppet grows ears and can now hear. [2.2: "is now listening"] Set. > i You are carrying: Test Puppet(#8051p) You have 168 Marks. > drop test Test Puppet> Dropped. Test puppet has left. Test Puppet> Elsa's Room Test Puppet> This is a small, sparsely furnished room. Test Puppet> Contents: Test Puppet> Elsa Test Puppet> F'jon (Continued in 'man 2.2.3'.) & 2.2.3 Test Puppet> Obvious exits: Test Puppet> Out Test Puppet has arrived. Dropped. > @force test=:squeaks. Test Puppet squeaks. > #8051 out Test Puppet> Corridor Test Puppet> A long, featureless corridor. Test Puppet> Contents: Test Puppet> Jyrax Test Puppet> Ball Test Puppet> Obvious exits: Test Puppet> Elsa's Room > #8051 :squeaks. Test Puppet> Test Puppet squeaks. > #8051 get ball Test Puppet> Taken. Test Puppet> Jyrax says "Hello, Test Puppet." (Continued in 'man 2.2.4'.) & 2.2.4 > @set #8051=!puppet Test puppet loses its ears and becomes deaf. [2.2: "is no longer listening"] Cleared. ======================= The puppet echoes back almost everything it hears. Note that when the puppet is in the same room as its owner, it does not echo back the output of commands like :. This feature prevents the puppet's owner from getting a lot of annoying echoed messages. Puppets and other objects that listen trigger the adescs of objects that they enter, either via teleport, or via enter. This is because they are considered to "look" at the object when they enter it. * * * * * (Continued in 'man 2.2.5'.) & 2.2.5 Somewhat related to the PUPPET flag is the ROBOT flag. The ROBOT flag cannot be set like other flags are; instead, you must use the "@robot" command to create a robot. Something created via this command is a cross between a player and a thing. Like a player, it may be connected to and moved about independently. Like a thing, it owns no objects, and belongs to the player who created it. It is "officially" considered of type PLAYER. Things which are set ROBOT cannot be picked up by robots; exits which are set ROBOT cannot be gone through by robots. This behavior is similar to KEY for puppets. The robot flag is useful if you are running a "real" robot (an external C or LISP construct), which needs to be a player, but still needs to be under your control. Also, a robot can use the OUTPUTSUFFIX and OUTPUTPREFIX commands, which many robot programs require. When you are not connected to the robot as a player, you can use it much like you would use a thing; for example, a robot may be set PUPPET. Creating a robot (via "@robot <name>=<password>") isn't cheap, though; it will, on most MUSHes, cost you 1000 pennies. Once created, it isn't possible to get rid of the robot without asking a wizard to destroy it. & 2.3 2.3 A Note about Privacy: @sweep Occasionally, conversations of a private nature occur on MUDs. From time to time, the presence of a puppet is forgotten. The @sweep command shows everyone and everything in that room which is listening. Something is considered to be listening if it is: a connected player, a puppet, an object with a @listen attribute, an object with ^-listen patterns (explained later), an AUDIBLE object or exit, or an object which has a user-defined command on it. The latter will be explained later. @sweep in 1.50 and 2.0 are significantly different, and so are discussed here separately. * * * * * 2.0 @sweep will show the following types and output: (Continued in 'man 2.3.2'.) & 2.3.2 Connected players. [player connected] Disconnected players. [player] Puppets belonging to connected players. [puppet(Owner name) connected] Puppets belonging to disconnected players. [puppet(Owner name)] Objects with a @listen or ^-pattern set. [messages] Objects with $commands set. [commands] Audible exits. [audible] Audible objects, if you are in the object. [audible] Parent objects. [parent] This type of sweep, the default, checks both your location and your inventory for listeners. There are also several switches for doing more specific sweeps. Note that specific sweeps only warn you about one form of listening - the form you are specifically sweeping for. You may, however, combine several switches to make a @sweep of whatever level of specificity you wish. @sweep/commands lists only objects which have $commands set - "[commands]" in the generic @sweep. (Continued in 'man 2.3.3'.) & 2.3.3 @sweep/listeners lists only objects which have a @listen set - "[messages]" in the generic @sweep. @sweep/exits checks for AUDIBLE exits in the room. @sweep/players lists only players and puppets. It does not tell you if a player or puppet's owner is connected, although it does tell you the owner of a puppets. @sweep/connected lists only connected players and puppets belonging to connected players. It tells you who owns the puppets. @sweep/here checks your location but not your inventory. Its reverse, @sweep/inventory, checks your inventory but not your location. * * * * * 1.50 @sweep will show the following types and output: (Continued in 'man 2.3.4'.) & 2.3.4 Connected players. [speech]. (connected) Disconnected players. [speech]. Puppets belonging to connected players. [speech]. Puppets belonging to disconnected players. [speech]. Objects with a @listen or ^pattern set. [speech]. Objects with $commands set. [commands]. Rooms with a @listen or ^pattern set. (this room) [speech]. Rooms with $commands set. (this room) [commands]. AUDIBLE exits, if the room is AUDIBLE. [broadcasting]. AUDIBLE objects, if you are in it. (this room) [broadcasting]. 1.50 "@sweep connected" will show the following types and output: Connected players. is listening. Puppets belonging to connected players. [owner: <owner>] is listening. Both types of 1.50 @sweep check both location and inventory. 1.50 will take the command switches "connected", "here", "inventory", and "exits", but not in combination. (Continued in 'man 2.3.5'.) & 2.3.5 * * * * * MudCore provides a command called +awake, which is a much more paranoid version of @sweep; it allows you to discover not just objects which are, in actuality, listening, but objects which might be listening. It follows chains of message forwarding through AUDIBLE exits and @forwardlists, as well as objects in the inventory of other objects set with @listen "*"; it thus allows you to discover just not what in the room might be broadcasting, but where it's broadcasting to. The command displays information in a tabular format, noting the Connected status of the owners of the objects. * * * * * (Continued in 'man 2.3.6'.) & 2.3.6 Puppets on MUSHes may be set DARK, but that flag has no effect on them, just as it has no effect on a normal player. However, wizards and wizard objects may be set DARK, and will disappear from the list of the room's contents, although they still appear on a @sweep. As a further caution, wizards who are set DARK also do not appear on the WHO list of connected players. (The rationale for allowing wizards to do this is explained in the last section of the manual). Doing a @sweep several times throughout a private conversation is strongly encouraged. Remember that you are not ever guaranteed privacy on a MUD. & 2.4 2.4 Attributes on Objects: Action Lists The @asucc, @afail, and @adrop attributes are frequently used on objects. Any command normally available to the object may be used in an action list. Object may also re-program themselves, by enclosing the commands which are to be deferred in curly brackets (braces), { }. ========================== [ Example 2.4: Bubbly Pie] > @create Bubbly Pie Bubbly Pie created as object #1308 > @adrop pie = @adrop me={:splatters on the floor.;@destroy me};:lands on the floor. If it's dropped again, it'll splatter. Set. > drop pie Bubbly Pie has left. Dropped. Bubbly Pie lands on the floor. If it's dropped again, it'll splatter. > get pie (Continued in 'man 2.4.2'.) & 2.4.2 Bubbly Pie has left. Taken. > drop pie Bubbly Pie has left. Dropped. Bubbly Pie splatters on the floor. You get your 10 Mark deposit back for Bubbly Pie. Bubbly Pie has left. ========================== & 2.5 2.5 Machines: @cost, @pay, and @clone Machines use up money as they run. Normally, the cost is 1/64 of a Mark per command, plus the normal cost of the command. For example, if a machine @creates something, the cost is 10 and 1/64 marks. In general, the cost of running a machine is insignificant, but an object which is in an infinite loop can quickly become extremely costly. Many MUSHes have vending machines. These exist to provide some color to the world, as well as to net the owner some money. It is generally considered impolite to have set the cost of a vending machine to anything far above what it costs to run the machine. The @cost attribute is an integer, which is the amount of money a machine must be given before it executes its "pay" programming. The @pay, @opay, and @apay attributes are used to program vending machines. (Continued in 'man 2.5.2'.) & 2.5.2 The syntax of the commands is standard: "@<command> <object>=<whatever>" and the attributes are triggered by the payment. The MUSH automatically gives change to people who give a machine too much money, and rebukes those who are too stingy, so you don't have to make sure that people are giving your machine that correct amount of money. Many vending machines produce objects. This is generally done by having a copy of the object inside the machine, and then having the machine clone the object. The syntax of that command is @clone <object>. The object can be referred to either by name or number. @clone places the newly created object in the room (or, in 2.0, if the /inventory switch is given, in the object's inventory). The object is identical in all ways, save for a different object number. Clone-type vending machines should be set OPAQUE. This flag, when set on something, prevents others from seeing what an object is carrying. Also, objects given by a vending machine should be set DESTROY_OK so that people can destroy them when they're done with them. (Continued in 'man 2.5.3'.) & 2.5.3 To @destroy an object you do not own, it must be DESTROY_OK and you must be carrying it; if you @destroy such an object, you will receive the message, "Destroyed. <Owner>'s <Object>(<#dbref>)". In 2.0, the owner will be told, "You get your 10 Mark deposit back for <Object>." In 1.50, the owner is given that message, and gets the additional notification, "Destroyed. <ObjectName>(<#dbref>) by <Player>." If you don't want to see annoying "You get your 10 Mark deposit back for <whatever>." messages every time someone destroys one of your objects, set the object QUIET. =================================== [ Example 2.5: Bubbly Pie Vendor ] > @create Hot Pie Hot Pie created as object #8301 > @create Bubbly Pie Vendor Bubbly Pie Vendor created as object #8302 > i You are carrying: (Continued in 'man 2.5.4'.) & 2.5.4 Bubbly Pie Vendor(#8302) Hot Pie(#8301) You have 158 Marks. > @set #8301=destroy_ok Set. > @succ #8301=You gobble down the hot bubbly pie. Set. > @asucc #8301=@destroy me Set. > @cost vendor=11 Set. > @pay vendor=The bubbly pie vendor sets down a hot, steaming pie. Set. > @apay vendor=@clone #8301 Set. > @tel #8301=#8302 Hot Pie has left. > @set vendor=opaque Set. > drop vendor (Continued in 'man 2.5.5'.) & 2.5.5 Bubbly Pie Vendor has left. Dropped. > give vendor=11 You paid 11 Marks The bubbly pie vendor sets down a hot, steaming pie. > get hot pie Hot Pie has left. You gobble down the hot bubbly pie. You get your 10 Mark deposit back for Hot Pie. Hot Pie has left. =================================== Database size is frequently an issue on MUSHes. One way of preventing the dreaded "database bloat" syndrome is to avoid using @clone whenever possible. MUSH provides for object "inheritance" via "parenting". (Continued in 'man 2.5.6'.) & 2.5.6 An object becomes parented via "@parent <child>=<parent>", where <child> and <parent> are of any type (and can be of different types). A child object inherits all attributes of the parent, unless it already has an attribute of that name, in which case the attribute on the child "blocks" that of the parent. While no object is allowed to have more than one parent, multiple levels of parents are allowed, with the "ancestors" passing their traits down to the child. In 2.0, objects also inherit any exits that their parents might have; these show up on the "Obvious Exits" list. In neither version of MUSH do objects inherit the locks or flags of the parent. Certain special attributes, like @startup, are also not inherited. Because in almost every regard, the child is identical to the parent, cloning becomes almost unnecessary; instead of using @clone, @create an object of the same name, set the flags and lock to be identical, and then @parent the 'clone' to the original object, so all attributes are 'inherited'. There is a convenient shortcut command in 2.0 which does all of this: "@clone/parent <parent>". This saves a large amount of database space and also makes it very easy to maintain updates of objects that you are making available for public consumption. (Continued in 'man 2.5.7'.) & 2.5.7 It is safe to allow someone to @chown an object after it is @parented; the child does not inherit any special kind of control powers over the parent. The child can, however, still inherits attributes off the parent object, even if the owners of the child and parent are different. & 2.6 2.6 Limiting object uses: @charges and @runout An object may be given charges, limiting its number of uses. Each success on the object uses one charge. To set the number of charges on an object, use: "@charges <object>=<integer>". When the charges on an object are exhausted, its @runout action list is executed. The syntax is "@runout <object>=<action list>". The action list is in the same form as the action lists used in asucc, adrop, etc. Any use of an action-attribute, such as @adrop, @asucc, @afail, @ause, or @ahear, will constitute use of a charge. @trigger also uses up a charge, although no other MUSH commands do. "@verb" triggered on something uses up charges, if an @a-action is specified (see the section on @verb later in the manual; basically, this command defines @attr/@oattr/@aattr-like triplets). Note that an object runs out when the number of charges is equal to 0. In other words, an object with @charges 2 can be used 3 times before it executes the @runout (the @runout is executed immediately after the third use of the object). (Continued in 'man 2.6.2'.) & 2.6.2 ============================ [ Example 2.6: Mug of Klah ] > @create Mug of Klah Mug of Klah created as object #135 > @asucc mug=@pemit %N=You drink some of the klah. Set. > @charges mug=2 Set. > @runout mug=:is now empty.; @name me=Empty Mug; :shatters.; @destroy me Set. > drop mug Mug of Klah has left. Dropped. > get mug Mug of Klah has left. Taken. You drink some of the klah. > drop mug Mug of Klah has left. (Continued in 'man 2.6.3'.) & 2.6.3 Dropped. > get mug Mug of Klah has left. Taken. You drink some of the klah. > drop mug Mug of Klah has left. Dropped. > get mug Mug of Klah has left. Taken. You drink some of the klah. Mug of Klah is now empty. Empty Mug shatters. You get your 10 Mark deposit back for Empty Mug. Empty Mug has left. ============================ & 2.7 2.7 Reacting to the Environment: @listen and @ahear These two attributes allow machines to respond to things they hear, including speech, poses, @emits, and whispers. The attributes are extremely useful for programming machines that sit idle until they hear the string "has arrived," or similar things. The "listen" string usually contains one or more wildcard characters, so that a single pattern of words can be listened for out of what may be an extremely complex string. For example, an object whose "listen" is set to "*smiles *" will match any occurrence of "smiles" followed by a space, such as "Elisa smiles happily." The "ahear" is the action list taken when the object's "listen" string is matched. Ahear's general format is similar to asucc, adrop, etc. There are also two refinements of ahear: amhear and aahear. Amhear only responds to strings that are generated by the object itself; ahear only responds to strings that are not generated by the object itself. Amhear plus ahear equals aahear, which allows the object to respond to anything it hears. (Continued in 'man 2.7.2'.) & 2.7.2 =================================== [ Example 2.7: Autogreet ] > @listen me=* has arrived. Elsa - Set. > @ahear me=:waves to %N. Elsa - Set. [ This forces Elsa to automatically wave to something entering the room. ] Abakin has arrived. Elsa waves to Abakin. =================================== Autogreets are somewhat annoying and are probably best used merely as programming examples. (Continued in 'man 2.7.3'.) & 2.7.3 One particularly costly loop is to have an object with a @listen of "*" and an @ahear which triggers on some particular pattern of words, sitting in a noisy room where the @ahear is evaluated several times a minute. This will cause your money supply to drop rapidly, although it is unlikely that you will notice the loop until your money is gone. It is noted here in hopes that you will avoid making this mistake. MUSH allows an additional way for objects to listen. Attributes can be scanned for ^ listen patterns (in 1.50, this only happens if no @listen is set on an object). These pattern triggers are in the format "^<pattern>:<action>". They function like a @listen/@ahear combination. Unlike @listen, messages which match the pattern do not "go through" to the objects that the object is carrying. In order to activate patterns on the object, though, it must be set LISTENER (in 1.50) or MONITOR (in 2.0). Also, the object which is the origin of the message must be able to pass the use lock of the object with the ^-pattern. (Continued in 'man 2.7.4'.) & 2.7.4 =================================== [ Example 2.8: Echo device ] > @create Echo Created: Object #4570. > drop echo Dropped. > @va echo=^* says, "*":@emit You hear an echo... %1 %1 %1... [ The first * matches %0, the second * matches %1. This is the "stack", described in more detail later. ] Echo - Set. > "Testing. You say, "Testing." You hear an echo... Testing. Testing. Testing.... =================================== & 2.8 2.8 Keeping your objects safe Unless you enjoy having your stuff stolen, or are planning on giving something away, it is usually a good idea to @lock the things you own. "@lock <object>=me" will prevent anyone other than yourself from picking up an object. Do note that it's usually rude to pick things up without permission from the owner. Locks are useful on more than just objects - they are often used on exits. For example, you might want to lock the entrance to your bedroom to yourself only. In general, successfully passing a lock - going through an exit, picking up an object, etc. - will trigger the @succ attributes. Failing to pass a lock will trigger the @fail attributes. The generic syntax is "@lock <object>=<key>". The object and key can be name, number, "me" or "here". If the key is a player, you must prefix the player name with a * - i.e. "@lock object=*Malachite". In general, if you want something to be locked against everything, lock it to #0. (Continued in 'man 2.8.2'.) & 2.8.2 To unlock an object, simply use @unlock <object>. Boolean expressions are allowed in locks, as are parentheses for grouping. A lock of 'obj1&obj2' means that the thing trying to pass the lock must be BOTH obj1 and obj2. A lock of 'obj1|obj2' means that the thing trying to pass the lock must be EITHER obj1 or obj2. Make sure that you don't confuse these. There is also a negation operator, '!'. '!obj1' means that the thing trying to pass the lock must NOT be obj1. Carry locks allow you to check if a player is carrying a certain object. @lock <object>=+<key> only allows someone carrying <key> to pass the lock. Is locks allow passage only if you are the key: @lock <object>==<key>. The normal @lock <object>=<key> allows passage either if you carry the key, or you are the key; thus, a carry lock plus an is lock equals a normal lock. (Continued in 'man 2.8.3'.) & 2.8.3 Indirect locks allow you to lock an object to the key of another object, thus creating "standard" locks. The syntax is @lock <object>=@<key> For example, if the <key> is locked to obj1|obj2, then <object> will also be locked to obj1|obj2. Ownership locks allow you to lock to all objects with the same owner. @lock <object>=+<key> only allows someone with the same owner as <key> to pass the lock. * * * * * You can lock things to attributes. For example, to make a door which only allows females past, @lock door=sex:f* Wild cards, greater than, and less than signs may be used. The latter two refer to alphabetical order: @lock door=name:<m will set the door passable to only those people whose names begin with a through l. (Continued in 'man 2.8.4'.) & 2.8.4 One special variant of the attribute lock is the evaluation lock. It evaluates an attribute, and compares it to a given value; if they are the same, then the object passes the lock. The attribute is evaluated as a U() function (see the discussion of user-defined functions later in this manual for details). The syntax is @lock object=<attribute>/<value> The person attempting to pass the lock is passed as the enactor to the attribute U-function. Evaluation locks are an advanced programming technique; don't worry if you don't understand them. * * * * * 2.0 provides a wide variety of locks; in additional to the "basic" lock, there are enter, leave, use, page, give, receive, telout, tport, and link locks. They are set using @lock/<lock name> and @unlock/<lock name>. An enter lock prevents someone from entering an object unless he passes the lock. The object must also be ENTER_OK or owned by the person trying to enter it. (Continued in 'man 2.8.5'.) & 2.8.5 Leave, give, and receive locks are obvious: they control who is allowed to leave the object, who is allowed to give the object, and who is allowed to give things to the object, respectively. A use lock prevents someone from "use"ing, performing $commands, or triggering ^-listen patterns on an object unless they pass the lock. This provides another form of security on objects; to prevent someone from abusing the $commands on your personal objects, all you need to do is to @lock/use it to "me", so you are the only person who can use the object. Please note that if you uselock yourself, you cannot be given money (since that could potentially trigger the @apay attribute on you). (Continued in 'man 2.8.6'.) & 2.8.6 A page lock prevents someone unless they pass the lock. It does not prevents their objects from paging you; to achieve this affect, you need to use the ownership lock primitive ($). If you want to prevent someone and all their objects from paging you, use "@lock/page me=!$*playername" -- i.e., if Ambar wants to prevent Joarm and his objects from paging her, she types "@lock/page me=!$*Joarm". The '!' signifies negation, the '$' signifies ownership, and the '*Joarm' forces the game to parse the name as a player name. Thus the lock reads "not objects owned by player Joarm". In 2.0, this also prevents Joarm and his objects from @pemit'ing to Ambar; in 1.50, the HAVEN flag is required to stop @pemits). A link lock controls who may link to a LINK_OK location (for linking exits or setting drop-tos for rooms) or an ABODE location (for setting the homes of players or things). * * * * * (Continued in 'man 2.8.7'.) & 2.8.7 Lock types in 1.50 are somewhat more limited. Enter locks and use locks are provided. Page locks are a subset of use locks (a use lock on a player is the page lock). Teleport locks are a subset of enter locks (an enter lock on a room is the teleport lock). They are set using the @lock/enter (@unlock/enter) and @lock/use (@unlock/use) commands. Except for the fact that they set the Enter/Teleport and Use/Page Keys instead of the regular Key field on an object, they behave like @lock/@unlock. When you examine a room, you will be shown the key "Teleport Key" instead of "Enter Key"; when you examine a player, you will be shown the key "Page Key" instead of "Use Key". 1.50 will, however, recognize the /tport and /page switches, and set the appropriate lock for you. In 1.50, be careful, since a page lock also acts like a use lock, on a player. If you @lock/use yourself to "me", not only will you prevent people from using $commands on you, but you will prevent people from paging you. You should, in general, avoid putting $commands on yourself. You probably also want to leave yourself page-unlocked. A page lock is useful if you don't want someone to page you but you don't want to set yourself HAVEN (and thereby block out all pages). (Continued in 'man 2.8.9'.) & 2.8.9 ---------- Changing ownership of an object: occasionally, you may want to give a gift to someone, and let them own an object that you have created. The @chown command allows you to change ownership of something. For mortal players, to change ownership of something, the original owner must set it CHOWN_OK. The player who is receiving the thing must then @chown <thing>=me. If the thing is an object, the receiving player must be able to pick it up. If the thing is a room or exit, the receiving player must be in the same room. Wizards can simply @chown #<object>=player. If you own an object that you want to make sure you don't lose, set its home to yourself, using @link <object>=me, and then set the object STICKY. Objects set STICKY will automatically go home when dropped; thus, when the object is dropped, it will return to you. ---------- (Continued in 'man 2.8.10'.) & 2.8.10 If, somehow, you manage to lose one of your objects, the @find and @search commands can be very useful. @find <string> will search the database for all objects and rooms that you control whose names contain <string>. @find without any arguments displays all objects and rooms controlled by you. This command costs (usually) 100 pennies, since it is computationally expensive. The exact cost can be found by "@list costs" (in 2.0) or "@config" (in 1.50). The cost for "@find" is the standard cost for all computationally expensive commands on a MUSH. "@find <string>=<first dbref>,<last dbref>" will allow you to limit the dbref range searched. This version of the command will check every object from <first> to <last>, inclusive. If you do not specify a dbref, <first> is assumed to be 0 and <last> is assumed to be the last object in the db * * * * * The @search command checks all rooms, objects, and exits owned by you. If used without arguments, it prints all of them out. @search can be restricted to certain classes - type (rooms, exits, objects, players), name (search for a string, like @find), and flags. @search is generally faster than @find. (Continued in 'man 2.8.11'.) & 2.8.11 The scope of a @search may be limited in a fashion similar to @find. The range must be the _last_ argument to @search. For example: "@search flags=p,100,500" would find all objects with the 'p' (PUPPET) flag between dbrefs #100 and #500. Note that @search and @find may be more expensive on certain MUSHes, especially those that run on slower machines, or are disk-based. 2.0 MUSH code is disk-based; limited @searches (restricted by a flag, for example) may not be terribly slow, but a @find might hang the server for several minutes (and bring the wrath of the wizards down upon you). * * * * * To get a count of how many objects you own, use "@stats". In 2.0, "@stats" with no switches returns the total number of objects in the database. "@stats/all" returns a breakdown of types in the database. "@stats/me" returns a breakdown of what you own. 2.0 @stats breakdowns cost the same as @find, so be careful with it. (Continued in 'man 2.8.12'.) & 2.8.12 In 1.50, "@stats" returns a breakdown of types in the database. "@stats me" returns a breakdown of what you own. Wizards can use "@stats <player>" to get a breakdown of what any player owns. & 2.9 2.9 Do it! -- falcon Issue the following commands: @create <falcon's name> @set <name>=puppet @desc <name>=<whatever it looks like> @lock <name>=me @succ <name>=You call to <name> and <he/she> lands on your arm. @osucc <name>=calls to <name> and <he/she> lands on %p arm. @fail <name>=You try to pick up <name> but <he/she> bites you! @ofail <name>=tries to pick up <name> but <he/she> bites %o! @drop <name>=You allow <name> to fly off your arm. @odrop <name>=allows <name> to fly off %p arm. @listen <name>=<Your name> has left. @ahear <name>=@wait 5={:flies after <Your name>; @set me=!puppet; @tel owner(me); leave; @set me=puppet} [ This last statement forces the puppet to follow you around. In order for this to work, however, the puppet must be set INHERIT. ] [ Obviously, you can have your own variations on these. ] & index1 INDEX OF SECTION I EXAMPLES: 1.2 Demonstration of basic commands 1.3a Desc 1.3b Succ 1.3c Fail and Drop 1.5 Basic character set-up 2.1 Basic object commands 2.2 Puppet 2.4 Bubbly Pie 2.5 Bubbly Pie Vendor 2.6 Mug of Klah 2.7 Autogreet 2.8 Echo device 2.9 Falcon & II MUSH Manual Version 2.008: Copyright 1993, 1994, 1995, Lydia Leong (lwl@godlike.com / Amberyl) Last revised 5/22/95. Section II: Building and an introduction to programming Table of Contents: 3. Building. 3.1 Creating a room: @dig, FLOATING flag 3.2 Connecting rooms: @open, @link and @unlink, LINK_OK flag, @entrances 3.3 Refinements: ABODE and JUMP_OK flags, drop-tos and STICKY flag on rooms 3.4 Exit conventions and the TRANSPARENT flag 3.5 Do it! -- home 4. Introduction to Programming 4.1 The stack and string substitutions - % substitutions 4.2 General attributes and action lists - @trigger and GET() 4.3 The queue: @ps, @wait, and @halt. HALT flag 4.4 Decision making: @switch (Continued in 'man II1'.) & II1 4.5 Other ways of triggering: @use and @startup 4.6 User-defined commands: $command, @scan, @verb 4.7 Introduction to functions: ADD() and other math, RAND(), TIME() 4.8 Do it! -- falcon control &3 3. Building &3.1 3.1 Creating a room Building a room is done quite simply, using the @dig command. The syntax is: @dig <Room name>=<in1;in2;in3;etc>,<out1;out2;out3;etc> in1, in2, and in3 are all exit "aliases" - typing any one of these names will bring you through the exit into the room. You may have as many of these as you wish; the names must be separated by semi-colons. in1 will be the visible "primary" exit name, which appears in the room's Obvious Exits list. out1, out2, and out3 are similar. This exit goes from the room you just dug to the room that you are in. On TinyMUDs, building convention dictates that exits be set DARK - i.e. no exit names appear in the "Obvious Exits" list. Many people like visible exits, however, and in the age of MUSHes, most exits are visible. Visible exits are the default. (Continued in 'man 3.1.2'.) & 3.1.2 You must control the room that you are @digging from. If you don't own a room, you should first @dig <Room name> and then @tel to that room. (Or use @dig/teleport, which builds the room then moves you there). You can then build off that room. If your room is not linked to something, however, you will occasionally get the message "You own a disconnected room, <Room name>." To avoid this, set the room FLOATING. ============================= [ Example 3.1: Living Room ] > l Foyer(#3108R) This is the foyer of a small private home. Obvious exits: Doorway > @dig Living Room=Living Room;lr;living,Foyer;f;out;o Living Room created with room number #3813. Opened. Trying to link... (Continued in 'man 3.1.3'.) & 3.1.3 Linked. Opened. Trying to link... Linked. > l Foyer(#3108R) This is the foyer of a small private home. Obvious exits: Living Room Doorway > lr Living Room(#3813R) Obvious exits: Foyer ============================= & 3.2 3.2 Connecting rooms To open an exit between two existing rooms, use the syntax @open <in1;in2;in3;etc>=#<room>,<out1;out2;out3;etc> This will open and link exits between the two rooms. You can also open an exit in but not out by simply using @open <in1;in2;in3;etc>=#room To change an exit destination, simply "@link <exit>=#<new room>". (Certain older versions of MUSH might require you to first "@unlink exit".) Be careful not to leave exits unlinked, because an unlinked exit will become the property of whomever @links it. If you want to have an exit going no place, like something called "sit" which displays the @fail message "You sit down." @link the exit to the room it is in - i.e. @open sit=here. @lock sit=#0. @fail sit=You sit down. You can let people open/link exits to one of your rooms by setting it LINK_OK. Note that people cannot open an exit from your room into something else; they can only link an entrance. (Continued in 'man 3.2.2'.) & 3.2.2 * * * * * The "@entrances <object>" command returns all links to <object> -- exits which are linked to the object, rooms which have their drop-tos set to the object, and players and things which have their homes set to the object. This command in 1.50 also takes switches, allowing you to specify exits, rooms, players, or things alone. This command is computationally expensive, costing the same as @find. You can limit the dbref range which @entrances searches by specifying "@entrances <object>=<first object>,<last object>". This is similar to restricting the range on @find. & 3.3 3.3 Refinements If you want people to be able to @teleport to your room, set it JUMP_OK. Beware - if you are in a JUMP_OK room and someone does a @whereis on you, the room number will be listed, and that person will be able to @tel to your location, so if you don't want unexpected visitors dropping in, it might be best not to set the room JUMP_OK. If you want other people to be able to set their homes in your room - i.e. be sent there automatically when they type 'home' - set the room ABODE. To set yourself or another object's home, @link <thing>=<here/#room>. In older versions of MUSH code, most notably MicroMUSH, the JUMP_OK and ABODE flags are merged; a room set ABODE may both be @linked to and @teleported to. * * * * * (Continued in 'man 3.3.2'.) & 3.3.2 Rooms may be @linked to other rooms, using @link <room1>=#<room2>. This sets a "drop-to". Objects dropped in room1 will automatically be sent to room2. "Drop-tos" may be delayed by setting the STICKY flag on room1. If the room is STICKY, the drop-to will not be performed until the last player has left the room. & 3.4 3.4 Exit conventions There are certain exit conventions used on TinyM*s which make following other players around easier. Exits should, if possible, have an @osucc and @odrop message. The @osucc message is triggered when someone uses an exit; it is displayed to everyone else in the room that the person just left. The @odrop message is triggered the same way, but is displayed to everyone in the room that the person enters. Thus, you can tell where people are going to, and where they are coming from. For purely aesthetic purposes, exits often have @desc and @succ messages. The @desc message is displayed when someone "looks" at an exit; the @succ message is displayed to the person who walks through the exit. * * * * * (Continued in 'man 3.4.2'.) &3.4.2 Several clever things can be done with "null" exits. These are exits that don't lead anywhere, but simply display a message to the player. For example, an exit named "sit" could give the message "You sit down." These exits are usually locked to #0, linked to the room they are in, and DARK. Use of these kinds of exits is generally discouraged on MUSHes that enforce building quotas and/or are trying to conserve disk space. In areas that use a lot of directions - north/south/east/west etc. - it is often helpful to have a null exit with the names of the remaining directions in it. For example, if a room has exits in all directions except east and west, a null exit called east;e;west;w with a @fail message of "You can't go that way." is very helpful. Otherwise, the player who types "east" will simply get a "Huh? (Type "help" for help.)" message. Before you make such fake exits in your rooms, though, it might be a good idea to check that your MUSH doesn't have them already defined in the Master Room. (Continued in 'man 3.4.3'.) &3.4.3 MudCore provides the Master Room support for these fake direction exits (and the message "There is no exit in that direction.") MudCore takes this a step further, however, providing customization of these fake exits without taking up more database space for extra "real" exits. Sometimes, when building an area, you will want to have, for example, a "north" exit which provides a message like, "You walk down a long corridor, discover nothing interesting, and return to where you started." and doesn't actually lead to another room. Such exits give the illusion of "space", making a small area feel larger. In MudCore, "fake" exits in the cardinal directions can be designed by placing the attributes DESC_<direction> and FAIL_<direction> on the room. For the above example, one might use, "&DESC_NORTH here=To the north is a long, featureless corridor." and "&FAIL_NORTH here=You walk down a long, corridor, discover nothing interesting, and return to where you started." * * * * * (Continued in 'man 3.4.4'.) &3.4.4 There is a flag called TRANSPARENT, which can be set on exits. If this flag is set, players looking at the exit see, after the exit description, the description of the next room and its contents (but not its succ/fail messages or exits). This is useful for creating the illusion of an archway or similar portal, where it makes logical sense to be able to look through it into the next room. In 1.50, the TRANSPARENT flag can also be set on rooms. This causes the obvious exits in the room to be displayed with each exit on a line by itself, giving the destination room. For example, you might have: Obvious exits: South leads to Foyer of the Bank. East leads to Intersection of Park and Hill Streets. instead of: Obvious exits: South East & 3.5 3.5 Do it! -- home This builds a very simple home of three rooms, linked in a triangle: Living Room | \ Workroom------Bedroom Issue the following commands: @dig/teleport Living Room [ The room will be dug and you will be moved into it. Note its dbref number ] @set here=FLOATING [ Suppresses disconnected room warnings ] @desc here=<whatever the room looks like> @dig Workroom=Workroom;w;work;south;s,Living Room;lr;living;north;n;out;o go workroom @desc here=<whatever the room looks like> @dig Bedroom=Bedroom;b;east;e;bed,Workroom;w;work;west go bedroom (Continued in 'man 3.5.2'.) &3.5.2 @desc here=<whatever the room looks like> @open Living Room;lr;living;northwest;nw;out;o=#<xxx>,Bedroom;b;bed; southeast;se [ Note: the previous line is wrapped-around. <xxx> is the room number of the Living Room, which you should have noted earlier. ] @set here=ABODE @link me=here [ Sets your home to this bedroom ] All three rooms are dug and linked - now, all you have to do is to spiffy up the exits, and you're all set! & 4 4. Introduction to Programming &4.1 4.1 The Stack and string substitutions The MUSH stores several variables, related to the cause of an action, the object that is executing an action, and the program state during that action, referred to as the "stack". These are accessed via a "percent substitution". One such variable is the "enactor" -- the object which caused an action to be performed. For example, if Sareena looks at a Plush Shaav Doll, Sareena is the enactor. The enactor is sometimes referred to as the "cause". The percent substitution '%N' returns the name of the enactor; in this case, it would be "Sareena". The substitution '%n' also returns the name of the enactor, but the first letter is in lower-case. Another substitution, '%#', returns the dbref number of the enactor. It is also possible to get the appropriate pronoun for the enactor. '%s' is the subjective pronoun (he/she/it). '%o' is the objective pronoun (him/her/it). '%p' is the possessive pronoun (his/hers/its). The capital letter forms ('%S', etc.) return the pronoun with the first letter (Continued in 'man 4.1.2'.) &4.1.2 capitalized. The game determines the pronoun to use by looking the @sex attribute on the enactor. These substitutions are equivalent to the function evaluations "SUBJ(%#)", "OBJ(%#)", and "POSS(%#)". In the example above, since Sareena is female, these substitutions would return "she", "her", and "hers", respectively. The substitution %l returns the dbref number of the enactor's location. This is useful when programming objects in the Master Room, and does not pose a "security risk", since the enactor has volunteered his position by triggering a $command or similar. The object performing the action can be referenced via '%!'. This returns the dbref number of the object. It is equivalent to the function evaluation "NUM(me)". In the above example, the object that has been triggered (and thus is performing the action) is the Plush Shaav Doll. * * * * * (Continued in 'man 4.1.3'.) &4.1.3 The "stack" in MUSH is simply a place where strings are stored; it is not a literal "stack" in the usual sense of having a top object which can be pushed and popped (as in MUF, or as defined in a standard computer science class). The stack consists of ten strings, numbered 0 through 9. Strings on the stack are referred to as %0, %1, %2, and so on, through %9. The mechanisms for copying strings into the stack will be explained later. Related to "percent" substitution is the V() function. It produces identical output to the equivalent percent substitution. For example, '[v(0)]' is equivalent to '%0', and '[v(N)]' is the same is '%N'. Throughout this manual, however, '%0' and other percent substitutions will be used instead of 'v(0)', since the evaluation of percent subtitutions is faster than the evaluation of V-functions. 26 registers, "numbered" va-vz, are available for programming, as part of the "standard" collection of MUSH attributes. They may be accessed by either %va, or [v(va)]. Note that the square brackets [] force something to be evaluated as a string. (Continued in 'man 4.1.4'.) &4.1.4 1.50 provides the additional registers wa-wz and xa-xz. These behave in a manner identical to va-vz. Percent substitutions should always be used instead of functions, where possible. In fact, you should ALWAYS use the percent substitution, unless you are SURE that the function version is required -- use %N instead of v(N), %! instead of num(me), and so forth. There are virtually no situations which require the functions, and thus using the function versions is inefficient and wasteful. Furthermore, please note that it's just "%0", NOT "[%0]"; the additional square brackets cause an unnecessary extra evaluation and thus are inefficient. Please note that if you access something with a percent-substitution, if you capitalize the first letter of the substitution, then the first letter of the text returned will also be capitalized. The S() function forces an additional round of pronoun substitution. This is useful if you are evaluating a string and want to force the parser to substitute in the values for all (non-escaped) functions. (Continued in 'man 4.1.5'.) &4.1.5 Percents can also be used to add in special characters. %r is a carriage return, %t is a tab character, and %b is a blank space. To show a literal "%", use "%%". '%r' is actually two characters (a carriage return and newline), and functions like STRLEN() will count it as two. Finally, there are temporary storage "registers", also numbered 0 through 9. They are set via the function SETQ(), and retrieved via the function R(), or through the %q-substitution. The values set via SETQ() are preserved through the entire command list associated with a given command. SETQ() is a function which just returns a blank string, and has the syntax, "setq(<register number>,<value>)". "r(<register number>)" retrieves the value. These functions will be explained in greater detail later. For now, just believe that they allow you to keep values around temporarily. The odd name SETQ comes from the programming language LISP. (Continued in 'man 4.1.6'.) &4.1.6 ====================================== [ Example 4.1: Percent demonstration ] > @sex me=female Set. > @va me=test Set. > say %va You say "test" > say %Va You say "Test" > say %N is demonstrating %p example. You say "Amberyl is demonstrating her example." > say [setq(0,testing for fun)]-- %q0 You say "-- testing for fun" > say [setq(0,testing for fun)]-- %Q0 You say "-- Testing for fun" ====================================== &4.2 4.2 General attributes and action lists The 26 registers are often referred to as "general attributes," since they can be used to store anything. Often, they are good places to put variables. In addition to being accessible by the v-function, they can also be accessed by using the GET() function. The syntax for this is "get(object/attribute)". "[get(me/va)]" is always equivalent to "[v(va)]" or "%va". GET() is very similar to the v-function, except it requires an object to get the attribute from. It may be used to get any attribute - registers, descriptions, non-standard attributes, etc. When working only with one object, it is generally more efficient to use a v-function instead of using GET(). GET_EVAL() is similar to GET(), except that it also evaluates the attribute. U() is also similar, and also evaluates the attribute, but it can be used for user-defined functions which take parameters (more on this later). (Continued in 'man 4.2.2'.) &4.2.2 GET_EVAL()'s syntax is like GET()'s: "get_eval(object/attribute)" U()'s basic syntax can be like either V()'s or GET()'s; either "u(attribute)" or "u(object/attribute)" is valid. (For information on how to pass parameters to U(), see a later section in this manual.) There is a subtle difference between the two functions, in addition to U()'s ability to take parameters The enactor of a GET_EVAL() is the object which is actually performing the GET_EVAL(); the enactor of a U() is the original enactor. (Continued in 'man 4.2.3'.) &4.2.3 ====================================== [ Example 4.2a: GET_EVAL() vs. U() ] > say %! You say "#3" > @create Test Test created as object #1034 > @va test=Enactor: %# -- Me: %! Set. > @vb test=$test: @pemit %#=Get_Eval: [get_eval(me/va)]%rU: [u(va)] Set. > test Get_Eval: Enactor: #1034 -- Me: #1034 U: Enactor: #3 -- Me: #1034 ====================================== If you're confused by the difference between GET(), GET_EVAL(), and U(), don't worry about it; details will be provided later, and you don't need to worry about the latter two functions if you're only doing very basic things with MUSH. (Continued in 'man 4.2.4'.) &4.2.4 * * * * * General attributes are often used to store action lists, which are long lists of commands, to be queued and executed. For example, you could @create an object called Test, and "@va Test=:explodes.; @destroy me" General attributes are usually activated by "triggers". The syntax for this is @trigger object/attribute. @trigger is usually abbreviated to @tr. Most of the time, one attribute will trigger another, but you can also type directly @tr object/attribute. For example, if you typed @tr Test/va, from the example above, you would see: Triggered. Test explodes. You get back your 10 Mark deposit for Test. Test has left. (Continued in 'man 4.2.5'.) &4.2.5 You may also pass variables on the stack from attribute to attribute, using trigger. Often, this is a must, since the stack is cleared when something new is triggered. The syntax is "@tr object/attribute=thing,thing,thing,etc." These new things become %0, %1, %2, etc., respectively, in the triggered register. Almost anything can be legally passed on the stack - function evaluations of any type, % arguments, numbers, strings, etc. ====================================== [ Example 4.2b: Sentence Reverser ] [ This object listens for four words and prints them out backwards. ] > @create Reverser Reverser created as object #25789 > @listen reverser=* says "* * * *" Set. > @ahear reverser=@tr me/va=%4,%3,%2,%1 [ Note that %0 is left out, since %0 is the * says - the person talking ] Set. > @va reverser="%0 %1 %2 %3 (Continued in 'man 4.2.6'.) &4.2.6 Set. > drop reverser Reverser has left. Reverser has arrived. Dropped. > "First second third fourth You say "First second third fourth Reverser says "fourth third second First" =========================================== &4.3 4.3 The queue and delayed execution The queue is the list of commands that the MUSH is going to execute. All commands are put into it. Most are evaluated quickly, in the order that they enter the queue (first to last), although @trigger takes at least one queue cycle to execute and @force at least two. Setting an attribute also takes time. One must be careful, when programming, that all commands are getting executed at the correct times. There is no hard and fast rule for this; experimentation is generally the only way to find out if your object is executing commands in the correct order. The queue may be checked by doing a @ps. If you have an infinite loop, this is likely to be very long. If you're not sure if an object of yours is infinite looping, @ps is a surefire way of checking. (Continued in 'man 4.3.2'.) &4.3.2 @ps separates the queue into four sections - Player, Object, Wait, and Semaphore. The player queue shows what you have triggered; the object queue shows what your objects have triggered. The Wait queue shows commands that will be executed in the future - commands that you have queued using @wait (explained below). The Semaphore queue shows semaphore countdowns (explained later). The total number of queued commands is also displayed. The default @ps switch is "@ps/brief". This shows the objects and the commands they are running. For the wait queue, it also shows the time to wait. For the semaphores, it shows the object that the command is waiting on, and if the semaphore is using the timer option, it also shows the remaining time. Finally, it displays the total count of queued commands in each of the four categories. "@ps/long" displays all the information that "@ps/brief" does, plus the enactor of the object, and any variables which are being passed on the stack for eventual execution as part of a @trigger. (Continued in 'man 4.3.3'.) &4.3.3 "@ps/summary" shows just the total count of queued commands. Note that @ps defaults to displaying the queue for the individual executing the @ps; only wizards may specify a player name or the "/all" switch. In the totals line, no matter what switches are specified, the numbers in each category are shown as Number1/Number2. The first number is the number of queued commands that you have in that category; the second number is the total number of commands queued in that category. Player and Object categories also have a "[#del]", where "#" is the number of commands deleted from the queue via @halt. * * * * * @ps in 1.50 is somewhat different. "@ps" with no arguments shows your personal queue - the objects and the commands they are running, and, for the wait queue, the time to wait. It also displays the total count of queued commands in each of the queues, in the format <Number of your commands> / <Total number of commands in that queue>. (Continued in 'man 4.3.4'.) &4.3.4 "@ps all" prints the queue for all players. Wizards can also do "@ps *<player>" to get a queue for that player. In both cases, a queue count is provided. Anyone may do a "@ps count" to get a count of the number of queued commands. * * * * * Infinite loops on objects are dangerous. Every queued command costs 1/64th of a penny. Plus, if an object loops badly enough, it can severely slow down the game for others. If you have a loop, you can use @halt. It clears your personal queue. @halt <command> clears your personal queue, and adds that new command to the queue. @halt <object>=<new command> clears that object's queue and adds that new command to the queue. The other way of halting something is to set the HALT flag on it. An object set HALT is essentially inert - it does not @listen, @triggers and @force don't work on it, etc. Also, in 1.50, UFUN() and ZFUN() will return "#-1 OBJECT HALTED" (recursion in a UFUN or ZFUN will automatically set the object HALT). (Continued in 'man 4.3.5'.) &4.3.5 The @halt command in 1.50 is slightly different. If you, or one of your objects, does a "@halt", it wipes out your entire queue. There is no way to clear the queue commands on just one object. You can also do @halt <player>=<new command>, which wipes out the player's entire queue and places the new command in it; if you are not a wizard, the player must be yourself. "@halt <object>" in 1.50 with no new command also sets the object HALT. If you just want to stop an infinite loop, use "@halt" with no arguments. There is one command called @allhalt (1.50. In 2.0, it's @halt/all) which is wizard-only. It clears the queue for the entire MUSH, and is used in emergencies. All connected players receive the message, "Your objects have been globally halted by <Wizard>." * * * * * (Continued in 'man 4.3.6'.) &4.3.6 The syntax of @wait is simple: @wait <number of seconds>=<command list> There is another, more complex, type of @wait which is timed on objects; this type of @wait involves a "semaphore" and will be described later. @wait is very useful for trying to get objects to execute commands in the correct order, as well as for doing time-delayed sequences - for example, descriptions on a room that change every few hours. ============================= [ Example 4.3: Explosive [ This will create an object which explodes fifteen seconds after being dropped. It gives a warning five seconds before it explodes. ] > @create Explosive Explosive created as object #23083 > @drop explosive=You set down the explosive and ignite it. Set. > @adrop explosive=@wait 10=:pulses dangerously. It'll explode in another five seconds!;@wait 15={:explodes!;@destroy me} Set. > drop explosive (Continued in 'man 4.3.7'.) &4.3.7 Explosive has left. You set down the explosive and ignite it. [ 10 seconds pass ] Explosive pulses dangerously. It'll explode in another five seconds! [ another 5 seconds pass ] Explosive explodes! You get your 10 Marks deposit back for Explosive. Explosive has left. ============================ &4.4 4.4 Decision making: @switch @switch is the closest thing to IF-THEN-ELSE on a MUSH. It is similar to the CASE statement in many programming languages. Its syntax is @switch <var>=<cond1>,<action1>,<cond2>,<action2>,<cond n>,<action n>,<default> @switch statements may be nested. One must be very careful about using commas and semi-colons when using @switch - any statements which contain either commas or semi-colons should be surrounded by curly braces { }. A comma will signal a new case/action, and a semi-colon the end of the @switch statement, if not in the curly braces. This is a commonly made mistake - when programming objects whose @switches do not appear to work, always check for misplaced commas or semi-colons. One thing to note is that the variable to switch on does not have to be evaluated within square brackets [ ]. Percent substitutions, like %va, are okay to switch on, as are functions simply stated, like words(v(va)). (Continued in 'man 4.4.2'.) &4.4.2 After the variable name is ALWAYS an equals sign. @switch %va=>1,:foo does NOT mean "if va is greater than or equal to one, :foo". It means "if va is strictly greater than one, :foo". The equals sign is simply part of the @switch syntax. If you want greater than or equal to, you will have to use the GTE() function. There are two option switches to @switch, /first and /all. In 2.0, which one is the default will depend on the MUSH ("@list options" to see). "@switch/all" performs the actions associated with all matching targets. In other words, if the targets are not mutually exclusive, more than one set of actions may be undertaken. Its 1.50 equivalent is the standard "@switch" command. "@switch/first" matches the first target it can, and executes the statements associated with that. Even if the targets are not mutually exclusive, only the actions associated with the first target matched will be executed. The 1.50 equivalent of this is the "@select" command. (Continued in 'man 4.4.3'.) &4.4.3 ============================= [ Example 4.4: Dog ] [ The dog will respond to simple commands that someone says ] > @create Dog Dog created as object #23084 > @listen dog=*"Dog, *" Set. > @ahear dog=@switch %1=sit,:sits down.,roll over,:rolls over obediently,play dead,:flops onto the ground. Set. > drop dog Dog has left. Dog has arrived. Dropped. > "Dog, sit You say "Dog, sit" Dog sits down. > "Dog, play dead" You say "Dog, play dead" (Continued in 'man 4.4.4'.) &4.4.5 Dog flops onto the ground. > "Dog, roll over You say "Dog, roll over" Dog rolls over obediently. ============================= &4.5 4.5 Other ways of triggering It is frequently useful to be able to trigger an object without having to pick it up, drop it, or have it listen for something. Therefore, MUSH provides for "using" objects. The attributes @use, @ouse, and @ause are associated with the "use" command. "use <object>" triggers these three registers (note the absence of the @ sign in triggering). An object can only be used if there is something in the @ause register. The person who uses the object is shown the message in the @use; the other people in the room see the @ouse message. The @ause command list is executed. This, in essence, works just like "get" and "drop" - the command "use" has simply been added. (Continued in 'man 4.5.2'.) &4.5.2 ======================================== [ Example 4.4: Bubbly pie revisited ] [ @using the bubbly pie performs an "eating action" and destroy. ] > @create Bubbly pie Bubbly pie created as object #30216 > @ause pie=@emit %N gobbles down the bubbly pie.;@destroy me Set. > use pie You use Bubbly pie Elsa gobbles down the bubbly pie. You get your 10 Marks deposit back for Bubbly pie. Bubbly pie has left. ======================================== Another way to trigger an object is to set its @startup. The commands in the @startup attribute are executed whenever the MUSH is restarted. This is useful for objects that need to run continuously, such as clocks. Objects with a @startup attribute are automatically given the STARTUP flag, represented by "z". This flag is simply used for internal accounting and does not affect anything else. &4.6 4.6 User-defined commands Perhaps one of the most common ways of triggering an object, user-defined commands are the closest things to MUCK @action that MUSH provides. They allow other players to type "command <arguments, if any>" and have that execute just as if the command was part of the MUSH. Unlike MUCK, however, there are few global user-defined commands. One must be in the same room with, or carrying, the object on which the command is defined. The only exceptions to this are exits/commands defined on the "master room". See the wizard section of this manual for details. These commands may be set on any register or non-standard attribute. The syntax is: @<attribute> <object>=$<command>: <action list> The command may take arguments by using * or ?, etc., in a format similar to that use by @ahear/@listen. (Continued in 'man 4.6.2'.) &4.6.2 User-defined commands are matched on every object in the room or in your inventory, so you may want to avoid command names that might be extremely common. Commands on the MUSH are parsed in the order: exit names, defined MUSH commands (like @desc, page, etc.), then user-defined commands, then Master Room commands. You should be extremely careful not to define commands on an object like $p *:<action>, since this will be parsed as "page" and it will be ignored by the object. Furthermore, note that many MUSHes have global commands which begin with '+' and mailers which use '-' as an editing indicator; thus, you should avoid starting your own commands with either of those characters. In 2.0, there are config options which determine two things: The first is the ability of an object to trigger its own $commands. The second is the ability to trigger $commands on players. One can, however, always trigger $commands on an object one is carrying or is in the same room. (Continued in 'man 4.6.3'.) &4.6.3 =============================== [ Example 4.6: Tray of Food ] [ This object is a tray of food which you can "eat", "drink" and "discard" ] > @create Tray of Food Tray of Food created as object #482 > @va food=$eat: @emit %N takes a slice of bread off the try and eats it. Set. > @vb food=$drink: @emit %N takes a cup of water off the tray and drinks it. Set. > @vc food=$discard: @emit %N discards %p tray of food;@destroy me Set. > eat Elsa takes a slice of bread off the tray and eats it. > drink Elsa takes a cup of water off the tray and drinks it. > discard Elsa discards her tray of food. You get your 10 Marks deposit back for Tray of Food. Tray of Food has left. ============================== (Continued in 'man 4.6.4'.) &4.6.4 Sometimes it's useful to be able to find out what $commands are triggered in a room. 1.50's @scan command allows you to check for a command match on objects that you control or that are set VISUAL. The syntax of this command is simply "@scan <string>". It checks for all possible matches on your location, its contents, yourself, your inventory, the zone or parent room of your location, your personal zone, and the master room. It will display the name of any objects which have commands which could match that string, followed by the number of commands matched in brackets. MudCore provides for similar functionality, via its '+scan' command. It checks for matches, starting from a specific object, and lists the attributes and command wildcard patterns which match (if you can examine the object), or the object which matches the commands (if you can't examine it). * * * * * (Continued in 'man 4.6.5'.) &4.6.5 One interesting and quite flexible command is "@verb". This command essentially allows you to define your own @attr/@oattr/@aattr triplets, allowing you to define your own "standard" verbs. This command is most useful for someone who is a wizard. This command is in the format: @verb <obj to read attributes off (victim)>=<obj to be told messages (actor)>, <attr name>, <default message for attr>, <oattr name>, <default message for oattr>, <aattr name>, <args> Most of those those fields should be self-explanatory; <args> are the values to be passed on the stack, as %0-%9. For example, to simulate "drop" with a command like "put", you would use a user-defined command something like: $put *: @verb [v(0)]=[v(N)],DROP,Dropped.,ODROP,dropped [v(0)].,ADROP (Continued in 'man 4.6.6'.) &4.6.6 Under 2.0, both the victim and actor must be controlled. Under 1.50, either both victim and actor must be controlled, or the thing which triggered the verb must be <actor> AND the object which issued the @verb command must be able to read the attributes from <victim>. Essentially, though, one needs to be a wizard in order to make this command generally useful. It is, however, a rather nifty thing to play with. &4.7 4.7 An introduction to functions Functions are generally used to manipulate strings and other input, or to generate or retrieve something. They are called using: <function-name>(<arg1>,<arg2>,<etc>) and evaluation often requires square brackets [ ] to be put around the function. It is imperative that the right number of arguments be passed to the function, and that the parentheses are correctly matched. You should always use %-substitutions instead of functions where possible, i.e., %va instead of v(va). Functions may be nested. Only one pair of square brackets is required around a given function call. Certain functions, such as V() and GET(), have already been introduced. Only a few functions require no arguments; TIME(), which simply returns the current time on the MUSH, is probably the most commonly used of these. All other functions require one or more arguments. The most commonly used, aside from the v, s, and get() functions, is the RAND() function. This generates a random integer between 0 and <number - 1>. There _is_ a limit to the number of arguments a function can take; you can find out what this is by asking a local wizard. This is generally 100. The limit is something to keep in mind if you are doing extremely large calls to a function like SWITCH(), which takes an arbitrary number of arguments. (Continued in 'man 4.7.2'.) &4.7.2 Functions used to access basic db info include NUM(), FLAGS(), OWNER(), LOC(), and MONEY(). These are publicly accessible, and give an object's database reference number, flags, owner, location (for a player), and money (also for a player). These functions will be discussed in more detail later. Other publicly accessible attributes include DESC, and are gotten using the GET() function. All attributes on an object set VISUAL are publicly accessible. Functions used to process lists will be covered later in this manual. The following functions are used for arithmetic: ADD(), SUB(), MUL(), DIV(). Note that SUB() does not exist in many versions of MUSH code; instead, one must add the negative of the second number. Some of these functions, such as SUB(), take ONLY two arguments. Others, like ADD(), can take multiple arguments (just like LISP arithmetic operators). For example, "add(2,3,4)" is equivalent to (and more efficient than) "add(2,add(3,4))". (Continued in 'man 4.7.3'.) &4.7.3 ============================= [ Example 4.7a: Abacus ] [ An object that performs arithmetic ] > @create Abacus Abacus created as object #19831 > @va abacus=$*+*: @emit The sum of %0 and %1 is [add(%0,%1)]. Set. > @vb abacus=$*-*: @emit The difference of %0 and %1 is [sub(%0,%1)]. Set. > @vc abacus=$*x*: @emit The product of %0 and %1 is [mul(%0,%1)]. Set. > @vd abacus=$*/*: @emit The quotient of %0 and %1 is [div(%0,%1)]. Set. > 1+2 The sum of 1 and 2 is 3. > 2x6 The product of 2 and 6 is 12. > 8-5 The difference of 8 and 5 is 3. (Continued in 'man 4.7.4'.) &4.7.4 > 9/3 The quotient of 9 and 3 is 3. ============================== Here's another example, demonstrating the combined use of @switch and RAND(), "use", and various %-substitutions. ====================================== [ Example 4.7b: Plush Shaav Doll ] [ To conserve space, 'Set.' messages after attribute sets have been omitted. ] > @create Life-size Plush Shaav Doll Life-size Plush Shaav Doll created as object #3895 > @desc doll=This is a plush doll nearly six feet in height. It's fuzzy and and cute, featuring the Grand Hierarch Shaav wearing his usual calm expression, although the effect is somewhat spoiled by the fluffy stuffing and bright green-button eyes. The life-sized doll is clothed in the black robes of the Grolims, and would probably seem to radiate evil if it didn't look so snuggly. It's probably made by the same people who made Belzoinks. (Continued in 'man 4.7.5'.) &4.7.5 > @succ doll=You prop up a life-sized plush doll of Shaav. > @osucc doll=grins evilly as %s props up a life-sized plush doll of Shaav. > @drop doll=You drop a life-sized plush doll of Shaav on its head. > @odrop doll=drops a life-sized plush doll of Shaav on its head. > @use doll=You pull a string on the doll's neck. > @ouse doll=pulls on a string on the plush Shaav doll's neck. > @ause doll=@switch rand(4)= 0, {:points a finger at %N and squeaks. "Die!"}, 1, {:screams:%tDeath!%tDestruction!%tChaos!}, 2, {:looks at %N for a moment, then booms to %o, "DOOM."}, 3, {:squeaks. "Mama!%b%bMama!%b%bMama!"} [ If Polgara (a female) types "use doll", she sees "You pull a string on the doll's neck", plus one of the following messages: ] Life-size Plush Shaav Doll points a finger at Polgara and squeaks. "Die!" Life-size Plush Shaav Doll screams: Death! Destruction! Chaos! Life-size Plush Shaav Doll looks at Polgara for a moment, then booms to her, "DOOM." Life-size Plush Shaav Doll squeaks. "Mama! Mama! Mama!" (Continued in 'man 4.7.6'.) &4.7.6 Note the use of "%b" and "%t" substitutions for blank spaces and tabs. Because "%t" inserts a literal tab character, the spacing will vary depending on where tab stops are set by the player's terminal. Also note that the @switch action clauses had to be enclosed in braces, since the statements used inside contained commas. ============================== &4.8 4.8 Do it! -- falcon control This section assumes that you have created the falcon as per section 2.9 of this manual. The object you will create in this example will be used to control it. <#FL> below refers to the dbref number of your falcon, and <FL> refers to the name of your falcon. The controller will give you the ability to force your falcon remotely to pose and think messages, return to you on command, and go to a player on command, using: '<message> to think a message, .<pose> to pose something, return to recall the falcon, and goto <player> to go to a player's location. Issue the following commands: @create <FL>'s controller [ Then lock it, desc it, etc. ] (Continued in 'man 4.8.2'.) &4.8.2 @va controller=$'*: @fo <#FL>=:chirps << %0 >> [ This is used for "speech" ] @vb controller=$.*: @fo <#FL>=:%0 [ This is used for forcing the falcon to pose ] @vc controller=$fly *: @fo <#FL>={:soars into the air and flies off.; %0} [ This is used to have the falcon fly in a given direction ] @vd controller=$return: @fo <#FL>={:returns to %N.; @set me=!puppet; @tel me=owner(me); leave; :soars towards you!; @set me=puppet} [ This brings the falcon back to you, with appropriate messages. The INHERIT flag must be set in order for this to work. ] (Continued in 'man 4.8.3'.) &4.8.3 @ve controller=$goto *: @fo <#FL>={@switch loc(*%0)=#-1,{p <your name>=I can't find %0}, {:goes to find %0.; @tel me=loc(*%0); :soars towards you!}} [ This is used to go to the location of a player. It first checks to see if the player can be found - if the player does not exist or the player is set UNFINDABLE, #-1 is returned and the falcon pages the player with the fail message - and then, if the player exists, teleports to that player's location, with appropriate messages. ] &index2 INDEX OF SECTION II EXAMPLES: 3.1 Living Room 3.5 Home 4.1 Percent demonstration 4.2a GET_EVAL() vs. U() 4.2b Sentence Reverser 4.3 Explosive 4.4 Dog 4.5 Bubbly pie revisited 4.6 Tray of food 4.7a Abacus 4.7b Plush Shaav Doll 4.8 Falcon control &III MUSH Manual Version 2.008: Copyright 1993, 1994, 1995, Lydia Leong (lwl@godlike.com / Amberyl) Last revised 5/22/95. Section III: Programming Applications 5. Vehicle programming 5.1 Entering and leaving objects: @enter, @aenter, @oenter, @oxenter, @leave, @aleave, @oleave, @oxleave 5.2 Inside vehicles: @idesc, @listen, relays, and commands, @remit, AUDIBLE, @prefix, @filter, @forwardlist 5.3 Do it! -- Wagon controller 6. Complex programming 6.1 Useful functions: General use: NUM(), LOC(), NEARBY() List creation: LCON(), LEXITS(), LATTR(), LWHO() Parsing: POS(), FIRST(), REST(), STRLEN(), MID(), WORDS() Matching: EXTRACT(), MATCH(), MEMBER(), UCSTR(), LCSTR(), CAPSTR() 6.2 Non-standard attributes: & and attribute flags (Continued in 'man III1'.) & III1 6.3 Introduction to @dolist 6.4 Time synchronization: semaphores, complex @wait, @notify, @drain 6.5 Security problems in programming, INHERIT, ESCAPE(), SECURE(), @fsay, @femit, @fpose 6.6 Debugging: PUPPET and VERBOSE 6.7 On the New Programming Style 6.8 Hints and tips 7. MUSH Programming: QuickStart Guide 7.1 Basic syntax 7.2 Conditional and loop constructs 7.3 Functions &5 5. Vehicle programming &5.1 5.1 Entering and leaving objects Objects can be used as containers if they are set ENTER_OK. You can enter an object by typing "enter <object>" and exit an object by typing "leave". You may enter any object that you own or is set ENTER_OK, and, in 1.50, that you pass the enter lock of. You may set EALIAS and LALIAS attributes on objects. The string in these two aliases may be used as a substitute for "enter <object>" and "leave". Entering an object triggers the messages @enter, @oenter, @oxenter, and @aenter. The player who enters is shown the @enter message. The others inside the object see the @oenter message. People in the room that the player just left (which may not necessarily be the same room that the object is in, if the object is @tel'ed to) see the @oxenter message. @aenter is the action list to be executed. (Continued in 'man 5.1.2'.) &5.1.2 Leaving an object triggers the @leave, @oleave, @oxleave, and @aleave messages. @leave is shown to the leaving player, @oleave is shown to the things inside the object the player just left, @oxleave is shown to the things in the room the player goes to (not the room that the container object is in - if the player teleports or goes home, for example, the @oxleave message is shown at the player's destination). @aleave is the action list to be executed. ======================== [ Example 5.1: Wagon ] [ This creates the most basic of container objects ] > @create Wagon Wagon created as object #953 > @enter wagon=You climb into the wagon. Set. > @oenter wagon=climbs up into this wagon. Set. > @oxenter wagon=climbs up into the wagon. Set. (Continued in 'man 5.1.3'.) &5.1.3 > @leave wagon=You climb down from the wagon. Set. > @oleave wagon=climbs off this wagon. Set. > @oxleave wagon=climbs off the wagon. Set. > @set wagon=enter_ok Set. ======================== &5.2 5.2 Inside objects: internal descriptions, echoing, and relays An object's internal description - the description displayed to those inside it - can be set using @idesc <object>=<description>. This functions identically to @desc, except it is only visible by those inside. To enable those inside the object to hear what is going on outside, set the @listen of the container to *. Anything that is in the object's @listen match will be relayed to the occupants. Talking and posing within objects is identical to talking and posing in rooms. Unless there is some sort of relay set up (described below), only those inside the object can hear you. * * * * * It is frequently useful to be able to talk to the outside world, and perform other actions, like looking at the room that the object is in. This is best done with user-defined commands on an object which is placed inside the container object. It's a bad idea to define the commands directly on the container, since they can be used by those who are outside the object. (Continued in 'man 5.2.2'.) &5.2.2 The simplest way to handle looking outside is to do a user-defined command which forces the object to do a "look". This is extremely inelegant, though. On MUSHes which provide it (all 1.50 and most 2.0), the command "look/outside" will allow you to look outside the container you are in. Taking objects which are outside, however, generally requires a user-defined command which forces the object to do a 'get' or @teleport. * * * * * In the past: The simplest way of doing relays is to add special user-defined commands for say and pose which force the object to emit. When the object emits, both players inside and outside the object will hear the message. Another, more direct, way of talking to the outside world from the inside - actually, of showing a message to only those outside - is to use @emit/room. This outputs the message to the outermost container - the room that the wagon or similar object is in. (Continued in 'man 5.2.3'.) &5.2.3 * * * * * The 'best' way to relay information, however, is to use the AUDIBLE flag. This flag essentially outputs text from one place to another, without danger of looping. When set on an object, anything "said" (posed, emitted, etc.) inside the object will be broadcast to the object's location, prepended with the string "From <name of AUDIBLE object>," The AUDIBLE object does not receive its own propagated messages, nor does it bounce back things it hears via "@listen *" from the outside world. AUDIBLE exits "funnel" noise from their source room to their destination room. In 1.50, the room itself must be set AUDIBLE, to activate AUDIBLE exits. The AUDIBLE exit propagates the message to the next room and no farther; if there are AUDIBLE exits in the next room, they do NOT pass the information on to their destinations. Messages channeled through AUDIBLE exits are prepended with, "From <name of AUDIBLE exit's source>," (1.50) or "From a distance," (2.0). (Continued in 'man 5.2.4'.) &5.2.4 2.0 also provides an additional command, @forwardlist. This command, with the syntax "@forwardlist <object>=<space-separated list of dbrefs>", forwards all messages heard by <object> to every object in the list of dbrefs. <object> must have its AUDIBLE flag set. You can filter out messages by setting the @filter attribute with a (possibly wildcarded) pattern to suppress. For example, if you don't want "<name> has left." messages, "@filter <exit>=* has left.", and messages matching that pattern will not be propagated. You can have more than one pattern; they should be separated by commas. (i.e., you can do: "@filter <exit>=* has left.,* has arrived." to suppress both arrival and departure messages). If the pattern contains a comma, the entire pattern should be enclosed in curly braces {} (just like in @switch). (Continued in 'man 5.2.5'.) &5.2.5 You can change the string prepended to propagated messages via the "@prefix" attribute on the AUDIBLE object. The game always puts a space between the prefix and the propagated message. Generally, prefixes are of the format, "From Mnedranth's back," or "In the gazebo," or the like -- something which indicates where the message is coming from. The game automatically prepends one if the @prefix attribute is not set. There are times when it is desirable to have no prefix prepended at all; in those cases, set the @prefix attribute to "\". There is an additional refinement: @infilter and @inprefix. @infilter suppresses text that is normally sent to the object via @listen (usually "@listen *"). @inprefix will prefix text forwarded from the outside with a string. Normally, text forwarded via @listen is not prepended with anything. &5.3 5.3 Do it! -- Wagon controller This section assumes that you have created a wagon as described in example 5.1. Below, <#wagon> refers to the dbref number of that wagon, and <#cont> refers to the dbref number of the controller. Enter the following commands: @idesc <#wagon>=<description> @listen <#wagon>=* @create Wagon controller @lock control=me drop control Possibilities: This is the old way of doing things: @va <#cont>=$'*:@fo <#wagon>=@emit {On the wagon, %N says, "%0"} @vb <#cont>=$.*:@fo <#wagon>=@emit {On the wagon, %N %0} [ These two commands define ' and . as alternates to " and : inside the wagon. They relay to the outside world, as well as to those inside the vehicle. (Continued in 'man 5.3.2'.) &5.3.2 There is one major problem with this: because of the @listen, the players inside the wagon hear exactly what the wagon hears, which is the message "You emit: <whatever>" This is ugly, but it's effective. ] @vc <#cont>=$view:@fo <#wagon>=l @vd <#cont>=$view *:@fo <#wagon>=l %0 [ These two commands substitute for look. ] This is the new way of doing things: @set <#wagon>=AUDIBLE @prefix <#wagon>=On the wagon, [ There is no need for the view command; "look/outside" will suffice. ] @ve <#cont>=$snag *:@fo <#wagon>=get %0 [ This command forces the wagon to get an object outside. ] &6 6. Complex programming &6.1 6.1 Useful functions Often, you will want to build machines that accept input from players only; the NUM() function is useful for checking whether or not something is a player. Simply evaluate: num(*%N), assuming that you are checking if the enactor is a player. The * forces %N to be evaluated as a player; if there is no player by that name, the function returns "#-1". The only case in which this trick with num(*%N) fails is if the enactor has the same name as a player. You can also use the TYPE() function to see if an object is a player. * * * * * The LOC() function is useful for several things. If it is used to locate a player, it will return the number of the player's location, assuming that the player is not set UNFINDABLE. If it is used to locate an object that you control, it will return the number of its location. It will return #-1 if you try to locate an object you don't control. Used on an exit, LOC() will return the dbref number of the exit's destination (to get an exit's source, use the HOME() function). The LOC() function returns the drop-to of a room; usually, this is #-1. (Continued in 'man 6.1.2'.) &6.1.2 Related to the loc function is the %L substitution. This returns the location of the enactor. Since it requires the enactor to trigger off a command of some sort, however, it cannot replace the LOC() function, unless you just want LOC(%#). * * * * * The NEARBY() function tests if one object is near another. It is called with: nearby(obj1,obj2). For this function to work, you must control at least one of the objects, or be near one of them. Object1 is considered to be nearby Object2 if it is in the same location, if it is being carried by Object2, or it is carrying Object2. If the two objects are nearby, the function returns a 1; otherwise, it returns 0. * * * * * (Continued in 'man 6.1.3'.) &6.1.3 The MUSH functions you will use most often will probably be those which return names and dbrefs. But almost all of MUSH programming involves the manipulation of lists - adding items, removing items, finding certain items within, etc. Lists, for MUSH purposes, are generally defined as a list of space-separated of words. The first word in a list is 1. Strings, on the other hand, are composed of arbitrary characters. The first character in a string is 0. Lists are basically special types of strings. Everything in MUSH is eventually treated as a string. Several functions create lists: LCON(), LEXITS(), LATTR(), and LWHO() are perhaps the most frequently used of these. LCON() gives a list of the dbref numbers of all objects in a room (including dark objects). LEXITS() gives a list of the dbref numbers of all non-dark exits out of a room. LATTR() gives a list of all attributes on an object. LWHO() gives a list of all non-dark connected players. * * * * * (Continued in 'man 6.1.4'.) &6.1.4 Several functions are useful for parsing lists. The POS() function, called with pos(string1,string2), returns the position number of string1 in string2. If string1 is not in string2, the function returns #-1. The first character is considered to be position 1. This function is particularly useful when checking flags on an object. For example, to check if an object is a puppet, check pos(p,flags(object)) If this returns anything but #-1, the object is a puppet. (An easier way to do this is to use the HASFLAG function: HASFLAG(object,puppet) will return 1 if the object is a puppet). For parsing, FIRST() and REST() are useful. They return the "head" and the "tail" of a list, respectively, and are basically identical to the LISP functions CAR() and CDR(). The "head" of a list is the first word in that list; the "tail" of the list is everything else. There's also the useful LAST() function in TinyMUSH 2.2, which returns the last word in a list. (Continued in 'man 6.1.5'.) &6.1.5 The WORDS() function counts the number of words in a list. This is useful for a loop counter ("repeat this action until the counter variable is greater than the number of words in the list"), and for finding the last word in a list, which is done as follows: Assuming that the list is in %0: extract(%0,words(%0),1) This counts the number of words in the list, then uses extract to take a one-word long list starting from that position - the last. (Of course, if you're programming in 2.2, you should just use the LAST() function instead of this somewhat-complicated extraction.) The STRLEN() function returns the length of an entire string (list). It is extremely useful when used in conjunction with the MID() function. The latter is called with: mid(string,position of first character,length) and returns a string of <length> characters starting from <position>. The first character in the string is numbered 0, not 1. Supposing you wish to return a string starting with its third character. You would use: mid(string,2,strlen(string)) * * * * * (Continued in 'man 6.1.6'.) &6.1.6 The EXTRACT() function, mentioned above, called with extract(list,first,length), is used to pull out words from the middle of a list. "Length" is the number of words to be extracted, _not_ the number of letters. MATCH(), called with match(list,pattern), returns the number of the word where <pattern> first occurs. The first word in the list is numbered 1. If no match is found, the function will return 0. The pattern may be straight text, or it may contained the wildcards * and ?. The MEMBER() function is similar, except that it does not take wildcards, and is case-sensitive, so match(a B c d,b) will return 2, but member(a B c d,b) will return 0. The case-sensitivity of MEMBER() can be worked around using the UCSTR(), LCSTR(), and CAPSTR() functions. These functions return a list with all letters capitalized, all letters in lowercase, or with the first letter of the first word of the list capitalized, respectively. (Continued in 'man 6.1.7'.) &6.1.7 EXTRACT() and MATCH() functions can be combined to match two lists: Suppose you have two registers, va and vb. The first contains a list of first names, and the second contains a list of last names. Given a first name, you want to find the last name. Assume that the first name given as input is in %0, and that it is in the va. Then, the matching last name will be: extract(v(vb),match(%va,%0),1) It uses the match() function to find the position of the name in the first name list, then uses extract() to get the word in the corresponding position on the last name list. There's a function which combines an EXTRACT() and MATCH(), called GRAB(). It takes the syntax "grab(list, wildcard)"; it finds the first element of the list which matches the wildcard, and returns that element. This is useful if you want to partial-match something, and then want to get its entire name. For example, the function call "grab(banana:yellow apple:red pear:green plum:purple, pear:*)" will return "pear:green". * * * * * (Continued in 'man 6.1.8'.) &6.1.8 There are two functions related to EXTRACT() and MATCH(), which operate on multiple elements of lists. They're called ELEMENTS() and MATCHALL(). "matchall(list, wildcard)" will return the element numbers of all elements of the list which match. "extractall(list of words, list of numbers)" will return the elements of the list which correspond to the given list of numbers. These two functions can thus be combined to locate, and then grab, multiple elements of a list. The MATCHALL() function can be used in conjunction with WORDS() to determine how many times a given word appears in a list, via "words(matchall(list, word))" -- MATCHALL() returns the list of element numbers which match, and the WORDS() counts them up. &6.2 6.2 Non-standard attributes and attribute flags Non-standard attributes act much like V-attributes, except that they may be arbitrarily named, as long as the names do not conflict with the names of any existing attribute. Objects can have an unlimited number of attributes, thus freeing the programmer from the necessity of creating multiple objects to handle extremely complex programs. Non-standard attributes are set using one of two methods: &<attribute name> <object>=<whatever> @set <object>=<attribute name>:<whatever> 1.50 also allows: @_<attribute name> <object>=<whatever> These attributes may be accessed via the get() and v-functions, %-substitutions of the form %attrib do not work for user-named attributes, as they do with @va-@vz. In all other respects, however, user-named attributes behave like @va-@vz. * * * * * (Continued in 'man 6.2.2'.) &6.2.2 Non-standard attributes may be locked (prevented from being changed by anyone other than the person doing the locking, or a wizard), or chowned separately of the object. The syntax for doing this is "@lock <object>/<attribute>"; "@unlock <object>/<attribute>" unlocks the attribute. Attributes can also have flags. These are set via the command "@set <object>/<attribute> = [!]<flag name>". There are three flags which may be set. The VISUAL flag makes an attribute public; anyone can read it via the GET() function. The NO_COMMAND flag prevents an attribute from being checked for $-commands and ^-listens. The NO_INHERIT flag prevents an attribute from being inherited by the children of the object. When you examine something, a locked attribute will have a "+" sign next to it. Attributes such as LAST are owned and locked to God; mortals may not change them. VISUAL attributes will be marked with "v", NO_COMMAND attributes will be marked with "$", and NO_INHERIT attributes will be marked with "i". (Continued in 'man 6.2.3'.) &6.2.3 Certain 1.50 configurations always give the dbref of an attribute's owner next to the attribute name; other configurations, and 2.0, only show the attribute owner's dbref is it's different from the object's owner. In 1.50, the ANSI_DISPLAY flag, when set on a player, will cause attribute names to be highlighted in boldface (if the player's terminal supports ANSI control sequences). This is useful if you are examining a very large object, and need to be able to quickly and easily see where the breaks between attributes are. * * * * * Non-standard attributes can make MUSH code much easier to read; it is also good practice to do a &purpose or &program register on the object which explains a little bit of the programming, for future reference. (Continued in 'man 6.2.4'.) &6.2.4 One useful thing about non-standard attributes is that they are very useful when programming objects which contain numbered registers - mail objects, bulletin boards, etc. Instead of going through the clumsy method of putting get(me/va) get(me/vb) get(me/vc) etc. in a register and using extract() to force an evaluation, you can directly set registers, like: @va object=$foo *=*:&r%0 me=%1 @vb object=$bar *:@pemit %N=get(me/r%0) Note the use of the brackets within the get() to force register evaluation early. The two statements above allow you to set and retrieve numbered non-standard attributes. &6.3 6.3 Introduction to @dolist One very useful command is @dolist <list>=<action>. It performs <action> for every item in the list, replacing the symbol "##" with a word from the list. For example, @dolist [lexits(here)]="[name(##)] will make you say the names of all exits in the room you are in. It can be used to multi-page: @dolist Culyn RosaLil Jellan = page ## = Hello! @dolist evaluates its arguments in order. So, for example, "@dolist Culyn RosaLil Jellan = page ## = Hello!" would page Culyn, then RosaLil, then Jellan. @dolist can also become rather confused by @switch statements and semi-colons. It is best to enclose the entire <action> section of the @dolist with braces - @dolist <list> = { <action> } This will prevent most errors. @dolist should replace most list-based loops. In the past, the following was an efficient implementation of the problem listed above: (Continued in 'man 6.3.2'.) &6.3.2 VA: $name-exits: @tr me/vb=[extract(lexits(here), 1, 1)], 1 VB: @switch %1=>[words(lexits(here)],{},{"[name(%0)]; @tr me/vb=[extract(lexits(here), add(%1,1), 1)], [add(%1,1)]} (It might be more efficient, if there are a lot of exits, to set @vc me=lexits(here) in the VA, but this is a small point). Now, all that's needed is: VA: $name-exits: @dolist [lexits(here)]="[name(##)] Not only is this much cleaner, but it's considerably faster than the old method. Triggers take up quite a bit of time to execute, and @dolist is vastly more efficient. &6.4 6.4 Time synchronization Semaphores are a very flexible tool for the synchronization of objects. In practical terms, they make sure things occur in a specified order, and prevent simultaneous actions from conflicting with each other. The name "semaphore" comes from the name for signal flags used on ships; in modern computer systems, semaphores are used to prevent sections of memory from being concurrently read from or written to by different processes. Indeed, one of the main purposes of semaphores in MUSH is preventing two things from attempting to change the same attribute at the same time. More generally, in MUSH, semaphores are good for two major things: preventing an already triggered object from being used again until it has finished the action list associated with that trigger, and preventing a command from being run until a certain other command has executed. It is, fortunately, not necessary to completely understand how semaphores work in order to use them. (Continued in 'man 6.4.2'.) &6.4.2 "Synchronized" actions are those which are correctly performed in some specified order. Semaphores effectively delay the execution of commands by queueing them up in the correct order, and preventing the execution of the next command before the previous command has finished, thus synchronizing the objects and actions. Every semaphore is associated with a number, called the "semaphore count", which is the number of actions (or in the case of MUSH, action lists) it is trying to synchronizing. Such actions are also referred to as "pending", "blocking", or "waiting" on the semaphore. * * * * * In MUSH, this semaphore count is stored in the SEMAPHORE attribute. A semaphore which has no commands pending will have a count of 0. If the semaphore count is positive, the semaphore has that many actions waiting on it. A negative semaphore count indicates that actions will not block on it; for example, a count of -3 indicates that the next three actions to wait on the semaphore will be immediately executed. (Computer science types: please note that this is the REVERSE of the convention generally used in operating systems theory!) (Continued in 'man 6.4.3'.) &6.4.3 Two operations are used to control a semaphore: wait, and notify. "Waiting" an action list on a semaphore puts those actions at the end of that semaphore's queue. Each time a semaphore is notified, the first action list on its queue is executed. Thus, an action which waits on a semaphore is delayed until the semaphore is notified. A command which is waiting for its associated semaphore to be notified is referred to as "blocked"; when its semaphore allows it to be executed, the command is then "unblocked." Eexecuting a wait on a semaphore increases its semaphore count by one, and executing a notify on a semaphore decreases the count by one. To execute a wait on a semaphore, use "@wait <semaphore>=<action>"; if <action> is a list of commands, it must be enclosed in curly braces. To notify a semaphore, use "@notify <semaphore>". Please note that the object that does the waiting executes the action; the semaphore is simply a timing device. Thus, in MUSH, you can use objects you do not own as semaphores, if they are LINK_OK. (Continued in 'man 6.4.4'.) &6.4.4 Objects waiting on a semaphore run their actions on a first-come, first-served basis, If #2, #3, and #4 execute waits, in that order, on semaphore object #5, the first notify the semaphore receives will begin #2's action list. The next notify will execute #3's action list, and so on. In other words, notification of a semaphore just releases the FIRST object which waited on it. It is possible to notify a semaphore repeatedly, by providing a numeric argument to @notify: "@notify <semaphore>=<times to notify>" If the number of times is not specified, the semaphore will be notified once. The semaphore executes the first <times> commands that were @waiting on it. As a reminder, if the semaphore is notified more times than it has commands queued, the semaphore count will become negative and further @waits dependent on that semaphore will be immediately executed, until the semaphore count is zero. This normal @notify is shorthand for the default switch, "@notify/first". (Continued in 'man 6.4.5'.) &6.4.5 If you want all waits pending on the semaphore to execute, use "@notify/all <semaphore>". This will cause all the actions waiting on the semaphore to be executed, and resets the semaphore count to 0. The other method of clearing a semaphore is "@drain <semaphore>". This will reset the semaphore count to 0. Actions waiting on the semaphore are simply discarded. In 1.50, destroying an object automatically executes a @drain on it. MUSH also allows for semaphore waits to have a timeout expiration. An object can use "@wait <semaphore>/<time in seconds>=<action>". If <semaphore> is notified before <time> passes, the action is executed and the semaphore count decreases by one, just as if a regular semaphore wait were done. If <time> passes, and the action is still blocked pending semaphore notification, it is executed anyway, and the semaphore count on the <semaphore> is decremented by one. * * * * * (Continued in 'man 6.4.6'.) &6.4.6 Most MUSH programmers will never use semaphores for anything more than basic synchronization. The most common form of this is preventing an action list associated with a trigger from being run while the action list from a prior trigger is still executing. This is especially important for vending machine objects that need to work on a single item at a time. One way of preventing this is to use an attribute BUSY; a @switch at the beginning of trigger checking for this attribute will tell you if the object is already trying to execute something. This, however, can force the player to make repeated attempts to trigger the object. A better way to do this is to associate the entire action list of the trigger with a semaphore. Triggers for action lists are generally @a-actions like @apay, or user-defined commands, like $buy. We thus set up our wait in one of the following two forms: @apayment object = @wait me={ @@ list of actions @@; @notify me} &DO_COMMAND object =$command: @wait me={ @@ list of actions @@; @notify me} (Continued in 'man 6.4.7'.) &6.4.7 We also need to initialize the semaphore state; this is best done by "@startup object = @drain me; @notify me". The @drain makes sure that we start with a clear semaphore. We need the @notify in the startup because we don't want the first action we trigger on the object to block. The semaphore state of the object should thus begin at -1. (When programming your object, please remember that the very first time you use it, you will have to @notify it.) In the above example, if someone tries to trigger the object before a prior trigger has completed, the action list will block. The last command in the prior action list is "@notify me"; this will clear the block, and allow the next action list to begin immediate execution. This simple technique is extremely useful for avoiding the headaches associated with preventing concurrent attribute updates. &6.5 6.5 Security problems in programming Although, in MUSH 1.50 and 2.0, any object that you own cannot force you, care should still be taken to make sure that objects are reasonable secure. Be extremely careful not to leave control objects lying around. _Always_ lock them to yourself, since possessive get works in 2.0 code. get <person>'s <object> will enable someone to take any unlocked object that you are carrying. One simple check you can make is to put a @switch in all commands that checks to see that the triggering object is its owner: @switch %#=<Your dbref>,{action list},{@pemit %#=You're not my owner!} Another secure way of doing this is to use "@lock/use" to prevent anyone but those who pass the lock from triggering $-commands on the object. One common problem is carelessness with semi-colons. Unless enclosed by curly braces { }, commands like %0 can be extremely hazardous. For example, take the object Foo, with a @va of $foo *:@emit %0 Type "foo bar" will force the object to emit bar. (Continued in 'man 6.5.2'.) &6.5.2 Under old parsers, there's a nasty trick that can be done: "foo bar;@destroy me" (or, for that matter, any other command). Foo will emit bar, and then happily blow itself up. This, obviously, is a Bad Thing. The @va should be $foo *:@emit {%0} This safely traps any semi-colons. Although this kind of trick shouldn't be (easily) possible to do under the new parser, it should still be something to keep in mind and avoid if possible. * * * * * Other problems involve registers that are directly set to whatever someone types in; if this is a force, triggering the register will execute the command. There are many variations on this; the most common is the bulletin board-type object which has a command like "post <subject>=<message>" and sets attributes to the subject and message, perhaps like this: $post *=*: &SUBJ_[secs()] me={From %N: %0}; &MSG_[secs()] me={%1} (Continued in 'man 6.5.3'.) &6.5.3 Here, the SUBJ attribute is safe, since the "From" component prevents the placement of a $command into the attribute. The MSG, however, is subject to something like this: "post Problem=$foo: @emit Gotcha!" This would set something like MSG_746606174 to "$foo: @emit Gotcha!" thus allowing any player to type "foo" and force the bulletin board to @emit Gotcha! This is, obviously, dangerous. This problem can be gotten around by setting MSG to "XX %1" or something similar, and using REST() to retrieve the message when it is needed. Never allow a player to directly set an attribute in such a manner, even for a moment! To do so is to invite trouble. ---------- A great many problems can be taken care of under INHERIT. Only objects set INHERIT may force their owner. Objects that aren't INHERIT may not force objects that are. If a player is set INHERIT, all of his objects are considered to be INHERIT. This flag is reset in a @chown. (Continued in 'man 6.5.4'.) &6.5.4 In 2.0, INHERIT objects owned by wizards have wizard powers. Any object with wizard powers may force anything else, regardless of its status. This flag is most useful for wizards and people who own large, complex, publicly used systems, such as mail. Sometimes it's useful to be able to have a non-INHERIT object force one that is, for a single command or group of commands. Since the most common use of this is for say/pose/emit output, in 2.0, the commands @fsay, @femit, and @fpose have been provided. These do the obvious. The only thing to note is that @fpose has a switch, @fpose/nospace, which does eliminates the space between the name of the object and its pose (making it equivalent to player ";"). * * * * * (Continued in 'man 6.5.5'.) &6.5.5 There are two other notable techniques for getting around INHERIT restrictions. Frequently, you are going to want to have a non-INHERIT object trigger one that is; this is especially true if you are a wizard on a 2.0 MUSH and should not, for security reasons, set objects INHERIT unless they absolutely have to be. You can use LINK_OK as a "@trigger-ok" flag in 1.50, but this is not possible under 2.0. Therefore, there must be a kind of "message passing" used. The first method is to uselock the object you want triggered (the "victim") to an auxiliary object inside it, and then have the non-INHERIT triggering object (the "actor") trigger the auxiliary, which then does something which matches a $command on the victim. In the following example, #88 is the auxiliary object. &DO_ACTION actor=$test *: @trigger #88/PROPAGATE=%0 &PROPAGATE #88=do_it %0 &ACTION victim=$do_it *: @dolist %0={@set ##=dark} @tel #88=victim @lock/use victim=#88 (Continued in 'man 6.5.6'.) &6.5.6 * * * * * Unfortunately, the $command method is not very fast. A better method is to use ^-patterns, and uselock the victim to the actor to prevent spoofing. The victim should also be kept in a secure location where it's not going to "hear" anything accidentally. The actor @pemit's an action pattern to the victim, which acts on it. In the following example, #90 is the victim. &DO_ACTION actor=$test *:@pemit #90=TEST_PCODE %0 &TEST_PCODE_LISTEN #90=^TEST_PCODE *: @dolist %0={@set ##=dark} @set #90=LISTENER (or MONITOR, in 2.0) @lock/use #90=actor As long as you are careful to make sure that the victim doesn't hear anything other than the actor, and then only when appropriate, this is the best method for getting around INHERIT. * * * * * (Continued in 'man 6.5.7'.) &6.5.7 The SECURE() function returns a string with all semi-colons and braces {} converted to spaces. This is useful if you want to prevent people from abusing your stuff via triggers and the like. The ESCAPE() function inserts the escape "\" character before all [ ], preventing the expression within from being evaluated - the string is returned exactly as typed. An escape character is also inserted before the first character of the string. The parser's handling of the escape character is reliable and consistent; another less reliable character which can act as an escape is the percent "%" character. &6.7 6.7 Debugging One of the things that is sadly lacking from MUSH is an easy way to debug objects. One is often reduced to having an object say registers out loud, or setting the object PUPPET. If the object is PUPPET, you will see all the output from the commands it executes, such as "Set." Unfortunately, in TinyMUSH 2.0, "Set." is used to acknowledge setting attributes, flags, and miscellaneous other things. Thus, puppet output can be rather useless (although an "I don't see that here." or "Huh?" or similar messages can be useful in notifying you of a typo). 1.50's output is a bit more informative; it will tell you which object is getting set, and differentiate between attribute sets and flag sets, enabling the PUPPET flag to be fairly useful for debugging purposes. * * * * * The VERBOSE flag has been provided for debugging purposes. It displays the commands getting executed, which is useful for checking evaluation order and the like. (Continued in 'man 6.7.2'.) &6.7.2 In 2.0, it displays all commands executed by the object to the object's owner, in the format <object name>] <command>. The output can be slightly messy, especially if @switches are involved. When it goes to evaluate the switch, it will display the entire switch statement; it will then show the entire branch of commands and then go to execute them. In 1.50, the VERBOSE flag displays all commands executed by the object to the object's owner, in the format <object dbref>] <command>. As with the 2.0 version of this flag, this is the command _before_ it has been interpreted by the parser. Placing a @force before the command _might_ be useful for _debugging purposes only_, since this will parse all the arguments, resulting in VERBOSE output which shows the final form of the arguments to the command. * * * * * (Continued in 'man 6.7.3'.) &6.7.3 In 1.50, there is a flag called DEBUG. Objects set DEBUG cause their owner to be notified of the results every call to the parser that that object makes. If used in conjunction with the VERBOSE flag, it can pinpoint precisely which expression is being evaluated to an undesirable quantity. The output is in the format <object dbref>! <unparsed string> => <parsed string> A similar flag, called TRACE, exists in 2.0. Its output, however, is in the format "<object name>(<dbref>)} '<unparsed string> -> '<parsed string>' * * * * * One interesting thing to note about @dolist is that it evaluates all switches on one "level" of command before executing the results of those switches. This is a quirk which is only affects the output of @dolist; in terms of execution order, @dolist simply repeats its command sequence on each item in the list, completing the command sequence for each item before beginning on the next. &6.8 6.8 On the New Programming Style The power of MUSH has increased dramatically in the past six months. Things that used to be difficult or slow are now fairly easy to do, mostly through the addition of new commands and functions. The biggest advances are in MUSH's treat of list processing. It is now quite simple to take a list and perform a series of functions on it, via the function ITER() or the 1.50 command @map. These functions "map" another function onto each element of a list, returning a list. (The terminology comes from the LISP programming language). ITER() and @dolist both use the "##" token to signify replacement, so it is fairly difficult to easily combine the two. 1.50's @map command maps a function onto a list, placing the new list into the MAPLIST attribute on the object executing the @map. This makes the parser somewhat more forgiving. @map can be thought of as a command version of the ITER() function provided by both MUSH versions. This command, however, is rather redundant and is likely to go away in future versions of 1.50; new programs should avoid its use. (Continued in 'man 6.8.2'.) &6.8.2 * * * * * Among the more useful complex list processing functions are SETUNION, SETINTER, and SETDIFF. These three functions return a sorted list from two lists. SETUNION concatenates two lists, removing duplicates. SETINTER returns the elements that the two lists have in common. SETDIFF returns the elements of the first list which are not in the second. The SWITCH() function also enables one to do the equivalent of @switch on a pattern, but instead of triggering an action, the SWITCH() function returns a string. (Continued in 'man 6.8.3'.) &6.8.3 The final major improvement is the U() function in 2.0 (also called UFUN() in 1.50. In 1.50, UFUN() also has a close relative, ZFUN()). This is a "user-defined function", which generally consists of a bunch of functions in an attribute. The uses of U() are beyond the scope of this section; suffice to say that it allows one to pass arguments into a function of one's own devising, and get a string back in return. It is most often used to break up complex evaluations into something more readable, or to make a commonly used sequence of functions easily accessable. This function is described in detail later in this manual. * * * * * The increasingly popular "switchless style" of programming eschews the actual @switch command in favor of using function evaluations to generate a word. The object is then @forced to execute the attribute named that word, via the V() function. Concatenation is merely a matter of leaving out things such as square brackets [] in the appropriate places. Under 1.50, the STRCAT() function can also be used to explicitly concatenate strings. (Continued in 'man 6.8.4'.) &6.8.4 A very simple example of "standard" style vs. switchless coding follows. Let's suppose we want an object to respond with one of three different, randomly selected, messages, when someone types something. Switchless style: &FOOBASE object=$foo: @emit [v(FOO[rand(3)])] &FOO0 object=Zero! &FOO1 object=One! &FOO2 object=Two! [ or, alternatively, you could put the @emit in each statement and leave it out in the main one, if you wanted to execute more complex commands. ] A slightly more interesting example, although not one that is really any more complicated -- there is a "cat" object upon which you wish to place a "pet cat" command which will get the cat to respond with a random action: (Continued in 'man 6.8.5'.) &6.8.5 Standard style: &PET_CAT cat=$pet cat:@switch [rand(3)]= 0,@emit The cat purrs when %N pets her., 1,@emit The kitty blinks at %N., 2,@emit The cat arches her back and hisses at %N. Switchless style: &PET_CAT cat=$pet cat:@emit [s(v(PETMSG[rand(3)]))] &PETMSG0 cat=The cat purrs when %N pets her. &PETMSG1 cat=The kitty blinks at %N. &PETMSSG2 cat=The cat arches her back and hisses at %N. In this particular example, the "switchless" style is wasteful; it is more efficient (as well as simpler) to use a @switch. The switchless style is useful when you have a very complex function with many possible outcomes, or need to run one huge batch of commands at a time, and want to avoid use of multiple @triggers. (Continued in 'man 6.8.6'.) &6.8.6 The switchless style for objects like this, however, does make it quite easy to expand upon the basic object. For example, to add more responses to the cat, one merey has to change the "rand(3)" to, say, "rand(10)", and then add new PETMSG attributes. It is much more difficult to edit switches (plus, the switchless style is easier to read). Some caution should be exercised when programming complex objects in the switchless style. Because "switchless" programs often involve extremely large, complicated function, they can take quite a long time to evaluate. Normally, in MUSH, commands are queued, so that no one object consumes a large chunk of time all at once. The switchless style more or less defeats that, forcing the game to focus all its time on evaluating that one monster function. This can contribute to server lag. One should thus save switchless programming for those objects that really require the speed provided by the switchless style. (Continued in 'man 6.8.7'.) &6.8.7 Do note that it is presently trendy to brag that a given program uses no @switches (or SWITCH()es, or whatever the unpopular function of the week happens to be). One should be careful to always choose the method which is actually faster, rather than the method which seems the most impressive. Incomprehensible MUSHcode is poorly-written MUSHcode, usually. * * * * * Further notes on the parser: while it is quite consistent in evaluation, many programmers continue to question exactly what is needed to accomplish certain types of evaluations. The '\' character is an escape character. When placed in front of a "special" character (like brackets or percent signs), it prevents them from being evaluated. Each escape character delays evaluation for one pass through the parser. The '%' character is most often used for "percent substitutions": %N, %r, %va, etc. When it is not being used for that purpose, though, it, too, acts like an escape character. (Continued in 'man 6.8.8'.) &6.8.8 The curly braces {} are used to group commands. Also, in "weird" functions which cause a second pass through the parser (such as ITER()) the curly braces are used to delimit functions that should not be evaluated on the first pass through the interpreter. The square brackets [] signal the start and end of a function evaluation group. They can be nested, and for the purposes of concatenation show where "normal" text ends and substitutions begin. Square brackets say, "Evaluate me now." If you put square brackets into something like a call to ITER(), you will need to escape them out. Note that functions such as ITER() and U() are _exceptions_ to the parser rules; because they call the interpreter again, the behavior of the special characters sometimes seems strange. Successfully getting ITER() to return the correct value depends on careful use of curly braces and escape characters. For further details on complex programming in this style, as well as notes on efficiency and further parser details, consult the final sections of this manual. &6.9 6.9 Hints and tips This is by no means a complete guide to MUSH. The code is constantly changing, and new features are constantly being implemented. There are far more commands and functions available than can be covered in a manual like this one. If you are interested in MUSH programming, you should read through all the help files, on @-commands, functions, and flags. The best way to learn how to program MUSH is to do it! Find some sort of project and work at it. If you come up with something really neat, I'd love to hear about it - I'm especially interested in unique applications of non-standard attributes. Besides, I like getting mail. The non-standard attributes available in TinyMUSH make certain ways of doing things obsolete. Most notable is the use of extract() and member() combined with a list containing va-vz to access the va-vz attributes in numerical order; attributes can now be numbered a1-a10, for example. Also, worrying about running out of registers is now a thing of the past. Corrections, complaints, or congratulations are always welcome; I can be found as Amberyl on most MU*s, or emailed at lwl@godlike.com &7 7. QuickStart Guide &7.1 7.1 Basic Syntax This section is really separate from the rest of the manual. It assumes that the reader is familiar with some other programming language, and simply wishes to learn how to get started in MUSH as quickly as possible. Such users will still benefit from reading the rest of the manual, since there are many programming applications in MUSH that don't really match anything one would do in another language; this brief section is simply intended to explain concepts in a slightly more compact, "traditional" computer-science fashion. Like most programming languages, MUSH has variables. There are temporary and permanent variables. Temporary variables are transitory, and are not saved in the database. They include the stack variables %0-%9, the enactor %# and related variables (the enactor name, %N, and the associated pronouns, %s, %o, and %p), the local registers r(0) - r(9), and so forth. Permanent variables are stored in attributes, and can be referenced by various functions which fetch variables. The most common of these are the V() function, which fetches an attribute off the current object, and GET(), which fetches an attribute off an arbitrary object. (Continued in 'man 7.1.2'.) &7.1.2 Attributes are divided into two categories, normally called "built-in" and "user-defined". Built-in attributes are set with the command "@<attribute name> <object>=<text>". User-defined attributes are set with the command "&<attribute name> <object>=<text>". You can set a built-in attribute using the user-defined syntax, but convention normally dictates the use of '@' for built-ins. The majority of built-in commands also being with '@', although some, such as "page", do not. Conventionally, MUSHcoded global commands begin with '+'. All commands are interpreted at runtime. Normally, the '=' sign is used to separate the main two arguments to a command. If there are multiple components to the second argument, these components are separated by commas. Curly braces are used to indicate groupings, and prevent interpretation of special characters enclosed within. The escape character '\' can also be used to protect special characters. (Continued in 'man 7.1.3'.) &7.1.3 MUSH is a scripting language, essentially. Most MUSH objects' "programs" either output text or change the database in some way. The generally accepted way to handle complex tasks is by manipulating lists or strings in some manner. Everything in MUSH is eventually treated as a string, and there is no system of types. Functional programming is at the heart of most complex MUSH items; MUSH inherits a few LISP constructs such as FOLD and SETQ. &7.2 7.2 Conditional and Loop Constructs There is no IF-THEN-ELSE construct in MUSH. Instead, there is a command called @switch, which takes the format: @switch <variable>=<condition1>,<action1>, <condition2>,<action2>, ... <conditionN>,<actionN>, <default> This is equivalent to the SWITCH-CASE construct in languages such as C and Pascal. Wildcard patterns are allowed in both the variable and the conditions, thus making this a fairly powerful construct. MUSH also lacks an equivalent of the FOR and WHILE constructs. Unfortunately, this type of structure is much more difficult to duplicate in MUSH. The closest is equivalent is the command @dolist, with syntax @dolist <list>=<action>, which performs an action repeatedly, substituting an element of <list> for the token '##' within <action>, each time. Thus, if you wanted to @create five items named 1, 2, 3, 4, and 5, you would try: @dolist 1 2 3 4 5 = @create ## (Continued in 'man 7.2.2'.) &7.2.2 FOR constructs in most programming languages typically perform a sequence of commands for some list of numbers, which might or might not be sequentially, but are mathematically related in some relatively simple fashion. The MUSH function LNUM() generates a list of numbers 0 to N. This list can then be manipulated via a function called ITER(). It is important to remember that because the language is list-oriented, manipulating lists generally turns out to be the most efficient way of coding something. Good MUSHcode generally puts a minimal number of actual action commands on the queue. Trying to write MUSH with the same type of logic you would use in C or a similar procedural language is generally a mistake. &7.3 7.3 Functions There are many, many functions in MUSH. Numeric manipulation, string manipulation, list manipulation, and functions to extract information from the database form the heart of the MUSH programming language. The important thing to remember here is that data in MUSH is untyped, and therefore, functions may be used to perform transformations on data types that one would normally not think of as useful for a particular type of data. Truncating a floating-point number, for example, can be as simple as taking X characters after the '.' of the number (which is simply a string). There are, however, MUSH functions to perform a variety of complex tasks; the above task can be done with TRUNC() or ROUND(), for example. Functions take a fairly traditional parameter-format, with the syntax: FUNCTION-NAME(<arg 1>, <arg 2>, <arg 3>, <arg N>) Parameters are normally evaluated left to right, before the relevant outer function is called; in general, the innermost functions are the first to be evaluated. &index3 INDEX OF SECTION III EXAMPLES: 5.1 Wagon 5.3 Wagon controller &IV MUSH Manual Version 2.008: Copyright 1993, 1994, 1995, Lydia Leong (lwl@godlike.com / Amberyl) Last revised 5/22/95. Section IV: Wizard's supplement 8. Owning and seeing everything 8.1 Special privileges, ROYALTY, and Powers 8.2 Attribute ownership - @atrchown and @atrlock 8.3 Object ownership - @chown and @chownall 8.4 The queue - @ps and @allhalt 8.5 The database - @entrances, @find, @search, and @stats 8.6 A note about security and privacy 8.7 The Master Room and command processing 9. Wiz-specific commands 9.1 Talking to other wizards: @wizwall and @wizemit 9.2 Messages: @wall, @listmotd, @motd, and @wizmotd 9.3 Locking out players: @login and @rejectmotd 9.4 Dealing with players: @newpassword, @pcreate, @boot, @toad, and @destroy 9.5 Dumping the database: @dump and @shutdown (Continued in 'man IV1'.) &IV1 10. Database control 10.1 Quota system: limited object creation - @allquota, @quota, and @squota 10.2 Money control: give and @poor 10.3 Consistency checking: @dbck and @purge, @dump/paranoid, @cut, @fixdb 11. Zones 11.1 Object zones 11.2 Parent room zones 11.3 ZoneMaster players 11.4 Some notes on local wizard control 12. The Fundamental Laws of Wizarding 12.1 Wizard Etiquette 12.2 The Five-Step Method of player management warning - nasty warning - gag - boot - newpassword 12.3 Wizard commands and when they ought to be used (Continued in 'man IV2'.) &IV2 This section was written with the 1.50 wizard in mind. If something is mentioned that doesn't exist in 2.0, and you're only interested in 2.0, ignore it. If you're going to play with the 2.0 wizard commands that aren't covered in this section, you probably know what you're doing and therefore don't really need those commands covered in this manual anyway. All wizards, however, will probably find something useful in section 12, which provides general rules for wizarding. Mortals may find it interesting reading. &8 8. Owning and seeing everything &8.1 8.1 Special Privileges, ROYALTY, and Powers A wizard, by definition, owns and controls everything except for God. A wizard may manipulate any object by using its dbref number, or by using *player. Remote get, look, and examine work, and a wizard may bypass the HAVEN flag by using "w *<player>=<message>" to whisper a message. A wizard may look at anything by looking at the dbref number, or by looking at *player. Warning: in 2.0 this triggers the odesc and adesc messages. The format of "look" for wizards is somewhat different: the object's name and dbref number is displayed on the line above the description. When looking at exits, the full exit name - including aliases - is displayed. Wizards can teleport to anywhere. In 1.50, the NO_TEL flag and teleport locks do not have any effect at all on wizards; in 2.0, however, wizards ARE affected by teleport locks. In 2.2, this is configurable. The UNFINDABLE flag does not prevent wizards from locating a player. A wizard can also obtain a player's location by using loc(*player), and get any attribute. There is _nothing_ in the database that a wizard cannot see, except for things like the password attribute and other "internal" dark-to-all attributes. (Continued in 'man 8.1.2'.) &8.1.2 The LWHO() function allows wizards to obtain a list of dbrefs of connected players. Also, in 1.50, wizards may watch connects and disconnects by setting the MONITOR flag on themselves, or grant the ability to do the same to another player. (NOTE: the 1.50 MONITOR flag is NOT like 2.0 MONITOR; the equivalent of 2.0 MONITOR is 1.50 LISTENER. 2.0 has no equivalent of 1.50 MONITOR). In both 1.50 and 2.0, WHO for wizards shows an extended listing with connect sites and other special information; to see the listing that mortals get, use the command DOING instead. * * * * * Pern version 1.14 introduced a ROYALTY flag (which can optionally be turned off at compile-time). This flag is settable by wizards. Objects ROYALTY can examine and teleport like wizards, but have no other wizard powers; all of the statements about wizards above also apply to ROYALTY. This flag does not exist in 2.0. (Continued in 'man 8.1.3'.) &8.1.3 PennMUSH 1.50 patchlevel 7 introduced a powers system. This enables wizards to grant specific objects, including players, the power to accomplish certain tasks that previous were restricted to wizards. Also, since the BUILDER and IMMORTAL flags from previous patchlevels are, intuitively, powers rather than real flag toggles, they were changed to powers in patchlevel 7. The powers include "teleport anywhere", "teleport anything", "examine anything", "boot", and most of the other useful wizard powers, save the ability to @force/control anything. Players who receive such powers should exercise them with the same caution a wizard should use, and they should not be given out lightly. A player granted all the powers has all the abilities of a ROYALTY, and more, except for the @rwall command. Therefore, prudence should be exercised in granting special powers. &8.2 8.2 Attribute ownership - @atrchown and @atrlock Attributes may be owned independently of objects. An unlocked attribute can be changed by the owner of the object. A locked attribute may only be changed by the owner of that attribute. Locked and unlocked attributes are, for the purpose of triggers and similar evaluation, exactly the same. Only wizards may set attributes on things that they do not own. The most recent player to change an attribute is its owner. Please note that it does not do any harm for someone else to own one of your attributes. If the attribute is locked, however, you will not be able to remove it _even if you are a wizard. A wizard may, however, unlock the attribute and then remove it. In 1.50, two commands are used to handle attributes. @atrchown changes the ownership. The syntax is @atrchown <object>/<attribute>=<new owner> @atrlock governs attribute locks: @atrlock <object>/<attribute>=<on or off> @atrlock <object>/<attribute> by itself returns the status of the lock. The 2.0 syntax (which also works in 1.50) is "@lock <object>/<attribute>", "@unlock <object>/<attribute>", and "@chown <object>/<attribute>=<player>". A normal "examine" shows the owner and lock status of attributes. &8.3 8.3 Object ownership - @chown and @chownall The @chown command is somewhat different for wizards. A wizard may @chown any object he is carrying, or any object by dbref number. The syntax is @chown <object>=<player>. Not that *<player> is _not_ used in @chown. @chownall transfers the ownership of everything a player owns to another player. @chownall <player> transfers ownership to the wizard who executes this command. @chownall <player>=*<new owner> transfers ownership to another player. Note the use of the pointer. A @chownall should NEVER be done without the consent of both parties involved, since it is usually difficult to reverse, especially if the recipient owns a lot of objects himself. &8.4 8.4 The queue - @ps and @allhalt Wizards can use @ps all to see the contents of the entire queue, or @ps *<player> to see the queue for one specific player. When the queue is very full, or a machine is looping so badly or triggering so many objects that simply halting it is not feasible, the command @allhalt can be executed. This clears out the entire queue - player, object, and wait, and displays to all connected players the message, "Your objects have been globally halted by <Wizard>." @allhalt should be used sparingly; if the server is slow, the queue may be full, but it may not necessitate an @allhalt. In 2.0, this is "@halt/all" rather than "@allhalt". &8.5 8.5 The database - @entrances, @find, @search, and @stats @entrances will work on "here" or on any room number, for a wizard. @stats can be broken down by player, using "@stats <player>" Note the absence of a pointer. Wizards need to be careful with @find, since @find with no arguments outputs the list of all objects that the wizard controls - i.e., the entire database. @search with no arguments, however, only does a @search on the wizard, printing out all the objects he owns. @search with any arguments, however, looks through the entire database, not just the wizard's objects. If you are looking for something, always try to do a @search instead of a @find, especially if you are on a 2.0 MUSH. @search which is restricted to a player is _considerably_ faster than @find. &8.6 8.6 A note about security and privacy. DARK. Wizards have enormous power. No notes on a MUSH are private; a wizard can examine them. Wizards can set themselves DARK, disappearing from the WHO list. In 1.50, any actions taken by a dark wizard show up as "Someone" rather than the wizard's name, if the game is so configured. Wizards cannot listen to whispers or pages, and always show up on a @sweep, whether or not they are dark. A wizard owns everything; he can change, destroy, or force it to do whatever he wants (unless it is God). Because of this, wizards must be VERY careful to make sure that there are no programming loopholes in their objects which would enable the objects to @force them to perform certain actions. Wizards should also be careful to announce their presence, if they are entering a room and are DARK. There is virtually no justification for spying on a private conversation (the exception may be listening to two players plotting how to crash the mud, or something similar). (Continued in 'man 8.6.2'.) &8.6.2 A wizard should _always_ check the location of a player before he teleports to that player. Being paged by a player does NOT mean that the player presently wishes for company. In general, the wizard should also ask permission to enter the room. A wizard should also never teleport a player anywhere without asking permission first - this is extremely rude. Occasionally, one tends to act before thinking, but a wizard should try to act with courtesy. Also, remember that in 2.0, wizards who exceed the idle time normally granted to mortals before an inactivity-timeout boot are set DARK. Wizards are also booted for inactivity, but can set their @timeout attribute to an arbitrarily high value; they will not be booted until that @timeout has expired. Wizards who are DARK from inactivity have a "d" next to their time instead of a "D", in the WHO list. In 1.50, wizards are never booted for inactivity. They are, however, set DARK at the mortal inactivity limit, if they are already set UNFINDABLE. (Continued in 'man 8.6.3'.) &8.6.3 If you are the sort of person who tends to forget that he is set DARK, you might want an automatic reminder given to you when you move. The following is useful for this: @move me=[switch(hasflag(%!,Dark),1,WARNING: You are set DARK.)] &8.7 8.7 The Master Room and Command Processing In both 1.50 and 2.0, the command parser goes through the steps shown here looking for a match on the command. If it finds one at a particular step, it stops and performs it. If more than one match (say, multiple south exits from a room), one is picked at random. $-commands are an exception to this rule, in that ALL $-commands that match are executed, except that the master room is only checked for $-commands if none are matched in the check around the player. The 2.0 evaluation order: - Uppercase commands (WHO, QUIT, etc) if typed at a terminal. - Prefix-character commands (", :, ;, etc) - The 'home' command. - Exits in the current room. - Exits in the master room - Internal commands. (Continued in 'man 8.7.2'.) &8.7.2 - $-commands in the following places, all checked in parallel: - Objects in your inventory. - Objects in your current location. - Your location (the room itself) - You (optionally) - $-commands in the ZONE chain of the player's location - $-commands in the ZONE chain of the player himself - $-commands in the following places, all checked in parallel: - Objects in the master room. - The master room itself. The 1.50 evaluation order: - Uppercase commands (WHO, QUIT, etc) if typed at a terminal. - The 'home' command. - Prefix-character commands (", :, ;, etc) - Exits in the current room. - Internal commands. - Enter aliases on objects in the current room. (Continued in 'man 8.7.3'.) &8.7.3 - Leave aliases on the object you are in, if applicable. - $-commands in the following places, all checked in parallel: - Objects in your current location. - Your location (the room itself) - Objects in your inventory. - If nothing has been matched: - If the zone of the player's location is a parent room, - Parent room exits - All $-commands on objects in the parent room - Else, - All $-commands on the zone object of the player's location - If still nothing has been matched: - All $-commands on the player's personal zone - As a last resort, when matching still fails, - Global exits (Master Room exits) - All global $-commands ($-commands on objects in the Master Room) (Continued in 'man 8.7.4'.) &8.7.4 In both 1.50 and 2.0, the Master Room contains exits and commands which are global to the entire MUSH. This should be used with caution, as having a lot of global commands slow down the MUSH. Make sure something is really necessary before adding it to the Master Room. If it's only useful in some specific area of building, consider using a Zone instead (see the later section of this manual). Also, because it is necessary to check every attribute on every object in the Master Room for commands which don't match anything, the number of attributes on objects in the Master Room should be kept to an absolute minimum. It is also a good idea to keep the number of objects in the Master Room down, since every object represents an additional UseLock which must be checked. As a general rule of thumb, if an attribute doesn't contain a $command, it shouldn't be on a Master Room object. For convenience, some data attributes can be kept on Master Room objects, but this should be avoided whenever possible, even if the data attributes are set No_Command. (Continued in 'man 8.7.5'.) &8.7.5 Note that the parents of objects in the Master Room, and, in 2.2, parents of objects which are contained by a ZONE chain, are not checked for $commands. This allows you to place data attributes on the parent, for easy reference, thus leaving you with little excuse for ever having non-essential attributes on the object itself. &9 9. Wiz-specific commands &9.1 9.1 Talking with other wizards MUSH code provides the commands @wizwall, @wizpose, and @wizemit. (In 2.0, these are @wall/wiz, @wall/wiz/pose, and @wall/wiz/emit). Note that none of these use an = sign. @wizwall and @wizpose are really the same command; "@wizwall :<pose>" is equivalent to "@wizpose <pose>" In the case of @wizwall <message>, it is "Broadcast: <Wizard> says, "<message>"" In the case of @wizwall :<pose> or @wizpose <pose>, the format is, "Broadcast: <Wizard> <pose>". @wizemit <message> shows the other wizards "Broadcast [<Wizard>]: <message>" 1.50 provides @rwall, @rwallpose, and @rwallemit. They work identically to the @wiz variants, except they send messages to both royalty and wizards, prefixed with "Admin:" instead of "Broadcast:". Only connected players may hear these messages. &9.2 9.2 Public messages Wizards can shout using @wall <message>. There is no way of blocking out shouts; therefore, this should be used sparingly. It sends the message to every connected player. Wizards can set the Message of the Day using @motd <message>. The message stays up until another wizard resets it, or the MUSH goes down. The MOTD is shown to players when they connect, and when they use @listmotd. Wizards also have a @wizmotd, which is shown to all connecting wizards. For wizards, @listmotd displays all MOTD messages. 2.0 refers to these commands as @motd, @motd/wizard, and @motd/list. &9.3 9.3 Locking out players In certain situations, there may be a need to disable all non-wizard logins. 1.50 code provides the @login <on or off> command. When @login is off, all player connections will be refused - after the welcome screen, the message set by using @rejectmotd <message> is displayed and the player is disconnected. In 2.0, the command to use is "@enable logins" or "@disable logins". The @enable and @disable commands may be used to tweak other things as well; this is probably the most commonly used of the options. The message displayed to players who cannot connect may be set using @motd/down. &9.4 9.4 Dealing with players There is no easy way to retrieve the lost password of a player. Therefore, it is simpler to simply use @newpassword <player>=<new password>. The player is informed that the wizard has changed his password. The player should then log in and change his password to something else that the wizard doesn't know. Never @newpassword a player without his knowledge, unless it's an emergency. @pcreate is a command which only works if the MUSH is compiled under registration. @preate <player>=<password> creates a player with the name and password. You may not create two players with the same name. * * * * * @boot *<player> can be used to force a player to disconnect from the MUSH. This is the general punishment for being obnoxious, or for idling for too long. If you are @booting someone for idling, you should, in general, page them with a warning five minutes before doing the @boot. (Continued in 'man 9.4.2'.) &9.4.2 @boot has another use -- killing off sessions of players who are unable to disconnect, due to network problems. Generally, in this case it is best to use "@boot/port", which allows you to boot a specific question. In 1.50, the wizard WHO shows descriptor numbers; in 2.0, you must use the SESSION command to get the descriptor number of a connection. "@boot/port <descriptor number>" then closes that specific connection. Players will often request to have "frozen", dead, connections @booted. In 1.50, players will be booted with a message notifying them that they have been booted off the game. In 2.0, players will also be told who booted them off, unless "@boot/quiet" is used, in which case the player will simply be abruptly disconnected without any kind of notification. * * * * * @toad turns a player into an ordinary object, called "A wart toad named <player>" (1.50), or "A slimy toad named <player>" (2.0) and @chowns all his objects to the wizard doing the @toad. The player will be booted off the game, prior to being turned into an object. (Continued in 'man 9.4.3'.) &9.4.3 @toad is actually somewhat redundant. @dest *player will @boot the player, @chown all his objects to the executing wizard, and @destroy the player itself. If possible, use this instead of @toad (TIM code and MUSE, however, may be somewhat different.) Be careful with @destroy #<dbref>. There is no check to see who the object belongs to or what it is - be _very_ certain that you have the right object number, or you may end up irretrievably blowing up an object which represents hours of programming. There is no way to retrieve @destroyed objects other than rooms (which may be set !GOING). 1.50 prevents this somewhat by forcing wizards to use @nuke to destroy objects which do not belong to them; please don't use @nuke as your standard command for destruction, or you'll defeat the purpose of this safeguard! The wizard command "slay" is a "kill" which always works. There's generally no reason to use it, unless programming a puzzle or something similar; all it does is give the player some money and send him home. &9.5 9.5 Dumping the database The command @dump writes the database to disk. In 1.50, this can take anywhere from a minute or two to half an hour - make sure not to do overlapping @dumps. Overlapping dumps can cause crashes, incomplete database writes, and other assorted undesired effects. In 2.0, you don't generally need to worry about doing dumps, as the disk database is saved fairly often and loss from crashes tends to be minimal. The command "@shutdown" shuts down the MUSH. It first does a @dump, gives all connected players the name of the wizard who did the @shutdown, and the message "Going down - Bye", then drops all connections. Don't do this without a very good reason. (Continued in 'man 9.5.2'.) &9.5.2 Note: if you have database damage and are shutting down because the game will crash or doing something else equally nasty in a few moments, DON'T! The straight @shutdown will save the database and your damage will be permanent. In such a dire situation, you have two options: find the God (or other person with direct access to the MUSH account) and have him "kill -9" the process, or do a "@shutdown/abort". This will cause the MUSH to dump core and die, allowing for the damage to be assessed later by someone skliled with a debugger. & 10 10. Database control & 10.1 10.1 Quotas - limited object creation If quotas are turned on, the command @allquota <limit> displays the current quotas and objects owned by all players, and then resets their quotas to the new limit minus the number of objects they currently own. @quota <player> reports the number of objects a player owns, and his quota still available. @squota <player>=<limit> reports the number of objects the player owns and his remaining quota, and then sets his quota to <limit> minus the current number of objects he owns. If the limit is 0, then @squota works identically to @quota. & 10.2 10.2 Money control Wizards have unlimited money, and can freely give out money to players using give *player=<money>. Wizards can also take away money from players by giving them negative money. It is generally not a good idea to give players vast amounts of money. The command @poor <money> resets the money of every player on the MUSH to <money>. This command should be used sparingly, if at all. & 10.3 10.3 Consistency checking The commands @purge and @dbck will probably not be used by most wizards. They check the database for corruption: @purge checks the destroyed object list and makes sure that all the objects on that list are really there; @dbck forces the database to run an internal cleanup and consistency check every ten minutes. @dbck automatically executes @purge. In 1.50, if, for some reason, your database is corrupted but the game is still up (usually through a bug in the internal compression routines), you will find that an attempt to dump the database will cause a segmentation fault and core dump (i.e. a nasty crash). You can generally get a clean database write by using the "@dump/paranoid" command, which very thoroughly scans the database before writing it to disk, checking for non-printable characters and the like. Someone with access to the game's account should then log in, back up the paranoid dump, and then kill off the game process in some manner. (Continued in 'man 10.3.2'.) & 10.3.2 2.0 provides the ability to do some database repair within the game. The first of the db repair commands is "@cut"; this cuts off the object or exit chain at the wizard's location by setting the "next" field of the object to NOTHING. The objects or exits beyond the cut retain their location, but are not accessible except via dbref number. They can be pasted back into their proper places via the @fixdb command. This command allows you to more or less arbitrarily set the fields of an object -- the contents, exits, location, and next pointers, as well as the owner, value, and name. These commands are NOT to be used unless you are CERTAIN you know what you are doing, as it is possible (and indeed rather easy) to produce database inconsistencies/loops which can hang or crash the server. & 11 11. Zones This section only applies to PennMUSH 1.50. Please see a later section of this manual for a description of Zones in TinyMUSH 2.2. & 11.1 11.1 Object zones A Zone Object (or, alternatively, ZMO - Zone Master Object) is simply an object that other objects have their zones set to. Objects that have their zone field set to a ZMO are referred to as being in that ZMO's zone. There's nothing at all special about a zone object, aside from that property. Anyone who passes the enter lock of a ZMO has control over objects in that zone. Someone who has control of an object via passing the enter lock on the ZMO can do many things that the object's real owner can do, including examining the object, changing its attributes, and setting flags on it. Two notable exceptions are the @chown and @destroy commands, which are reserved to the object's real owner. Also, players NEVER control other players. This control property of zones makes it possible for two or more people to work on a project together. A wizard is needed to @chzone a player to a zone; henceforth, everything that player builds is automatically part of that zone. The wizard-only @chzoneall command changes the zone of everything a player owns. Wizards should not change the zone of a player without first obtaining the approval of all other players in that zone. (Continued in 'man 11.1.2'.) & 11.1.2 For example, if I, Amberyl, wished to work on a project together with Annalyn and Tavella, I would @create an object (let's call it MyZone), and then "@elock MyZone=*Amberyl|*Annalyn|*Tavella". This would allow myself, Annalyn, and Tavella to control all objects in the zone. (PLEASE NOTE: wizards should never be in a zone with anyone! It's far too easy for a regular player to abuse an item belonging to that wizard). Players may @chzone their own objects to a ZMO that they own, or are in the enter lock of. It isn't necessary to be personally zoned to a ZMO in order to be able to control objects in it (the only criterion is the enter lock of the ZMO); it is perfectly possible to be zoned to something else (or Nothing), and @chzone the stuff you build to the desired zone. (Continued in 'man 11.1.3'.) & 11.1.3 Zones have several other nifty features. First of all, $-commands on the ZMO are "global" to rooms that are part of that ZMO, and to things which are personally part of that ZMO. Also, objects can perform a "@zemit <zone> = <message>" to emit a message to all rooms in that zone. This command is computationally expensive and costs 100 coins, but it can be very useful if you have, for example, a space station which uses an intercom system for announcements. & 11.2 11.2 Parent room zones Parent rooms are a slightly more complex version of zones. The main difference is that, instead of the ZMO being a thing, the ZMO is a room. All information above about object zones also applies to parent rooms. Parent rooms have several more useful properties. First, a parent room is a bit like the Master Room. It is treated as "global" by objects which are zoned to it. If you are in a room zoned to a parent room, you can use exits in that parent room. You can also use $-commands defined on objects placed in the parent room. This essentially allows you to create small areas on the MUSH with their own local "global" commands and exits, without cluttering up the Master Room. Since local commands take precedence over global ones, you don't have to worry about global commands interfering with your local parent room ones. Unless you want to use parent room exits, you should use object zones. Object zones are faster, and cause less overhead for the server to worry about; this is an important consideration if the MUSH is big. & 11.3 11.3 ZoneMaster players One final type of zone control is possible, using a player with the ZONE flag set. Such players, called ZoneMasters, behave like ordinary players, but any object that they own is controlled by whomever passes the ZoneMaster's enter lock. For convenience, objects which pass the ZoneMaster's enter lock are referred to here as "zone controllers". Zone controllers are able to @chown objects to the ZoneMaster. However, such objects count against the original owner's quota, and the object's creation cost remains charged to the original owner, not to the ZoneMaster. This enables quotas to be set on individual players, while still allowing a "builder character" to own all the objects. The ZoneMaster is more secure than a ZMO, since INHERIT objects may be placed within the zone without any abnormal danger; since all objects within the zone have the same owner, there is no danger of having one's unzoned objects @forced through a security hole. Unlike a standard ZMO, however, the ZoneMaster does not act as a master object; $commands are not inherited off it. (Continued in 'man 11.3.2'.) & 11.3.2 Note that this is different from standard zoning in that the "zoned" objects are actually owned by the ZoneMaster; thus, it is possible to use @chzone to place the object in yet another zone, for the purpose of inherit $commands, and so forth. & 11.4 11.4 Local wizard control It is possible, through use of zones, to have "local wizards". This is accomplished by @chzone'ing all players that you want the local wiz to control, to a ZMO (or parent room; ZMO will be used in this discussion to refer to both). This ZMO should be @elock'ed to just that local wiz. That player will then have control over all objects created by players in that zone. Unlike a real wizard, however, he may not @force players. This may or may not be desirable, depending on your philosophy of administration (assuming you are a god). Local wizards have almost all the powers of normal wizards, without the accountability and expected responsibility of full wizards. Although certain systems, such as MUSE, have used player ranks in a way similar to local wizards, they do not give such complete control to non-wizard (or wizard-equivalent) players. It is the recommendation of this writer that local wizard control only be used if the MUSH is extremely large, and organized into big, but mostly self-contained, groups which require an administrator who must be able to control the objects of his underlings. & 12 12. The Fundamental Laws of Wizarding The section below comes from a "lecture" that I've given new wizards on MUSHes that I'm a "senior" wizard on. I've had several requests for it in printed form, so it appears here, as kind of a summary to this section of the manual. It summarizes rules for wizard conduct, and gives a nutshell guide to wizard commands. The full lecture is available via ftp from the same site where you obtained this manual; it will probably be called "wiz-ethics" or something similar. All administrators are highly encouraged to read the full lecture; this manual only hits the highlights. & 12.1 12.1 Wizard Etiquette There are three things that players REALLY hate, and which generally constitute abuse of wizard powers. The first is @forcing a player to do something. There's really never a reason to do this. You can almost always duplicate the effect of a command that a player issues by just doing it yourself. Forcing a player to say or pose something is obviously unethical. Forcing a player's objects is also generally a bad idea. Always ask permission before @teleporting a player, or teleporting to a non-public location, even if the location is JUMP_OK. The first is just courtesy; the only reason to ever teleport a player is if he refuses to come of his own free will. It has the potential to mess up some command the player is trying to execute; waiting a moment or two for the player to come on his own isn't going to kill you. Following the second rule will prevent you from teleporting into potentially embarrassing situations. Just because someone is paging you doesn't mean that he necessarily wants to talk to you in person. (Continued in 'man 12.1.1'.) & 12.1.1 Don't wander around DARK. If you're going to hang around dark because you don't want people bothering you, stay in one of your rooms. If you encounter another player, announce your presence. The same thing applies to puppets who are DARK. Some people believe that the best way to handle player discipline is to teleport to their location, DARK, and spy on them. This is a breach of ethics, in most cases. Unless you believe that players are deliberately conspiring to destroy the MUSH, do not use DARK for anything other than hanging around without getting bothered by player pages. Two other brief points: don't examine private objects unless you need to, and ESPECIALLY, do not use wizard powers to take code or code segments without permission. In addition, never, ever, wiz while intoxicated! Your judgement isn't steady, and you are also very likely to accidentally mistype a command and cause some sort of minor (or major) disaster. & 12.2 12.2 The Five-Step Method of Player Management If a player is behaving inappropriately, start with warnings and move up to more direct action. The exception are spammers and those who are being destructive towards the database; @boot and/or @destroy those players immediately. For players who are simply being obnoxious, breaking some of the MUSH rules, etc., page them and politely tell them that their behavior is unacceptable. If they don't respond to this "nice" warning within five or ten minutes, page them again with a more strongly worded warning. If they continue to misbehave, try a threat: "Do that one more time and you're going to be @booted." The next thing to try, if the twink is being stubborn, is setting the twink GAG. Most people will disconnect of their own accord if they're gagged. For really obnoxious, stubborn people, you can then resort to @boot. If the twink returns, do a @newpassword. Note that if you are planning on doing a @newpassword to lock out a twink, you should @newpassword _before_ you @boot, since otherwise, the player can log right back in before you have a chance to type the @newpassword command. (Continued in 'man 12.2.1'.) & 12.2.1 The reason for using @newpassword instead of @toad or @destroy is that @newpassword is reversible. You should never use @toad or @destroy on a player who owns stuff; that is a decision for God to make. Just do a @newpassword and send mail to the other wizards. Summary: 1. Nice warning 2. Not nice warning / threat 3. Gag 4. Boot 5. Newpassword & 12.3 12.3 When to use wiz commands @allhalt: This is an emergency-only command. It will halt all commands on all objects, and clears out the wait queue. If there are any objects which need to run continuously, this command should be used as a last resort. Try to be considerate and retrigger objects that need to run continuously, if you do an allhalt. @boot: Never @boot without a good reason. If a player is on twice and he can't kill one of his connections, he will generally page you and ask for a @boot; don't do one unless he gives you permission. The other use for @boot is detailed in the previous section. @chownall: Never, ever, do one of these without the explicit permissions of both of the parties involved. It's not a bad idea to send mail to God (or at least make sure he knows) if you do one of these. @dbck: The @dbck command is used to force a database consistency check. All GOING objects get recycled. Unless you know what you're doing, there's no real point to doing one. @purge: This checks the destroyed object list for corruption. It's normally done as part of a @dbck. (Continued in 'man 12.3.1'.) & 12.3.1 @dump: In 1.x, you don't want to do overlapping dumps. If you don't have FORK enabled, you want to shout before doing one, because it will probably cause quite a bit of lag. Otherwise, @wizwall that you're doing a dump, so the other wizards know not to do one right after you do. @login: You can turn MUSH logins on and off with this command. Unless you've got some kind of emergency which necessitates locking out all players, don't use this. More importantly, don't forget to turn logins back on after the emergency is over. @daytime: This turns on or off computationally expensive commands. God generally makes the decision when these commands should be enabled. Turning them off when they should be turned on probably won't hurt anything, but turning them on when they need to be off has the potential to get God very upset with you. Only 1.50 has this command. (Continued in 'man 12.3.2'.) & 12.3.2 @shutdown: In general, only wizards with access to the MUSH account should do @shutdowns, so they can bring the MUSH back up afterwards. The exception is if the MUSH is not autorestarted, and needs to be taken down for a machine reboot or something similar. If the database has somehow been corrupted, a @shutdown may or may not be a good idea -- this command automatically does a @dump, and you might be making your problem semi-permanent. When in doubt, contact God; to shut down the game without doing a @dump, use @shutdown/abort. The most important thing to remember: USE COMMON SENSE. ------ That's it for wizard commands and advice. Comments, corrections, and suggestions should be emailed to Amberyl, at lwl@godlike.com &V MUSH Manual Version 2.008: Copyright 1993, 1994, 1995, Lydia Leong (lwl@godlike.com / Amberyl) Last revised 5/22/95. Section V: The Art of Psychocoding 13. Basic Concepts 13.1 Attribute naming and coding style 13.2 String concatenation and the switchless style 13.3 What the parser really does 13.4 Zones in TinyMUSH 2.2 14. Tricks of the Trade 14.1 Adding to, removing from, and comparing lists 14.2 Function building blocks: U() and SWITCH(), DEFAULT(), @function 14.3 Formatting strings: SPACE(), REPEAT(), LJUST(), and RJUST() 14.4 Lists instead of @dolists: ITER() and FILTER() 14.5 Recursion: theory and practice, FOLD() 15. Efficiency 15.1 Parameter passing: U(), ULOCAL(), local registers, SETQ(), and R() (Continued in 'man V1'.) &V1 15.2 How the queue works 15.3 Pipelining 15.4 Queue cycles vs. CPU cycles Unlike the rest of this MUSH manual, this section is intended for the programmer who already has some experience coding MUSH. Nonetheless, newcomers to MUSH might find it helpful to scan through this section, especially the "Basic Concepts". Familiarity with MUSH terminology, major commands, and important functions is assumed. It is suggested that the reader be on-line while reading this manual, with access to the help text for the various functions and the ability to test out anything which doesn't seem clear. Some of the functions (Continued in 'man V2'.) &V2 are not explained in gory syntactic detail, since the help text does exactly that; the purpose of this manual is to provide information that is not found in the help, and, thus, concentrates on style, techniques, and quirks that are not immediately obvious. --------------------------------------------------------------------------- & 13 13. Basic Concepts & 13.1 13.1 Attribute naming and coding style A user of MUSH once complained that most MUSH code "looks like line noise". It's quite possible to write totally unreadable MUSH code; even with the introduction of the "@@" comment, almost nobody ever comments their MUSH code, either via "@@" or via attributes which explain what the object is supposed to do and how it does it. Code that is already difficult to fathom is made even more incomprehensible by semi-random naming of attributes and lack of whitespace. * * * * * Attributes should be named intelligently and consistently. One useful naming convention is to call any attribute which contains a $command DO_<name of command>; for example, an attribute with a "scan" command might be called DO_SCAN. Any attributes triggered by that command would be called SCAN1, SCAN2, etc. Some people may prefer to place the $command in SCAN0; this makes it simple to see everything $scan related by doing an "examine object/SCAN*". (Continued in 'man 13.1.1'.) & 13.1.1 User-defined functions used by the $scan command might be named as SCAN_<descriptive name>_FN, such as "SCAN_FLAGS_FN". Temporary registers set by $scan might be called SCAN_<descriptive name>_TEMP. The general description of how $scan works could be called SCAN_COMMENT, and the "help" for it would logically be named SCAN_HELP. A recommended naming scheme for constants is to make their names based on what the attributes contain; a DBREF suffix indicates a dbref number, a NAME suffix indicates a name, and a NUM prefix indicates a counter of some sort. * * * * * Liberal use of whitespace is also encouraged. Whenever possible, leave spaces; a space should be left before the opening brace of an action list associated with a @switch, after the ':' separating a $command from its associated action list, after the commas in arguments to functions, around the '=' sign between command arguments, and every other place spaces can be left without affecting command evaluation. (Continued in 'man 13.1.2'.) & 13.1.2 Practicing a good coding style also helps. Actions associated with a @switch should be enclosed in curly braces, as should each group of parameters passed to a @trigger. Action lists for @dolist, @wait, and other similar commands should also be enclosed in curly braces. Even if there is only a single action, the braces make it very clear what commands or parameters are associated with each other. * * * * * Certain small things speed up evaluation. For example, whenever possible, use '%0', '%N', and other percent substitutions instead of '[v(0)]', '[v(N)]' and similar v-function equivalents. The percent evaluations are faster; the only time when the v-function equivalents are needed is when explicit string concatenation is required. Similarly, the percent-substitution '%b' is much faster than '[space(1)]'. (Continued in 'man 13.1.3'.) & 13.1.3 When possible, use "%#" instead of "%N" or "*%N". This removes one layer of name-matching when hunting for the object you are trying to reference, and also guarantees that you will get the enactor, and not merely something with the same name as it. Also, in 2.0, and, to a lesser extent, in 1.50, objects do not check the name of their container when trying to name-match. Furthermore, for objects holding global commands, a match on a name %N will fail if the enactor is not nearby. The match for the dbref %#, on the other hand, will always work, no matter where the enactor is located. Avoid excessive use of the [] bracket grouping with functions. Unless you are forcing immediate evaluation of an expression for the purposes of string concatenation, only one set of brackets is needed around the entire function. If the function is the only argument to a command, the brackets are not even needed - "@pemit %#=[v(string)]" is the equivalent to "@pemit %#=v(string)". In the interests of readability, however, it is advisable to use the bracket delimiters around groups of functions within a large, complex function call; this is especially true within lengthy SWITCH() or ITER() evaluations. & 13.2 13.2 String concatenation and the switchless style The increasingly popular "switchless style" of programming eschews the actual @switch command in favor of using function evaluations to generate a word. The object is then @forced to execute the contents of the attribute named by that word. A broader definition of the switchless style includes the entire concept of using complex function evaluations to replace nested @switches and similar control structures. This cuts down on the total number of queue cycles needed to execute a complex program. The switchless style relies very heavily on string concatenation. This can be accomplished by using the square brackets to force immediate evaluation of the expression within the brackets. 1.50 also provides the STRCAT() function, which explicitly concatenates strings. * * * * * (Continued in 'man 13.2.1'.) & 13.2.1 For example, suppose we create a "cat" object. We want to define a $command, "pet cat", on it, which causes the cat to give the person one of several random messages. This is quite simple to code in the standard style: &DO_PET_CAT cat = $pet cat: @switch rand(3)= 0, {@emit The cat purrs when %N pets her.}, 1, {@emit The kitty blinks at %N.}, 2, {@emit The cat arches her back and hisses at %N.} The switchless style method would be: &DO_PET_CAT cat=$pet cat: @emit [s(v(PETMSG[rand(3)]))] &PETMSG0 cat=The cat purrs when %N pets her. &PETMSG1 cat=The kitty blinks at %N. &PETMSSG2 cat=The cat arches her back and hisses at %N. The brackets around the 'rand(3)' forces that evaluation to be done immediately; the result is then concatenated with PETMSG. Thus, if rand(3) is 1, we evaluate "[s(v(PETMSG1))]". (Continued in 'man 13.2.2'.) & 13.2.2 This method of picking a message is very useful. If we decide we want ten messages instead of three, we merely use @edit on the DO_PET_CAT attribute, and change 3 to 10. Then, we can add seven more PETMSG attributes. * * * * * The switchless style generally works by selecting among attributes. Attributes used for switchless programming frequently have names ending in a number, since it's generally easier to use MUSH to generate a number than a name. Frequently the number will be 0 or 1, since boolean functions like AND(), OR(), and NOT() evaluate to 0 or 1. A broader definition of "switchless" programming is discussed later in this manual, in the section concerning SWITCH(). Note that it is difficult to program "truly" switchless objects; generally, it is neither convenient nor desirable to do so. Instead, the aim of switchless programming is to reduce the number of queue cycles needed through indirect selection on a string. (Continued in 'man 13.2.3'.) & 13.2.3 Note that because the switchless style makes heavy use of functions, it may use a large amount of CPU time without using many queue cycles. The queue cycles-CPU time tradeoff will be discussed later in this manual. & 13.3 13.3 What the parser really does Many MUSH programmers are curious about exactly what the parser does. A large number of programmers who are not familiar with MUSH internals like to say, "The parser is consistent." Throw away that notion. It's wrong. The parser is far from consistent. Various commands have their own little quirky ways of being parsed. Even certain functions handle their arguments differently. To the user, however, it should _appear_ that all things are parsed the same way. Most MUSH commands take one of three forms: 1. <command> <argument> 2. <command> <argument 1> = <argument 2> 3. <command> <argument> = <argv 1>, <argv 2>, <argv 3>, ... (Continued in 'man 13.3.1'.) & 13.3.1 In most cases, the MUSH figures out what command you want, and then figures out how to parse the rest of the string you typed. Most of the time, arguments are run directly through the parser and the results handed to the command handler. There are several major exceptions to this. The most notable of these are @switch and @dolist. In @switch, he first argument (the variable to switch on) is evaluated; the comma- separated arguments are not. @switch evaluates these arguments as needed. For the @dolist command, the first argument (the list to use) is evaluated immediately. Then, a find-and-replace is done on the second argument, sequentially replacing the "##" token with elements of the list. The final result of such substitutions is then passed to the evaluator. * * * * * The MUSH parser evaluates expressions recursively. The main server routine to do this is called 'exec'; from this point on, the expression "the string is exec'ed" will be used to refer to the evaluation of the string by the parser. (Continued in 'man 13.3.2'.) & 13.3.2 Most uses of square brackets force another call to exec. Also, every argument to a function is passed through exec, as are the arguments of most commands. Let's take a look at our cat example above. If we type "pet cat", the object attempts to execute "@emit [s(v(PETMSG[rand(3)]))]". The server goes through the following: 1. It looks up "@emit" in the command table. The server discovers that this command takes one argument, which is evaluated. It thus passes "[s(v(PETMSG[rand(3)]))]" to exec. 2. exec attempts to evaluate that string. It sees the brackets, and invokes exec a second time, with the brackets stripped, so we are now evaluating "s(v(PETMSG[rand(3)]))" 3. s() is a function, so exec is called yet another time to evaluate the argument to s(). We now need to evaluate "v(PETMSG[rand(3)])" 4. v() is also a function, so we call exec on its argument. We are thus left with "PETMSG[rand(3)]" (Continued in 'man 13.3.3'.) & 13.3.3 5. exec scans that string until it sees the pair of brackets. It then invokes yet another exec call to evaluate the contents of the brackets. We now evaluate "rand(3)". 6. Because rand() is a function, we once more invoke exec, on "3". 7. "3" is just a string literal, so exec returns "3". 8. Having figured out the arguments to rand(), we evaluate it. Let's say "rand(3)" is equal to "0". 9. This expression is then concatenated with "PETMSG", giving us the string "PETMSG0". This is our argument to v(). Now that we have our argument, we evaluate "v(PETMSG0)", giving us, "The cat purrs when %N pets her." 10. We now pass this to s(). "s(The cat purrs when %N pets her.)" evaluates to "The cat purrs when Amberyl pets her." (assuming Amberyl is the enactor). Note that because we used '%N' rather than '[v(N)]', no additional execs are required to get the name of the enactor. (If we used '[v(N)]', two additional exec calls would be required: one to strip the brackets, and one to evaluate the argument to V(). Thus, you can see that it's quite a bit more efficient to use '%N'.) (Continued in 'man 13.3.4'.) & 13.3.4 11. This string is passed to the command handler for @emit, which shows it to the appropriate people. Notice how many evaluations such a simple string can need! When coding, it is important to think not just about how many queue cycles something uses; most functions must evaluate all their arguments, and the computation time taken to do this eventually adds up. Although this is usually on the order of milliseconds, the actual cumulative delay, on a MUSH running on a busy machine, can add up to a significant amount of time. * * * * * One important effect of functions evaluating their arguments first before executing the function is that attempts to turn space-separated lists into comma-separated lists for functions frequently fail. For example, MAX(v(LIST), 25), where LIST is "1, 10, 83, 4" causes the game to find the MAX of "1, 10, 83, 4" (which the string-to-integer conversion functions trims simply to "1") and "25", thus causing the function to return "25", rather than "83", which is what one might have expected it to return. (Continued in 'man 13.3.5'.) & 13.3.5 Similarly, using a LIST of "1 10 83 4" and then evaluating an expression such as "MAX([iter(v(LIST),{##,})] 25)" doesn't work. The ITER() generates the list "1, 10, 83, 4," but this is NOT equivalent to "MAX(1, 10, 83, 4, 25)"; the erroneous use of ITER() instead causes MAX() to look for the maximum of "1, 10, 83, 4," (which turns into "1") and "25". There are several ways of getting around this; see the discussions on ITER() and FOLD() for examples. * * * * * Two important functions do not immediately evaluate their arguments. ITER() and SWITCH() only evaluate their arguments when the arguments are needed. ITER() does a brute force find-and-replace for "##" on its second argument, much like @dolist does; to avoid needing odd combinations of escapes in the second argument, none of the arguments are evaluated until the ITER() function actually uses them. Because SWITCH() calls can be massive, only those arguments which must be evaluated will be evaluated. This is important to keep in mind if you are using functions with side-effects, such as 1.50's create(). (Continued in 'man 13.3.6'.) & 13.3.6 Note that if you are making multiple identical calls to functions, all the calls are evaluated _separately_; the MUSH does _not_ know that it has evaluated that expression before. This is definitely something to remember, if you use functions like U() to evaluate huge complex expressions. By the same logic, "@pemit me=add(rand(3),rand(3))" does NOT return the same value for those two rand() calls. Ways of avoiding multiple identical function calls will be discussed later in this manual. Finally, note that the UNIX random-number generator is very poor, and for small values of N, often returns the same value several times in a row, even if the overall distribution over, for example, 1000 trials, is evenly apportioned. If, for some reason, it is important that your numbers be "more random", you may want to try a method such as using a very large value of N, and then using a MOD() call to bring it into the appropriate range. For example, to generate a number from 0 to 9, one can use "rand(10)"; however, one will get a better random distribution via "mod(rand(1000),10)". & 13.4 13.4 Zones in TinyMUSH 2.2 Zones in TinyMUSH 2.2 are somewhat different from PennMUSH 1.50 Zones, and therefore deserve a separate explanation. This section only applies if Zones are permitted by the MUSH's configuration. If you are standing in a room, type a command which is not matched by an exit, or locally, or internally, and the parent of your current room is set ZONE, all objects inside that parent will be checked for $commands, just as if that parent object were the Global Master Room. If there are no command matches, then if the parent of that parent is also ZONE, objects in it will be checked. This goes on until something is matched, or the parent is no longer set ZONE. If there are still no matches, the same process is repeated, starting from your own parent object, if it's ZONE. If still nothing is matched, command checking proceeds to the Global Master room. (Continued in 'man 13.4.1'.) & 13.4.1 * * * * * Essentially, what this means is that the ZONE chain creates a chain of Local Master Rooms. They provides a great deal of power and flexibility that normal parent rooms don't provide; they confer all the benefits of normal parenting, in addition to this specialized form of command-checking. Commands matched via zone checks are executed by the objects which contain the commands, NOT by the room or player itself. Therefore, this method is extremely useful for permitting players other than the Builder Character (or generic area owner) to execute commands for a given area. It also alleviates the necessity of setting all rooms in the area INHERIT, if a certain local command requires INHERIT. (Continued in 'man 13.4.2'.) & 13.4.2 Because the ZONE chain is essentially a chain of Master Rooms, the same caveats which apply to Master Room programming apply to objects in these chains. Objects in the rooms should have a minimal number of attributes on them. Because the parents of objects in the rooms are not checked for $commands, @parent'ing such objects to a data object will reduce the checks needed without significantly altering the programming style for such an object. Security, of course, is important -- ZONE chains should be isolated, and unauthorized players should be prevented from gaining access to them. --------------------------------------------------------------------------- & 14 14. Tricks of the Trade & 14.1 14.1 Adding to, removing from, and comparing lists Lists are the heart and soul of MUSH. Earlier in this manual, ways of finding certain items within a list, adding to lists, and removing from lists were discussed. The standard list-operation functions fare poorly when there are items in the list which are identical. In particular, we often want to avoid adding items to a list which are already in it, get the result of merging two lists, and remove items from a list without destroying the spacing of the list. The REMOVE() function generally leaves an extra space in the list. We can get around this problem by passing the results of a REMOVE() call to S(); indeed, this method of forcing another round of pronoun substitution is useful for compressing undesired spaces. The SQUISH() function removes extra spaces, and is useful when a second round of evaluation is not desirable. Thus, REMOVE() is adequate for most of the times we wish to remove an item from a list. (Continued in 'man 14.1.1'.) & 14.1.1 * * * * * The SETDIFF() function provides a better way to remove items from a list. SETDIFF(<list 1>,<list 2>) removes from <list 1> all items that are in <list 2>. It can also be thought of as returning all the elements of <list 1> that aren't in <list 2>. This is useful when you want to guarantee that you have removed every occurrence of an item; objects like communicator systems are best programmed like this. The SETUNION() function is useful for adding an element (or elements) to a list. It merges two lists, removing duplicates. Like SETDIFF(), this function is useful when you want to guarantee that you have no duplicates; in a communicator system, this is especially important. The SETINTER() function returns the elements that are in both lists. This is also for use in merging lists; elements that are only in one list get eliminated. This is also useful for telling if all elements of one list are in another; evaluate the SETINTER() of the two lists, and COMP() it to the list you are interested in. (Continued in 'man 14.1.2'.) & 14.1.2 * * * * * One interesting example problem in list manipulation is the removal of a specific element by its position. This is a problem encountered when it is not possible to use REMOVE(), which removes the first instance of an elements by name, or SETDIFF(), which removes all occurrences of the elements, also by name. For example, if we keep two attributes on an object, one with the names of dragons, and the other with the colors of those dragons, and we want to remove one of the dragons from both lists, we encounter this problem; while names in the first list should be unique, many of the words in the second list will not be (we will probably have many Blue dragons, for example.) If our list of dragon names is in the attribute NAMES and the corresponding colors list is in the attribute COLORS, we can delete, given a dragon name, its corresponding color from the colors list by using EXTRACT() to return all words before that position, and all words after that position. We determine the position using the MEMBER function, as per the standard list-matching routine described earlier in this manual. The code is thus: (Continued in 'man 14.1.3'.) & 14.1.3 &COLORS object=[extract(v(colors), 1, sub(member(v(names),%0),1))] [extract(v(colors), add(member(v(names),%0),1), words(v(colors)))] Once we've deleted the color from the colors list, we can safely use REMOVE() or SETDIFF() to remove the dragon name from the names list. * * * * * The above problem can be more easily solved by the judicious application of a built-in function callled LDELETE(), which deletes an element from from a list, given the element position to delete. LDELETE() has two brethren functions, called INSERT() and REPLACE(), which take the arguments <list>, <pos>, <word>. The first function inserts <word> into <pos> of <list>, and the second function replaces <pos> element of <list> with <word>. All three of these functions should be used instead of clumsy EXTRACT() manipulations of the type described above. & 14.2 14.2 Function building blocks Two functions form the core of switchless programming: U() and SWITCH(). The first (also known as UFUN() in 1.50) allows the MUSH programmer to define, in a limited sense, his own functions, while the latter pattern-matches a string against a list of other strings, and, instead of triggering an action, as does @switch command, the SWITCH() function simply returns a string. U() takes up to ten arguments. The first argument is an attribute, which specifies where to look for the function definition. This argument can be a name, or it can be an object/attribute pair. The remaining arguments to U() are parameters to be passed on the stack (i.e. as %0 - %9). Thus, stack parameters to a U() evaluation are purely local - they are not at all related to the value of the "global" stack. A evaluation like "u(FOO_FN, bunch, of, words)" would pass "bunch" as %0, "of" as %1, and "words" as %2. Then, the contents of FOO_FN would be evaluated with those values. For example: (Continued in 'man 14.2.1'.) & 14.2.1 > &TEST me=%0 has [strlen(%0)] characters and [words(%0)] words. %1! > "[u(TEST, Test string, Neat)] You say, "Test string has 11 characters and 2 words. Neat!" In this example, %0 is "Test string", and %1 is "Neat". Note that those values are only true within TEST, though. For example, values passed by @trigger are unchanged: > &TEST me=%0 has [words(%0)] words. > &ACT me=say %0 %1 %2 - [u(TEST,%1)] - %0 > @trigger me/act={a 1}, {b 2 3}, {c 4 5 6} You say, "a 1 b 2 3 c 4 5 6 - b 2 3 has 3 words - a 1 >From the @trigger, %0 is "a 1", %1 is "b 2 3", and %2 is "c 4 5 6". In the TEST evaluation, %0 is "b 2 3", since that was the parameter passed to it. But when we return to evaluating the output of the @trigger, %0 is still "a 1"; the value of %0 in the U() evaluation of TEST does not change the real value of %0. (Continued in 'man 14.2.2'.) & 14.2.2 * * * * * 1.50 and 2.0 handle U() evaluation differently; 1.50's GET_EVAL() is basically identical to its U(). For the differences between 2.0's GET_EVAL() and U(), see the earlier section of the manual on GET(). The only other difference between 1.50 and 2.0's U() is that 1.50 does not force immediate evaluation of a U() unless it is surrounded by square brackets. In other words, in 1.50, "&FOO_FN object=strlen(%0)" and "&FOO_FN object=[strlen(%0)]" are handled differently - without the brackets, the game does a local evaluation and substitution, and pastes that in, instead of forcing an immediate evaluation. For example: > &TEST1 me=strlen(%0) > &TEST2 me=[strlen(%0)] > "Test1: -[u(TEST1,foo)]- Test2: -[u(TEST2,foo)] You say, "Test1 -strlen(foo)- Test2: -3-" (Continued in 'man 14.2.3'.) & 14.2.3 The programmer is allowed slightly more flexibility when immediate evaluation is not forced. In general, it is good programming practice to put square brackets around the functions contained in an attribute called by U(). * * * * * The U() function is usually used to clean up code which would otherwise be horrendously complicated and unclear. Also, because the parameters passed to U() are only evaluated once, if you need to evaluate an expression which utilizes a complex expression several times, you can simply make that complex expression a parameter to U(). This method of using U() is detailed later in the manual, in the section dealing with efficiency. (Continued in 'man 14.2.4'.) & 14.2.4 A general rule of thumb of putting an expression in a U() is, "If you use it more than once, and it contains more than two or three nested functions, or it is more than 70 characters (one line) long, make it a U()." The 70-character rule has its exceptions, but anything you can't type without using emacs or some other kind of parentheses/brackets-matcher is Too Long and should go into a U() attribute by itself. * * * * * U() is often used for permission checks on objects. For example, a bulletin board might only permit the original poster of a message to delete it. In the future, however, you might wish to allow wizards to also delete messages. Rather than having to scan through all the bulletin board code, it'd be simpler just to change a single U() function. Thus, when writing the board code, it'd be good to call something like OK_TO_DELETE_FN, even if the check for permission to delete is simple. As long as you keep the parameter list the same, you shouldn't have any trouble swapping in a new OK_TO_DELETE_FN should you ever change your mind about who is allowed to delete messages. (Continued in 'man 14.2.5'.) & 14.2.5 * * * * * "Switchless programming" is a bit of a misnomer; this style frequently involves the SWITCH() function, although it generally avoids the @switch command. The SWITCH() function is quite similar in format to @switch, but instead of performing a command list based on matching a string pattern, it returns another string. It is as flexible of a pattern-matcher as @switch, taking the '*' and '?' wildcard characters. The obvious use for this function is turning one string into another string; if there is more than one match possible, SWITCH() returns the first one. SWITCH() does not evaluate its arguments until it needs to; therefore, if you have side-effect functions within a SWITCH(), such as 1.50's CREATE(), remember that they will not get evaluated unless the corresponding pattern is matched. (Continued in 'man 14.2.6'.) & 14.2.6 When combined with U(), SWITCH() is an extremely powerful tool. One common application of this combination is to return boolean values (0 or 1) based on some string. The most frequently used example of this is HASATTRIB - the determination of whether or not an attribute exists on a certain object. If the attribute exists, it evaluates to 1; if not, it evaluates to 0. The code for this is simple: &HASATTRIB_FN object=[switch(get(%0/%1),,0,1)] It is then called via [u(HASATTRIB,object,attribute)]. If the GET() returns nothing, then there's no such attribute, and the evaluation is 0. Otherwise, it's 1. Boolean returns can be extraordinarily useful in conjunction with SETQ() and R(). For example, if you want to print out a '+' for every time a function returns 1, and a '-' when the function returns 0, you can do a '[setq(0,-)][setq(1,+)]' and then print out '[r(u(COMPLEX_FUNCTION))]' instead of continually SWITCH()ing for the string to print. (Continued in 'man 14.2.7'.) & 14.2.7 * * * * * Note that one common use of SWITCH() -- returning a value if a certain attribute does not exist -- is made unnecessary by the addition of the functions DEFAULT(), EDEFAULT(), and UDEFAULT(), in TinyMUSH 2.2. These functions take the basic syntax: function([<object>/]<attribute>, <default>[,<parameters for U()>]) Instead of writing the following: [switch(v(TEST),,No test string.,v(TEST))] [switch(get(#10/TEST),,No test string.,get_eval(#10/TEST))] [switch(v(TEST),,No test string.,u(TEST,%#))] one could write, respectively: [default(TEST,No test string.)] [edefault(#10/TEST,No test string.)] [udefault(TEST,No test string.,%#)] (Continued in 'man 14.2.8'.) & 14.2.8 This elimination of the extra attribute retrieval is valuable, and the lack of a SWITCH() removes the need to do any sort of wildcard pattern match. ---------- SWITCH() can be used to entirely eliminate a @switch. For example, here's a typical lengthy @switch construction: @switch v(num)=0,@emit [v(apple)],1,@emit [v(apple)],2,@emit [v(apple)], 3,@emit [v(apple)],4,@emit [v(apple)],5,@emit [v(pear)],6, @emit [v(orange)],7,@emit [v(orange)],@emit [v(cherry)] This can be reduced to: @emit [v([switch([and(gte(v(num),0),lte(v(num),4))],1,apple, [switch(v(num),5,pear,6,orange,7,orange,cherry)])])] (Continued in 'man 14.2.9'.) & 14.2.9 This has the advantage of reducing the extra queue cycle involved in the @switch. It isn't quite as easy to read as the @switch statement, initially, but with practice, switchless-style coding becomes just as simple to follow. (Note that extra brackets have been added in the example above in order to make it easier to read.) The repeated calls of 'v(num)' are inefficient. A better method follows below; this time, the extra brackets have been left out. @emit [setq(0,v(num))][v(switch(and(gte(%q0,0),lte(%q0,4)),1,apple, switch(%q0,5,pear,6,orange,7,orange,cherry)))] This can be even further compressed by a technique which will be described in another section; it combines several switch patterns into one. @emit [setq(0,v(num))][v(switch([and(gte(%q0,0),lte(%q0,4))]:%q0,1:*,apple, *:5,pear,*:6,orange,*:7,orange,cherry))] * * * * * (Continued in 'man 14.2.10'.) & 14.2.10 There are also variations on this theme, which don't necessarily eliminate the @switch. For example, if instead of the simple @emit example above, there were different action lists associated with each value of the NUM attribute, it would be impossible to eliminate the @switch efficiently. It would be possible to generate, as a string, the action list to be run, and then @force the object to do it, but that wouldn't be any timed gained. Here is a more complex example of a @switch for which this is true: @switch v(num)=0, {@pemit %#=Success.}, 1, {@pemit %#=Success.}, 2, {@pemit %#=Success.}, 3, {@emit Disaster!}, 4, {@emit Disaster!}, 5, {@tel %#=#100; &victim me=[v(victim)] %#}, {@pemit %#=Failure.} The best approach to something like this is to generate a "code string" via the SWITCH() function, and then @switch on that code. Usually, this looks best if done in combination with a U(), but for this example, we'll simply write it out; just realize that the left hand side of the '=' sign would probably be best put in a U(). (Continued in 'man 14.2.11'.) & 14.2.11 @switch switch([and(gte(v(num),0),lte(v(num),2))],1,OKAY, [switch(v(num),3,BAD,4,BAD,5,OTHER)]) = OKAY, {@pemit %#=Success.}, BAD, {@emit Disaster!}, OTHER, {@tel %#=#100; &victim me=[v(victim)] %#}, {@pemit %#=Failure.} In this particular case, the switchless code is not an improvement over the original @switch. However, if we ever want to change what constitues "okay", "bad", "other", or failure, all we have to change is the expression on the left hand side of the '='; we don't have to rewrite the entire command. If it's put in an attribute as a U() instead, this becomes even easier; we simply need to change that attribute. * * * * * (Continued in 'man 14.2.12'.) & 14.2.12 Here is a variant of the "code string" procedure which does not use SWITCH(). Instead, it generates several words based on the values we are interested in switching on, and takes advantage of wildcards. In this case, the first number generated tests for 0 <= num <= 2, the second number for num = 3 or num = 4, and for convenience, the value of num itself as the third word. @switch [and(gte(v(num),0),lte(v(num),2))] [or(eq(v(num),3),eq(v(num),4))] [v(num)]= 1 * *, {@pemit %#=Success.}, 0 1 *, {@emit Disaster!}, 0 0 5, {@tel %#=#100; &victim me=[v(victim)] %#}, {@pemit %#=Failure.} When using this kind of switch, it is usually safer to use "@switch/first" (also called "@select") to ensure that we only match the first case that applies. (Continued in 'man 14.2.13'.) & 14.2.13 The multiple-code-words method is most efficient when used to eliminate multiple @switch statements. For example, consider the case of checking valid input for a command which takes the format "test <four letter word> <player name>". We want to display an appropriate error message. The simplest way to code this up is: $test * *: @switch [eq(strlen(%0),4)]=0, {@pemit %#=Invalid word.}, {@switch [num(*%1)]=#-1, {@pemit %#=Invalid player.}, {@trigger *%1/TEST_ATTRIB}} This costs us extra queue cycles, though, and also doesn't catch the case of both arguments being incorrect. Using code words, we can fix that: $test * *: @switch/first [eq(strlen(%0),4)] [num(*%1)]= 0 #-1, {@pemit %#=Invalid word and player.}, 0 *, {@pemit %#=Invalid word.}, * #-1, {@pemit %#=Invalid player.}, {@trigger *%1/TEST_ATTRIB} (Continued in 'man 14.2.14'.) & 14.2.14 Note that because we use patterns which are not mutually exclusive ("0 #-1" also matches "0 *" and "* #-1"), we must use @switch/first. * * * * * Other interesting tricks can be done with @trigger and SWITCH(). For example, in cases where we want to pass a number of complex function evaluations to a later evaluation, we might want to use a @trigger instead; if we eliminate the @switch in the process, this turns out to be the same number of queue cycles, and syntatically neater. Take the following example, based on the notes above. This time, however, instead of sending the message to %#, we want to send the message to something defined by the complex black-box user-defined function called with u(BIG_FN,%#,revwords(%0)) -- too much to type repeatedly. Thus, we end up with something like: $test *: @trigger me/[u(FOOBLE_FN,%0)]_TRIG=u(BIG_FN,%#,revwords(%0)) (Continued in 'man 14.2.15'.) & 14.2.15 We use FOOBLE_FN (whatever that happens to be) to generate the name of the attribute to trigger, and now we end up writing that parameter out only once. This technique is particularly useful in extremely large switch statements which have many cases and long action lists. ---------- The major reason to write code using U() and SWITCH() isn't speed, for large projects. It's modularity. If you define some kind of U() to check permissions to run a command, for example, if you ever want to change the criteria, all you need to do is to change the definition of that U(). SWITCH() is also particularly good at reducing complex expressions that might otherwise require several @switches by means of the "code strings" method; it's easy to jam multiple @switch clauses into a single @switch by providing a complicated SWITCH() to generate a word that can be @switch-cased on. One should, however, be wary of trying to reduce all code down to the minimum number of queue cycles; some amount of readability is also important, as is the amount of "real CPU time" needed to execute a given MUSH program. This trade-off is discussed in detail later in this manual. (Continued in 'man 14.2.16'.) & 14.2.16 There are certain functions whose functionality is frequently needed, but simple enough to code in MUSH that they are not worth hardcoding into the server. To provide some kind of standardization for these functions, and to get around the occasionally clumsy U() calling convention, MUSH provides a mechanism called "@function". It enables the global definition of a U() as an imitation "real" function. A Wizard (or, in 1.50, someone with the Functions power) can specify a name for the function and the place where it can be found, and then anybody on the MUSH can use it as if it were a built-in function. The syntax is: @function <function name>=<object>,<attribute> <function name> is the name used for the function, and <object> and <attribute> specify the name of the attribute and the object on which it can be found. The parameters passed to an invocation of a function defined in this way are passed as %0 - %9. Therefore, any function normally called via U() can be defined globally simply by adding it to the "local global" function table via @function. For example, in 1.50: > &HASATTRIB_FN #10=[switch(get(%0/%1),,0,1)] > @function hasattrib=#10,hasattrib_fn (Continued in 'man 14.2.17'.) & 14.2.17 tells the game to add HASATTRIB to the functions table, and to use the attribute HASATTRIB_FN on object #10 when evaluating that. Then one could simply do: > @desc me=The writer of the MUSH manual. > say [hasattrib(me,desc)] You say, "1" This would be equivalent to "say [u(#10/HASATTRIB_FN,me,desc)]", but is syntactically much neater, as well as faster. Also, because of the @function, any player on the MUSH can use HASATTRIB(), even if he can't directly read the attribute. Thus, players do not have to see the code in order to use it. If the function were a simple U(), another player would have to be able to read the attribute - it would either have to be public, set public via the VISUAL attribute flag, or visible because the object it was on (#10) was set VISUAL. (Continued in 'man 14.2.18'.) & 14.2.18 Note that the syntax in 2.2 is "@function <function>=<object>/<attribute>". 2.2 also takes the switch "/privileged"; if this is given, the function is evaluated as if it was performed by the object on which it was stored (giving players access to information which, for example, might only be accessible to Wizards under normal circumstances). Otherwise, the function is evaluated as if it were stored on the invoker. (Also note that the above example is somewhat irrelevant in 2.2 -- the HASATTR() function performs that functionality.) & 14.3 14.3 Formatting Strings A lot of "MUSHtoys" involve the "pretty-printing" of output; frequently, this means listing output in neat columns. To do this, one must calculate the number of spaces needed to get to the place where the next "real" string should start. This can be accomplished in one of two ways. The most efficient way to do this is to use the RJUST() and LJUST() functions, which right- and left-justify a string, respectively. The first argument to these functions is the string to print, and the second argument specifies the field width. Strings that are too long do not get truncated. Both of these functions take an optional third argument, which specifies the fill character to use; if no third argument is given, a space is used. Thus, if you want to %0 to start at column 1, %1 to start at column 20, and %2 to start at column 45, the expression "[ljust(%0,19)][ljust(%1,24)]%2" will work. (Continued in 'man 14.3.1'.) & 14.3.2 * * * * * In old versions of 2.0, a slightly clumsier method must be used. The SPACE() function is used to print spaces; you must calculate the number of spaces to print based on the length of the string you are printing. For the case above, the equivalent expression, using the SPACE() function, is "%0[space(sub(19,strlen(%0)))]%1[space(sub(24,strlen(%1)))]%2" The major problem with printing something in this way concerns recalculation of the same string. If, for example, you had, instead of %0, %1, and %2, three large, complex functions, you would have to evaluate those functions twice, once to actually print it, and once to calculate the length of the string. For extremely complicated functions, the doubling of this work may cause a significant loss of speed when the object is used. This can be avoided by defining RJUST and LJUST as U() functions: &LJUST_FN object=%0[space(sub(%1,strlen(%0)))] &RJUST_FN object=[space(sub(%1,strlen(%0)))]%0 The extra overhead of invoking another U() function generally is less than that of computing a large function evaluation. (Continued in 'man 14.3.3'.) & 14.3.3 Related to the SPACE() function is the REPEAT() function, which repeats an arbitrary string a given number of times. The strings are concatenated with each other, without spaces separating each repetition. It can be used to fake the three-argument version of LJUST() and RJUST(): &LJUST3_FN object=%0[repeat(%2,sub(%1,strlen(%0)))] &RJUST3_FN object=[repeat(%2,sub(%1,strlen(%0)))]%0 Generally, though, this function is used for generating long lines of asterisks, dashes, and other symbols used for ASCII graphics or division of output into fields. * * * * * One common formatting task is the formatting of poses, says, and the like, for arbitrary commands. Suppose, for example, that you have a chat system, which takes a command of the format, "$chat *". You want the following: (Continued in 'man 14.3.4'.) & 14.3.4 chat Hi! ==> Fire says "Hi!" chat :waves. ==> Fire waves. chat ;'s idling. ==> Fire's idling. Assuming that the * is going to end up as %0, and the chatting person's going to be the enactor, the following works: [switch(%0,:*,%N [delete(%0,0,1)],;*,%N[delete(%0,0,1)],%N says "%0")] Note that we use DELETE() here to remove the first character of the string, if necessary, instead of using MID() to get everything after the first character; deleting one character is a lot faster. & 14.4 14.4 Lists instead of @dolists The most powerful list-creation facility available in MUSH is the ITER() function. ITER() takes two arguments, a space-separated list of words, and a format string of some sort (which can contain other functions). The format string is evaluated for each element of the list, with the "##" token being replaced by the list element. The result is also a list, with each element separated by a space. The simplest example of an ITER() is something of the form "[iter(%0,##)]", which just returns back %0. The most common use of ITER(), however, is turning a list of dbrefs into a list of names: "[iter(lcon(here),name(##))]" returns a list of names of the objects in a room. (Continued in 'man 14.4.1'.) & 14.4.1 The mundane uses of ITER() are fairly obvious; it's used to transform one list into another list. A more sophisticated use of ITER() is using the function to replace a @dolist-@pemit combination with a single @pemit and ITER(). This is useful for bulletin board objects, mailer objects, WHO list formatters, and other mass-output devices. Because ITER() places a space between each element of the list returned, to correctly format such a list so that the elements are returned one per line, a "%r" should be placed at the beginning of the format string. For example, to return the list of contents in a room, by name, one to a line, use "[iter(lcon(here),%r[name(##)])]" The "%r" must come first, not last; otherwise, the output would be indented by one space. Using @dolist: @pemit %#=You see:; @dolist lcon(here)={@pemit %#=name(##)} Using ITER(): @pemit %#=You see:[iter(lcon(here),%r[name(##)])] * * * * * (Continued in 'man 14.4.2'.) & 14.4.2 One is frequently interested in obtaining only those members of a list for which a certain expression is true. For example, the construction "[iter(v(list),switch(u(FILTER_FN,##),1,##,))]" is quite common. It means "return all those elements of the list contained in the attribute LIST, for which FILTER_FN evaluates to 1." Because this construction is very frequently used, 1.50 provides a more efficient short form, via the FILTER() function. The first argument to FILTER() is an attribute or object/attribute pair (just like U()'s first argument), and the second argument is a list. FILTER() returns all elements of the list for which the first argument evalutes to 1. Thus, the equivalent to the expression above would be simply "[filter(FILTER_FN,v(list))]". * * * * * (Continued in 'man 14.4.3'.) & 14.4.3 ITER() can also be used to solve the MAX() problem from earlier in this manual -- taking a space-separated list and passing it to a function as a comma-separated list. The MUSH parser, when it sees a function evaluation, attempts to evaluate each argument to the function, using the comma to separate each argument. The problem encountered with turning the space-separated list to the comma-separated list was that the comma-separated list was generated _after_ the parser had already determined where the argument began and ended. Thus, we must delay the evaluation. We do this by causing the escaping the function, without escaping its arguments, so that the arguments are evaluated (using ITER() to turn the space-separated list into a comma-separated list), and then running the entire thing through the S() function, which causes a second parser pass. Presuming that the space-separated numeric list is in the LIST attribute on the object -- LIST was "1 10 83 4" in the earlier example -- and we want to also compare it to the number 25, we end up with the following: s(\[MAX([iter(v(LIST),{##,})] 25)\]) (Continued in 'man 14.4.4'.) & 14.4.4 The parser reacts in the following manner: it sees the S() function, and goes to evaluate the argument inside. The argument inside evalutes to [MAX(1, 10, 83, 4, 25)] -- the ITER() generates the string "1, 10, 83, 4, " and concatenated with the 25, generates the above string. Because of the '\' escapes, MAX is considered a string and not a function; the '[]'s around the ITER() force that evaluation to complete. Now, the S() function evaluates [MAX(1, 10, 83, 4, 25)], which is 83, our desired result. This technique works, in general, for converting any space-separated list to a comma-separated list to be passed to a function which requires comma-separated arguments. & 14.5 14.5 Recursion "Recursion" is a difficult term to define; it can be loosely described as a process by which an expression uses itself to determine its value. A "recursive function" calls itself, stopping when it reaches a "base case". This can probably be best illustrative via an example. The mathematical expression "n!" ("n factorial") means the product of all whole numbers between 1 and n, or, since formal sigma (summation) notation is difficult to write in pure ASCII, informally expressed by the formula: n! = (n)(n - 1)(n - 2)(n - 3)...(1) For example, 4! = (4)(3)(2)(1) = 24. One quickly notes, however, that this is equal to (4)(3!) = (4)(3)(2!) = (4)(3)(2)(1) Therefore, we can write: n! = (n)((n - 1)!) Because the factorial expression is used to determine a factorial, we can say that the factorial function is recursive. (Continued in 'man 14.5.1'.) & 14.5.1 All recursive expressions must have some kind of base case; otherwise, the function will continue to evaluate itself forever. For the factorial function, the base case occurs when n = 1; the function simply returns 1. * * * * * Recursion can be done quite simply in MUSH, although the built-in function evaluation limit prevents the "stack" of functions from growing too large. We can write a U() function to evaluate factorials, using a SWITCH() to check for the base case: &FACTORIAL_FN object=[switch(%0,1,1,mul(%0,u(FACTORIAL_FN,sub(%0,1))))] The expression "[u(object/FACTORIAL_FN,4)]" will return "24". Note that this is a literal translation of the mathematics involved. "If 1, return 1. Else, multiply our current number by the factorial of our current number minus 1." One of the beauties of recursion is that it usually follows quite naturally from the verbal description of the algorithm. (Continued in 'man 14.5.2'.) & 14.5.2 * * * * * There is a built-in function called FOLD() which is intended for use in recursion. The first argument to FOLD() is the name of an attribute which is to be treated as a U(), and the second argument is a list whose members will be passed one at a time as %1 to the U() evaluation. (Note that the U() function is not directly involved in the FOLD() operation, but the attribute is evaluated like a U(), so for convenience's sake, we'll call it a U() evaluation). If there is no third argument, which could be called a base case, the first element passed for the first time is given as %0. Normally, the result of the previous evaluation is passed as %0. To translate our factorial function into FOLD()'s syntax, we need to generate a list. The obvious method is to generate a list of all numbers between 1 and n, and multiply them all together. The LNUM() function will generate all numbers between 0 and n-1, so to get all numbers between 0 and n, we must use LNUM(add(%0,1)). To eliminate that 0, we use the REST() function. (Continued in 'man 14.5.3'.) & 14.5.3 &FACTORIAL_FN obj=[fold(FACT_AUX_FN,rest(lnum(add(%0,1))),1)] &FACT_AUX_FN obj=[mul(%0,%1)] This is considerably faster than the "pure" recursive method, and has the additional advantage of not running us up against the function recursion limit. Under a normal recursion limit, our first try at writing the factorial function fails when n is greater than 9; using FOLD(), we don't hit the recursion limit at all, since the nesting is never more than 3 functions deep (1 is the U() call to FACTORIAL_FN, 2 is the call to FOLD(), and 3 is the call to MUL()). * * * * * (Continued in 'man 14.5.4'.) & 14.5.4 Most people probably don't compute factorials in their daily MUSHing. A more practical application of recursive technique is the "pretty printing" of output into columns. If, for example, we wish to print a list in three columns, we should check to see if our current list has 3 words or less, and, if so, print them; otherwise, we should print the first three words, a newline, and then call our column function again on the remainder of the words in the list (i.e. word #4 on). This can be written as: &COLUMN_FN object=[switch(gt(words(%0),3), 0,[u(FORMAT_FN,%0)], [u(FORMAT_FN,extract(%0,1,3))]%r[u(COLUMN_FN,extract(%0,4,words(%0)))])] &FORMAT_FN object=[first(%0)]%t[first(rest(%0))]%t[rest(rest(%0))] Thus, the expression "[u(COLUMN_FN,lnum(8))]" gives us 0 1 2 3 4 5 6 7 (Continued in 'man 14.5.5'.) & 14.5.5 By changing FORMAT_FN, we can do other interesting things with our words; the example above is not necessarily the optimal way to pass arguments to FORMAT_FN, if the expression is very complex; it might be better to pass FORMAT_FN three arguments, doing the FIRST()/REST() extractions before calling FORMAT_FN. This is another case where FOLD() is useful. Because we can only grab one item off our list at a time when using FOLD(), we need to have some other way of determining when to insert a carriage return. For three-column output, we want to insert a carriage return every three words; therefore, if the number of words in the string is divisible by 3, we add a newline, otherwise, we add a tab. The code is then quite simple: &COLUMN_FN object=[fold(FORMAT_FN,%0)] &FORMAT_FN object=%0[switch(mod(words(%0),3),0,%r,%t)]%1 * * * * * (Continued in 'man 14.5.6'.) & 14.5.6 FOLD() can also be used to randomize a list under 2.0 (there is a built-in function, SHUFFLE(), in 1.50 and 2.2, which randomizes lists). The list cannot be too large, or the function invocation limit will cause an error, but using FOLD() is still more reasonable than most other list randomization methods. What we want to do is to take the elements of the list one at a time and randomly put them into positions of another list. This can be done, fairly effectively, by doing the following: 1. Start with a blank new list. 2. Take an element of the original list. Put it in the new list. 3. Take the next element of the original list. Put it either before or after the element of the new list. 4. Take the third element of the original list. Insert it into the new list at a random position. We can use FOLD() to accomplish this, with %0 as the new list, and %1 as an element, using the following code: (Continued in 'man 14.5.7'.) & 14.5.7 &SHUFFLE_FN object=[fold(SHUFFLE_LIST_FN,%0)] &SHUFFLE_LIST_FN object=[insert(%0,add(rand(words(%0)),1),%1)] * * * * * Finally, FOLD() is very good for dealing with space-separated lists that need to be passed to functions which require comma-separated arguments, such as the example of MAX() used earlier in this manual section. Given an attribute LIST, containing something like "1 10 83 4", one can find the maximum of the numbers in it using this code: &MAXLIST_FN object=[fold(MAXTWO_FN,rest(v(list)),first(v(list)))] &MAXTWO_FN object=[max(%0,%1)] * * * * * (Continued in 'man 14.5.8'.) & 14.5.9 Recursion is a very natural technique for generating output which follows a clearly defined pattern. Unfortunately, due to the function recursion and invocation limits, it is frequently not a usable for large values (or long lists, etc.), unless you are using FOLD() or some other technique for reducing the number of functions on the stack at a given time. Nonetheless, it can be the fastest and cleanest way to accomplish a task, and it is not a technique which should be overlooked. --------------------------------------------------------------------------- & 15 15. Efficiency & 15.1 15.1 Parameter Passing MUSH evaluates every single expression it receives; it has no memory of what has already been evaluated. Thus, if you write something like "[extract(get(#100/list),2,1)] [extract(get(#100/list),5,1)]", the "get(#100/list)" is evaluated twice. This isn't disastrous, but if instead of "get(#100/list)", you had something like "iter(setinter(lattr(#100/DATA_*),lattr(#100/NUM_*)),mid(##,rand(5),rand(2)))" (probably expressed as a U() function), repeating the same thing twice would be extremely inefficient. The best way to avoid evaluating complex expressions multiple times is to pass them as arguments to a U(). Because having extra function calls generates more overhead, this is a technique which should be restricted to those instances where the expressions are either very complex, use computationally expensive operations (such as SETINTER() and other sorting functions, ITER(), and large SWITCH() expressions), or are used three or more times. (Continued in 'man 15.1.1'.) & 15.1.1 The second example above is an excellent candidate for such reduction. The best way to write it would be something of the form: &EXPR_FN object=[u(AUX_FN,iter(setinter(lattr(#100/DATA_*),lattr(#100/NUM_*)), mid(##, rand(5), rand(2))), 2, 5)] &AUX_FN object=[extract(%0,%1,1)] [extract(%0,%2,1)] This might be further improved by putting the ITER() in an expression by itself; it's still of bearable length, but if that particular ITER() is used another time in the same MUSH program, it should definitely go into a U(). ---------- (Continued in 'man 15.1.2'.) & 15.1.2 One alternative to using U() parameters to avoid evaluating an expression twice is the SETQ()/R() function combination. There are ten "registers", 0 through 9, which can be used for temporary storage. The registers are local to a command list -- that is, they persist through more than one queue cycle, within the direct chain of evaluation, as triggered by a $command, attribute/oattribute/attribute, or the like. They are set via the function evaluation "[setq(<register number>,<expression>)]". This expression evaluates to a null string, and therefore can be inserted into a string without affecting its value. The R() function is used to retrieve the appropriate register; the %q percent-substitution is equivalent to this function. (Continued in 'man 15.1.3'.) & 15.1.3 The order of parser evaluation definitely makes a difference when using SETQ(). It is advisable to put all SETQ() functions at the beginning of any function evaluation; expressions are evaluated left to right, outside to inside, and you can check whether or not values are being set in the order you think they are via use of the DEBUG flag, but for clarity, SETQ() expressions should be placed at the beginning of the string. Also, you should make sure that nested U() functions don't attempt to use the same registers; remember that registers are local to a command evaluation, _not_ to a function evaluation. If you need to have nested U() functions that re-use registers (for example, you have some extremely complex computations that require large numbers of temporary variables), you may wish to consider use of the ULOCAL() function instead. This function is identical to U(), save that R() registers within it are considered "local variables". That is, the original values of the registers are restored when the inner function exits, and the original values _are_ passed into the inner function. In this respect, they are somewhat like VAL parameters in Pascal, or like ordinary function parameters in C. (Continued in 'man 15.1.4'.) & 15.1.4 You should, however, avoid the unnecessary use of ULOCAL(). While syntatically cleaner than U(), the copying of variables which is necessary to preserve the old values also increases the computational cost of the function. Do, however, note that any use of SETQ() within a global-defined @function should ALWAYS be done within ULOCAL() unless you deliberately intend to change the value of the register for the remainder of the associated command list; otherwise, you might inadvertently change a value that the calling user is attempting to preserve. * * * * * Please note that SETQ() is a FUNCTION, not a command. It should thus be nested within a command, NOT placed on its own. In other words: RIGHT: $test *: @pemit %#=[setq(0,revwords(%0))][u(A_FN,%q0)]--[u(B_FN,%q0)] WRONG: $test *: [setq(0,revwords(%0))]; @pemit %#=[u(A_FN,%q0)]--[u(B_FN,%q0)] (Continued in 'man 15.1.5'.) & 15.1.5 The most obvious use for SETQ() is to cut down the number of times a complex expression is evaluated; all that is needed is a single evaluation as part of a SETQ(). A secondary use is as temporary storage for some variable needed by FOLD(), FILTER(), or similar functions that take a limited number of parameters. For example, it is frequently desirable for a FILTER() to know the dbref of the enactor. But because the U()-type function called by FILTER() only knows about the one element of the list being evaluated, it can't get the enactor unless that information is stored someplace else. In that case, simply setting '%#' into an R()-register solves the problem. * * * * * Related to intelligent parameter-passing is U()'s usefulness as a tool for hiding details of implementation. For example, there are many different ways to count the number of times a word occurs in a list. If a U() called COUNT_FN is used, instead of writing out the expression every time, by simply changing the COUNT_FN attribute, different methods can be tried. For example, any of the following would work, if %0 is the list and %1 is the word: (Continued in 'man 15.1.6'.) & 15.1.6 &COUNT_FN object=[sub(words(%0),words(edit(%b%0%b,%b%1%b,%b)))] &COUNT_FN object=[words(iter(%0,switch(%0,%1,%0)))] &COUNT_FN object=[setq(0,%1)][filter(AUX_FN,%0)] &AUX_FN object=[eq(comp(%0,%q0),0)] &COUNT_FN object=[setq(0,%1)][fold(AUX_FN,%0,0)] &AUX_FN object=[add(%0,eq(comp(%0,%q0),0))] * * * * * Sometimes, you will want to use the same complex function evaluation across several commands. In this case, if you have something sufficiently large and don't mind the extra queue cycle needed, you can use @trigger to pass that function evaluation as a parameter. @trigger is extremely useful for parameter manipulation; this is one of the few ways that the stack variables %0 through %9 can be directly manipulated. Do not ignore its use as a method for reducing the number of complicated functions that need to be evaluated. & 15.2 15.2 How the Queue Works The "queue" is the place where all commands are placed before being run. A queue is exactly what it sounds like; the first command to be put on the queue is the first command to be executed (the queue is executed from "head" to "tail"). New commands are always put at the tail end of the queue. The mysterious MUSH queue is actually three separate queues. They are referred to as the "Player", "Object", and "Wait" queues. The first queue is the where commands that are going to be immediately executed are placed. This includes anything directly typed by a connected player, plus the first X commands from the object queue (where X is usually between 0 and 50. It is 3 by default.) The second queue is where anything done by an object is placed; when the commands are due to be executed, they are placed on the player queue. The third queue is where all objects waiting for some event to occur are placed; when its wait expires, an action in the wait queue is moved onto the command queue. (Continued in 'man 15.2.1'.) & 15.2.1 The MUSH executes a loop which can be simplified down to: 1. Check to see if any waits have expired. If so, put those commands in the command queue. 2. Check to see if anybody typed anything, and if so, put those commands in the player queue. 3. Put X commands from the command queue into the player queue. 4. Execute the player queue. This may cause more commands to be put at the end of the command queue. 5. Go to 1. We can think of the player and command queues as simply being a single queue, as long as we keep in mind that something typed from the keyboard frequently executes before a command issued by an object. Every command in MUSH is a single item on the queue. For the purposes of this discussion, we will call a "queue cycle" one such command (rather than referring to a queue cycle as one iteration of the loop given above, since that's a purely internal measure). Every command that an object gives is put on the end of the queue. (Continued in 'man 15.2.3'.) & 15.2.3 * * * * * Certain commands "nest" other commands. For example, the @switch command frequently takes the format: @switch %0=foo, {@pemit %#=Got it.}, {@pemit %#=Failed.} The game does not queue up the @switch and the @pemit one after another. Instead, it executes the @switch, then puts the correct action at the end of the queue. Thus, several other commands may occur between the @switch and the @pemit. This is further complicated by the way action lists (commands of the format "@@ action 1; @@ action 2; @@ action 3") are handled. Given that expression, those three actions are queued one after another. However, nested action lists are considered part of command they are part of, and are not queued up until that command is executed. For example: "@emit 0; @switch %0=foo, {@emit yes; @emit YES}, {@emit no; @emit NO}; @emit 1" is queued as the following: (Continued in 'man 15.2.4'.) & 15.2.4 @emit 0 @switch %0=foo, {@emit yes; @emit YES}, {@emit no; @emit NO} @emit 1 The first command is executed as you would expect, and the output is "0". Next, the @switch is executed. The resulting actions are put at the tail of the queue, so the queue becomes (assuming %0 is foo): @emit 1 @emit yes @emit YES Note that the "@emit 1" executes BEFORE the @emits associated with the @switch, despite the fact that when the code is typed, the "@emit 1" comes after the @switch. This behavior also applies to @dolist, @force, @wait, @trigger, and all other commands which execute other commands. This can be acutely obvious when you have a construction like: (Continued in 'man 15.2.5'.) & 15.2.5 @emit Begin; @dolist a b c=@emit ##; @emit End This produces: Begin End a b c That behavior is extremely important when you have commands which must execute only _after_ all commands in a @dolist has finished executing; in that type of case, a @wait, either timed or semaphore, is usually the best solution. & 15.3 15.3 Pipelining "Pipelining" is a term borrowed from the jargon of microprocessors. In that field, it refers to the practice of feeding the next instruction to the CPU, before the previous instruction has completed. The "pipeline" ensures that there will always be an instruction waiting for the CPU. If the previous instruction was a branch and the processor predicts the incorrect next instruction, the processor must then go fetch the correct next instruction; nonetheless, this is not a loss, since, had there not been a previous fetch, there would have been a delay anyway while the processor went hunting for the instruction. A similar principle can be applied to MUSH programming. Simply put, there are many MUSH commands which do something along the lines of the following: $test *: @switch/first num(*%0)=#-1, {@pemit %#=No such player.}, {&TEST_OWNER %#=owner(*%0); @pemit %#=Owner test set.} (Continued in 'man 15.3.1'.) & 15.3.1 Note that no matter what happens, you will always have at least two commands to queue: the @switch, and the @pemit. In the case of correct syntax, there'll be three commands: the @switch, the @pemit, and the attribute set. This doesn't make sense, from the viewpoint of efficiency. Since the correct case is going to be the one encountered the most frequently, it shouldn't, ideally, be any slower than the error case. You can usually save yourself a queue cycle by doing the following: $test *: @pemit %#=switch(num(*%0),#-1,No such player.,Owner test set.); &TEST_OWNER [switch(num(*%0),#-1,#-1,%#)]=owner(*%0) (Continued in 'man 15.3.2'.) & 15.3.2 Basically, we "assume" that the set is going to succeed, most of the time, and go ahead and do it anyway; to avoid accidentally scribbling on the attribute if we've encountered an error, we SWITCH() to make the object try to set TEST_OWNER on a non-existent object in the case of an error. We have, granted, added extra wildcard matches by the use of two SWITCH() statements where before there was a single @switch, but we've saved on a queue cycle, so we've probably won, overall -- this varies on a case-by-case basis, of course. We can do this sort of "pipelining" for @trigger, @dolist, and similar things. It's no longer more efficient when there are more than two queue cycles involved, but since many MUSH situations do reduce down to something of this format, this "pipelining" technique proves extremely useful. & 15.4 15.4 Queue Cycles vs. CPU Cycles One of the major drawbacks to the "switchless" coding style is that it tends to reduce the queue cycles needed to perform an action at the expense of short evaluation times for a command. While the action may take less time to execute, because other objects' queued commands are not getting executed between each stage of the action, the overall computation time ("CPU cycles") needed by that action group may not be significantly reduced, or, indeed, may actually be increased. Switchless coding tends to eliminate @switch and @trigger, two commands which force a "delay", due to the property of these two commands described in the earlier section on the workings of the queue. In doing so, however, the programmer frequently uses complex function evaluations which take a long time to evaluate, and, worse still, may evaluate the same expression repeatedly. One simple example of such a tradeoff is the following: (Continued in 'man 15.4.1'.) & 15.4.1 @va object=$test *: @pemit %#=[extract(v(colors),match(v(list),%0),1)] [extract(v(sizes),match(v(list),%0),1)] [match(v(list),%0,1)] vs. @va object=$test *: @trigger me/vb=%#,[match(v(list),%0)] @vb object=@pemit %0=[extract(v(colors),%1,1)] [extract(v(sizes),%1,1)] %1 The second way is slower, since it takes two commands instead of one, but it's also more efficient. The best way to do the above would be @va object=$test *: @pemit %#= [setq(0,match(v(list),%0))][extract(v(colors),r(0),1)] [extract(v(sizes),r(0),1)] [r(0)] (Continued in 'man 15.4.2'.) & 15.4.2 One must also remember that even if the total time to evaluate a switchless and non-switchless version of the same action is the same, other players are forced to wait longer for their commands to execute under the switchless, because the game is running the big complex evaluation all at once. While the _total_ time spent waiting remains constant, the time-between-each command is increased. Therefore, considerate programmers don't program gigantic expressions which hog the server for several seconds at a time. Good programmers find better ways to split up the evaluation, so that it doesn't take several seconds to evaluate. If you write something which lags a MUSH running at an ordinary speed, you haven't programmed it well. (Continued in 'man 15.4.3'.) & 15.4.3 As more programmers code switchless style, the amount of time between each command increases. On a fast MUSH, the difference is not generally noticeable, but if you really want to know if your code is any good, try running it on a slow machine (NOT a slow network -- you should victimize a MicroVAX or something similar). The switchless style when used intelligently shouldn't slow down the game, but it's far too easy to abuse. Function invocations should be kept to a minimum. In general, if an attribute containing a command list exceeds about 12 lines of text, it is Too Large. Intelligent use of switchless programming usually generates strings to be outputted. Using massive nested switch() constructs usually implies that something is not as efficient as it could be; the same is true with iter(). The way a list is stored is frequently more important than the way it is accessed; by storing data differently, it's often possible to cut down on the complexity of retrieval. The switchless style is best when it eliminates large numbers of nested @switch's; other uses, especially those involving recursion, should be carefully considered before being used. (Continued in 'man 15.3.3'.) & 15.3.3 ---------- That's the end of this manual; I hope it's been helpful. Please remember that this is copyrighted material; I've put a lot of work into this and am not likely to be pleased by others stealing my work. Please see the first section of this manual for the terms of the copyright and other information. Comments, corrections, and suggestions should be emailed to Amberyl, at lwl@godlike.com &wiz-ethics Godlike Technologies Amberyl's Wizard Ethics This is an expansion of the section of the MUSH manual called "The Fundamental Laws of Wizarding". Questions and comments should go to lwl@godlike.com. The content of the lecture has been unmodified since 10/23/94. _________________________________________________________________ Administrators on a MUSH have a considerable amount of power. With that power comes the responsibility to use it wisely, for the actions of an administrator reflect not only on himself, but on the MUSH as a whole. Because an admin is in a position of power, he should attempt to set a positive example in all dealings, whether in-character or out-of-character, Common sense will usually suggest a decent solution to a given problem; there are, however, some situations where the ethical and "correct" thing to do is not immediately obvious. _________________________________________________________________ (Continued in 'man wizeth1'.) &wizeth1 Players are frequently paranoid about privacy. This is one "right" that you should _always_ attempt to respect; thus, this lecture begins with a discussion of the DARK flag. When you are DARK in a room, players who do a 'look' cannot see you, and you do not appear on the WHO list, but a @sweep of the room _does_ show that you are listening and connected. Therefore, there is a chance that you will be noticed, and, if you are "illegally" in the room DARK, it is almost certain that the players in the room will be quite upset. The reason why players don't like DARK wizards wandering around should be obvious -- they're afraid of private conversations being snooped on. Therefore, whenever you go _anyplace_ DARK, the first thing you should type upon entering the room is a "hello" or similar announcement of your presence. Make this a habit! If you do it regularly, even when the players in the room know you'll be entering DARK, there's less of a chance you will forget at some crucial moment. If you plan to be DARK, and wandering around, for an extended period of time, in order to arbitrate an RPG conflict or similar event, the players involved should, if possible, know of your presence, and if a (Continued in 'man wizeth2'.) &wizeth2 private conversation begins, it is your ethical responsibility to inform the players of your presence immediately, _even if_ this lets players know that they are being judged. Your personal responsibility to promote privacy overrides any RPG mechanics. Some MUSH gods may allow their wizards to go DARK in order to observe players whom are suspected of trying to harm the game, either by harming the database or crashing the server; some other gods may also allow wizards to go DARK to observe the actions of a player harassing other players. In the latter case, the wizard should indicate his presence to the players in the room who are not "under suspicion". I cannot emphasize enough how important it is to obey the protocols for using the DARK flag. This is one of the greatest sources of player distrust in wizards, because it is so _easy_ to abuse. Players should be able to have private conversations without doing a @sweep all the time. While privacy on a MUD is never, ever, guaranteed, you should do your best to contribute towards it. _________________________________________________________________ (Continued in 'man wizeth3'.) &wizeth3 The second of the frequently-abused wizard powers is @teleport. There are a number of rules that should be observed when @teleporting. First of all, never teleport a player without warning him first. If possible, the player should page you with an acknowledgement before you do the actual @tel. This prevents, for example, an untimely @teleport wrecking someone's set of a @desc on a room. No one enjoys suddenly being yanked to another room, and it doesn't hurt you to page the player first. Never teleport to a room with players without asking the permission of the players there first. The exception to this are public hangouts. A room which is merely JUMP_OK is not necessarily a public hangout; use some judgement when determining whether or not rooms are public. Unless the room is clearly public, ask first; it doesn't hurt you to be polite. (Continued in 'man wizeth4'.) &wizeth4 If permission is not likely to be granted, then notify all players in the room (@remit works well for this) that you are going to be coming in, before entering the room. You should wait a moment between the of the announcement and the teleport; this should prevent you from hearing something that you shouldn't. Never make assumptions about whether or not it's okay to teleport into a situation. For example, just because a player is paging you for help with coding some complex object _doesn't_ mean that he wants you to teleport into his room and help him out. I know this from experience -- as a new wizard, I once struggled to help someone, remotely, with a complex MUSHcoded object, and, finally, after fifteen minutes of fruitless pages, I finally decided to teleport in and see what errors the object was outputting. Unfortunately, I bamf'ed myself straight into the middle of a hot 'n steamy tinysex session. Since then, I've made it a rule to always ask before teleporting. _________________________________________________________________ (Continued in 'man wizeth5'.) &wizeth5 Don't abuse "examine" or the power to see private information like sites. The rules for examining private objects will vary from God to God. On a MUSH where a strict theme is enforced, wizards will probably be permitted to examine anything in order to check for theme consistency. You should never share or show any object, or part of an object, that is not yours to another player, without getting the permission of the owner. Also, don't steal code. "Stealing code" includes @decompiling/logging/cloning without permission, even if you're just using the code for your own personal edification or enjoyment. If it's that important to you, ask permission. Sitenames should never be given out. If a player is Unfindable, you shouldn't give out his location. Some people MUD to escape people they know in Real Life; some players carry vendettas cross-MUD. Other people simply prefer not to let others know where they are, VR or RL. You should respect this desire. _________________________________________________________________ (Continued in 'man wizeth6'.) &wizeth6 You should generally avoid modifying other people's objects, even if you're fixing code problems or typos. The only exception to this is publicly owned building owned by a wizard-played builder character (the core landscape for the MUSH, the downtown area, etc.) If you encounter an object which is inefficiently programmed or looping, and thus slowing down the game for everyone else, simply set it HALT and mail the owner; don't fix the problem. Also, don't @destroy anything of anyone else's unless they explicitly ask you to. If it's an item which is illegal in some way, send the owner a warning, and give him a certain period of time (a few days, usually, depending on how often the player logs in) in which to get rid of it. If it's not gone by the end of the period, then you can destruct it. If the illegal item is sitting in a public place, you should feel free to teleport it back to its owner. _________________________________________________________________ (Continued in 'man wizeth7'.) &wizeth7 Never, ever, @force a player, unless it is absolutely necessary. For the same reasons, don't use @trigger and similar commands to make a player do something against his will. Also, don't change the attributes on a player; for example, it is generally considered unethical to change a player's description to something humiliating. Wizards might occassional get into practical joke wars, but mortals shouldn't become involved. If you need to change an attribute on a player, make sure you copy the old attribute to a backup attribute on the player, so that they can recover it later, if need be. _________________________________________________________________ Wizards can pile huge numbers of things on the queue, and make use of computationally expensive commands like @find and @search with impunity. If you need to do something that will severely lag the game, do it when there aren't many players on. Also, note that very large @dolists cause the MUSH process size to grow, and should be avoided whenever possible. _________________________________________________________________ (Continued in 'man wizeth8'.) &wizeth8 Wizards should be uselocked at all times. Private wiztoys should also be uselocked. Any item with wizard powers MUST be checked for security; if you are not a competent enough MUSH programmer to be able to do this yourself, you should show the item to a wizard who is. In addition, administrators and objects owned by administrators should never be zoned to a Zone to which mortals have access. Never give a wiz-power item to a mortal. If a mortal absolutely MUST have a wizard-power item to do what he wants, consider @chown'ing that particular system to a wizard. If this isn't possible, then make absolutely certain that the mortal is trustworthy. Every administrator on the game should be aware of all wiz-power items in the hands of mortals. _________________________________________________________________ (Continued in 'man wizeth9'.) &wizeth9 Avoid annoying players with shouts. @wall cannot be blocked, and, therefore, you should make sure that everything you shout is informative and necessary. On a very small, friendly MUSH, it might be considered acceptable to @wall hello and goodbye; on most MUSHes, though, especially RPG ones where "mood" and "atmosphere" are important, @walls of that sort should be avoided. Shouting to announce a @shutdown in five minutes is acceptable; shouting to announce game problems is acceptable. Shouting to announce that you've had a bad day and shouldn't be paged should probably be avoided. First of all, it makes you sound like a twit; second, it accomplishes nothing useful and implies, "stay out of my way or something bad will happen to you." If you really want to avoid player pages, go DARK and set a pagelock. Players shouldn't be afraid of you on a "professional" wizard level, and you should try to avoid actions which cause players to fear you. _________________________________________________________________ (Continued in 'man wizeth10'.) &wizeth10 Wizards should be thoroughly familiar with MUSH. While "psychocoder" status is not necessary, a good understanding of MUSH mechanics is necessary. Never type a command as a wizard without understanding what it does. You should have read the MUSH manual; even if you don't understand all of it, you should read and understand the wizard section. Every object in the Master Room should have a specific purpose. ONLY objects used for global commands should be in the Master Room. As few total attributes should be on Master Room objects as possible; any data objects should be contained on separate objects NOT in the master room, and the Master Room objects should NOT be parented to the data objects. Every attribute in the Master Room represents another attribute that must be checked for $commands when a player types something; thus, having unnecessary objects and attributes in the room will slow the game down. If you need a place to store objects used as global parents and whatnot, use a room _other_ than the Master Room. Master Room code should, obviously, be as efficient as possible. _________________________________________________________________ (Continued in 'man wizeth11'.) &wizeth11 There should be at least one wizard on the game who is familiar with the technical aspects of the server. Also, backups should be done on a weekly basis, or more often, if possible. This will save you a lot of grief should your database get eaten by a bug or other problem. If your currently-running game begins to show signs of database corruption, it is probably a good idea NOT to @shutdown; instead, log into the game account and 'kill -9' the MUSH process. It's also a good idea for wizards to know the phone number of the God / codehack / site admin, for emergencies. If you have the necessary technical background, you are highly encourage to at least take a glance at the server code. Knowledge of the server workings helps your MUSH programming ability, and enables you to make intelligent decisions that involve disk, memory, and CPU usage. Also, get to know wizards on other MUSHes; this gives you people to go to for help should something unfortunate befall your MUSH. _________________________________________________________________ (Continued in 'man wizeth12'.) &wizeth12 Never MUSH while intoxicated or otherwise not fully in control of your actions. If you observe another wizard who is MUSHing drunk, @boot them. The other wizard will probably be annoyed, but this is better than having damage inadvertently caused by someone who isn't thinking clearly. Know when to log off or back out of a situation. If you feel like you're about to explode, get up, walk around for a bit, get a drink of water, and come back when you feel like you're ready to cope rationally with the situation. If possible, call in another administrator to handle the situation. Never, ever, perform wizard actions out of anger; it is likely that you will regret them later. A wizard's ability to deal with players is usually the final test of his skills. Troublemaking players are quick to find wizards who are easily provoked, and frequently enjoy pushing all the buttons they can. When you page a player, no matter how annoyed you might be, you should try to remain calm and polite. Don't swear at players, or behave in a manner that would allow the player to focus a discussion away from his wrongdoing to a mistake _you_ made. (Continued in 'man wizeth13'.) &wizeth13 Players who are spamming or exhibiting behavior detrimental to the server or database should be @booted; if it's a one-time-deal character like "You_Suck", a @destroy is also in order. Other players should receive warnings before any direct action is taken. If a player is being obnoxious or breaking MUSH rules, page them and politely let them know that their behavior is unacceptable. If the player is not idle, and does not respond to your page within a few minutes, repeat your page. If the player simply ignores you, or refuses to change his behavior, page with a stronger warning, but continue to remain polite. If this doesn't work, tell them, "If you don't stop that, I'm going to @boot you." (Continued in 'man wizeth14'.) &wizeth14 This will normally at least open a dialogue between yourself and the player in question. If the player isn't willing to discuss it, and continues the obnoxious behavior, set them GAG. Most players will quit voluntarily. For really stubborn players, @boot will work. If they come back and continue to be annoying, then @newpassword them. You should NOT @destroy a player who isn't a one-shot annoyance. @destroy is not reversible, but a player can be re-@newpassworded should he decide to come back and play by the rules. When talking to a player, you should attempt to remain calm and polite, no matter how much the player is trying to provoke you. Ignore personal insults, obscenities, and the like, if some useful discussion is occurring. If the player is just continuing to be annoying, end the discussion and simply tell them, "Do this again and I'll @boot you." You're not required to be a saint or martyr. (Continued in 'man wizeth15'.) &wizeth15 Whenever you enter a major conflict with a player, you should log the situation, if possible. You should send the other wizards, via email, a summary of the situation, and, if necessary, the log. This will prevent the player from accusing you of saying or doing things that you did not, and will give you a "record" of the player's behavior, in case the player claims that he didn't do what you claimed he did. The SUSPECT flag and @comment attribute are also good for letting other wizards know about suspicious players. _________________________________________________________________ These guidelines aren't here to make the task of wizarding more difficult for you. They exist to provide some basic guidelines of ethical behavior; in general, if you follow these guidelines, players will find it quite difficult to accuse you of abusing your wizpowers. They thus work for your protection, as well as the players'. Players frequently take wizard mistakes personally and seriously, and may decide, "because wizard X did this, all other wizards probably do, too." Players who are paranoid tend to be more difficult to deal with; thus, for the sake of your sanity and that of every other wizard who (Continued in 'man wizeth16'.) &wizeth16 ever has to deal with that player, try to avoid giving the a reason to be paranoid. If you consistently have personal problems with a player, you should also avoid wizard dealings with that player; have another wizard handle the situation instead. In all things, use common sense, and try to remain calm and impartial. When all else fails, log off. _________________________________________________________________ Based on a lecture first given on TinyKrynn, 2/92, by Amberyl. &README READ THIS NOTICE FIRST This notice should be located in a directory titled something like "mushman-2.008", which contains the files "man2x0" through "man2x5". (The files might have been renamed, but the content should be as described below.) These files constitute a document entitled, "Amberyl's MUSH Manual", sometimes referred to as, "The TinyMUSH Manual", or, simply, "The MUSH Manual". This material is COPYRIGHTED by the author, Lydia Leong (lwl@godlike.com), sometimes known on the Net as "Amberyl". All rights and privileges of reproduction are hers, in accordance with United States or International Copyright Laws. As of the date of this manual's release (May 22nd, 1995), there is a United States copyright application being filed for this document, though even prior to this official record of copyright, this document is still subject to the conditions outlined below, and protected by US and international copyright law. (Continued in 'man README1'.) &README1 Users are granted to print and reproduce this manual in its UNMODIFIED entirety, as long as the copyright notices are retained and due credit is given. This manual may be quoted in a publication -- electronic or otherwise -- within the normal academic protocol of appropriate lengths of quotations and due credit. This manual may NOT be used within another major document (such as another manual), nor published in print form (whether or not the publisher makes a profit from such publication), nor may it be hypertextified or otherwise changed in format, without the explicit permission of the author. This notice must be retained together with any electronically-retrievable version of this manual. (Continued in 'man README2'.) &README2 I apologize for needing to spell these terms out in such an explicit manner, but there are certain creatures on the Net who have happily taken advantage of my generosity in making this manual freely available, and decided to make a buck off it themselves. To protect myself from further incidences of such theft (and to avoid further lining the pockets of my lawyer), to keep track of the uses of this manual, and to ensure that no unknown "splinter" versions of this manual arise, I'm explicitly stating the terms under which this manual is distributed -- you can use it for your own education and enjoyment, or print it out to line your bird cage, but please, if you're going to change it or reprint it, please contact me first. This manual is now shareware. If you found it useful, please consider sending $10 to the address obtainable by fingering 'lwl@godlike.com' At the very least, it'll help pay back legal expenses incurred from protecting the copyright on this manual (and thus your privilege of getting it free from the Net). (Continued in 'man README3'.) & README3 (No, I'm not giving out details on the legal tangle, until it's settled. I'll post an explanation when it's all over.) Thanks. I hope you find this manual useful. Comments and corrections are always appreciated. -- Lydia Leong (lwl@godlike.com) May 22nd, 1995