A Look at Trigger Gates in Mudlet

The Problem

So once again I found myself sitting in the Mudlet #help channel on Discord and someone came in with a question. And the solution to this question is one that comes up fairly frequently, but can be difficult to explain without examples. I resolved the issue on Discord and asked them if I could use the example to reference back to. And here we are.

The Journey

To start with, someone came in and said they needed help matching a block of text from Lost Wishes. And this is the block they needed to parse

 <===============================(Dominates)===============================>
| Dom 1: Necromancer fellow HP: 18915 SP: 36651 LVL: 195 NEED: 21849 KILLS: 0 
| STR: 168 INT: 213 DEX: 202 CON: 187 WIS: 199 CHA: 235
| 
| Dom 2: Black cat HP: 18915 SP: 38021 LVL: 195 NEED: 21150 KILLS: 0 
| STR: 224 INT: 230 DEX: 201 CON: 171 WIS: 227 CHA: 148
| 
| Dom 3: Mindwitness HP: 16200 SP: 31630 LVL: 180 NEED: 14056 KILLS: 0 
| STR: 168 INT: 157 DEX: 217 CON: 219 WIS: 194 CHA: 152
<=========================================================================>

This is a perfect candidate for gated triggers, as you may not know exactly what comes inside the block but you know exactly how it starts, a general pattern for the pieces in the center, and exactly how it ends.

I’m going to walk through the different pieces of this trigger one at a time, but the complete trigger stack can be downloaded as an xml you can paste into the Trigger editor in Mudlet here.

The Parent Trigger

The first trigger we make is the parent trigger or the trigger gate itself. This is the first line in the block, in this case:

<===============================(Dominates)===============================>

We set the fire length of this trigger to 99, because we’re not sure how many lines will be in the center bit. In the code for the trigger we wipe/recreate a table to hold our dominated mobs (dominates). We also delete the line, because we want this parsing to be invisible in the main display.

dominates = {}
deleteLine()
Screenshot of the trigger head. Also shows the overall layout of the triggers when complete.

The Inner Trigger Gate

For this one in particular, we’re going to use a nested trigger gate. Each block of information on a dominated mob is two lines long, so we’ll use a trigger gate to match these. You could get by without these inner triggers being gated but it’s good practice. This new trigger must be dragged on to the first trigger to establish the parent<->child relationship. The icon for the first trigger will become a funnel when this happens. We set a fire length of 1 because we know the next pattern comes on the very next line. Because we’re matching and parsing information out of this line, we’ll use a regex trigger. The pattern in this case is:

\| Dom (?<number>\d+): (?<name>.+) HP: (?<hp>\d+) SP: (?<sp>\d+) LVL: (?<lvl>\d+) NEED: (?<need>\d+) KILLS: (?<kills>\d+)

And the code in the scripting box looks like this:

dominatenum = tonumber(matches.number)
dominates[dominatenum] = {
  name = matches.name,
  hp = tonumber(matches.hp),
  sp = tonumber(matches.sp),
  lvl = tonumber(matches.lvl),
  need = tonumber(matches.need),
  kills = tonumber(matches.kills)
}
deleteLine()

Because it’s the first line in the block, we create a new entry in the dominates table. We use the number provided by the block as the key, and copy in all the information we have obtained.

Inner trigger head.

Inner Trigger Gate Child

This trigger captures the information out of the second block. Once you create it, you drag it on to the previous trigger, as shown in the pictures. We’ll use another regular expression for this trigger, with the pattern of:

\| STR: (?<str>\d+) INT: (?<int>\d+) DEX: (?<dev>\d+) CON: (?<con>\d+) WIS: (?<wis>\d+) CHA: (?<cha>\d+)

And the script will be:

local info = dominates[dominatenum]
info.str = tonumber(matches.str)
info.int = tonumber(matches.int)
info.dex = tonumber(matches.dex)
info.con = tonumber(matches.con)
info.wis = tonumber(matches.wis)
info.cha = tonumber(matches.cha)
dominates[dominatenum] = info
deleteLine()

First we retrieve the information we stored in the previous trigger, then we add the new information to it, and assign it back to the dominates table.

Inner Trigger Gate Child: the second line of info on a dominated mob

Tidying up

We’ve got two more triggers to go. These sit just below the inner trigger gate parent, but beneath the very first Trigger Parent at the very top. The first is to catch the ’empty’ lines between blocks. I went with a regex here as well, just in case. Pattern is:

^\|\s*$

This one just does deleteLine() as there is no useful information to parse.

Closing the gate

The last trigger matches the line at the very end of the block, and is responsible for closing the entire thing up neat and tidy. I used an exact match pattern, mattching:

<=========================================================================>

And with the script:

setTriggerStayOpen("Dom Gate", 0)
replaceLine("Dominated beings scanned\n")

I used replaceLine so it wasn’t -entirely- silent. The setTriggerStayOpen call is important, it causes “Dom Gate” (the very first trigger we created above) to stop checking its child triggers for matches. It’s resetting it for the next time the first line is caught by the trigger gate.

The final trigger in the stack, which closes the outer trigger gate and stops all these inner triggers from being checked.

The Destination

And here we are, having successfully created not one, but two trigger gates. For some people, it helps to think of them as groups of triggers which Mudlet is enabling and disabling for you based on the fire length. We used setTriggerStayOpen to indicate to Mudlet that we wanted to close the trigger gate before those 99 lines were up. You could emulate this to some extent using enableTrigger and disableTrigger, but letting Mudlet do it for you is handier and allows you to do some filtering of what gets passed to the child triggers, as if you check the “only pass matches” check box then rather than opening the child trigger up for checking future lines, it only sends the part of the line which matched the parent trigger on to the child trigger for evaluation. But that’s a dive for another time.

Conclusion

Hopefully you’ve learned a little bit more about Mudlet’s trigger engine and some of the lesser talked about things it can do while following along with this. And hopefully this demonstration has provided you with some idea of when you might want to use a trigger chain. For another example of a trigger chain and when you might want to use one, check out this video from Vadi about capturing data in Mudlet. And in the meantime…

Happy MU*ing!