http:// www.metamage.com / mush / croaker / chapter6.html

Croaker's TinyMUSH Manual

Last update: December 8, 2004

Author: Croaker

Editor: Josh Juran <wanderer@metamage.com>

URL: <http://www.metamage.com/mush/croaker/>


6. Other triggers and attributes: LISTEN, COST, and CHARGES

This section will discuss two new triggers and the attributes they affect. LISTEN will allow machines to react to what they hear, and COST will allow them to react to being given a certain amount of pennies. Also introduced is a way to limit the number of times an object can be used by using the CHARGES attribute.

6.1. LISTEN and AHEAR

By far one of the most flexible set of attributes for creating machines is the LISTEN and AHEAR pair. These attributes allow the machine to respond to things they hear, such as what players or other machines say or do. This allows a machine to respond to a spoken command, or wait for a message like "blank has arrived."

The LISTEN attribute is set to a string that the machine is to respond to. Almost always this string includes wildcard characters to allow the machine to be triggered by a single key word or phrase in a lengthy string.

Wildcards should be familiar to anyone using DOS or Unix systems. The wildcard "*" matches any number of characters in a string. The string "s*" will match any string beginning with the letter s. The string "*hello*" matches any string where the word "hello" appears, like "croaker says 'hello there!'."

The wildcard "?" will match a single character. The string "*s?t*" matches the string "sit", "he sat", or "his time" (a single space matching as the "?" in this case.) Note that this is very different from "*s*t*" which will match a string like "some time."

LISTEN is set as any other attribute, namely:

@listen object = string

such as:

@listen foo = *kill *

Which will match any occurrence of the word "kill" which is followed by a space (i.e., it would not match the string "shiva killed foo.")

When the string in the LISTEN attribute matches a string the object hears, the action list in the AHEAR attribute is run similar to the way ASUCC is run if an object is picked up. For example:

@ahear foo = "now now, let's not have any violence"

would have the machine foo respond to a string like "let's kill the wizard!" with a calming message.

Example: Using LISTEN and AHEAR

Create a simple machine to run west then east upon hearing the phrase "trigger phrase"

@create simple ahear machine
Created.

@listen simple = *phrase*
Set.

@ahear simple = west; east
Set.

