Register to post in forums, or Log in to your existing account
 

 Related 
Contents
Triggers
  #ACTION
  #ALARM
  #CONDITION
  #EVENT
  #FIRE
  #MXPTRIG
  #NOINPUT
  #ONINPUT
  #RAISEEVENT
  #REGEX
  #SENDSB
  #SETPROMPT
  #TEMP
  #TRIGGER
  %alarm
  %state
  Create/Modify Settings
  Introduction to Triggers
  Pattern Matching
  Trigger Tester
  Trigger Types
  Triggers
  Triggers
Triggers
  Pattern Matching
  Auto Login
  Trigger Types
  Trigger Tester
  Multistate triggers
Related Links:
  #CONDITION
  #TRIGGER
  Trigger Types
Multistate triggers [[cmud_multistate]] 
Triggers in CMUD can have more than one state. This feature allows you to chain together different events and can be very powerful. You can add new trigger states to a trigger with the #cond command or with the New State option in the Package Editor. The important things to remember when writing multistate triggers are that:

  • Only one state is active for each trigger at any given time.
  • Trigger states have a sequential order (by default). When one state fires, the next state becomes active.
  • Some trigger types are only usable in a sub-state of a trigger (a substate being any state except the first).

The first point above is the most important: Only one state per trigger is active at any time. The other states aren't checked while they're not active.

Each state has its own properties. Options like case sensitivity, regular expression and so on can all be set for each state individually. Simply select the substate from the treeview and you'll be able to edit its options. The only exception is the ID field - the ID of the parent trigger is always used, regardless of which state is currently active.

Trigger types can also be mixed and matched amongst the various states of a trigger - the only rule is that the substate-only types cannot be used for a parent trigger. The following are detailed examples for each trigger type:

Sequential Patterns

This is the simplest kind of multistate trigger. 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.

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. We could emulate this behavior without using a multistate trigger by using 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. And it only gets more complicated the more triggers you try to add, but adding states is easy.

Skip Lines

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. In the GUI, when you select a trigger type from the drop-down box at the bottom, a field will appear for you to enter your parameter.

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.

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. Now we've had 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 before it sends "Hello" (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 without using a multistate trigger. You'd need 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! And imagine if you had four or five Skip states in the same trigger!

Wait

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 without multistate triggers like this:

#TRIGGER {Zugg} {#T- FirstTrigger;#CW high,red;#ALARM +5 {#TEMP {Hello} {#T+ FirstTrigger;#CW high,blue}}} "FirstTrigger"

Messy.

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.

Now that the #wait command is working, this sort of solution is of limited use, except that Wait states don't create new threads. They can be used without fear of causing thread-related problems.

Loop Pattern

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. The next five times the MUD sends "Hello", the text will be colored bright blue. 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.

Loop Lines

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.

Resetting Triggers

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 #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. States are numbered starting from 0, so state 0 is the first state, the "Inventory" state. 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.

Loop Expression

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 maximum 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, or until @a stops being 1, whichever happens first. If @a isn't equal to 1 when the first state matches, the trigger will reset back to the first state.

Duration

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.

Within Lines

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. You could emulate this without using multistate triggers by doing:

#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 very easily. For example:

#TRIGGER {Pattern1} {}
#COND {Pattern2} {} {Within|Param=1}
#COND {Pattern3} {command} {Within|Param=1}

ReParse

Sometimes you might want to match a trigger on the same line that has already been tested. For example, 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, CMUD 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, CMUD 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 ReParse trigger type is easier and more flexible than this.
Viewer Comments [0 - Post your comments]

Jump to:  

© 2009 Zugg Software. Hosted by Wolfpaw.net