This tutorial goes deep into the new advanced trigger features provided in
zMUD version 6.20 and later. Before reading this document, you should read
the "Introduction to Triggers" section of the help file included with
zMUD so that you understand basic trigger operations.
Contents
- Trigger States
- Trigger Types
- Sequential Patterns
- Skip Lines
- Wait
- Loop Pattern
- Loop Lines
- Resetting Triggers
- Loop Expression
- Duration
- Within Lines
- Regular Expressions
- ReParse triggers
- Manual States
- Accessing patterns
from previous states
- MXP Triggers
- Summary
In version 6.20, the concept of multiple trigger states and conditions was
introduced. This feature allows you to chain together multiple events in
powerful ways. Some of this functionality could be emulated in previous
versions (such as multi-line triggers), but much of the power is brand new.
Trigger states allow you to group multiple triggers into a single
trigger. At first, this might seem similar to grouping triggers into class
folders. However, there are several important differences:
- Only one trigger state is active for each trigger.
- Trigger states have a sequential order (by default). When one state
fires, the next state becomes active.
- There are several new trigger types that can only be used within a trigger
state.
The first point mentioned above is the most important: Only one state per
trigger is active at any time. Unlike classes, where all triggers within
the class are normally active. This causes trigger states to be fast and
efficient.
Each trigger state has its own properties. Normal options like
"case sensitive" can be set for each trigger state individually.
Simply select the active trigger state on the main tab of the trigger editor,
then switch to the Option tab to change options.
The new State tab in the trigger editor allows you to see all of the trigger
states for that trigger at once. You can drag/drop states to put them in
different order, insert new states, or delete existing states. You can
also modify the pattern and condition for each trigger state.
In past versions of zMUD, there have only been 4 trigger types:
- Pattern: trigger fires when text matching the pattern is received from the
MUD.
- Expression: trigger fires when the expression becomes true
- Command: trigger fires when the command text entered by the player matches
the pattern
- Alarm: trigger fires at a specific time or after a given interval.
In version 6.20 and later, several new trigger types have been added that
work within trigger states:
- Skip: ignore the next N lines from the MUD. Then fire when the
pattern matches the MUD text.
- Wait: wait for a given interval, then fire when the pattern matches the
MUD text.
- Loop Pattern: fire trigger the next N times the pattern matches the MUD
text.
- Loop Lines: fire trigger when pattern matches text within the next N lines
from the MUD.
- Loop Exp: fire trigger for each line from the MUD where the expression
pattern is true.
- Duration: fire trigger for each line from MUD that matches pattern within
the time interval.
- Within Lines: Fire trigger if pattern matches MUD line within N lines of
previous state.
- Manual: Fire trigger when state is activated with the #SET script command.
- MXP: See section below on MXP triggers
Any of these new types, along with the 4 original types, may be freely mixed
as multiple states within a single trigger.
Reading the above descriptions probably doesn't help in understanding how
they are used. So here is an example of each new trigger type.
In each of the following examples, the command line text needed to create the
full trigger will be given. The new #CONDITION command is used to add
additional trigger state conditions to an existing trigger (or the last trigger
defined, by default). You could also use the trigger editor to enter these
examples.
First, let's look at a simple example of a trigger with two states, *both* of
which are Pattern states. The first pattern will be "Zugg", the
second pattern "Hello":
#TRIGGER {Zugg} {#CW high,red}
#COND {Hello} {#CW high,blue}
The #COND command adds a new state to the previously defined trigger.
So now, what happens if the MUD sends the text "Hello"?
Nothing! Remember, only one state is active at a time, and since we just
defined this trigger, the first state is active, so it's looking for the text
"Zugg". This trigger isn't going to do anything until it
receives the "Zugg" text first. Once the MUD sends
"Zugg", then the first trigger state fires, and colors that text
bright red. Then it advances to the next state within the trigger, which
is the Hello state. Now the trigger is waiting for the text
"Hello". If the MUD sends "Zugg" again, it is
ignored. Only when the MUD sends "Hello" does the trigger fire,
coloring the text bright blue, then advancing to the next state.
Since we are at the last state, the trigger wraps around and the first state
becomes enabled again. You can set an option to have the entire trigger
disabled after it successfully matches all states, but by default, the first
state is just reactivated.
So, what did we just create? A trigger that first waits for the text
"Zugg", then waits for the text "Hello". It doesn't
matter how much text the MUD sends between these two patterns. In the old
version of zMUD, we could emulate this behavior with the following code:
#TRIGGER {Zugg} {#T- FirstTrigger;#CW high,red;#T+ SecondTrigger} "FirstTrigger"
#TRIGGER {Hello} {#T- SecondTrigger;#CW high,blue;#T+ FirstTrigger} "SecondTrigger"
The above example uses two triggers in different trigger classes. When
the first trigger fires, it disables itself and activates the second
trigger. When the second trigger fires, it disables itself and activates
the first trigger. But this is more complicated than using the new trigger
states since it requires 2 separate triggers and 2 separate class folders,
cluttering up your script settings.
OK, we will start this example with a normal Pattern trigger. Then
follow it with a Skip state. The trigger will remain dormant until the
initial pattern is received. When this initial pattern is received, the
trigger moves to the next state, which is the Skip step. The Skip step
will ignore the next 5 lines from the MUD, then fire on the next line that
matches the second pattern.
#TRIGGER {Zugg} {#CW high,red}
#COND {Hello} {#CW high,blue} {Skip|Param=5}
Notice the Options field, where we specify a string list of options, giving
the name of the trigger type, followed by the setting for the Trigger Parameter
(Param) option. The Param option is used to give numeric parameters to the
new trigger states. In this case, Param is the number of lines we want to
skip.
Now, with this trigger entered and active, we get a line from the MUD that
says: "Hello". Nothing happens yet. The trigger is still
waiting for the initial pattern "Zugg" to be received from the
MUD. OK, so now the MUD sends the line "Zugg". The initial
trigger fires, and colors this text bright red. The trigger advances to
the next state, so now the Skip pattern is activated. The parameter we
gave to skip was 5, so the next 5 lines are ignored. If the MUD sends
"Zugg", nothing happens...remember, the original pattern is no longer
active...the Skip pattern is active, and we are waiting for 5 lines...that was
the first line.
Next, the MUD sends "Hello". Again, it is ignored because
that was only the second line. The MUD now sends line 3, 4, and 5.
Doesn't matter what text are on these lines, since the trigger is skipping
them. OK, now we have gotten 5 lines from the MUD and ignored them.
So now, as soon as the MUD sends the "Hello" pattern, the Skip state
will fire and color the text bright blue. If the MUD sends different text,
like "Zugg" again, it is ignored. The trigger is waiting for the
"Hello" text. Once the MUD finally sends "Hello", it
is colored bright blue, and the trigger advances to the next state.
Since we were already at the last state, the trigger wraps around back to the
first state. So now the original "Zugg" state is active again.
There is no easy way to emulate this behavior in previous versions of zMUD,
without a very complex script that could count lines from the MUD. It
would have looked something like this:
#TRIGGER {Zugg} {#T- FirstTrigger;#CW high,red;#VAR LineCount 0;#T+ SecondTrigger} "FirstTrigger"
#TRIGGER {(%*)} {#ADD LineCount 1;#IF (@LineCount >= 5) {#T- SecondTrigger;#T+ ThirdTrigger}} "SecondTrigger"
#TRIGGER {Hello} {#T- ThirdTrigger;#CW high,blue;#T+ FirstTrigger} "ThirdTrigger"
What a mess! Not to mention the fact that the (%*) pattern used to
count lines received from the MUD is slow because of the wildcard.
Hopefully you can start to see the power of the new trigger states.
For the "Wait" trigger type, we will look at two examples.
First, an example similar to the last one:
#TRIGGER {Zugg} {#CW high,red}
#COND {Hello} {#CW high,blue} {Wait|Param=5000}
In this case, Param is the number of milliseconds the trigger should wait
before starting to match. So the MUD must first send the text
"Zugg" to fire the first pattern. Now we are on the second
pattern and waiting for 5000 ms, or 5 seconds. Any text received from the
MUD during this 5 seconds is ignored. Once the 5 seconds is up, the
trigger waits to receive the "Hello" text. When it does, it is
colored in bright blue, and we advance to the next trigger state, which wraps
back to the first state.
The above example could be emulated in previous versions like this:
#TRIGGER {Zugg} {#T- FirstTrigger;#CW high,red;#ALARM +5 {#TEMP {Hello} {#T+ FirstTrigger;#CW high,blue}}} "FirstTrigger"
Messy. Also notice that the old versions could only wait for a certain
number of seconds, instead of milliseconds. The #WAIT command might have
been used, but it has lots of side effects.
OK, for the second example, what happens when there is no text pattern for
the Wait state? Here is the example:
#TRIGGER {Zugg} {#CW high,red}
#COND {} {#BEEP} {Wait|Param=5000}
Well, first we wait for the text "Zugg" from the MUD to
arrive. Then the trigger colors the text in bright red, and advances to
the Wait part of the trigger. The trigger waits for 5 seconds. When
it's done, it notices that the pattern is empty, which causes the trigger to
fire immediately, sounding the #BEEP.
In previous versions of zMUD, you would emulate this with:
#TRIGGER {Zugg} {#CW high,red;#ALARM +5 {#BEEP}}
or
#TRIGGER {Zugg} {#CW high,red;#WAIT 5000;#BEEP}
Of course, the #WAIT command had side effects and could pause the reception
of text from the MUD or other trigger processing. The new Wait trigger
state does not have these side effects. So, if you want to issue a series
of commands to the MUD with a delay between them, you could do this:
#ONINPUT {Go} {First Command}
#COND {} {Second Command} {Wait|Param=2000}
#COND {} {Third Command} {Wait|Param=2000}
#COND {} {Fourth Command} {Wait|Param=2000}
Now, this starts with a command input trigger, which fires when you enter the
matching text on the command line. So, when you enter the command
"Go", the text "First Command" is sent to the MUD, followed
by a 2 second delay, then the "Second Command" text, then another 2
second delay, then the "Third Command" text, and so on.
You could do this in previous versions with the #WAIT command, along with its
side effects.
The "Loop Pattern" type fires a trigger a specific number of times
when the pattern matches the text from the MUD. Here's an example:
#TRIGGER {Zugg} {#CW high,red}
#COND {Hello} {#CW high,blue} {LoopPat|Param=5}
Once again, the trigger first waits for the text "Zugg" to come
from the MUD. Then it colors the text in bright red and moves to the next
state. In this case the second state of the trigger is looking for the
text "Hello" from the MUD. Any other text from the MUD is
ignored. When the MUD sends "Hello", it will be colored in
bright blue. But only for the next 5 times the MUD sends
"Hello". When the fifth "Hello" is received from the
MUD, the text is colored blue, but then the trigger state is done, and it
increments to the next state, wrapping around to the first. Now
"Hello" will be ignored again until "Zugg" is received from
the MUD to fire the trigger again.
The "Loop Lines" type will fire the trigger whenever the pattern is
matched during the next N lines. Once N lines have been received, the
trigger state is automatically incremented. For example:
#TRIGGER {Zugg} {#CW high,red}
#COND {Hello} {#CW high,blue} {LoopLines|Param=5}
Once the "Zugg" text is received from the MUD, the trigger will
then look at the next 5 lines from the MUD. If any of those lines contains
the "Hello" text, it will get colored bright blue. Once the 5
lines have been received, it increments to the next trigger state, and wraps
back to the beginning.
So, what happens if the LoopLines pattern is empty? Well, then it will
match any text on the next 5 lines. So, for example:
#TRIGGER {Inventory} {#VAR Inv ""}
#COND {} {#ADDITEM Inv %line} {LoopLines|Param=5}
This trigger would wait until the MUD sent the word
"Inventory". It would then clear out the @Inv variable and then
take the next 5 lines from the MUD, putting each line into the @Inv variable as
a new item in a string list.
What if you wanted to end the previous trigger before the 5 lines were
up? For example, what if you wanted to collect the inventory until a blank
line was received? Well, you can use the new #STATE command to set the
current state of any trigger. Setting a trigger state back to zero resets
the trigger. So, you could do the following:
#TRIGGER InvTrig {Inventory} {#VAR Inv "";#TEMP {^$} {#STATE InvTrig 0}}
#COND {} {#ADDITEM Inv %line} {LoopLines|Param=99}
OK, so there are a couple of new items to explain in the previous
example. First, notice that we have given the trigger a name of "InvTrig".
This is the ID of the trigger and can be used by many other commands to control
the trigger. For example, you can use the trigger ID in a #T- command to
disable a specific trigger. In this case, we use the InvTrig name in the
#STATE command to reset the trigger back to state 0. This is done with a
Temporary trigger that waits until a blank line is received and then resets the
state. The Param=99 gives a limit on the number of lines we will add to
the inventory.
Remember that only one trigger state is active at a time. So, in order
to watch the MUD for a blank line, we needed to create a second trigger.
While the first trigger is adding lines to the @Inv variable, the second
temporary trigger is waiting for a blank line to reset the first one. The
#TEMP command is a good trick for creating a trigger that you just want to fire
once. Notice that we didn't have to use any trigger classes in this
example.
The "Loop Expression" state will fire the trigger for each line
that is received while the Expression given in the Pattern field is true.
Normal Expression triggers only execute when the expression is true when any
variable is set. The Loop Expression type is a way to continue performing
an action while an expression is true. However, keep in mind that with the
Loop Expression type, the expression is only tested when a new line is received
from the MUD.
Consider this difference:
#TRIGGER (@a=1) {It matched!}
As soon as the variable @a becomes 1 (for example, enter "A=1" on
the command line), the trigger will fire. It doesn't need any text from
the MUD to execute. Now try a new Loop Expression trigger:
#TRIGGER (@a=1) {Hello!} "" "LoopExp"
Enter "A=1" on the command line. Notice this trigger doesn't
fire yet. But now, for each line received from the MUD, it will
fire. Set A=0 and now it will stop firing.
The Param for LoopExp gives the number of times the trigger will fire.
So, for example:
#TRIGGER {Zugg} {#CW high,red}
#COND {@a=1} {#BEEP} {LoopExp|Param=2}
This will wait for the "Zugg" text from the MUD. Then, if
@a=1 it will beep for the next 2 lines. If @a isn't equal to 1, the
trigger will reset back to the first state.
The "Duration" state will fire the trigger for each line that is
received which matches the pattern within the given number of
milliseconds. After the given number of milliseconds have expired, it will
automatically increment to the next trigger state. Note that unlike the
Wait type, a line of text must be received from the MUD in order to test the
clock. The trigger will not increment to the next state unless a line is
received from the MUD. For example:
#TRIGGER {Zugg} {#CW high,red}
#COND {Hello} {#CW high,blue} {Dur|Param=5000}
First the trigger waits to receive "Zugg" from the MUD. Then,
for the next 5 seconds, any line that contains the word "Hello" is
colored in bright blue. If the pattern is empty, than any line from the
MUD received in the time interval will cause the trigger to fire.
The "Within Lines" type will fire the trigger if the pattern is
received within the next N lines. For example:
#TRIGGER {Zugg} {#CW high,red}
#COND {Hello} {#CW high,blue} {Within|Param=5}
This will wait for the "Zugg" text from the MUD. Then, if
"Hello" is received within the next 5 lines of text, it will be
colored bright blue. If 5 lines are received without seeing the
"Hello" text, the trigger resets (it does not advance to the next
state).
This is one way to handle a multi-line trigger. For example:
#TRIGGER {Pattern1} {}
#COND {Pattern2} {command} {Within|Param=1}
This would only execute the "command" if the Pattern2 was on the
line immediately following Pattern1. In previous versions of zMUD you
could emulate this with:
#TRIGGER {Pattern1$Pattern2} {command}
Seems simpler, but this old multi-line trigger is much slower to process than
the new Within syntax. Also, the Within syntax allows you to match more
than just two lines. For example:
#TRIGGER {Pattern1} {}
#COND {Pattern2} {} {Within|Param=1}
#COND {Pattern3} {command} {Within|Param=1}
(added in zMUD v6.25)
Instead of using the normal zMUD trigger pattern matching engine, you can
also specify normal unix regular expressions. Mark the trigger condition
with the RegEx type. Then the Pattern field is interpreted as a regular
expression. The following regular expression patterns are supported:
- . match any single character
- * match zero or more of the previous pattern
- + match one or more of the previous pattern
- ? match zero or one of the previous pattern
- (exp) group the regular expression "exp" into a single pattern
- exp1 | exp2 match expression exp1 OR expression exp2. Any number of
expressions can be listed, separated by |
- [abc] match a range of letters. In this case, the letters a, b, or c are
matched. You can specify a range of characters using the - operator. For
example [a-z] matches any lowercase character. Putting ^ before the range
defines a range of characters that are excluded. For example [^a-z] matches
anything *except* a lowercase character.
- ^ matches the beginning of line
- $ matches the end of the line
- \ escapes the next character, matching that character verbatim. For example
\* matches an asterisk. \\ matches a slash itself. Note that to prevent zMUD
variable parsing the @ character must still be escaped with the normal zMUD
escape character (default of ~) rather than the \ character.
- \s a space character (ascii 32)
- \p the | pipe character
- \w a word delimiter (matches \s!"&()*+,-./:;<=>?@[\]^`{|}~)
- \h a hex character (0-9A-F)
Note that unlike normal zMUD triggers, there is no way to
"capture" text from the MUD using regular expression triggers. They
simply define a pattern to match. In order to capture text from the MUD, use
the #CONDITION command to follow the regular expression trigger with a normal
zMUD pattern that can capture the text and mark the condition with the ReParse
type (see below). This will cause zMUD to reprocess the line that matched the regular
expression so that you can capture text from it.
(added in zMUD v6.25)
Sometimes you might want to match a trigger on the same line that has
already been tested. For example, if you match a line using a regular
expression, you then want to parse the line with normal zMUD trigger patterns
to extract text. Or, you might want to split up multiple tests of the
same line into different states.
When a state has a type of ReParse, the same line that matched the previous
trigger state is tested again against the Pattern of the trigger state.
If the pattern matches, then the commands for the state are executed.
However, unlike other states, zMUD increments to the next trigger state in the
list, no matter whether the ReParse state matched or not.
For example:
#TRIGGER {(%w) tells you} {}
#CONDITION {Zugg} {#ADD NumZugg 1} "reparse"
#CONDITION {Darker} {#ADD NumDark 1} "reparse"
This trigger waits until a line of the format "somebody tells
you" is received from the MUD. When this line is received, the
first state matches, and since there are no commands, it just increments to
the next state. The next state is a ReParse state, so it checks the same
"somebody tells you" line and if the word Zugg is anywhere in the
line, it increments the NumZugg variable. Then, regardless of whether
this state matched, zMUD goes to the next state. The next state is
another ReParse state which now checks the same "somebody tells you"
line for the word Darker. If the word Darker appears anywhere in the
line, the NumDark variable is incremented. Then, regardless of whether
Darker appeared in the line, the trigger state is incremented, looping back to
the beginning of the trigger, where is waits for another line from the MUD.
You could create this example without using trigger states with the old
trigger:
#TRIGGER {(%w) tells you} {#IF (%trigger =~ "Zugg") {#ADD NumZugg 1};#IF (trigger =~ "Darker") {#ADD NumDark 1}}
but the new ReParse trigger state is easier and more flexible than this.
You can also use these new trigger states to implement a formal "State
Machine". If you set the state to "Manual", then the
trigger will only execute and advance to the next state when you use the #SET
command in a script. Or you can use the #STATE command to set the next
state position for the trigger. Combinations of #SET and #STATE can be
used to create all sorts of text parsing possibilities.
Also, the #SET command can be used to fire a particular trigger state before
it would normally be executed. Then, when the trigger gets to that state
normally, it will detect that the state has already matched, and skip it.
In normal triggers, the %1..%99 variables are set to the text that matches
any patterns placed within parenthesis. For example:
#TRIGGER {(%d)Hp} {#VAR hp %1}
can be used to save your hp into a variable called @hp.
But %1..%99 only refer to the current pattern of the current state. To
access patterns from previous trigger states, you can use the new %t1..%t99
syntax. These variables match all of the patterns saved by any trigger
state so far. So, for example:
#TRIGGER {Name: (%w)} {}
#COND {Level: (%d)} {char.name=%t1;char.level=%t2} {Within|Param=1}
would save the name and level of your character, but only if the line with
the "Level:" information came right after the line with the
"Name:" information. In previous versions of zMUD, you'd have to
store the Name information into a temporary variable and wait till you got the
Level info. Or, in this case, you'd have to use the old style of
multi-line triggers.
The %t1..%t99 variables are stored in the order that the states are
executed. So, if you are playing with the #STATE command to manually
change state values, you can change the order in which these variables are
filled.
The MXP Trigger type is a special pattern trigger that fires when the given
MXP tag is received from the MUD. You can also list specific arguments for
the tag, and the trigger will only fire when those arguments are received.
The #MXPTRIG command can be used to quickly set a MXP trigger. For
example:
#MXPTRIG {color red} {hello}
will fire whenever the MXP tag <COLOR red> is received. Note that
for color values, the trigger will also match if the MUD specifies a numeric
color value. So, the previous trigger would also fire on <COLOR
#FF0000>.
By default, MXP triggers actually fire when the closing MXP tag is
received. So, in the above example, the trigger actually fires when the
</COLOR> closing tag is received. The %0 parameter is filled with
the text sent by the MUD between the opening and closing tag. So, if the
MUD sent:
<COLOR red>Hello World</COLOR>
The trigger would fire and %0 would contain "Hello World".
If you modify the trigger options and turn off the "Newline" option
and turn on the "Prompt" option, then the MXP trigger will fire on the
initial tag instead of the closing tag. In this special case, you can also
modify the behavior of the MXP tag before it is parsed by zMUD. NOTE: You
can only modify the arguments of OPEN MXP tags. SECURE tags cannot be
modified.
To modify the arguments of an OPEN tag, the %mxp database variable is filled
with the argument data for the tag sent by the MUD. You can change fields
in the %mxp variable to change the tag arguments.
For example, the <COLOR> tag has two arguments: "FORE" for
the foreground color, and "BACK" for the background color. If
you set up a Prompt MXP trigger:
#MXPTRIG {color} {} "" "Prompt|NoCr"
then if the MUD sends the tag <COLOR Fore=Red Back=Blue> then your
trigger will fire and %mxp.fore will be "red" and %mxp.back will be
"blue". If the MUD just sent <COLOR red> then %mxp.fore="red"
and %mxp.back="".
Since the <COLOR> tag is an OPEN MXP tag, you can change it's arguments
by modifying the %mxp fields. For example, let's say that you hate the
color red and want to change all red text to blue. You would use a Prompt
MXP trigger like this:
#MXPTRIG {color red} {%mxp.fore="blue"} "" "Prompt|NoCr"
Whenever the MUD sends <COLOR red>, your trigger will fire. It
will then change %mxp.fore from "red" to "blue". This
changes the tag to <COLOR blue> and causes the following text to be blue
instead of red.
Using Prompt MXP triggers, you can remap the MXP colors sent by the MUD, or
change the fonts set by the MUD. Note that you can only change the
arguments of a tag, not the tag itself. There is no way to remove a
tag. Also, you should avoid using the #SUB or #CW commands in an MXP
trigger...the operation is undefined.
The addition of multiple states to each trigger, and the addition of new
trigger types provides great new power in zMUD scripting. Very complex
scripts can now be reduced to much simpler trigger states. And operations
not possible in previous versions can now be attempted.