look simple
simple ahear machine(#4284)
You see nothing special.
Listen:*phrase*
Ahear:west; east

"trigger phrase!
You say "trigger phrase!"
simple ahear machine runs like the wind into New Main Street to the west.
simple ahear machine has left.
simple ahear machine has arrived.

now a simple hear-respond machine...

@ahear simple=say I heard the word!
Set.

look simple
simple ahear machine(#4284)
You see nothing special.
Ahear:say I heard the word!
Listen:*word*

"Now I say the word...
You say "Now I say the word..."
simple ahear machine says "I heard the word!"

You'll notice that in the last part of that example that the machine did not go into an infinite feedback loop event though its output contains the phrase to trigger it. This is because the AHEAR action list is only run if a string that was not generated by the machine itself matches the string in the LISTEN attribute. Usually, this is what you want, since otherwise it is very easy to get into a feedback loop where the machine constantly re-triggers itself.

There are two more attributes related to LISTEN: AMHEAR and AAHEAR for those occasions that you want a machine to be able to trigger itself by saying something. AMHEAR will only respond to strings generated by the machine (i.e. the exact opposite of ahear.) AAHEAR will react to all strings, whether generated by itself of something else. These attributes can be used to do things like have the AKILL or ADROP trigger further actions by whispering to itself. There are better ways to do this, however (see the section on extra attributes and the trigger command.)

Example: AMHEAR and AAHEAR

reset the ahear so it won't interfere

@ahear simple =
Set.

@amhear simple = say I heard the word!
Set.

"word
You say "word"

nothing happens, since the object didn't generate it itself, but of we force it to say "word"...

@force #4284 = "word
simple ahear machine says "word"
simple ahear machine says "I heard the word!"
simple ahear machine says "I heard the word!"
. . .

and we get into an infinite feedback loop the only thing to do is to kill, @halt, or @destroy the object

. . .
simple ahear machine says "I heard the word!"
simple ahear machine says "I heard the word!"
simple ahear machine says "I heard the word!"
simple ahear machine says "I heard the word!"
@halt

now reset AMHEAR and do the same thing for AAHEAR

@amhear simple =
Set.

@aahear simple = say I heard the word!
Set.

"word
You say "word"
simple ahear machine says "I heard the word!"
simple ahear machine says "I heard the word!"
simple ahear machine says "I heard the word!"
simple ahear machine says "I heard the word!"
simple ahear machine says "I heard the word!"
simple ahear machine says "I heard the word!"
kill simple = 100
simple ahear machine says "I heard the word!"
You killed simple ahear machine!

Notice both I and it could trigger it off...

6.2. Runaway objects and the @halt command

At this point, it would be a very Good Thing(tm) to explain how to stop one of the aforementioned runaway machines. As can be seen above, one can either kill a runaway, or use the @halt command on it. There are drawbacks to both methods, however. Killing an object will stop what it's currently doing, but will trigger its AKILL attribute. Normally this is fine, but if the AKILL was what sent it into an infinite loop in the first place, killing it will be of no avail. The @halt command, which is simply given without any parameters, will halt all of the objects you own. This means that not only will the malfunctioning machine be stopped, but all of the machines you may have doing their thing will also stop their operations. It does not affect any machines "waiting around" with LISTEN attributes and such, however, just those that are actually doing something at the moment.

To digress for a moment, it may be helpful to explain what happens when an object triggers itself. Suppose half way through its AKILL action list an object whispers a phrase to itself which triggers its AMHEAR action list. The AMHEAR action list is not run immediately, but is placed into a queue of action lists to be run later. Only after the AKILL has finished will the AMHEAR action list be allowed to run (assuming that there was no other action lists queued before the AMHEAR.)

Either killing or @halting an object will remove all the action lists in the object's queue and terminate the currently running action list. Killing the object stops just that object (but, of course, will trigger its AKILL, if any), while @halt will stop all the objects you own.

Note that there are several other ways to stop out of control objects. You can @destroy them, or you can run out of money.

Example: runaway object

Note: don't be an idiot. Never do this on purpose...

@create loop killer
Created.

@akill loop killer = "another iteration!; kill me = 100
Set.

drop loop killer
Dropped.

@link loop killer = here
Home set.

kill loop killer = 100
Halted.
You killed loop killer!
loop killer says "another iteration!"
Halted.
loop killer killed loop killer!
loop killer says "another iteration!"
Halted.
loop killer killed loop killer!
loop killer says "another iteration!"
Halted.
loop killer killed loop killer!
loop killer says "another iteration!"
Halted.
loop killer killed loop killer!
loop killer says "another iteration!"
Halted.
loop killer killed loop killer!
loop killer says "another iteration!"
Halted.
loop killer killed loop killer!
loop killer says "another iteration!"
Halted.
loop killer killed loop killer!

@halt
loop killer says "another iteration!"
Halted.

Note that "Halted." is printed out every time loop kills itself. This is because it is being halted by killing itself. However, the AKILL action list gets run after the object is killed and is thus restarted. The @halt command or waiting for me to go broke were the only two alternatives since killing the thing would only set it off again. I would have eventually run out of money, since I lost 50 pennies every time it killed itself (killing itself cost 100, and it got 50 back.) Overall, running that object cost me about 500 pennies.

6.3. Wildcards and Variables

When a string is matched by the string in the LISTEN attribute, the characters matched by the wildcards are not simply discarded. The sub- strings matched by the wildcards are placed in nine variables, numbered 0 to 9, and can be retrieved by using the function v(). Functions will be discussed later, but for now it is enough to know that putting [v(0)] in a string places the substring matched by the first wildcard in the LISTEN attribute into the action list. The substring matched by the second wildcard can be had by putting a [v(1)] in the AHEAR, and so forth up to [v(9)].

The normal variables in MUD such as %N, which holds the name of the object responsible for triggering the action, can be had in this manner as well, by placing [v(n)] in the string. In fact, [v(n)] is set up for every trigger, not just LISTEN, and so could be used in an ASUCC, OSUCC, SUCC, or similar attribute.

When used in an attribute, the variables 0-9 and N will be replaced with the text they store. This allows the machine to respond to what, specifically, was said or done to set it off, and to know who was responsible for triggering it. The section on functions will give a clearer explanation as to what exactly v() and the [] mean, and how to use them precisely.

Example: Using variables

look simple
simple ahear machine(#4284)
You see nothing special.
Aahear:say I heard the word!
Listen:*word*

@aahear simple =
Set.

@ahear simple = say After word, I heard: %1
Set.

"Now after word it will print this
You say "Now after word it will print this"
simple ahear machine says "After word, I heard: it will print this""

Notice in the last example there was an extra quote. The quote was included since it was part of the string that was matched by the wildcard. Many times you want to eliminate this trailing quote. The next example demonstrates how to do this.

Example: eliminating the trailing quote

@ahear simple = "I heard: %1
Set.

@listen simple = *this*"
Set.

"notice that after this there is no extra quote
You say "notice that after this there is no extra quote"
simple ahear machine says "I heard: there is no extra quote"

and the extra quote is gone

The wildcard ability is very powerful. You can specify a input format and have the LISTEN attribute parse a line that matches. For example, you could have the machine accept three parameters which are separated by, say, commas or colons.

Example: using LISTEN to parse a string

@listen simple = *hear*,*,*
Set.

@ahear simple = say var0 = %0; say var1 = %1; say var2 = %2; say var3 = %3
Set.

"It will hear this, that, and the other thing.
You say "It will hear this, that, and the other thing."
simple ahear machine says "var0 = Croaker says "It will"
simple ahear machine says "var1 = this"
simple ahear machine says "var2 = that"
simple ahear machine says "var3 = and the other thing.""
Here's an application of the AHEAR and LISTEN attributes. I was curious to see how many people (if any) had ventured through my Drawing Room (which is directly off the Room of Junk and Killing.) The object in this example does this. It takes a "photo" of the person who walks through the room. Before making the photo, any old photo of them is destroyed so that the camera won't have duplicate copies of each person. As you can see, it works, and a few people have actually stumbled into my place.

Example Object: surveillance camera

look a survalence
A survalence camera(#863)
This is just an ordinary camera, but it looks a little out of place here.
Feel someone's watching you?
Ahear:@destroy picture of %0;@create picture of %0
Listen:*has arrived*
Carrying:
picture of Croaker(#4487)
picture of Butler(#7495)
picture of Sugar(#6090)
picture of Number_Ten_Ox(#5279)
picture of Khamul(#4782)
picture of spooz(#5702)
picture of Mocker(#4757)
picture of(#4485)

6.4. COST and APAY

The COST and APAY attributes work exactly like LISTEN and AHEAR, except that COST is a number of pennies an object has to be given before it will trigger APAY. This allows vending machines that can charge for the cost of the objects they make (or even be able to make a profit if you set the COST higher than the number of pennies needed to run the machine.)

The COST attribute has to be set to the number of pennies that the object needs to be given for it to trigger APAY. APAY contains the action list that will be run when the machine is given the amount of pennies specified in COST. Attempting to give the machine less than the COST amount will garner the player a snide little error message (and no pennies will be given to the object.) Any excess over the COST amount will be returned to the player as change. This is automatic, so you don't have to worry about programming your machine to handle these situations.

COST also has two other attributes associated with it: PAY and OPAY. These work like SUCC and OSUCC, the player giving pennies will see the message stored in the PAY attribute, and everyone else will see the message in the OPAY attribute. This allows for more flexibility in creating success messages.

Note that the commands @clone, give, and the chown_ok and destroy_ok flags are very useful in creating vending machines.

Example: COST and APAY

@create stuff vendor
Created.

@cost stuff = 10
Set.

@pay stuff = Thank you for buying some stuff.
Set.

@apay stuff = @create stuff; @set stuff = destroy_ok; @adrop stuff =
{:implodes!;@destroy me}; give %N = stuff
Set.

drop stuff vendor
Dropped.

give stuff vendor = 10
You get 0 in change.
Thank you for buying some stuff.

stuff vendor gave you stuff.
look stuff
stuff(#5142d)
You see nothing special.
Adrop::implodes!;@destroy me

drop stuff
Dropped.
stuff implodes!
You get your 10 penny deposit back for stuff.

give stuff vendor = 5
Feeling poor today?

give stuff vendor = 15
You get 5 in change.
Thank you for buying some stuff.
stuff vendor gave you stuff.

6.5. Restricting the number of uses: CHARGES

The CHARGES and RUNOUT attribute pair allow for an item to be used for a certain number of times and then perform some action, like going home or @destroying itself. This can be used to create magic wands that turn to dust and other such limited use items. The CHARGES attribute is set to the number of times an object can be successfully used. A "use" in this case involves triggering any of the AHEAR, ADROP, ASUCC, etc. attributes. Each time the object is used, the CHARGES attribute is reduced by one. If the object is used when the CHARGES attribute equals zero, the action list in the RUNOUT attribute is run. The RUNOUT attribute can do anything, like @destroy the object, force it to go home, whatever. Remember, though, that if the object is to be used again it is going to have to have its CHARGES attribute reset.

Example: CHARGES and RUNOUT

@create foo
Created.

@charges foo = 3
Set.

@runout foo = "Bye now!;@destroy me
Set.

@asucc foo = "I'll run out soon!
Set.

drop foo
Dropped.

get foo
Taken.
foo says "I'll run out soon!"

drop foo
Dropped.

get foo
Taken.
foo says "I'll run out soon!"

drop foo
Dropped.

get foo
Taken.
foo says "I'll run out soon!"

drop foo
Dropped.

get foo
Taken.
foo says "Bye now!"
Halted.
You get your 10 penny deposit back for foo.


Last updated December 10, 2004 by Josh Juran
Metamage Labs