Manual:Technical Manual
Multi Session Gaming
Mudlet lets you play several simultaneous MUD sessions. However, currently we have the restriction that you cannot use the same profile twice. Consequently, if you want to play three characters on the same MUD at the same time, you need to make 3 profiles with 3 different names e.g. [email protected], [email protected] etc.
Split Screen
Mudlet has a split screen. If you scroll up in the MUD text screen (or any other mini console window), the screen will split in two parts. The lower part will follow the MUD output while the upper part will stay focused on your scrolling. This way you can read easier through old passages without missing what is going on at the moment.
Split screen can be activated via the scroll bar, page up / page down keys or the mouse wheel. Scrolling back to the end will close the split screen automatically. A click on the middle mouse button will close the split screen immediately as well as pressing control+return on the keyboard (command+return for mac). The size of the 2 parts of the split screen can be modified by dragging the separator bar in the middle with the mouse. Split screen usage is necessary when selecting text in order to copy it to trigger editor e.g. when making triggers. If you don’t use split screen when selecting text, new arriving text will upset your selection.
Command Line Auto-Completion, Tab-Completion and Command History
Mudlets command line is especially designed for MUD and MUSH playing. It aims at reducing the amount of typing by using autocompletion and tab completion schemes that are optimized for typical MUD playing situations. The command line offers tab completion (TAB key with or without shift) and autocompletion (cursor up and cursor down keys).
Tab completion searches the last 100 lines in the MUD output buffer for words matching the word that you are currently typing. This is useful if you have to type complicated long names. You only have to type the first letters and then press the tab key until the proposal is what you want.
Autocompletion tries to match your current typing with your command history. Example: If you typed the same command a while ago, you only have to start typing the first couple of letters and then press the cursor up key until the proposal matches the command that you want to use.
Command history - if you haven’t started to type anything yet, you can browse through your command history with the cursor up and cursor down keys. However, if you have started typing pressing cursor up will result in a shot at autocompletion.
Escape key - to get out of autocompletion you can press the ESC key any time. After pressing ESC the entire text gets selected and you can overwrite it or get back into command history mode.
Logging Output to text or HTML Log Files
Press on the little button with the blue folder with the green arrow icon on the lower right side of the command line to turn on plain text logging. Click again to stop logging. This will inform you about the file name and path of the log file. If you want to log in color you can chose to log to files in html format, but note that due to the nature of HTML, these log files tend to get very large quickly.
Log files can be found at the mudlet home directory in your profile directory. To get the path to your mudlet home directory you can run a script like this one: <lua> echo( getMudletHomeDir() .. "\n" ) </lua> Log files are stored in "<mudletHomeDir>/logs". Each profile has its own <mudletHomeDir> path. Log files have a similar naming convention to the autosaved profiles: date#time.txt or date#time.html
Exporting and Importing Profiles or Packages
Mudlet supports XML packages that can be imported and exported while playing. You can find a package section on the forum at http://mudlet.sourceforge.net where you can download demo packages or ready made packages for your MUD or upload your own packages to share your work with other players. A package can contain anything ranging from a single trigger to hundreds of button groups, trigger groups aliases - in other words, entire "systems".
Importing Packages
To import a package, open the script editor and click the import icon, select your xml package file and import it. Package files might be compressed to save space. If the file is compressed, you’ll need to uncompress it before importing it. The imported package will be stored permanently in your profile when you safe the profile. If you don’t like the imported package, delete its components manually or simply don’t save the profile. Then the new content will be lost on restart. Good packages will be organized in such a way that they will be easy to update or remove.
Packages are nothing else, but profiles - or parts of profiles that can be exchanged, imported and exported between your own profiles and between different players. From a technical perspective a package and a profile xml file are the same thing.
Exporting Packages
Exporting items or groups is also easy. Select your item or a group containing many items or subgroups and then click on the "Export" button. You’ll be prompted for a file name and that’s it. Package file names should end with ".xml" to make import easier for users on all platforms. You can send your cool trigger, buttons, timers etc. to your friends or use them in another profile of yours. Exporting your good stuff is strongly recommended as it makes MUDding more enjoyable for everybody. Share your knowledge and help others! Mudlet stores your saved profiles as XML files in your home directory under "<mudletHomeDir>/current". To get <mudletHomeDir> run a script like this: <lua> echo( getMudletHomeDir() .. "\n" ) </lua> To export your entire profile e. g. to make a backup on a flash drive, use the "save profile as" button. You can export packages of a trigger branch, alias branch, function key, scripts etc. by clicking on a folder in the tree of the particular item in question and then click on the export icon. You’ll be asked to give a filename for your package. You can export arbitrarily complex structures by moving them into a folder that e.g. is named after the package you’d like to make.
Sharing your System with others - Making complex Packages
If you have written a nice set of triggers, buttons, scripts etc. or maybe even a fully fledged "system" of some sort and you want to share it with others, you can make nice packages that other people can import with a single mouse click. The recommended procedure to make a large package that contains groups of all sorts of items e.g. trigger groups, individual triggers, scripts, buttons, aliases etc., is to export the entire profile with "save profile as". Then create a new empty profile e.g. with your package name. Don’t give a server or port name in the connection dialog and "connect". Mudlet will load the new empty profile although you are offline. Open the script editor and import your previously exported profile and then delete all items that are not part of the package that you are making. Finally, make a folder (group) that is named after your package name and version number e.g. "Johnny’s curing system 12-3-2009" and move all respective items into this folder and repeat the same procedure for all triggers/aliases/scripts etc.. This will make it much easier for other people to import your package and stay updated if you release a newer version. Then save your profile and use the "save profile as" button to create your package.
Using Variables in Mudlet
One of the major design goals in Mudlet was to integrate scripts and variables into triggers/aliases/buttons etc. as seamlessly as possible. The usage of variables in Mudlet is distinctly different from the other major clients, as native Lua scripting has been integrated into Mudlet from the ground up. As scripts are so closely intertwined with the core of Mudlet, variables do not need any special treatment as in other clients i.e. there is no need for code such as: <lua> totalKills = getVariable("killedMonsters") + getVariable("killedVillains") echo( "kills=" .. totalKills ) </lua> In Mudlet, the above code translates into: <lua> totalKills = killedMonsters + killedVillains echo( "kills=" .. totalKills ) </lua> If you define a variable in any given script in Mudlet, be it a trigger, a button, an alias key, an event handler, a free function, etc. It can be used from within any context without any special getVariable() type of function or any special variable symbols, such as @myVar etc.. In Mudlet all variables are native Lua variables. Each session (= profile/connection tab) runs in its own dedicated Lua interpreter. Consequently, all scripts of a profile are compiled into the same Lua interpreter and thus their code runs in the same variable space. All scripts from another simultaneously opened profile will not interfere because each profile uses its own Lua interpreter. Note Everything shares the same variables & functions.
To give you an example: Let’s make a little trigger that counts how many monsters you have killed. Each time you have made a new kill, the trigger matches on the kill message and runs a little script that increases the amount of kills by one each time the trigger fires - in other words, each time you kill a monster and the kill message is sent from the MUD. For the kill counter we declare a variable and call it myKills. This variable is going to hold the number of kills we’ve made so far. The script will look like this: <lua> myKills = myKills + 1 </lua> Then we add a little alias, a button or a keybindings that executes another little script that prints your current kill statistics on the screen. The attached script would look like this: <lua> echo( "I have killed " .. myKills .. " monsters today." ) </lua>
Lua variables can be either a string or a number. They are whatever there were initially initialized with or what data type they can be converted to. <lua> a = "Jim" b = "Tom" c = 350 d = 1
--Then you can write:
e = c + d and e will equal 351 e = a .. b and e will equal "JimTom" note that you cant use a+b to concatenate string values. For this you must use .. e = a .. c and e will equal "Jim350" </lua> There is another form of variables in Lua called tables which can be used for lists, arrays or dictionaries. This is explained later. For an in-depth coverage of variables in Lua take a look at a Lua tutorial e. g. this one on numbers http://Lua-users.org/wiki/NumbersTutorial and this one on strings http://Lua-users.org/wiki/StringsTutorial or this one on Lua tables http://Lua-users.org/wiki/TablesTutorial
Let’s get back to our example. The trigger script expects myKills to be a number so you have to initialze the variable myKills to 0 before running the script for the first time. The best place to initialize variables is in script script outside of a function definition as this code is executed when the session is loaded or if you compile the script again after you have edited it. To do this click on the "Scripts" icon and select the standard script "My Global Variable Definitions". If you are using an older version of Mudlet or a profile that doesn’t have this script item, simply click on the "Add" icon and make your own script. You can call it whatever you want to. Add following code to initialize your new variable myKills to a number and set its value to 0: <lua> myKills = 0 </lua> Whenever you edit this script, it will be recompiled and the code will be run as it is not part of a function definition. This is the major difference between trigger-scripts, alias-scripts etc. and script-scripts. Script-scripts can contain an unlimited amount of function definitions and also free code i. e. code outside of a function definition - code that is not enclosed by function xyz() …. end. On saving the script the script gets compiled and the free code is run instantly. All other item scripts, i. e. trigger-scripts etc., secretly define a function name for the script and thus the script is not free code, but function code. When a trigger fires Mudlet calls this invisible function name to run the script e.g. trigger738(), button82(), alias8(). This means that if you define variables inside a trigger script the variable will not be defined before the trigger runs for the first time. However, if you define this variable as free code in a script-script the definition becomes available immediately on script save. Now, whenever you add new variables to your variable definition script, the script gets run and your old variables will be reinitialized and reset to 0. This will be no big problem in most cases as you won’t work on your systems while really playing in most cases. To solve this problem you have two options:
First option: Add a script group (a folder) and add a new script item for each variable you define. This way, editing a variable definition will only reset the edited variable and none of the others that are defined in different scripts. This has the added advantage that you have a nice graphical overview of your defined variables. Note Organize your variables
Second option (more advanced): Change the variable initialization to only initialize the variable if it hasn’t been initialized before, thus keeping the values of previously defined variables. This would look like this: <lua> if myKills == nil then -- this code initializes the variable myKills to the number 0 if it hasn't been initialed before
myKills = 0
end
</lua>
In Lua all undefined variables are initialized to the value nil. The value nil is not the same thing as the number 0 or the empty string "". What it means is that a variable that has the value nil has not been declared yet and does not exist. If a variable does not exist, it cannot be used as its value is undefined at this point. Consequently, increasing the value of nil by one is impossible as the variable doesn’t exist yet and will lead to a Lua error. The above script simply checks if the variable myKills has been defined yet and if it hasn’t, it will declare the variable and set its value to 0. === Lists Having variables that hold a single value is the most important usage of variables, but very often you’ll like to define variables that hold a list of values e. g. a list of your enemies, a list the items you are currently carrying etc.. To define a variable to be a list you need to declare it to be a Lua table. Let’s declare the variable myEnemies as a list containing the names of your enemies:
<lua>
myEnemies = {}
</lua>
You can now add new enemies to this list by calling the Lua function table.insert( listName, item )
e.g.
<lua>
table.insert( myEnemies, "Tom" )
table.insert( myEnemies, "Jim" )
</lua>
To print the contents of your enemy list on the screen you can run this script
<lua>
listPrint( myEnemies )
</lua>
Now let’s make a little alias that adds a new name to your enemy list when you type "add enemy " followed by the name of the enemy e. g. "add enemy Peter" Open the alias editor by clicking on the alias icon. Click on the "Add" icon to add a new alias. Choose any name you like for your alias e.g. "add new enemy" and then define following pattern for the alias: ^add enemy (.*) Then add this little script in the script editor below:
<lua>
table.insert( myEnemies, matches[2] )
echo( "Added a new enemy:" .. matches[2] .. "\n" )
</lua>
Save the alias and try. Alias are explained in detail below. Another way to declare a list is to define its values directly.
<lua>
myEnemies = { "Peter", "Jim", "Carl", "John" }
</lua>
To remove an item from the list you can use the function listRemove( listName, item )
.
Saving Variable Values to Disc (Stub)
Having statistics scripts that last as long as the session lasts is a nice thing, but it makes more sense to write the variables to disc and reload them when you play the next time. To do this you have to save your variables. Mudlet has 2 ways to implement variable persistence. First, you can tell Mudlet to save all of your variables on exit automatically and ask Mudlet to automatically restore them when the session gets reloaded the next time you play on this profile. Second, you can take care of saving your variables yourself and reloading them yourself. This gives you more control and will be the preferred solution in bigger systems.
To be continued …
React on input - Mudlets Alias Engine
Note: A screencast is available on getting started with aliases - watch it!
Mudlet has a feature called "aliases" that allows you to set up certain phrases or commands that will automatically trigger certain actions or responses. These actions are defined using a specific type of language called "Perl regex expressions." Aliases only work on the input that the user types into the command line, while a separate feature called "triggers" works on the output that the game sends back. When you type a command and press enter, it is sent to the alias feature to see if it matches any of the phrases or commands that have been set up. If it does match, the alias will take over and either send a different command to the game or run a script (a set of instructions) that you have defined. This system is powerful because it gives you control over what happens when a certain phrase or command is entered. You can even change the user's input as it is being processed, allowing you to manipulate the command before it is sent to the game.
The example in the diagram above shows 2 matching aliases, but only one of them sends commands to the game - and only if the player is healthy enough to attack the opponent. The other alias that matched the user input (enemy) choses a fitting opponent and sets the variable enemy accordingly, otherwise it issues a warning that attacking one of the available enemies would be too dangerous.
For an alias to match the command text the same rules as explained above in the trigger section apply. However, to simplify matters there is only one alias type. As alias are not performance critical we could reduce the amount of trigger types to just Perl regex as this type can do it all and performance is no issue with alias as the amount of data is much less. Even if you type like a madman you’ll never get close to sending the same amount of text to the game than the amount of text the game sends back to you.
What does it mean that a regex is true or "matched"? A trigger or an alias fires - or executes its commands/script - when the text matches the pattern of the trigger or alias. In most cases this means that the text contains the trigger/alias pattern. If the regex pattern is reen
then a text "The green house" will match because "reen" is contained in the text. More complex regex patterns can also hold information on where in the text a certain pattern must occur in order to match. ^tj
only matches when the letters "tj" occur at the beginning of the text. Consequently, a text like "go tj" would not match. Regex patterns can also capture data like numbers, sequences of letters, words etc. at certain positions in the text. This is very useful for game related scripting and this is why it is explained below.
Let’s get back to alias. We start with a simple example.
We want Mudlet to send "put weapon in bag" whenever we type "pwb". Consequently, the pattern is pwb
and as the task is so simple it’s enough to enter "put weapon in bag" in the send field. Then we click on save to save the changes and activate the alias by clicking on the padlock icon. Then we leave the trigger editor and test our new alias. After typing "pwb" and pressing return Mudlet will send the command "put weapon in bag" to the game.
Let’s move on to a more complicated example that is needed very often.
We want our script to automatically put the weapon in the correct bag as we have many bags and many weapons. The pattern stays the same. ^pwb The ^ at the beginning of the line means that the command starts with pwd and no other letter in front of this. If we define our pattern more clearly, the pattern will match less often. Without the ^ the alias will match and the alias script will always be run whenever there is the sequence of letters "pwb" in your commands. This may not always be what you want. This is why it’s usually a good idea to make the pattern definition as exact as needed by being less general. The more general the pattern, the more often it will match.
Back to our task: The pattern is ^pwb
. Let’s assume that we have defined 2 variables in some other script. The variable "weapon" is the weapon we use and the variable "bag" is the name of the bag. NOTE: In Mudlet global variables can be accessed anywhere from within Mudlet scripts - no matter if they have been defined in a trigger script, in an alias script or in a key or button script. As soon as it’s been defined it somewhere it is usable. To make sure that a variable is local only, i. e. cannot be referenced from other scripts, you have to use the keyword local in front of your variable definition. Back to our alias: Pattern is: ^pwb
Script is:
send(f"put {weapon} in {bag}")
Depending on the values of our variables weapon and bag the command "pwb" will be substituted with an appropriate command. To set your weapon and bag variables we use 2 more aliases: Alias to set the weapon: uw (\w)+
Script:
weapon = matches[2]
send( "wield " .. weapon )
To set our bag variable: Pattern:^set bag (.*)
bag = matches[2]
Now let’s go back to our initial problem. We want an alias to put our current weapon into our current bag. But what happens if we are in the middle of a fight and absolutely need to sip a healing potions because we are close to death and cannot take the risk that the bag may be too full to take the weapon? We want to upgrade out little alias to take into account that the bag may be full and chose an empty bag instead. To do this we set up a trigger that detects messages that indicate that the attempt to put the weapon in the bag failed. In this trigger we execute this little bag-is-full-detection-trigger Trigger Pattern: (type substring) Your bag is full.
Script:
bagIsFull = true;
This detection trigger will set the variable bagIsFull
to true as soon as it sees the message "Your bag is full.". Then you know that you have to use your spare bag to take the weapon.
Now we have the tools to write an improved version of our little alias script:
if bagIsFull then
send(f"put {weapon} in {spareBag}")
else
send(f"put {weapon} in {bag}")
end
The next example is one of the most common aliases a tell alias: Pattern:^tj (.*)
Script:
send("tell Jane " .. matches[2])
Sadly, Jane is your fiancée and the one thing she is a vegetarian and absolutely hates all words that relate to meat. Luckily, you know enough about aliases by now to make her believe that you’d never ever even think about meat. So you head to your global function script (any script item will do as long as you define your variables outside of your function definitions. See the scripts chapter below for more information. In your script "my global functions" you add a Lua table containing a list of all of all words that a vegetarian might hate. For example:
happyJaneTable = {"meat", "burger", "steak", "hamburger", "chickenburger"}
Now you can upgrade your tell-jane script to automatically search our tell for all words that Jane might not like. In case such a word is found we substitute the entire tell with "How is the weather?".
for key, value in ipairs(happyJaneTable) do -- looking at each element of the list
badWord = happyJaneTable[key] -- check out the Lua table chapter below for more info
begin, end = string.find(command, badWord) -- begin holds the start position of the word, end* the end-position
if begin ~= nil then -- we have found a bad word
send("tell Jane How is the weather?")
return
end
end
Alias Examples
Alias:
^cc( .+)?
Script:
local target = matches[2] or ""
send("c cure critical" .. target)
Default keybindings in the main window
Keypress | Action |
Left Arrow | Moves the cursor one character to the left. |
Shift+Left Arrow | Moves and selects text one character to the left. |
Right Arrow | Moves the cursor one character to the right. |
Shift+Right Arrow | Moves and selects text one character to the right. |
Home | Moves the cursor to the beginning of the line. |
End | Moves the cursor to the end of the line. |
Backspace | Deletes the character to the left of the cursor. |
Ctrl+Backspace | Deletes the word to the left of the cursor. |
Delete | Deletes the character to the right of the cursor. |
Ctrl+Delete | Deletes the word to the right of the cursor. |
Ctrl+A | Select all. |
Ctrl+C | Copies the selected text to the clipboard. |
Ctrl+Insert | Copies the selected text to the clipboard. |
Ctrl+K | Deletes to the end of the line. |
Ctrl+V | Pastes the clipboard text into line edit. |
Shift+Insert | Pastes the clipboard text into line edit. |
Ctrl+X | Deletes the selected text and copies it to the clipboard. |
Shift+Delete | Deletes the selected text and copies it to the clipboard. |
Ctrl+Z | Undoes the last operation. |
Ctrl+Y | Redoes the last undone operation. |
Ctrl+<Click> | Select the whole line. |
Ctrl+Enter | Close split screen (scroll all the way to the bottom). |
Ctrl+F | Search for text in the main output window. |
Considerations
- Mudlet 4.17.2 currently does not support named capture groups for aliases.
- You will need to use
matches[1]
,matches[2]
etc. instead.
- You will need to use
- You can use modifiers for these regexes. Declare these at the start, in the form
(?modifiers)
- For example, to match the command
test
, ignoring case, you would need to declare the regex as being case-insensitive, like so:(?i)^test$
- For example, to match the command
Trigger Engine
Unlike alias that define patterns that match on user input, triggers define patterns that match on game output. In other words, triggers react to text that has been sent by the game, whereas alias react to user commands that have been typed into the command line.
Simple Trigger Matching
Note: QUICKSTART: Here is a simple video tutorial on how to make basic triggers in Mudlet.
Steps To Create A Trigger
- Click on the "Add" button to create a new trigger.
- Put the text pattern that you’d like to trigger on into the trigger conditions table on the right side of the screen above the script editor.
- Choose the correct pattern type of your trigger pattern in the drop down list on the right side of the trigger pattern i.e. in the second column of the trigger pattern table. If you define multiple patterns in the same trigger, your trigger will run whenever any one of these patterns matches unless you chose the AND-trigger option, in which case the trigger will only fire if all patterns match within a specified amount of lines from the game. For more advanced information on AND and OR trigger see the corresponding AND/OR trigger section below.
- Define what happens when the trigger matches. Usually, this will be done by a Lua script in the Lua editor below the table with the pattern list. In the beginning, you can simply choose the "highlight trigger" option to make your trigger highlight the text that it has triggered on or send a clear text command to the game whenever the trigger fires until you have learned enough Lua to more meaningful scripts. Clear text command can be defined in the "send plain text" input box next to the trigger name above the pattern table.
- Save the new trigger and then activate it with the padlock icon button. By default, new triggers are deactivated and thus will do nothing unless you explicitly activate them. Activated triggers show a green tick in the little blue box on the left side of the trigger name in the trigger tree on the left side of the script editor dialog.
There are three ways to save your changes; click on the save icon, adding another new trigger, clicking on another trigger item. Triggers that have not been saved yet cannot be activated.
Note: If you see a bug icon instead of an activation box, there is some error that prevents activation of your trigger. Check the error message above the pattern table.
Example: You want to trigger on the word "pond" in a line such as: "The frog swims in the pond. Plop." All you need to do is add "pond" as a pattern and choose "substring" as a pattern type. Then enter the command you like to send to the game if the trigger matches. For example enter "drink water" into the "send plain text" input box and activate your new trigger. Your new trigger will send the command "drink water" to the game any time it sees the word "pond" somewhere in the game output.
Simple Highlighter Triggers
Note: Beginners should use Mudlets automated highlight triggers in the beginning to get the hang of the different trigger and pattern types quicker.
- Create a new trigger and add a pattern as detailed above.
- Click on the "highlight trigger" option and pick a foreground and a background color. When the trigger matches it automatically highlights its pattern.
- Save.
This is one of the most used form of triggers in mudding. It is a quick way of highlighting words that are important to you at the moment. This helps you spot things at a single glance instead of reading the entire text. You don’t have to know anything about scripting, regular expressions etc. to use highlight triggers. Just type in the word you like to be highlighted, select appropriate colors, save the new trigger and activate it.
More advanced users will often want to do custom highlighting from within scripts. This is how it works: If you want to highlight the word "pond" in the above example you have to add the following little Lua script to the script editor on the lower right side of the Script Editor window:
selectString( "pond", 1 )
fg( "red" )
bg( "blue" )
resetFormat()
Testing Triggers
You can test your triggers with the `echo
alias from the command line. This requires the echo package, which is installed by default on a new profile.
`echo A test line.
`echo One line.$Next line.$Third line.
Or from your Lua code using feedTriggers
feedTriggers([[
You are carrying:
a credstick a pair of steel toed boots (worn)
a Soko Sukeban (worn) a medikit
a cherry moon katana a GPS device
a NV contacts a Tsunami MkII
a MeshGirl Helm an Alto 500 CellPhone
a Kantan Sukeban (worn)
Your load barely slows you down.
]])
Alternatively, you can also record a replay and play it - triggers will be matched on it.
Pattern Matching Types
Substring
This trigger type will match any text on a single line. Useful for matching strings of text which don't change at all.
Examples:
Pattern: | pond |
String: | The frog swims in the pond. |
Outcome: | match (pond matches the word pond) |
Pattern: | pond |
String: | The frog swims in the swamp. |
Outcome: | no match (pond does not match swamp) |
Perl Regex
This trigger type will match text using Regular Expressions (Regex). Regex in Perl is a special text string for describing a search pattern within a given text. For example \w+ is used to match a single word, or \d is used to match a single digit.
Pattern matching also allows the match to be saved in the matches table variable if enclosed in parentheses. matches[1] contains the entire line of the trigger whereas matches[2] contains the first regex pattern (in our examples pond or swamp).
Examples:
Pattern: | The frog swims in the (\w+). |
String: | The frog swims in the pond. |
Outcome: | match (\w+ matches pond, and is also saved to matches[2] variable) |
Pattern: | The frog swims in the (\w+). |
String: | The frog swims in the swamp. |
Outcome: | match (\w+ matches swamp) |
Pattern: | The frog swims in the (\w+). |
String: | The fish swims in the pond. |
Outcome: | no match (fish should be changed to a regex pattern \w+) |
Note: regex101.com is highly recommended to testing out your regular expressions. It includes a pattern tester, cheat sheet and explanations about the matches.
Start of Line
This is a combined variation of substring matching and regex matching. This type will match any line with text at the start of the string only.
Examples:
Pattern: | The frog |
String: | The frog swims in the pond. |
Outcome: | match (The frog is at the start of the string) |
Pattern: | frog swims |
String: | The frog swims in the swamp. |
Outcome: | no match (frog swims is not at the start of the string) |
Note: This is exactly the same as using a Perl regex pattern with the ^ anchor (start of a line).
Exact Match
The string must match exactly the pattern. On a whole line, this is the same as using a perl regex pattern with the ^ and $ (start and end of line) anchors.
Examples:
Pattern: | The frog swims in the pond. |
String: | The frog swims in the pond. |
Outcome: | match (the entire line matches) |
Pattern: | The frog swims in the pond. |
String: | The frog swims in the pond. The bird flies in the sky. |
Outcome: | no match (The entire line does not match as the bird information has been added. This would match a start of line pattern though) |
Lua Function
The purpose of Lua function triggers is to be able to run Lua scripts as trigger conditions. This trigger types takes an actual Lua function as the pattern. This function must return true or false to evaluate correctly, but will only match when returned true.
For example, let's say we wish to enable our entire offensive combat triggers when we commence combat, but otherwise be disabled when we are not in combat. One would create a trigger chain that contains all the offensive triggers. The chain head (the root trigger of the chain) simply contains this Lua function condition as its only trigger condition: return inCombat
Now, whenever you enter combat you set the variable "inCombat" to true and thus automatically enable you entire combat triggers that are otherwise disabled.
Examples of patterns:
if myVar > 20 then return true else return false end
if myFunc() < myVar then return true else return false end
return myFunc()
if myFunc returns a boolean value
Another example:
Create a new trigger with the pattern checkHealth()
. Define a new Lua function checkHealth()
. Open the script editor, add a new script "health monitor" and add this code to the new script.
function checkHealth()
if health <= 100 then -- health is a variable we capture from e.g. the prompt or GMCP
echo( "WARNING: Low health! I have to run away!\n" )
send( "flee" ) -- we send a command to run away to the game
return true
else
return false
end
end
Lua Syntax Primer: Expressions
Note: Mudlet used to only allow the boolean true value as the return value to consider the pattern to have been a match. It has since been improved to allow any value Lua would consider true - but if you see any Lua Function Conditions (LFC) making use of and true or false at the end, that'd be why.
if A == B then (...) ----> if A equals B
if A ~= B then (...) ----> if A *NOT* equals B
if A <= B then (...) ----> if A is smaller or equal to B
if A >= B then (...) ----> if A is greater or equal to B
if A < B then (...) ----> if A is smaller than B
if A > B then (...) ----> if A is greater than B
The operators and and or behave differently in Lua than in most other programming languages as they are not guaranteed to return a boolean value.[1]
Lua Function Conditions (LFC) effectively means that you run the Lua code they represent on every single line that is received from the game, unless the LFCs are part of a multi-condition trigger, in which case the LFCs would only be run when they would be the current test-condition of their respective trigger state. LFCs are implemented in such a way that the trigger engine only calls the compiled byte code, but running as few scripts as possible is always the best idea. LFCs are nice and handy, and for most people the performance aspect will not be relevant at all.
Line Spacer
To make the engine skip lines in a multiline / AND trigger, use the line spacer pattern: select 'line spacer' as the type and put in a number on the left to make it go to the next X lines, for example 1 will make it go to the next line. See the multline section below for further explanation.
Color Trigger
This type of trigger matches the color of the text, as opposed to the actual text incoming. Both foreground and background matching is available and you will be prompted to select the required colors.
Examples:
Pattern: | Foreground color [ANSI 12], Background color [ANSI 15] (blue foreground on a white background) |
String: | The bird flies in the sky. |
Outcome: | match (the COLOR matches) |
Prompt
This type of trigger will only work with servers that have implemented the telnet suboption GO AHEAD. If you server does not implement GA this will be shown in the bottom right corner of Mudlet with the characters No GA.
This option has one function, to match your prompt and will fire when it does.
Multi-Line Triggers and Multi-Condition Triggers
Multi Condition Triggers are the most advanced feature of Mudlets trigger engine. Like trigger chains or filter chains they are a form of AND-Triggers. All conditions in the list must have matched within the specified margin of lines (delta), in order to trigger the script. Normal triggers fire and run the script as soon as one of the conditions in the regex list is met i.e. if one of the regex/string matches match - or the Lua function condition returns true, the trigger script is run. In multiline triggers, however, each single regex/string/Lua function condition in the list has to have matched within the specified margin of lines at least once to trigger the script. The sequence of the conditions is binding. This means that if the 10th regex on the regex list would be matched on the eleventh line after the match of the first line happened, the trigger will NOT run unless the margin of lines is set to 11. If condition #3 is true but currently #2 is waiting to be true, condition #3 is ignored and must be true again after condition #2 has been true. Conditions can also be Lua Functions or plain Lua code that returns a boolean truth value. You can mix all types of conditions to build complex multi-condition triggers that only fire if all conditions are met. This is a very powerful feature as it reduces the amount of scripting to a minimum or even takes away with the need to script formerly complex processes completely. Multicondition triggers are multi-line triggers, i. e. the conditions can all be met in a single line or many lines after the first condition has been fulfilled. This effectively reduces the amount of complexity as you have all the important conditions placed into a single trigger and all the tedious bookkeeping, variable and condition state accounting is being done by Mudlets trigger engine. The result of this is that the amount of manual condition checking via many different trigger scripts and legions of if condition1 == true then check condition2 can be forgotten about. All you have to do is to define the conditions and the final action that is taken if the trigger fires.
Note: to retrieve wildcards in multi-line or multi-condition triggers, use the multimatches[line][match] table instead of matches[].
Note: This diagram shows what steps are involved in the process of problem solving with Mudlets trigger engine. The main question is: How do we arrive at a solution to our problem, and how can we simplify the problem as much as possible?
Example: Let’s go back to our pond & frog example. We have explained the difference between AND-triggers and OR-triggers. If you have a room description consisting of 3 lines:
- You see a pond
- You see a frog.
- The frog sits on a stone.
Every single one of these 3 lines will be fed into the trigger engine one after another. If we define an OR-trigger with a condition list consisting of 3 condition patterns:
- condition #1 pattern = pond
- condition #2 pattern = frog
- condition #3 pattern = stone
Whether or not a condition is found to be true also depends on another property, namely the type of the condition. The condition type can be among others:
- substring matching → the condition is true if the condition pattern is a substring of the output line from the game. In other words: If the pattern "pond" is contained in any line of output, the condition is true.
- begin of line matching → the condition is only true if the condition pattern can be found at the beginning of a line from the game.
- Perl regular expression → the condition is true if the Perl regex pattern is matched. You’ll find more information on Perl regex below.
- Lua code that returns a truth value e.g. a call to a function check() that return either true or false depending on the condition
In our example we chose condition type "substring" for all three conditions. Consequently, the trigger will fire the command or script 3 times i. e. the trigger will do on each line it is matched against because in every line at least one condition evaluates to true because the pattern is a substring of the line.
- in line #1 we get: pond = true.
- in line #2 we get frog = true and
- in line #3 two conditions are true i.e. frog=true and stone = true
Because an OR-trigger fires on the first condition that is true and ignores the rest the trigger will only execute 3 times on these 3 lines although 4 conditions are true.
Note: CAUTION: The multi line condition switch must be turned off to get an OR-trigger! If the multi-line condition switch is turned on the trigger becomes an AND trigger which means that the trigger only fires if all conditions are true and fulfilled in the correct sequence. With OR-triggers the sequence is irrelevant.
To complicate matters, however, you don’t want your trigger to fire 3 commands, because you want to use this room description as a whole to fire your trigger e. g. this pond is the only kind of ponds in the entire world that doesn’t have poisoned water. So you want to make sure that you only drink water from a pond of this kind and from no other pond. Your solution is to use Multi Condition Triggers (MCT). If you check the MCT checkbox this trigger will fire only once from now on - and only if all conditions are met i e. when you can guarantee that you only drink water from a good pond because your drinking trigger is matching on the entire room description despite that this room description my be spread over a number of lines. (NOTE: If you have word wrap turned off in your game chances are that the entire room description will be contained in a single line, but we are trying to keep the examples as easy as possible.)
Sadly, there are many unfriendly people in this world and somebody goes around and poisons your good ponds. Consequently, you would want to examine the frog and find out if it is poisoned before drinking water from the pond. This is difficult because the villain is a mean magician who used illusion spells to make everything look like the good pond. To solve the problem you can now resort to Lua function conditions in the trigger condition list that perform certain check ups to put the current room description into a wider context e. g. check if you have been here before etc. This adds yet another level of complexity to your problem but this is a very powerful means to use the full potential of Mudlets MCTs.
You can combine all forms of conditions with trigger chains, filters and Lua functions. Mudlet gives you relatively easy to use tools that require no programming background. However, these tools can evolve into complex powerful problem solving mechanisms if they are combined with each other thus enabling non-programmer users to solve problems that would need a profound programming background in other gameclients. However, unlocking the full potential of Mudlet requires you do learn some Lua basics. In this manual we’ll try to be as easy on you as we can in this respect, but it’s highly recommended that you dig deeper into Lua after a while. It’s one of the easiest fully fledged scripting languages available, easy to learn and the fastest of them all, and this is why it has been chosen by us. You don’t need to become a programmer to be able to use Mudlet effectively. All you have to know is how to work with variables and how to do if conditions and maybe a few loops. But knowing more won’t harm you and it will make your systems more effective.
"multiline / AND" and "OR" Condition Triggers
Multiline, or AND triggers execute their respective command or script only if all conditions in their respective conditions expression list are fulfilled.
OR-Triggers execute when any one of their conditions is true. To make OR-Triggers you simply have to add a few conditions to the conditions list e.g. in our example: "pond", "frog", "Plop". The trigger will now also execute on lines like this: "A frog is green" or "You hear a loud Plop!" because "frog" matched in the first line and "Plop" matched in the second line.
Multiline / AND triggers execute when all of their conditions are true. Taking the previous patterns of "pond", "frog", "Plop" and clicking the 'AND' button will make the trigger not fire on "A frog is green" or "You hear a loud Plop!" anymore, because all 3 patterns don't match. It however would fire on "In a brown pond, the frog said Plop!" because all 3 patterns matched. As you might have noticed, all 3 patterns are on the same line: this means that in a multiline/AND trigger, the next pattern starts matching on the same line to make this work.
To make the engine skip lines in a multiline / AND trigger, use the line spacer pattern: select 'line spacer' as the type and put in a number on the left to make it go to the next X lines, for example 1 will make it go to the next line.
The simplest form of AND-Triggers in Mudlet are Trigger Chains or Filter Chains, whatever you’d like to call it.
Trigger Chains & Filter Chains
"Chains" and "filters" are different trigger group entities in Mudlet and serve completely different ends.
Chains
A chain is defined in Mudlet by making a trigger group and adding a trigger pattern to the group. A group without a pattern is a simple trigger group that serves no other purposes than adding structure to your trigger system in order to organize your triggers better. Such a normal trigger group will always grant access to its children unless it has been explicitly disabled (= all access to itself or any of its children is locked) either manually or by a script.
A trigger group with a defined trigger pattern, however, will behave like a normal trigger and match if the pattern matches. Such a trigger group is called "chain head". A chain head will only grant access to its children if the trigger pattern has matched and the chain has been enabled. Thus, chains can be looked at as a mechanism to automatically enable and disable triggers or groups of triggers when a certain condition has been met i. e. the trigger pattern of the chain head has been matched. (However, technically this is not correct as disabled child triggers will not be invoked if the chain head matches. In other words, in chains you can still have enabled/disabled elements. The idea of a chain can better be described by necessary and sufficient condition - both of which need to be met before a child trigger is being run.)
Adding child triggers to this group will add elements to the trigger chain. These chain elements will only be activated if the chain head has matched before and thus opened the trigger chain. The chain stays open until the "keep chain open for x lines" value has been reached. The default is 0 which means that the chain only stays open for the current line. When access to the chain has been granted all child triggers will be tested against the content of the current line. Consequently, trigger chains are a means to automatically enable/disable trigger groups without the hassle of enabling and disabling trigger groups manually. This has 2 important advantages: Chains are faster than conventional solutions and chains reduce the complexity of your scripts and greatly reduce the usual system bugs that inevitably go along with enable/disable trigger xy function calls as it’s very difficult and error prone to enable and disable your triggers correctly in large complex trigger systems. This is one of the most powerful features in Mudlet and should be used whenever possible.
Let’s look at a practical example for a trigger chain:
My newbie prompt on Achaea looks like this 500h, 500m ex-
when I have balance and 500h, 500m e-
when I have lost balance. We are going to develop a prompt detection trigger that raises the event gotPrompt and sets the variables myBalance=true
or myBalance=false
To begin with, we add a new trigger group and add the Perl regex pattern to detect the prompt:
^(\d+)h, (\d+)m
The pattern reads in plain English: At the beginning of a prompt line there are is a number directly followed by the letter h, a comma, a space and another number followed by the letter h. Whenever this pattern matches the trigger will fire and we’ll know that we have a prompt line. We use the 2 numbers that we captured in our pattern to update our health and mana stats.
Detecting balance is more difficult as balance is indicated by the letters ex- on the same line after the prompt and imbalance is indicated by the letters e-. As we have set a pattern in a trigger group (folder), the folder is turned into a trigger chain head. It will now only let data through to its children when its own pattern is matched. In other words, the child triggers of the trigger chain will only receive data on prompt lines. We are going to take advantage of this by adding two simple substring triggers to detect balance and imbalance. The balance trigger pattern is ex- and the imbalance detector pattern is e-.
In the two balance detection triggers we now write myBalance=false
and myBalance=true
respectively plus a little echo on the screen that shows our current balance status.
We could now add a call deleteLine()
to the prompt detection trigger to erase the prompt from the screen if we don’t want to see it as we have computed all relevant information.
Filters
You can turn a trigger chain head into a filter by checking the "only pass matches" option. This changes the content of what is forwarded as trigger text to the children triggers in the chain. Chains forward the content of the current line as trigger text whereas filters forward the matched pattern instead of the current line. In other words, the text of the current line is filtered according to the pattern of the filter. For example: You want to know the exits in the current room. The simplest solution is a simple filter on You see exits to: (.*)
Then you simply add triggers to the filter chain such as north
, south
, west
, east
etc. The direction triggers will only be called if the filter head has matched.
Imagine the following scenario: You want to collect some berries. You know that the room contains some berries if the room description contains the words "You are inside a forest. There are some strawberries." or "You are inside a forest. There are some blackberries." and there are always only one kind of berry in the room. You make a new substring trigger for this line, but instead of choosing a regular trigger, you chose to add a new trigger group. Now you add You are inside a forest\. There are some (\w+)\.
as an Regular Expression to the expression list of the trigger group. When adding conditions to a trigger group, the trigger group turns from an organizational unit into a filter unit, if the "filter" option is checked. From now on this folder is a filter and will only let its matches pass through. In our case this is exactly what we want, because we don’t want to collect all sorts of berries, but we only want 2 particular kinds, namely, strawberries and blackberries, and we know that these berries can only be trusted if they are picked inside a forest as other areas may contain contaminated berries. Now you add two regular triggers to our berry-in-the-forest filter - one containing the condition: strawberries
and the other one blackberries
. Then we add the commands to pick the particular kind of berry to both triggers (send field). Now what happens is that as soon as a room description reads "You are inside a forest. There are some strawberries/blackberries." the filter will let the kind of berry pass through to our two berry triggers and they will issue commands to pick berries, if there are such berries. However, in any other situation the words "strawberries" and "blackberries" will NOT trigger a pick - only in the above scenario when the filter parent’s condition is met. This is a very nice way to solve complicated problems with a few easy filter chains. This example is trivial, but using filter chains will rapidly show you how formerly complicated things that could only be solved with very complicated regular expressions can be solved easily now with a couple of filter chains.
Named Capture Patterns
You can use named patterns - that is, giving your captures names instead of [2], [3], [4] numbers - by adding ?<variablename> to your capture in Mudlet 4.11+:
-- before:
(\w+) = matches[2]
-- now:
(?<name>\w+) = matches.name or matches["name"]
(?<weapon>\w+) = matches.weapon or matches["weapon"]
This can be particularly handy if you have several patterns in a trigger and the location of matches[n] moves around. Try this out in a demo package.
Named capture groups still will be available under their respective number in matches
.
You can programatically check if your Mudlet supports this feature with mudlet.supports.namedPatterns
.
Example:
Let's assume we want to capture name and class of character, but MUD outputs it two ways.
My name is Dargoth. My class is a warrior.
I am a wizard and my name is Delwing.
Instead of creating two different triggers we can create only one containing alternative matching patterns:
^My name is (?<name>\w+)\. My class is a (?<class>\w+)\.
^I am a (?<class>\w+)\ and my name is (?<name>\w+)\.
Notice that class and name come in different order, therefore in first case we will have name under matches[2] and in second case under matches[3]. With named capture groups in both cases we will have name available under matches.name and class under matches.class.
Code example to handle named matches above:
echo("Name: " .. matches.name .. "\n")
echo("Class: " .. matches.class .. "\n")
Note: To verify whether named groups are available you can use flag mudlet.supports.namedGroups
.
The Timer Engine
Mudlet supports 4 different sort of timers:
- Regular GUI Timers that fire repeatedly in a certain interval specified by the user.
- Offset Timers are child timers of a parent timer and fire a single shot after a specified timeout after their parent fired its respective timeout. This interval is an offset to the interval of its parent timer. Example: parent timer fires every 30 seconds and by doing so kicks off 3 offset timers with an offset of 5 seconds each. Consequently, the 3 children fire 5 seconds after each time the parent timer fired. Offset timers differ visually from regular timers and are represented with a + icon for offset. Offset timers can be turned on and off by the user just like any other timer.
- Temporary Timers are very useful, and are the most common type of timer used for scripting purposes. They behave like regular timers, but they are one shot only timers.
- Batch Job Timers are a form of timer that issue a sequence of commands/scripts according to a certain timeout instead of a single command/script. This is a very important tool if the sequence of commands is important. Timers depend largely on the operating system you are using and it cannot be guaranteed under certain conditions that if you have set up 5 timers to fire after 1.3 seconds that the sequence in which they fire is the same sequence in which they were created. If the sequence of commands is important, you should always use batch job timers as this form of timers guarantees that the sequence of commands is observed. For example: If you want to make an auto-miner bot that digs its way into a gold mine digging left, down, down, right, left, down until a trigger catches a message that indicates that the mine is going to collapse and bury your poor soul unless you run for your life and head out of the mine. In this scenario the sequence of commands is vital as you’d lose your way and die. This is a classical case for a batch job timer.
The most common usage of temporary timers is the function tempTimer(). It lets you specify a timeout after which a script is being run e.g.
tempTimer( 0.3, [[send("kill rat")]] )
This will issue the command "kill rat" after 0.3 seconds. Other clients call this kind of function wait() or doAfter() etc. It is one of the most used functions in MUD scripting. tempTimer() is a single shot timer. It will only fire once and is then marked for deletion. TempTriggers(), by contrast, live through the entire session and are never deleted unless you explicitly delete them or disable them.
Another often used function in the context of timers is enableTimer( timerName ), or disableTimer( timerName ). Those are the counterparts of enableTrigger( triggerName ) and disableTrigger( triggerName ), enableKey( keyName ) etc..
NOTE: Temporary timers cannot be accessed from the GUI and are not saved in profiles.
To be continued ….