-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- MUSE Programming Guide v2.0 A guide for learning and resolving the common problems of the MUSE programming language through explanation and example Written by wyvern (chris@vchicago.org) Released 8/3/98 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Table of Contents ~~~~~~~~~~~~~~~~~ I. Introduction A. Introduction to MUSEing B. Introduction to this Document II. Programming Errors, Hints, & Fix-ups A. The Parser B. Pronoun Substitutions C. An Introduction to Functions D. Commands and Parsing E. Events F. Wildcard Registers G. Queued Commands III. The Function Library A. Strings B. Math C. Time D. Conditional D. Database IV. Necessary Concepts A. User-Defined Functions B. Parents C. Zones V. Conclusion A. C and Robots B. About the Author C. Note from the Author D. Future Notes I. A. -- Introduction to MUSEing MUSEs are just like any other virtual reality program such as MOO, MUCK, MUSH, or MUD. Users can interactively communicate with each other, create and program objects, play games, or explore. The first MUSE is called MicroMuse, at chezmoto.ai.mit.edu, port 4201. There are many MUSEs on the Internet; some come and go, while others remain. I. B. -- Introduction to this document This document was originally meant to start where Falryx's MUSE manual leaves off. Though it is in no way a sequel or copy of the manual, you should be familiar with most concepts and commands in the manual before you read this document. In this text, I will show some of the concepts of MUSE code, some "tricks" that people use, and help you resolve common problems that occur. Any problems, questions, mistakes in this guide, or comments should be sent to chris@vchicago.org. A sidenote: When you see a '[...]' in this text, it means text has been cut out to save bandwidth. There are bound to be plenty of mistakes, please report them to me. I can only make better versions with your help (or praise!) II. A. -- The Parser: Catch-22 The infamous MUSE "parser" simply refers to the substitutions that MUSE goes through with almost every string that passes through it. Depending on the server, there can be many different levels of "parsing" that happen. Most occur in the source code and are for output formatting, so regular users don't need to worry about them. Why do programmers always complain about the parser? Because it is the thing that almost always causes problems. Look at it this way: some commands will run strings through a parser, and some commands won't. Some commands will run strings through one parser, while other commands will run strings through another. Sometimes a command will run a particular string through a parser more than once. The parser runs through an extra time for programmed commands. And finally, the parser will return different results for each user. However, ultimately, the same reasons the parser is evil also help make MUSE so interactive and definable. The parser is a way of manipulating data for different users, so something doesn't look the same all the time. In order to understand the parser, you must look at it closely, step by step. II. B. -- Pronoun Substitutions: Simply Marvelous There are PRONOUNS which get substituted. They are identified by a percent sign (%) grouped with another character: %n enactor's name %# enactor's dbref# %c enactor's colored name %f enactor's reflexive pronoun %o enactor's object pronoun %p enactor's possessive adjective %q enactor's possessive pronoun %s enactor's subject pronoun Aren't those fun? What are they good for? Say Ba-Pan walks into a bar (I'm being serious!) and the bar doesn't use pronouns. The best the bar can @emit to everyone in its @aenter is: Someone walked into the bar! But this isn't worth bo-diddly! No one cares that "someone" walked into the bar. It would be much more useful if the bar could tell us who it was that entered. That is why we have %n. It tells us the name of the enactor -- that is, the person who triggered the @aenter. @aenter here=@emit >%N walked into the bar! == turns into == >Ba-Pan walked into the bar! Oh, but there's so much more you can do: @aenter here=@emit >%N steps in and tips %p hat! == turns into == >Ba-Pan steps in and tips his hat! There are some more fun pronoun tricks! In order to get a V-register (that is, an attribute on yourself like @va, @vb, ... @vz, etc.), you can do the following: @va me=this is my @va attribute! >>wyvern - Set. "%va >>You say, "this is my @va attribute!" You can do sort of the same thing to get an attribute that isn't in a V-register: @desc me=a grumpy old man. >>wyvern - Set. "%/desc/ >>You say, "a grumpy old man." The syntax to do that is %//. In order to get your own @odesc, for instance, you would use %/odesc/ instead of %/desc/. A few other things: %r will be substituted with a carriage return, %t will be substituted with a tab, and %k will be substituted with a space: @emit This is the first line.%rThis is the second line. >>This is the first line. >>This is the second line. @emit A tab is between these arrows -->%t<-- >>A tab is between these arrows --> <-- @emit 1%k2%k%k3%k%k%k4 >>1 2 3 4 More fun awaits you! An extra thing to remember is that the CASE of your pronouns make a difference. For instance: "%p and %f >>You say, "his and himself" "%P and %F >>You say, "His and Himself" II. C. -- Functions: Only the Beginning FUNCTIONS are also substituted. MUSE contains a large library of functions which take various strings as input, make some sort of calculation, and output that calculation. Here are some examples: @aenter here=%N steps into the bar, seeing that the time is [time()]. == turns into == Ba-Pan steps into the bar, seeing that the time is Wed Jul 22 15:44: 17 1998. We will explore the MUSE function library in more depth later on, and use some common functions in examples on the way there. II. D. -- Commands and Parsing: Putting It All Together When you input a command, it sometimes gets parsed. To understand how it is parsed, you must first understand that there are three types of commands. The first type of command is a command that is not parsed at all. These are generally special commands, and they begin with @n, like @nemit, @npemit, etc. They retain all spaces and will keep the string exactly as it should appear: @nemit yabba dabba doo >>yabba dabba doo The second class of command runs through pronoun substitution. Pronoun substitution parses but functions and pronouns. It removes all extra whitespaces, too. Functions *must* start with a left bracket ([) with this type of command! say My name is %N, my dbref is %#, and 1 + 1 = [add(1,1)]. >>You say, "My name is wyvern, my dbref is #2, and 1 + 1 = 2." @echo Z i Zx add(1,1) [sub(1,1)] >>Z i Zx add(1,1) 0 The third class of command only runs through exec. What is exec? It is a parser in the MUSE source which will parse functions, but not pronoun substitution. Pronoun substitution runs the entire string through exec, but this third class will only run the first function it encounters, per argument, through. This is not as important as the second type of command, but is useful if you would like to parse something correctly: p [num(me)]=[name(me)] >>I don't know who [num(me)] is. p num(me)=[name(me)] >>wyvern (w) pages: [name(me)] p num(me)=name(me) >>wyvern (w) pages: wyvern p num(me)=x %p x >>wyvern (w) pages: x %p x For the most part, we will be focusing on the second type of commands when dealing with the parser issue. You can usually use little "tricks" to skip steps, such as: examine num(*wyvern) @tel loc(*wyvern) These work better than using '@whereis wyvern' and then @tel'ing to the location or finding my number and then examining me. Why can't you use examine [num(*wyvern)] here? Well, MUSE does not parse the argument for examine. When you are typing in a command, you can sometimes leave out the []'s, especially if the command doesn't parse the argument. GOOD BAD page first(lwho())=hi! page [first(lwho())]=hi! +mail wyvern=flip(hi!) +mail wyvern=[flip(hi!)] This is mostly because when MUSE first encounters your command, it has to find out what text is the command and what text is the arguments. For instance, in +mail wyvern=hi!, it finds the equal (=) sign that separates the command and arguments. Now it knows that '+mail' is the command, 'wyvern' is the first argument, and 'hi!' is the second argument. If one of the arguments is a function without braces, it'll parse it. But if a command isn't one of the commands that goes through the command cycle, like " (these are called TOKEN COMMANDS), then it won't work. For example: "flip(hi!) >>You say "flip(hi!)" say flip(hi!) >>You say "!ih!" The parser will parse things when you don't always want them parsed. Put a '%' in front of a string to prevent substitution. say %[add(1,1)] >>You say, "[add(1,1)]" say %%N >>You say, "%N" say %% >>You say, "%" To get around the removal of spaces, you can use the spc() function: say Hi[spc(2)]Hi![spc(3)]HIIII![spc(4)]whee. >>You say "Hi Hi! HIIII! whee." If you want a space in a text, a quick and easy way to do it is: say Hi % Hi >>You say, "Hi Hi" The function s() can be used to run a string through the parser: say [add(1,1)] >>You say, "2" say %[add(1,1)] >>You say, "[add(1,1)]" say [s(%[add(1,1)])] >>You say, "2" Pronouns and functions do not mix: say [string(%#,10)] >>You say, "%#%#%#%#%#%#%#%#%#%#" You can use s() with pronouns in functions to force them to parse: say [string(s(%#),10)] >>You say, "#2#2#2#2#2#2#2#2#2#2" However, this is sloppy, so one should use the v() function instead of pronouns inside functions: say [v(#)] >>You say, "#2" say [name(%#)] >>I don't see %# here. >>You say, "" say [name(v(#))] >>You say, "wyvern" What can you stick inside of v? Pretty much whatever you could with pronouns: %# ---> [v(#)] %n ---> [v(n)] %va ---> [v(va)] %p ---> [v(p)] %/desc/ ---> [v(desc)] Curly braces ({}'s) are another important part to the parser. By using them, you are telling certain functions and commands to not truncate the string between the two braces. In other words, a string between two braces tells the parser to "keep this together!" Example? Take, for instance, the edit function. It accepts three (and only three) arguments. The first argument is the string to be modified, the second argument is the string within the string that will be taken out, and the third argument is the string that will replace the string that was taken out: [edit(abcdef abcdef,a,x)] ===> xbcdef xbcdef [edit(Wild Wonder!,Wild,Hey Wow)] ===> Hey Wow Wonder! But say we have a string that contains a comma (moo,cow). How do we edit this? If we try to plug it into edit, this will happen: [edit(moo,cow,cow,dog)] ===> Function (EDIT) only expects 3 arguments With four commas, the parser thinks we are trying to feed four arguments to edit() instead of three. This is not allowed! In order to fix the problem, we must use curly braces: [edit({moo,cow},cow,dog)] ==> moo,dog Here are some more examples (try them to see why they don't work!): GOOD BAD RESULT [flip({comma,sentence})] [flip(comma,sentence)] ecnetnes,ammoc [s({%n, %p, and %s})] [s(%n, %p, and %s)] wyvern, his, and he [string({,},10)] [string(,,10)] ,,,,,,,,,, II. E. -- Events: What Makes the World Go Round An event lets you determine when something happens. Sure, you can use the @wait command, or perhaps an action-trigger such as @adesc or @afail... But this only brings your objects to life to a certain degree. If you want an object to perform an action when a user (or certain user, or perhaps even any user on a certain condition or a certain time) says something, or types something, then you are looking at EVENTS. Events fall under two categories: !-events, which trigger a certain command when an objects hears something, and $-events, which do the same when a user types something outside of, inside of, or in the same room as an object. Here are some simple examples of !-events: @create fee >>fee created. drop fee >>Dropped. @va fee=!*hello*fee*:"Hello %n! >>fee - Set. >>fee grows ears and can now hear. say Hello, fee! >>You say, "Hello, fee!" >>fee says, "Hello wyvern!" @vb fee=!*has arrived*:"Welcome %n! >>fee - Set. p bp=Join me, please. >>You paged Ba-Pan with: Join me, please. >>Ba-Pan (bp) page-poses: Ba-Pan nods. >>Ba-Pan has arrived. >>fee says, "Welcome Ba-Pan!" >>Ba-Pan says, "hello, fee!" >>fee says, "Hello Ba-Pan!" $-events work somewhat the same way, but they require that the command be typed. They are generally more common than !-events: @vc fee=$hit fee:pose shrieks in pain! >>fee - Set. hit fee >>fee shrieks in pain! As you can see, in both forms of events, what a user actually types is separated from the actions the object takes by a colon (:). When waiting for something to be typed, an object will use a $-event. When waiting to hear something, an object will use a !-event. By using a semi-colon (;), an object can chain multiple commands together for a single event: @vc fee=$hit fee:pose shrieks in pain!;say go away, %n! >>fee - Set. hit fee >>fee shrieks in pain! >>fee says, "go away, wyvern!" II. F. -- Wildcard Registers: Serving the User As you saw in the previous section, asteriks (*) can be used as "wildcards" for events. What you may not know is that the strings that match these wildcards are stored in temporary registers called WILDCARD REGISTERS. These are named poorly, for MUSE sometimes stores things other than wildcard strings in them. Let's take a close look: @va fee=!* has arrived.*:say Hi, %0! >>fee - Set. p gn=Come here. >>You paged Gnat with: Come here. >>Gnat has arrived. >>fee says, "Hi, Gnat!" As you can tell, when Gnat entered the room, every object in the room was given the message "Gnat has arrived." Since * is a wildcard, this message matches the trigger message "* has arrived.*" The word Gnat, being matched by the first wildcard, gets stuck in the first wildcard register. Since there is no text after the period, nothing matches the final wildcard. However, that is okay and will count as a match; nothing will be stuck in the second wildcard register. If we had three or four asteriks, information would be stuck in the third and fourth wildcard registers appropriately: @desc fee=To request a character, type: request playername=password= realname=email. >>fee - Set. @vz fee=$request *=*=*=*:@pemit %#=You are now registered. >>fee - Set. l fee >>fee >>To request a character, type request playername=password=realname= >>email. request wyvern=yikes!=Chris=chris@vchicago.org >>You are now registered. request wyvern=zoo >>I don't understand. Okay, so what's happening here? The command didn't work the second time because the string didn't match the trigger string. The trigger string was looking for at least four equal signs and found none. The first time, however, the command worked! This is how the data was stored in the wildcard registers: Input String: request wyvern=yikes!=Chris=chris@vchicago.org Match String: request *=*=*=* 1st Register: wyvern 2nd Register: yikes! 3rd Register: Chris 4th Register: chris@vchicago.org Of course, we realize that the command is worthless the way it is right now. After all, it stores the data in these wildcard registers, but it doesn't use any of it! Since the wildcard registers are pretty much emptied out after the command is processed, the whole idea of having the user input data is futile unless we have a way of accessing this data. We do this through ... yes!, the parser: To access Use or Use --------- --- -- --- 1st register %0 [v(0)] 2nd register %1 [v(1)] 3rd register %2 [v(2)] Nth register %N-1 [v(N-1)] 10th register %9 [v(9)] Alas, registers only number up to ten, but ten are enough. Let's try an almost practical example of using registers: @vz fee=$request *=*=*=*:@pemit %#=You are registered as so:%rName:%t%0%r Password:%t%1%rReal Name:%t%2%rEmail:%t%3%r%rThanks! >>fee - Set. l fee >>fee >>To request a character, type request playername=password=realname= >>email. request wyvern=yikes!=Chris=chris@vchicago.org >>You are registered as so: >>Name: wyvern >>Password: yikes! >>Real Name: Chris >>Email: chris@vchicago.org >> >>Thanks! Remember that %t is translated as a tab and %r is translated as a carriage return (newline)! So now we have received data from the user in one form, and then outputted it to the user in a different form. This is very important and useful! Later on, we will be able to store data for later (or different) use. As we enter the next section, keep in mind that for each time a string gets parsed, it has a separate level of wildcard registers! II. G. -- Queued Commands: The Good, the Bad, and the Ugly Most people can handle the parser without a problem. The trouble comes in when people try to handle the parser WITH queued commands. The thing to remember is this: *all* queued commands go through pronoun substitution even before they even hit the command line. Remember this! Let's pursue it a bit deeper... @create fee >>fee created. @va fee=$tell fee *:say %n told me %0! >>fee -- Set. tell fee hello >>fee says, "wyvern told me hello!" Immediately after fee's !-event was triggered, the parser placed the execution string in the QUEUE. The queue is where all indirect commands are placed. The queue sorts out which commands were entered first, and then executes them in the order which it received them (you got it -- FIFO!). The queue knows that fee wants to execute `say %n told me %0' and knows that the enactor's name is "wyvern". It also knows from the command's parse level that %0 is "hello". So, the queue first sends the string through pronoun substitution, THEN it has fee execute the command: In queue: say %n told me %0! Pronoun substitution: say wyvern told me hello! Say command's parser: say wyvern told me hello! Execution: fee says, "wyvern told me hello!" It all seems pretty straight-forward, right? Or maybe not :) At any rate, say we try something different and add another percent sign (%) in front of the pronouns: @va fee=$tell fee *:say %%n told me %%0! >>fee - Set. tell fee hello >>fee says, "wyvern said hello!" This is different than if we were to input the command directly -- if we inputted 'say %%n told me %%0!' directly, the pronouns wouldn't parse, right? Let's examine closely what happens: In queue: say %%n told me %%0! Pronoun substitution: say %n told me %0! Say command's parser: say wyvern told me hello! Execution: fee says, "wyvern told me hello!" If we *really* don't want the pronouns to parse, we're going to have to add more percent signs. The question usually is: how many do we need to add? If we add three, this will happen: In queue: say %%%n told me %%%0! Pronoun substitution: say %n told me %0! Say command's parser: say wyvern told me hello! Execution: fee says, "wyvern told me hello!" Why is this? Remember that all a percent sign does is tell the parser not to parse *the next character*. Therefore, when examining the string %%%n, we see the first percent sign cancels out the second, returning one percent sign, and the second percent sign cancels out the character n, returning the character n. Therefore, the result is: %%%n ===> %% + %n ==(parser)==> % + n ===> %n ==(parser)==> wyvern Do you think four will work? In queue: say %%%%n told me %%%%0! Pronoun substitution: say %%n told me %%0! Say command's parser: say %n told me %0! Execution: fee says, "%n told me %0!" If you think you have a handle on that, you've only just begun to understand the parser. You'll need to watch out for commands that will manage to go through the parser three times, four times, and even more, depending on how you have written a routine. For example, the @foreach command is well-known for this (the lwho function returns a list of dbref#s of people who are currently connected): @va foo=$mywho *:@foreach lwho()=@pemit %#=[v(0)] is online! >>foo - Set. mywho this is wildcard register 1 >>this is wildcard register 1 is online! >>this is wildcard register 1 is online! >>this is wildcard register 1 is online! In queue: @foreach #2 #4 #12=@pemit %#=[v(0)] is online! Pronoun substitution: @foreach #2 #4 #12=@pemit #2=this [...] 1 is online! In queue (*): @pemit #2=this is wildcard register 1 is online! Pronoun substitution: @pemit #2=this is wildcard register 1 is online! Pemit cmd's parser: @pemit #2=this is wildcard register 1 is online! Execution: this is wildcard register 1 is online! [Note that before the command gets to queue, the lwho() is filled in already because it did not have brackets ([]'s) around it and was given as an argument. In the next example, we will put brackets around it for you to see what happens.] [*] Remember that @foreach sticks each of the commands it executes back into the queue. This is why it is parsed twice. Since it is given a list of three dbref#s, it actually sticks the same command in the queue three times. We will only show one command to save space. The problem comes in because we have two separate sets of wildcard registers, one for each level of parsing. The original wildcard register is the one filled in by the wildcard -- register 1 ([v(0)]) holds "this is wildcard register 1". The second set of registers is filled in by @foreach -- this is where each of the dbref#s returned by lwho() are filled in: @va foo=$mywho *:@foreach [lwho()]=@pemit %#=%[v(0)] is online! >>foo - Set. mywho this is wildcard register 1 >>#2 is online! >>#18 is online! >>#100 is online! In queue: @foreach [lwho()]=@pemit %#=%[v(0)] is online! Pronoun substitution: @foreach #2 #4 #14=@pemit #2=[v(0)] is online! In queue: @pemit #2=#2 is online! In queue: @pemit #2=#4 is online! In queue: @pemit #2=#14 is online! Pronoun substitution: @pemit #2=#2 is online! Pronoun substitution: @pemit #2=#4 is online! Pronoun substitution: @pemit #2=#14 is online! Execution: #2 is online! Execution: #4 is online! Execution: #14 is online! ### Hint ### Some commands that parse the thing twice to keep an eye on are @foreach, @wait, @force, and @switch. If you use these commands multiple times, you may have to really pay close attention to parsing! Another pain associated with the queue and commands such as @foreach is the first-in first-out handler it has. You must remember that the queue will execute commands in order it receives them! Take this example: @va foo=$whohere:@pemit %#=--- Who's here? ---;@foreach lcon(here)= @pemit %#=%[name(v(0))] is here!;@pemit %#=--- List done!! --- >>foo - Set. whohere >>--- Who's here? --- >>--- List done!! --- >>Mr.Q is here! >>wyvern is here! Apparently, the "--- List done!! ---" comes second, though it's supposed to display last. There are numerous ways to fix this, but the idea is that you want to make sure the last @pemit enters the queue AFTER @foreach enters all its commands into the queue. This is what is happening now: 1. User executes $-event. 2. $-event's commands are placed in the queue, in order: a. "Who's here?" @pemit is put into queue. b. @foreach is put into queue. c. "List done!!" @pemit is put into queue. 3. "Who's here?" @pemit is parsed and executed. 4. @foreach is parsed and executed. a. @foreach places its "Mr.Q" @pemit into the queue. b. @foreach places its "wyvern" @pemit into the queue. 5. "List done!!" @pemit is parsed and executed. 6. "Mr.Q" @pemit is parsed and executed. 7. "wyvern" @pemit is parsed and executed. What we need to make happen is somehow make something input the last @pemit into the queue. That way, it will be entered *after* @foreach. We could use the @wait command and have it wait a second afterward, but that is sloppy. We could have the command @trigger a different attribute, but that is just as sloppy. The best way is to have fee @force itself to execute the command: @va foo=$whohere:@pemit %#=--- Who's here? ---;@foreach lcon(here)= @pemit %#=%[v(0)] is here!;@force me=@pemit %#=--- List done!! --- >>fee - Set. whohere >>--- Who's here? --- >>Mr.Q is here! >>wyvern is here! >>--- List done!! --- This is what the queue does, now that we've fixed it: 1. User executes $-event. 2. $-event's commands are placed in the queue, in order: a. "Who's here?" @pemit is put into queue. b. @foreach is put into queue. c. @force is put into queue. 3. "Who's here?" @pemit is parsed and executed. 4. @foreach is parsed and executed. a. @foreach places its "Mr.Q" @pemit into the queue. b. @foreach places its "wyvern" @pemit into the queue. 5. @force is parsed and executed. a. @force places its "List done!!" @pemit into the queue. 6. "Mr.Q" @pemit is parsed and executed. 7. "wyvern" @pemit is parsed and executed. 8. "List done!!" @pemit is parsed and executed. %x and [v(x)] ~~~~~~~~~~~~~ Unspoken Truth: [v(0)] and %0 work basically the same. Same with [v(n)] and %N, and so on. There's only one difference: v() will keep things like ;'s, and isn't as parsed as % is. In example: @va foo=say %0 or [v(0)] >>foo - Set. @tri foo/va=yes;yes >>Triggered. >>foo says "yes yes or yes;yes" In this example, you can see that %0 replaces the ; with a space, while [v(0)] leaves it there. There's a wizbug, though, that is very dangerous. In some commands, the parser will separate the ; as a different command. Therefore, if something like the above is parsed as a queue-command that is parsed twice (@force, @wait, @foreach, @switch, etc.), it can result in a bug: @set foo=puppet >>Flag set. @va foo=$test *:@switch [v(0)]=blee,{"I refuse to say blee!},{"I am say ing [v(0)] for %n!} >>foo - Set. test blee >>foo says "I refuse to say blee!" test yakkety >>foo says "I am saying yakkety for wyvern!" test yakkety;"uck. bug. >>foo says "I am saying yakkety" >>foo says "uck. bug. for wyvern!" test yakkety;"uck. bug.; >>foo says "I am saying yakkety >>foo says "uck. bug." >>foo> Huh? (Type "help" for help) Let's take this step by step. The code is meant for the object, foo, to say whatever arguements a player gives to the 'test' command on the object. Except the code tests to see if the arguement is 'blee' first, and tells the player that it doesn't like to say blee. Everything turned out okay, until I started typing things like: ``test yakkety;"uck. bug.'' This command did the following in the queue: a) @switch test yakkety;"uck. bug.=blee,{"I refuse to say blee!},{"I am say ing test yakkety;"uck. bug. for wyvern!} b) "I am saying test yakkety;"uck. bug. for wyvern! c) You say "I am saying test yakkety" d) You say "uck. bug. for wyvern!" Because the ';' separates two different commands when coding objects, and because [v(0)] keeps it there (and because @switch parses twice), the parser thinks there are two commands, and performs them both. The rest is history. ### FIX-UP ### To fix the [v(0)] in the above example, use [s(\[v(0)])] instead of [v(0)]. It will keep all the characters like [v(0)] does, but not parse them into separate commands, causing wizbugs. You can also use [s(%[v(0)])], but that also gets sloppy if the string starts with a pronoun substitution letter such as N (%N) or P (%P). To elaborate more on this bug. If you use [v(0)], it will keep semi colons in the string. If this string with the semi colons is executed, then the MUSE will separate the string into two separate commands. In some cases when the string is something that the user types in and the user types in a semi colon and then a command, then the second command will be executed and be whatever the user types in. Therefore, the user can 'fool' MUSE and your object into executing a command that you may not want it to. By using [s(\[v(0)])], semicolons are kept in but are not parsed into two separate commands. [s(%[v(0)])] will also work, but if arg0 is something like 'Nanaimo' then it will turn up like 'wyvernanaimo' because it thinks the %Nanaimo is %N and anaimo. Puppet Controllers ~~~~~~~~~~~~~~~~~~ If you have a puppet controller that has something like this on it: Va:$puppet *:@force #2222=%0 Now, this will force your puppet, #2222, to perform whatever commands you type after 'puppet'. However, if your controller isn't @luse'd to you, then anybody can control your puppet. Even worse, they can control you! Say I, #2, own a puppet, #2222, and the controller has the code above and is lying on the ground. If itsme came along and typed this: puppet @force #2=@ann I have a wizbug on a controller! The result would be, against my will: wyvern(#2Pv) announces "I have a wizbug on a puppet controller!" I don't even have to be connected! @luse puppet=me. This will disallow other users from using it. Or, even easier: @va control=$puppet *:/[scomp(v(#),owner(me))]/@force #2222=%0 The //'s after the colon: If it parses to be '0', then it won't execute the code. And, as we learned before, to make the puppet nicer: @va control=$puppet *:/[scomp(v(#),owner(me))]/@force #2222=[s(\[v(0))] III. A. -- The string function library: Manipulating Texts and Lists Note: a LIST is a number of different strings in one string, each of them separated by a DELIMITER. The default delimiter is a space. Here are some common string functions that are useful: strlen(_s1_): Returns the length of _s1_. [strlen(123456789)] ==> 9 [strlen(abba dabba)] ==> 10 strcat(_s1_, _s2_): Concatentation of _s1_ and _s2_. [strcat(123,456)] ==> 123456 [strcat(a,b,c)] ==> abc first(_list_): Returns the first item in _list_. [first(one two three)] ==> one [first(a b c)] ==> a rest(_list_): Returns all but the first item in _list_. [rest(#2 #3 #4)] ==> #3 #4 [rest(#1)] ==> mid(_s_, _nstart_, _nchars_): Returns _nchars_ number of characters from the _nstart_th character of string _s_. Characters start at position 0! [mid(anaranjada,2,4)] ==> aran [mid(1 2 3 4,2,1)] ==> 2 [mid(1234,0,3)] ==> 123 delete(_s_, _nstart_, _nchars_): Returns string _s_ after removing _nchars_ number of characters from the _nstart_th character. Characters start at position 0! [delete(anaranjada,2,4)]==> anjada [delete(1 2 3 4,2,1)] ==> 1 3 4 [delete(1234,0,3)] ==> 4 extract(_list_, _nstart_, _nitems_): Returns _nitems_ number of items from list, starting at the _nitems_th item. [extract(The Sears Tower is very tall,2,2)] ==> Tower is [extract(#2 #3 #4,1,2)] ==> #2 #3 [extract(#2 #3 #4,1,0)] ==> remove(_list_, _nstart_, _nitems_): Returns _list_ after removing _nitems_ number of items, starting at the _nstart_th item. [remove(The Sears Tower is very tall,2,2)] ==> The Sears very tall [remove(#2 #3 #4,1,2)] ==> #4 [remove(#2 #3 #4,1,0)] ==> #2 #3 #4 pos(_s1_, _s2_): Returns the position in _s2_ where _s1_ first appears. [pos(ohm,meohmy)] ==> 3 [pos(a,a b c)] ==> 1 wmatch(_list_, _item_): Returns the position of the first _item_ in _list_. [wmatch(#4 #5 #6,#5)] ==> 2 [wmatch(a b c,a)] ==> 1 match(_s1_, _s2_): Returns false if strings don't match, or else the same value as wmatch() if _s2_ matches an item in list _s1_. Wildcards are allowed in _s2_. [match(wyvern,yvern)] ==> 0 [match(gnat,gnat)] ==> 1 [match(gnat,*na*)] ==> 1 [match(foo yay frnkzk,frnkzk)] ==> 3 string(_s_, _n_): Returns _n_ instances of string _s_. [string(-,12)] ==> ------------ [string(abc,4)] ==> abcabcabcabc flip(_s_): Returns _s_ in reverse order. [flip(gnat)] ==> tang [flip(wow!)] ==> !wow ljust(_s_, _n_): Justifies _s_ to the left in a row of _n_ characters. [ljust(left,10)] end! ==> left end! rjust(_s_, _n_): Justifies _s_ to the right in a row of _n_ characters. [rjust(right,10)] end! ==> right end! cjust(_s_, _n_): Centers _s_ in the middle of a row of _n_ characters. [cjust(center,10)] end! ==> center end! commas(_n_): Inserts commas into numbers. [commas(1)] ==> 1 [commas(1000)] ==> 1,000 [commas(123456789)] ==> 123,456,789 foreach(_list_, _routine_[, _in delimiter_[, _out delimiter_]]): runs every item in _list_ through _routine_, and then returns the output. _list_ may be separated by _in delimiter_ rather than a space, and _out delimiter_ will be used to separate the output list. [foreach(a b c,[v(0)])] ==> a b c [foreach(a b c,[v(0)]x)] ==> ax bx cx [foreach(a!b!c,{[v(0)]},!, )] ==> a b c [foreach(a b c,{[v(0)]},,!)] ==> a!b!c [foreach(a!b!c,{[v(0)]},!,x)] ==> axbxc III. B. -- The math function library: Useful with Homework add(_n1_, _n2_): Returns the interger sum of _n1_ and _n2_. [add(1,1)] ==> 2 [add(52,48)] ==> 100 [add(1.4,1.4)] ==> 2 sub(_n1_, _n2_): Returns the interger difference of _n1_ and _n2_. [sub(1,1)] ==> 0 [sub(52,48)] ==> 4 [sub(2.4,1.3)] ==> 1 mul(_n1_, _n2_): Returns the interger product of _n1_ and _n2_. [mul(1,1)] ==> 1 [mul(7,6)] ==> 42 [mul(2.4,2.4)] ==> 4 div(_n1_, _n2_): Returns the dividend of _n1_ and _n2_. [div(42,6)] ==> 7 [div(2.1,1.1)] ==> 2 %%% NOTE: div(), mul(), add(), and sub() only return intergers. %%% If you want to return w/ a floating point, use fdiv(), fmul(), %%% fadd(), and fsub(). They follow the exact same syntax as above. %%% Example: %%% say [fdiv(5,2)] %%% You say "2.500000" eq(_n1_, _n2_): Returns true if _n1_ equals _n2_. [eq(1,1)] ==> 1 [eq(2,1)] ==> 0 lt(_n1_, _n2): Returns true if _n1_ is less than _n2_. [lt(2,1)] ==> 0 [lt(1,2)] ==> 1 [lt(2,2)] ==> 0 gt(_n1_, _n2): Returns true if _n1_ is greater than _n2_. [gt(2,1)] ==> 1 [gt(1,2)] ==> 0 [gt(2,2)] ==> 0 gteq(_n1_, _n2_): Returns true if _n1_ is greater than or equal to _n2_. [gteq(2,1)] ==> 1 [gteq(1,2)] ==> 0 [gteq(2,2)] ==> 1 lteq(_n1_, _n2_): Returns true if _n1_ is less than or equal to _n2_. [lteq(2,1)] ==> 0 [lteq(1,2)] ==> 1 [lteq(2,2)] ==> 1 pi(): Returns an estimate of PI, near 100 digits. [pi()] ==> 3.1416[...] deg2rad(_angle_): Converts _angle_ from degrees to radians. [deg2rad(45)] ==> 0.785398 [deg2rad(0)] ==> 0.000000 rad2deg(_angle_): Converts _angle_ from radians to degrees. [rad2deg(0.785398)] ==> 45.000000 [rad2deg(0)] ==> 0.000000 cos(_angle_): Returns the cosine of _angle_, where _angle_ is given in radians. [cos(pi())] ==> -1.000000 [cos(0)] ==> 1.000000 sin(_angle): Returns the sine of _angle_, where _angle_ is given in radians. [sin(pi())] ==> 0.000000 [sin(0)] ==> 0.000000 tan(_angle_): Returns the tangent of _angle_, where _angle_ is given in radians. Any multiple of pi/2 will return "Inf". [tan(deg2rad(45))] ==> 1.000000 odd(_n1_): Returns true if _n1_ is an odd number. [odd(1)] ==> 1 [odd(2)] ==> 0 [odd(3)] ==> 1 even(_n1_): Returns true if _n1_ is an even number. [even(2)] ==> 1 [even(3)] ==> 0 [even(4)] ==> 1 rand(_n_): Returns a random number, between 0 and _n_-1. [rand(1)] ==> 0 [rand(2)] ==> 0 or 1 [rand(3)] ==> 0 or 1 or 2 abs(_n_): Returns the absolute value of _n_. [abs(-100)] ==> 100 [abs(100)] ==> 100 simp(_n_): Simplifies a floating point number by trimming off excess 0's (and any number after the sixth decimal place). [simp(1.0000)] ==> 1 [simp(1.1234567)] ==> 1.23456 base(_n_): Converts a number from one base to another base. [base(100,10,2)] ==> 1100100 [base(1100100,2,10)] ==> 100 III. C. -- The time function library: For When You Don't Have a Watch xtime(): Returns the number of seconds passed since 1 January, 1970 GMT. [xtime()] ==> 902087629 time(): Returns the current time and date. [time()] ==> Sun Aug 2 15:50:13 1998 xtime(_s_): Returns the number of seconds passed between 1 January, 1970 GMT and the date contained in string _s_, which must be in the same format as time() returns. [xtime(Jun 24 17:00:00 1999)] ==> 930258000 [xtime(time())] ==> 902087629 time(D, _seconds_): Returns the time and date, _seconds_ seconds past 1 January, 1970 GMT, in the enactor's time zone. [time(D,0)] ==> Wed Dec 31 19:00:00 1969 [time(D,xtime())] ==> Sun Aug 2 15:50:13 1998 tms(_seconds_): Formats seconds into a usable time. [tms(120)] ==> 2m [tms(3600)] ==> 1h tml(_seconds_): Formats seconds into a usable time. [tml(86410)] ==> 1d 00:10 [tml(120)] ==> 00:02 [tml(3600)] ==> 01:00 III. D. -- Conditional Statements: What If? Remember that "true" just means a condition results in a number greater or equal to one. "false" means the condition equals 0 (and sometimes #-1). lor(_c1_, _c2_): Returns true if either _c1_ or _c2_ are true. [lor(1,1)] ==> 1 [lor(1,0)] ==> 1 [lor(0,0)] ==> 0 land(_c1_, _c2_): Returns true if both _c1_ and _c2_ are true. [land(1,1)] ==> 1 [land(1,0)] ==> 0 [land(0,0)] ==> 0 lnot(_c1_): Returns false is _c1_ is true; returns true if _c1_ is false. [lnot(1)] ==> 0 [lnot(0)] ==> 1 truth(_s_): Returns false is _s_ is an empty string, 0, or #-1. Otherwise, it returns true. [truth(#-1)] ==> 0 [truth()] ==> 0 [truth(0)] ==> 0 [truth(yay!)] ==> 1 [truth(1)] ==> 1 if(_c_, _true_): If _c_ is true, it will return argument _true_. [if(1,this is true!)] ==> this is true! [if(0,this is true!)] ==> ifelse(_c_, _true_, _false_): If _c_ is true, it will return argument _true_. Otherwise, it will return _false_. [ifelse(1,this is true!,this is false!)] ==> this is true! [ifelse(0,this is true!,this is false!)] ==> this is false! switch(_s_, _s1_, _a1_, _s2_, _a2_, ... _s6_, _a6_, _a_): Will attempt to match _s_ with _s1_ through _s6_. If a match is found, will return the respective _a?_ argument. If no match is found, _a_ will be returned. [switch(1,1,one,2,two,none)] ==> one [switch(2,1,one,2,two,none)] ==> two [switch(0,1,one,2,two,none)] ==> none III. E. -- Database Functions: Detective for Hire Note: Many of these functions require certain powers that normal citizens don't have, especially if you are trying to obtain information regarding objects that (a) don't belong to you and (b) aren't set visible. Note: Oftentimes it is helpful to use the lookup token (*) in database functions. If this is used in front of a player's name, it will return the player's dbref#. Every database function requires the dbref# in place of the player's name if the player isn't in the same room as you. "Me" is substituted with your dbref# and "here" is substituted with its own dbref#. Example: [num(wyvern)] == I don't see wyvern here. ==> [num(*wyvern)] ==============================> #2 quota(_dbref#_): Returns the object-quota limit of a player. [quota(me)] ==> 100 quota_used(_dbref#_): Returns the number of objects owned by a player. [quota_used(me)] ==> 13 idle(_dbref#_): Returns the number of seconds a player has not entered a command (has been "idle"). [idle(me)] ==> 0 [idle(*gn)] ==> 3782 onfor(_dbref#_): Returns the number of seconds a player has been connected. [onfor(me)] ==> 3682 get(_dbref#_, _attribute_): Gets the data from _dbref#_'s _attribute_. [get(me,desc)] ==> You see a short grumpy man. [get(me,lock)] ==> #2 flags(_dbref#_): Returns the flags set on _dbref#_. [flags(me)] ==> Pbcg@ [flags(here)] ==> RJ num(_dbref#_): Returns _dbref#_'s dbref# (yes, this sounds silly, but perhaps you are in the same room as a person and don't know their dbref#?) [num(*wyvern)] ==> #2 [num(*gn)] ==> #4 [num(here)] ==> #10 name(_dbref#_): Returns an object's name. [name(here)] ==> O'Hare Parking Lot [name(me)] ==> wyvern unparse(_dbref#_): Returns an object's name, number, and flags. [unparse(me)] ==> wyvern (#2Pbcg@) [unparse(here)] ==> O'Hare Parking Lot (#10RJ) link(_dbref#_): Returns an object's home. [link(me)] ==> #10 lattr(_dbref#_): Returns a list of attributes that are set on an object. [lattr(me)] ==> Desc Va Vb Lock lattrdef(_dbref#_): Returns a list of defined attributes on an object (usually defined by @defattr). [lattrdef(me)] ==> fun looks pub owner(_dbref#_): Returns the dbref# of the owner of _dbref#_. [owner(me)] ==> #2 [owner(here)] ==> #4566 lcon(_dbref#_): Returns a list the objects _dbref#_ contains. [lcon(here)] ==> #2 #4 #18 lexit(_dbref#_): Returns a list of exits leading out of a room. [lexit(here)] ==> #81 zwho(_dbref#_): Where _dbref#_ is a zone, lists all players (connected and disconnected) in the rooms of the zone. [zwho(#20)] ==> #1 #2 #4 #7 #11 [...] lwho(): Lists all players connected to the server. [lwho()] ==> #2226 #1400 #5948 [...] parents(_dbref#_): Lists all the parents of _dbref#_. [parents(me)] ==> #100 #15 #5 [...] children(_dbref#_): Lists all the children of _dbref#_. [children(#5)] ==> #4 #19 #353 #2 [...] inzone(_dbref#_): Where _dbref#_ is a zone, returns a list of all rooms in the zone. [inzone(#20)] ==> #0 #10 #15 [...] controls(_obj1_, _obj2_, _power_): Returns true if _obj1_ has the power of _power_ over _obj2_; false otherwise. [controls(#2,here,modify)] ==> 1 [controls(#7,here,modify)] ==> 0 haspow(_player_, _power_): Returns true if _player_ has the power of _power_. [haspow(#2,modify)] ==> 1 [haspow(#7,modify)] ==> 0 entrances(_dbref#_): Returns all the entrances leading to a room. [entrances(#10)] ==> #81 #1695 #1890 #2461 class(_player_): Returns the class of a player. [class(#2)] ==> Director [class(#7)] ==> Guide type(_dbref#_): Returns the type of object that _dbref#_ is. [type(#2)] ==> Player [type(here)] ==> Room [type(fee)] ==> Thing [type(#20)] ==> Zone objmem(_dbref#_): Returns the amount of memory _dbref#_ is using up. [objmem(me)] ==> 5257 [objmem(here)] ==> 859 playmem(_dbref#_): Returns the amount of memory a player and all his/her objects use up. [playmem(me)] ==> 25467 nearby(_obj1_, _obj2_): Returns true if _obj1_ and _obj2_ are in the same room, or in the contents of one another. hasflag(_dbref#_, _flag_): Returns true if _dbref#_ has _flag_ set on it. [hasflag(me,c)] ==> 1 [hasflag(me,J)] ==> 0 [hasflag(here,J)] ==> 1 rmatch(_obj1_, _obj2_): Returns the dbref# of any object that matches _obj2_ in _obj1_'s room. This is useful for obtaining the dbref# of an object remotely. [rmatch(me,here)] ==> #10 [rmatch(*ff,here)] ==> #1200 IV. A. -- Making your own functions: The Mad Scientist in You Creating your own functions is an important concept in MUSE. You simply write your own functions, using strings and pre-built functions together in a specially-defined attribute. When you feed an argument to the function, the MUSE parser will stick it into one of the wildcard registers: function(arg1, arg2, arg3, arg4, ..., arg10) argument number wildcard register =========================================== arg1 ==> v(0) arg2 ==> v(1) arg3 ==> v(2) arg4 ==> v(3) arg-N ==> v(N-1) arg10 ==> v(9) Here are some examples: @def me/myfunc=function @myfunc me=Name: [name(v(0))], Number: [num(v(0))], Flags: [flags(v(0))] "[myfunc(me)] >>You say "Name: wyvern, Number: #2, Flags: Pbc" This works because "me" is the first argument to myfunc(). MUSE parser replaces all the v(0)'s with "me" and then parses the function: Function: Name: [name(v(0))], Number: [num(v(0))], Flags: [flags(v(0))] Arg1 : me Parser 1: Name: [name(me)], Number: [num(me)], Flags: [flags(me)] Parser 2: Name: wyvern, Number: #2, Flags: Pbc You can also combine two functions together: @myfunc me=[div(add(v(0),v(1)),v(2))] "[myfunc(4,8,2)] >>You say "6" Function: [div(add(v(0),v(1)),v(2))] Arg1 : 4 Arg2 : 8 Arg3 : 2 Parser 1: [div(add(4,8),2)] Parser 2: [div(12,2)] Parser 3: 6 Functions are easier to store things in, compared to having to use [get(me,va)] all the time: @myfunc me=This is a note to myself. "[myfunc()] >>You say "This is a note to myself." Let's point out a problem with the foreach() function. The foreach() function takes the each item in a list given to it in arg1, and then parses it through a method in arg2: [foreach(one two three,[strcat(v(0),!)])] ==> one! two! three! As you can see, foreach() takes the first item, "one", runs it through the second argument -- [strcat(v(0),!)] -- and then moves on to the next item in the list, "two". foreach() goes through all the items in the list and then spits out the result of all of them passing through arg2. But the question comes in: How do we make it so a user can determine what he wants to add one after each item? The code above adds an exclamation mark after each item. Perhaps sometimes we want to add the letter x, or sometimes we want to add the word "dog". How do we do this? @myfunc me=[foreach(one two three,strcat([v(0)],v(0)))] We would expect that to make the most sense. However, it simply doesn't work. foreach() requires that the second argument be enclosed in brackets if you want it to parse. This situation is called a DOUBLE FOREACH and has been understood by less people than anything else has in the realm of softcode. Most people can't even touch this. The function needs to be written using the s() function: @myfunc me=[foreach(one two three,s(%[strcat(v(0),[v(0)])]))] Function: [foreach(one two three,s(%[strcat(v(0),[v(0)])]))] What it says: For every item in my list (one, two, and three), run it through [strcat(v(0),[v(0)])]. The result will be [strcat(one,[v(0)])], [strcat(two,[v(0)])], and [strcat(three,[v(0)])]. Each of these functions will be parsed through the s() function. The result will be [strcat(one,arg1)], [strcat(two,arg1)] and [strcat(three,arg1)]. To explain this any further would require going even deeper into explaining the parser. It is not really important to understand *exactly* why this work. It is important, however, it understand the syntax for the day when you run into this problem. The syntax is: [foreach(_list_, s(%[_routine_()]))], where _list_ is your list of items to parse through _routine_(), and _routine_() contains v(0) where each item of _list_ will be, and [v(0)] where the arg2 passed to the function will be. Here are two complex examples of this: 1. The fstring() function takes a string and removes all vowels from it. This is my favorite example because it uses a double foreach *twice*: fstring(): [foreach(lnum(wcount(v(0))),s(%[doword(extract([v(0)],add(v(0 ),1),1))]))] doword(): [edit(foreach(lnum(strlen(v(0))),s(%[if(iscon(mid([v(0)],v(0) ,1)),mid([v(0)],v(0),1))])),spc(1),)] iscon(): [ifelse(wmatch(b c d f g h j k l m n p q r s t v w x y z,v(0 )),1,0)] [fstring(foofidy)] ==> ffdy As you can see (or most likely, not), the above function is broken up into separate functions that do separate things. The first function, iscon(), returns 0 if the argument passed is a vowel, else 1. The second function, doword(), takes every character in a word, and then eliminates it if it's a vowel. Then it edits out the spaces left in the word, if there are any after it's been editted out. (Not all MUSEs have an edit()-like function). I could've used edit() to edit out all the vowels, but it wouldn't show a good coding example. The third function, fstring(), takes every word in a string and passes it to doword() for editting, eventually getting all the vowels editted out of a sentence, and then returns it. 2. The random color function inserts random-color ANSI codes before each character in a string. This is another favorite example of mine, because it crashes on most MUSE platforms and spurred me to rewrite the parser on VirtualChicago: rcstr(): [edit(nspc(in(v(0)),~,spc(1))] pop(): [ifelse(scomp(v(0),spc(1)),strcat(|&@|,v(0)),~)] in(): [foreach(lnum(strlen(v(0))),s(%[pop(mid([v(0)],v(0),1))]))] IV. B. -- Parents Parents are objects that hold code and data that are shared with their children, and each child executes its copy of the code independently. @create Parent of Fun >>Parent of Fun(#2222v) created. @lbearing #2222=all >>Locked. @set #2222=haven [You should set parents HAVEN to prevent accidental execution.] >>Flag set. @def #2222/foofcmd=inherit >>Attribute defined. @foofcmd #2222=$@foof *:@emit %N foofs [name(s(*%0))]. >>Parent of Fun - Set. @addparent me=#2222 >>Parent of Fun(#2222Hv) is now a parent of wyvern(#2Pvc). @addparent *foo=#2222 >>Parent of Fun(#2222Hv) is now a parent of foo(#57272Pevc). @foof foo >>wyvern foofs foo. [Now say foo types 'foof wyvern':] @foof wyvern >>foo foofs wyvern. [Back to wyvern.] @delparent me=#2222 >>Parent of Fun(#2222Hv) is no longer a parent of wyvern(#2Pvc). @foof foo >>I don't understand. The marvel of parents is that all their children have the same unique code. If you own a parent, you can decide what sort of code you want to have on other people who are children of your parent. You can change just the parent object, and the code of all the children objects will change. What is even better is that the code on the parent object is the only code that will take up memory -- the code on all the children just refers to the parent, so no extra memory is used up. If you have a project and you want certain rooms in it to perform certain events, you can add them all to one parent and just change the parent rather than editing all the rooms (or copying to code to each of the rooms)! If you want all every exit in your room to be the color red and @locked to people who are children of parent #5, you can just @create a parent, set its @color to "r!" and its @lock to "[is_a(v(#),#5)]" and then @addparent every exit to your parent, rather than typing the @color and @lock for every exit. What is more cool is that you can change the color of all your exits all at once by simply changing the @color attribute on the parent. IV. C. Zones A ZONE is like a parent object for a group of rooms. When a group of rooms are @zlink'd to a zone, they act as if they all have the ZONE as their parent. More importantly, when a group of rooms are @zlink'd to a zone, there are special commands and functions that can be used to gather information from/manipulate all the rooms in the zone. To create a zone object, you use a special command called @zcreate. This creates a zone. Once your zone is created, you @zlink every room you want in the zone to the zone. Special commands (such as @zemit) and functions (inzone() and zwho()) allow a player to keep track of certain rooms as a group. This is a powerful feature because it organizes every room together. What is even more spectacular is that you can create zones within zones (within zones within zones, etc.) to your own desire. Here is an example. Say #3571 is a zone object for a group of rooms that make up a project called the Lambent Building, and #51111 is one of these rooms. @va #3571=$lbwho:@pemit %#=;@pemit %#=[cjust(---Who is in the Lambent Building?---,79)];@pemit %#=;@npemit %#=[spc(1)][ljust(Name:,19)][ljus t(Connected?,13)][ljust(Idle:,8)][ljust(Location:,38)];@foreach [zwho( me)]=@npemit %#=[spc(1)]%[ljust(name(v(0)),19)]%[ljust(switch(hasflag( v(0),c),1,Yes,No),13)]%[ljust(switch(hasflag(v(0),c),1,tms(idle(v(0))) ,-),8)]%[ljust(name(loc(v(0))),38)] @tel #51111 >>Lobby [Lambent Building](#51111RLJf) >>This lobby is bustling with people [...] separate sections. >>Contents: >>Clerk >>Obvious exits: >>in Hallway lbwho >> ---Who is in the Lambent Building?--- >> >> Name: Connected? Idle: Location: >> amante No - Lobby [Lambent Building] >> wyvern Yes 1s Lobby [Lambent Building] >> sparky No - sparky's house >> Dude No - Peanut's And Big-D's Foyer >> Zargon No - Lobby [Lambent Building] NOTE: This command was written a long time ago on MicroMUSE. MicroMUSE does not allow spoofing, and that is why the code is written to stick a space before everyone's name in the list. On more modern MUSEs, such as VirtualChicago, the space before the name is not required for the command to work. V. A. -- MUSE Robots A robot is not something you can program using MUSE softcode (although it is not impossible to do so!), but something that is written using a higher-level programming language from outside of the server. When the robot's program is run, it connects to the MUSE and can interact just as a normal player would interact. Robots can take many forms online -- some are meant for specific purposes, while others are written for the purpose of AI (artificial intelligence). Several robots have come and gone, the most popular being that of the Maas-Neotek. For those who have some experience in both C and MUSE, contact me and I can set you up with a robot player on VirtualChicago, as well as a shell account and simple robot source from which to start. Be warned: this takes some dedication! V. B. -- About the Author Christopher J. VandenBussche ("wyvern") has been involved with TinyMUSE for five years. He founded VirtualChicago in 1994 and has served as a Director ever since. He has written many documents to help users better use the software, and has written many robots. He has built many projects on MicroMUSE, such as wyvern's castle and the MicroTaxis. He has served as a member of his local city's FreeNet steering committee and also worked as a system administrator for a local ISP. He is currently interested in code design and implementation. Chris has also involved himself in writing various works of fiction, short stories, and poetry. He attends the College of William and Mary (the nation's oldest college after Harvard) where he studies biology, chemistry, mathematics, and liberal arts, and works for two newspapers, one of which he is editor. V. C. -- Note from the Author I hope you've liked this manual. I've invested a great deal of time in it because I still feel there is a future for text-based virtual worlds. I've run VirtualChicago (http://www.vchicago.org; server runs at vchicago.org:4201) for four years this month, and feel it offers something different than graphical and avatar-based worlds do. Over the years, many people used TinyMUSE software in an educational way. Due to the fact that TinyMUSE is designed to be social and text-based, proper grammar, spelling, and punctuation have been values and something easily taught in the environment. The coding base also allows for a great deal of complex thought when building and programming objects. It is because of this that I write this document. I hope that a few people will read it over and try to understand all the concepts before they are forgotten. I request that you put them to use and practice them so that others may enjoy your work, and that you should learn from it. V. C. -- Future Notes This is version 2 of the MUSE Programming Guide. If you would like to see anything else in this guide, or point out any mistakes, send email to: chris@vchicago.org This version will most likely not be updated for some time. The next document that will be released will be "wyvern's workbook" -- it will include lessons on how to make specific objects and function routines from the knowledge in this guide.