Introduction

Welcome to Mudlet, a modern MUD client for GNU/Linux, Windows and Mac OSX that offers all the tools to get the most out of your gaming experience.

Throughout this document we hope to familiarize you with the basic aspects of Mudlet, from the interface to its very powerful and feature rich scripting backend The main focus in the development of Mudlet has been efficiency and performance, but we also try to make it as easily accessible as possible. However, keep in mind that this is a complex piece of software with a large set of tools that does require some deeper understanding of the underlying principles. To use Mudlet in any meaningful way, you have to take a closer look at the technical section in this manual.

If you are not familiar with using modern MUD clients in general you should also take a look at the general introduction to MUD clients section at the beginning of this manual. You are welcome to ask questions on the forum of our website. If you find a bug, please let us know.

Note Everything is centered around the central Lua scripting unit. Everything shares the same variables & functions and everything is accessible from anywhere.

Quick Start

To connect to the MUD of your choice, click on Connect. !

  • Create a new profile by clicking on Add New Profile.

  • Specify the host and the port of the MUD (This information is usually found on the MUD’s homepage).

  • Now just click Connect to play!

1. General Introduction to Modern MUD Clients

1.1 Mudlets automation features

Mudlet offers a vast array of standard features to automate or otherwise improve your gaming experience. These include, but are not limited to:

  • Aliases – user-defined text input, which is converted into a different, usually longer input before being sent to the MUD, e.g. gg → get gold from ground;put gold in 2.bag.

  • Keybindings – also known as hotkeys, allow executing certain user-defined commands by simultaneously pressing a specific combination of keys, e.g. CTRL+H → send say Hello Miyuki! to the MUD or plays La Marseillaise

  • Triggers – execute user-defined commands upon receiving specific out from the MUD, e.g. MUD sends You see Elyssa standing here. → send poke Elyssa to the MUD.

  • Timers – delay the execution of a command or execute it after a specified period of time, e.g. throw gauntlet to Eric-wait 3 seconds-exclaim Let us end this here!

  • Variables – allow the user to store text or numbers for easier use inside scripts.

  • Events – allow the user to make triggers for specific events like when Mudlet has connected to the MUD, or even user-defined events to use in complex system making.

Scripting allows the user to create automation utilities (triggers, aliases) with the Lua scripting language. The possibilities of what can be achieved by using this technique are, practically speaking, endless - ranging from teaching the client how to heal your character in combat for you to creating a fully automated robot (known as `bot`s) that will completely take over the control for your character (especially useful for completing repetitive, rudimentary tasks).

1.2 Automation and MUD rules

Effectively speaking, it is possible to create an AI (Artificial Intelligence) that does everything you can do in a MUD. Even more so, the program will be able outperform you in almost every routine operation. The difficulty of creating such a program depends on the task it has to perform: gathering loot being very easy, walking through a dungeon and leveling you character being moderately easy and socially interacting with other real people being outrageously difficult (see A.L.I.C.E.). In the end of the day, you’re teaching your client to process information and act the way you consider best suited. Because scripting is so powerful, it can give you a competitive advantage that some people consider it unfair or even cheating. As of the moment of this writing (2 November 2008), this sort of automation can be best observed in commercial massively-multiplayer online role-playing games (MMORPG), known as gold-farming or power-leveling. The basic idea is creating a system that will raise your character to the maximum level and gather in-game currency in the process, both of which can be effectively exchanged for real-world currency. The spread of the above aspects can have much more far reaching consequences than just being unfair, such as inflation, loss of balance in terms of game-mechanics or, ultimately, a complete crash of in-game economy. For more information see the paper "Simple Economics of Real-Money Trading in Online Games" by Jun-Sok Huhh of the Seoul National University. For these, and various other, reasons the administrators and owners of the corresponding virtual worlds can forbid the usage of automation tools. A failure to comply can result in suspension or deletion of the user’s character or account for future denial of service. By including scripting support in Mudlet, we effectively give you the ability to create and utilize AI tool-kits, however, we do not endorse or promote the usage of these systems if it’s prohibited in your MUD. Keep in mind that by cheating you can lessen the quality of gameplay for both, your fellow players and yourself.

1.3 Basic scripting

The following part of the guide is written for new beginners in scripting. If you’re coming over from zMud or cMud, you may move on to section two.

1.4 Variables - basics

Variables are containers for data. In Lua, they can store numbers or words. You can use variables to store important information like how much gold do you have, or have them remember things for you.

The syntax for making a variable remember a number is the following:

variable = number

Or to make it remember some text:

For example, here we’ll set the myhealth variable to number 1345, and the myname variable to Bob:

You can also do basic maths easily, for example:

To concatenate strings together, you can use the .. expression:

1.5 How to send text to the mud

To send a command to the MUD, you can use the send() function. Data inside the quotes is sent to the MUD.

For example, the following code sends the command to eat bread:

If you’d like to include variables in the send command, you need to prefix and suffix them with two dots outside the quotes, like this:

send("My name is " .. full_name .. ". What's yours?")

1.6 How to echo text to yourself

To echo (show text to yourself) you can use the echo() or the insertText() function. For example, the following code will display Time to eat dinner! on your screen:

echo("Time to eat dinner")

If you’d like to include variables in your echo, you concatenate the value of your variable to the text:

my_gold = 5
echo("I have " .. my_gold .. " pieces of gold!")

1.7 Aliases

The aliases are the most basic way of automating the gameplay - you can use aliases to shorten the amount of typing you do. For example:

Example - Brew’o'Matic 6000

You’re walking around the epic dungeon of the Unholy Firedragon of Westersand, gathering roots in order to brew a potion and thus restore the growth of hair on Farmer Benedict’s bald head. Once you see a root, you need to:

open the bag of tools
get the sickle of damnation from the bag of tools
cut the root of hair-growth
clean the sickle of damnation of deadly root acid
put the sickle of damnation in the bag of tools
close the bag of tools
open the magical bag of storing
take the root
put the root into the magical bag of storing
close the magical of storing

and once you’re done, do the exact same thing nine more times… trice a day.

Alternatively, you just create an alias that would do this all with a single command - for example, quest.

1.8 Making a simple alias

To get started, go click on the Aliases button in Mudlet, and then on the Add one. This will make a blank alias for you, which we’ll now fill in.

The Alias name field is optional - it’s mainly used for the alias listing that you see on the left as an easy way to distinguish all of the aliases. You can just name our alias test for now. The Pattern field is where you put your regex pattern to describe on what command that you are typing in the command line, your new alias will spring into action. Let’s say, we want our new alias to send the command "say hello" whenever we type "sh". The regex pattern would be "^sh$". Then we put "say hello" into the substitution field. After you have saved and activated your new alias "test", whenever you type "sh" Mudlet will not send "sh", but "say hello" instead. We call this substitution process alias expansion.

Mudlet uses Perl regular expression aliases. Regexes are a special way of matching patterns of words. For the beginners it is enough to think of them as a general way to specify the words itself and their placement within the line. For basic alias it is enough to know that the character ^ symbolizes the beginning of the line and the character $ symbolizes the end of the line. If you want to make an alias "tw" that sends the command "take weapon", you don’t have to care about placement or pattern matching in general. All you need to do is fill "tw" in the field called "Regex" and type "take weapon" in the field called "substitution". Then you need to save the new alias by clicking on the "Save" icon in the top middle. The symbol for unsaved items disappears and makes way for a little blue checkbox. If this box is checked the alias is active. If the blue box is empty, the alias is deactivated and will not work unless you press the "activate" toggle padlock icon. Now you are ready to go. Type "tw" in the command line and press the enter key. Mudlet will send "take weapon" to the MUD. Alias as basically, a feature to save you a bit of typing - much like buttons which will be described in detail in section two of the manual. To learn more about more complex aliases have a look at section 2 of the manual.

1.9 Simple Triggers

Triggers are an automation feature offered in all MUD clients. They help you respond quicker a particular situation and generally make things more convenient for you since you need to do less manual work as your triggers will do the hard work for you often times. This helps you concentrate more on the important aspects of the game and lessen stress. The way a trigger works is simple: You define some text that you want to trigger some action. This is called the trigger pattern. When the trigger "sees" this text in the MUD output, it’ll run the commands you’ve told it to. Example: Whenever you see a bunny, you want to attack it. You type "bunny" in the data field titled "add to list" and then either press the enter key or click on the little + icon next to the input line to add this word to the list of texts this trigger fires on. Now you type "kill bunny" in the field called "send plain text". Then click on the save icon to save the trigger and activate your new trigger (= blue checkbox icon in front of the trigger name in the trigger tree on the right side is checked). When the trigger is active each time the word "bunny" will appear in the MUD output, your trigger will issue the command "kill bunny" automatically as long as it is active. When you want to stop hunting bunnies, you can simply select the bunny trigger and then click on the padlock icon to deactivate the trigger. The trigger will stop firing until you re-enable it again via the padlock icon. If you lock a group of triggers or an entire trigger branch, all triggers in this branch will be locked until you remove the lock again. The locking starts from the root of the tree down to the end. As soon as a lock is met the trigger engine will skip the locked branch. Locking and unlocking branches is one of the most common actions you have to take care of when playing. You turn on your defensive triggers when engaging into a battle and you turn them off afterwards, or you turn on your harvesting triggers only when you are going to harvest.

Tip Beginners should use Mudlets automated highlight triggers in the beginning to highlight the text that has been triggered on to get the hang of the different trigger and pattern types. Click on the "highlight trigger" option and pick a foreground and a background color that you like to highlight your trigger with. When the trigger matches it automatically highlights its pattern. This is the most used form of triggers in mudding as it is a quick way of highlighting words that are important to you at the moment. 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.
1.10 Matching one unknown

You can also set up a trigger to gather the scimitars, gold or whatever the skeletons could carry around with them. Since we do not know what the loot is, we will need to set up a trigger to match the line and take whatever was dropped. Examples:

The skeleton drops ring.
The skeleton drops gold.
The skeleton drops scimitar.

The skeleton drops_ is the generic segment of the line, the loot itself varies. Thus, we need to tell the client to take_ whatever the skeleton dropped. We do this by setting up a so-called regular expression:

Perl Style Regular Expression: The skeleton drops (.*)\.
Script: send("take " .. matches[2])

The expression (.*) matches any characters that the client receives between The skeleton drops_ (NB: notice the blank at the end) and the full-stop. matches[2] simply transfers the first matched text fitting the search criteria into the output (matches[1] contains the entire matched text, matches[2] contains the first capture group. More on this in section two of the manual).

1.11 Matching multiple unknowns

Now, let’s try making a trigger that would gather the loot from anybody:

Perl Style Regular Expression: (.*) drops (.*).
Script:
send("take " .. matches[3])

In this case, any time somebody, or something, drops something else, or someone else, the client will pick it up. Note that we used matches[3] instead of matches[2] this time, in order to pick up the second match. If we used matches[2], we’d end up picking up the skeleton’s corpse.

1.12 Matching known variants

If you’re playing a MUD in English, you’ll notice that these triggers probably won’t work due to English syntax. Compare:

The skeleton drops apple.
The skeleton drops an apple.

Chances are that you’ll see the later a little more often. If we used our old RegEx, the output would look something like this.

INPUT: The skeleton drops an apple.
OUTPUT: take an apple

Most MUDs can’t handle determiners, such as articles (i.e. a, an, the) or quantifiers (e.g. five, some, each), in user-input. To match this line we could either create multiple triggers matching every possible article or a regular expression filtering out these words:

Perl Style Regular Expression: (.*) drops (a|an|the|some|a couple of|a few|) (.*).
Script:
send("take " .. matches[4])

Once again, note that we’re using the third match (matches[4]) this time. NOTE: Certain other languages, with a morphology richer than that of English, might require a somewhat different approach. If you’re stuck, and your MUD-administrators don’t prohibit the use of triggers, try asking on the corresponding world’s forums.

For more information, see the chapter Regular Expressions.

1.13 explain basic regex characters (^, $, (\w+), (\d+) and .*) and how to use them properly.

Retrieving wildcards from triggers

Wildcards from triggers are stored in the matches[] table. The first wildcard goes into matches[1], second into matches[2], and so on, for however many wildcards do you have in your trigger.

For example, you’d like to say out loud how much gold did you pick up from a slain monster. The message that you get when you pick up the gold is the following:

You pick up 16 gold.

A trigger that matches this pattern could be the following:

Perl Style Regular Expression: You pick up (\d+) gold

And in your code, the variables matches[2] will contain the amount of gold you picked up (in this case, 16). So now to say out loud how much gold did you loot, you can do the following:

Script: echo("I got " .. matches[2] .. " gold!")
More advanced example

Here’s an example by Heiko, which makes you talk like Yoda:

Perl Style Regular Expression: ^say (\w+) *(\w*) .*?(.*)
Script:  send( "say "..matches[4].." "..matches[2].." "..matches[3] )

What it does here is save the first word, the second word and then the rest of the text into wildcards. It then says rest of the text first, then the first word and then the second word.

1.14 How to highlight words

To highlight something in the MUD output, make a trigger and use the "highlight trigger" option to highlight the matched trigger pattern.

Optionally, you can also make use of the bg() and fg() functions in scripting to highlight.

1.15 Keybindings

Keybindings, or hotkeys, are in many respects very similar to aliases, however, instead of typing in what you want to be done, you simply hit a key (or combination of keys) and let the Mudlet do the work.

HOTKEY: F1
command on button down: sip earl gray

HOTKEY: F2
command on button down: sip ceylon mint

Now you just have to listen, or rather read, carefully and hit either F1 or F2 to claim that prize.

Another practical use for keybindings would be creating a so-called "targeting system", which is especially useful for grinding down armies of pumpkin-men in MUDs without auto-attack. See the Variables chapter for further details.

1.16 Timers

Timers, as the name suggests, can be used to execute a specific command at a certain time, after a certain time or once every so often.

Main 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. ernie@avalon.de, bert@avalon.de 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. 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).

  1. 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.

  2. 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.

  3. 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.

  4. ESC: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:

echo( getMudletHomeDir() .. "\n" )

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:

echo( getMudletHomeDir() .. "\n" )

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 a 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

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:

totalKills = getVariable("killedMonsters") + getVariable("killedVillains")
echo( "kills=" .. totalKills )

In Mudlet, the above code translates into:

totalKills = killedMonsters + killedVillains
echo( "kills=" .. totalKills )

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:

myKills = myKills + 1

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:

echo( "I have killed " .. myKills .. " monsters today." )

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:

myKills = 0

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:

if myKills == nil then    -- this code initializes the variable myKills to the number 0 if it hasn't been initialed before
    myKills = 0
end

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:

You can now add new enemies to this list by calling the Lua function listAdd( listName, item ) e.g.

listAdd( myEnemies, "Tom" )
listAdd( myEnemies, "Jim" )

To print the contents of your enemy list on the screen you can run this script

listPrint( myEnemies )

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:

listAdd( myEnemies, matches[2] )
echo( "Added a new enemy:" .. matches[2] .. "\n" )

Save the alias and try. Alias are explained in detail below. Another way to declare a list is to define its values directly.

myEnemies = { "Peter", "Jim", "Carl", "John" }

To remove an item from the list you can use the function listRemove( listName, item ). === Saving Variable Values to Disc 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 …

Input Triggers - Mudlets Alias Engine

Important QUICKSTART: Here is a video tutorial on how to do basic aliases in Mudlet: http://blip.tv/file/2288749

Alias are triggers that operate on user input. Mudlet uses hierarchically ordered powerful Perl regex expression alias. We have explicitly chosen not to offer multi condition alias, alias chains or the same trigger types the trigger engine offers because this would be way over the top and is simply not needed for alias expansion - although it should be noted that the processes are closely related, except that aliases, i.e. input triggers, operate on a different data stream, namely the user input stream, whereas triggers operate on the MUD output data stream. The only real difference between output triggers and input triggers is that input triggers can change the input stream they work on, whereas output triggers cannot change the stream itself - output triggers can change what is printed on the screen as a result of the stream, but they cannot change the stream itself. This is a fundamental difference and a deeper understanding is key to getting to grips with Mudlets alias engine. When you enter a command on the keyboard and press enter or return, the text that you have typed in the command line will be forwarded to the alias unit, i. e. the input trigger unit, in form of the Lua variable command. This variable will be matched against all active alias in the hierarchy unless access to an alias branch is locked by the user or by a script. If an input trigger matches, it will intercept the user command and the original command will be ignored. Upon a match the clear text command is being send as a command to the MUD in replacement of the original command, if a clear text command has been specified by the user and the attached alias script is being executed. However, the initial command that the user has typed in the command line will not be sent unless you do this as part of your script. Consequently, if you want your input trigger to send a command to the MUD, you’ll either have to specify a clear text command for simple cases or send commands via the attached alias script e.g. send("kill monster"). You may argue that you feel that it is unnecessary work to be forced to send a command replacement yourself, but this very fact makes our alias system way more powerful because it gives you complete control about what is happening. Why is this so? The initial user command is being held in the Lua variable command. When this value changes within the alias unit processing chain, the initial user input that the input triggers work on can be rewritten and changed in the process. Consequently, you can substitute the user input step by step - or alias by alias - without that anything happens as far as sending commands is being concerned unless you explicitly decide to do so.

Note .

The example in the diagram above shows 2 matching aliases, but only one of them sends commands to the MUD - 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 input trigger 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 MUD than the amount of text the MUD 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 MUD 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 MUD.

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 "pwd" 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:[,#b0e0e6]^pwb Script is:

send( "put " .. weapon .. " in " .. bag )

Depending on the values of our variables Weapon and bag the command "pwd" 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:[,#b0e0e6]^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( "put " .. weapon .. " in " .. spareBag )
else
    send( "put " .. weapon .. " in " .. bag )
end

The next example is one of the most common aliases a tell alias: Pattern:[,#b0e0e6]^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

Mudlets Trigger Engine

Unlike alias that define patterns that match on user input, triggers define patterns that match on MUD output. In other words, triggers react to text that has been sent by the MUD, whereas alias react to user commands that have been typed into the command line.

Simple Trigger Matching

Note .script editor screenshot
Important QUICKSTART: Here is a simple video tutorial on how to make basic triggers in Mudlet: http://blip.tv/file/2288760

To begin with, click on the "Add" button to create a new trigger. Then 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. Afterwards, chose 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 MUD. For more advanced information on AND and OR trigger see the corresponding AND/OR trigger section below. The next step is to define what should happen 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 chose the "highlight trigger" option to make your trigger highlight the text that it has triggered on or send a clear text command to the MUD 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. Finally, you need to 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 is three ways to save your changes. 1. click on the save icon 2. adding a new trigger 3. clicking on another trigger item. Triggers that have not been saved yet cannot be activated. If you see a bug icon instead of an activation box, there is some error that prevents to activate your trigger. Check the error message above the pattern table.

Simple Highlighter Triggers

Tip Beginners should use Mudlets automated highlight triggers in the beginning to get the hang of the different trigger and pattern types quicker. Click on the "highlight trigger" option and pick a foreground and a background color. When the trigger matches it automatically highlights its pattern. This is the most used form of triggers in mudding as 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()

"AND" and "OR" Condition Triggers

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!" However, it will not execute on "With a loud plop the frog dived into the water." because "plop" in the line is in lower case letters and your condition specified a "P" in upper case. 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 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.

Filters

You can turn a trigger chain head into a filter by checking the "filter" 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 forrest." 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" 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. From now on this folder is a filter and will only let data pass through that matches it’s condition. 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-forrest 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 forrest." the filter will let the line containing the room description 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. It should be noted that filter chains only work on single lines. This means that if the filter chain head "You are inside a forrest" is in a different line than the other child chain element triggers (stawberr- and black berry triggers) the triggers will not fire as the line that is being let through the filter does not contain the words strawberries or blackberries. Most MUDs, however, offer a feature that lets your client do the word wrapping. This is a very powerful tool for scripting and should be enabled by you in your MUD as then the entire room description or the entire tell will be sent out to Mudlet in one line and Mudlet will do the word wrapping for you. Now you can use filter chains much more effectively as the line content comprises many lines. Triggering will become much easier. Ask your MUD operators how to enable this feature. Most big MUDs can do this these days. Let’s look at a practical example for a trigger chain:

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 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:

1. You see a pond
2. You see a frog.
3. 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:

  1. substring matching → the condition is true if the condition pattern is a substring of the output line from the MUD. In other words: If the pattern "pond" is contained in any line of output, the condition is true.

  2. begin of line matching → the condition is only true if the condition pattern can be found at the beginning of a line from the MUD.

  3. Perl regular expression → the condition is true if the Perl regex pattern is matched. You’ll find more information on Perl regex below.

  4. 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.

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 and 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 MUD 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 MUD clients. 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.

Lua Code Conditions & Variable Triggers - Expression Triggers

In a Lua Code/Function Condition (LFC) you can run Lua code inside the trigger engine directly. The easiest example would be a simple variable trigger: Add a new trigger and chose pattern type Lua Function Condition. Then define this pattern: if health ⇐ 100 then escape() end Another formulation of the same would be: checkHealth() For the last formulation to work you have defined a Lua function checkHealth(). Open the script editor, add a new script "health care" and add this code to the new script-script.

function checkHealth()
    if health <= 100 then
        echo( "WARNING: Low health! I have to run away!\n" )
        startEscape()
        return true
    else
        return false
    end
end

Lua function conditions effectively means that you run the Lua code they represent on every single line that is received from the MUD, 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.

Scripting: Generating Triggers - Special Trigger Types for Scripting Needs

Temporary Triggers (scripting only)

Temporary triggers are lightweight triggers that are tailored for typical scripting needs. These are only available via Lua scripting. They are not stored in profiles, but stay in memory as long as the program runs or until they are deleted (killTimer()). There are several forms of temp-triggers that address different scripting needs. Check the Lua API table for reference.

Line Triggers (scripting only)

Line triggers trigger on a specified line in the future - or a sequence of lines - irrespective of the content of the line. This type of trigger can be very handy in scripting if you know what is coming e.g. you want to parse a table from the MUD, maps etc.

Enabling and Disabling Triggers in Scripts

enableTrigger() disableTrigger()

Testing your triggers

Externally

RegexPal - a good website for testing your triggers against a set of text. [kiki-re - stand-alone program which will also tell you which matches are translated to what wildcard number.

The Timer Engine

Mudlet supports 4 different sort of timers:

  1. Regular GUI Timers that fire repeatedly in a certain interval specified by the user.

  2. 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.

  3. 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.

  4. 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 ….

Buttons and Custom User Toolbars

Note .

Scripting with Mudlet

Lua tables can basically be considered multidimensional arrays and dictionaries at the same time. If we have the table matches, matches[1] is the first element, matches[n] the n-th element.

a = "Tom"
matches[1] = "Betty"
b = matches[1]
c = a .. b and e will equal "TomBetty"

To output a table you can use a convenience function printTable( name ). This function is defined in LuaGlobal.lua and can be found in your home directory under ./mudlet (note the leading dot ) Profiles and mudlet_documentation.html are stored in this directory as well.

Lua interface functions to Mudlet - or how do I access triggers, timers etc. from Lua scripts

How to get data from regex capture groups? Regular expression capture groups (e.g. "(\d+)" ) are passed on to Lua scripts as a Lua table matches. To make use of this information inside Lua scripts, you need to specify the number of the capture group within the regex.

Example: You have (\d+) weapons and they are (?:(\b\w+\W+)+)

This regex contains 3 capture groups, but only the 2 green colored ones contain data as the red capture group is a non-capturing group. Consequently, your Lua script gets passed only 2 instead of 3 capture groups and matches[3] is undefined.

In your Lua script you may write following program in order to print the number and status of your weapons on the screen:

number_of_weapons = matches[1]
status_of_weapons = matches[2]
notice = number_of_weapons .. status_of_weapons
echo( notice )
send( "put weapons in backpack" )

-- the following 2 lines color the first capture
-- group red and the second group blue
-- see below for details

selectCaptureGroup( 1 )
setFgColor( 255,0,0 )

selectCaptureGroup( 2 )
setFgColor( 0,0,255 )

The best way is to use selectCaptureGroup( number ) to select the proper capture group and then perform your actions on it e.g. replace(), highlight etc. Note: Both selectCaptureGroup() and matches[n] start with group 1.

How to select all occurrences of "Tom" and highlight them?

You add a function like this to a script containing you main function definitions. Note that script items differ from all other "scripts" in triggers, timers, actions etc. because they require you to put your code in proper functions that can be called by your other trigger-scripts, timer-scripts etc. via normal function calls. Trigger-scripts, timer-scripts etc. cannot contain any function definitions because they are automatically generated functions themselves because this makes usage a lot easier.

To come back to our question how to select all occurrences of "Tom" and highlight them:

function setBgColorAll( word, r, g, b )
    i = 0
    word_count = 1
    while i > -1 do
        i = selectString(word, word_count)
        if i == -1 then
             return
        end
        word_count = word_count +1
        setBgColor( r, g, b )
    end
end

Then you simply define a substring matching trigger on the word "Tom" and in the trigger script you call above function:

setBgColorAll("Tom", 255,50,50)

Sending commands to the MUD or printing information messages on the screen

To print information messages on the session screen you can use the echo( message ) function, or insertText( text). Currently, it only takes one string as argument.

To send a command to the MUD, you can use the send( command ) function. Note: everything you send via send() will be processed by the alias processing unit. In Alias scripts the command that is being sent to the MUD is contained in the variable command that you can change in the context of Alias scripts. Alias take regular expressions, as well. As a result, you can use following regex and script to talk like Yoda: Perl regex:

say (\w+).*(\w*).*(.*)

script:

send( "say " .. matches[3] .." " .. matches[1] .." ".. matches[2] )

Note: The variable "command" contains what was entered in the command line or issued via the expandAlias( ) function. If you use expandAlias( command ) inside an alias script the command would be doubled. You have to use send( ) inside an alias script to prevent recursion. This will send the data directly and bypass the alias expansion.

Changing text from the MUD or reformatting text (highlight, make bold etc.)

When sending commands to the MUD - from now on referred to as output stream - alias scripts find the command that was issued by the user stored in the variable "command".

By manipulating the value, the command can easily be changed before it is being sent to the MUD.

However, things get much more complicated with the data received from the MUD – from now on referred to as input stream. Before triggers can be run on the MUD data, Mudlet has to strip all format codes from the text and store it in data structures associated with the text. Consequently, the text that is being passed on to the trigger processing unit is a small subset of the data received from the MUD. If you want to edit, replace, delete or reformat text from within your trigger scripts you have to keep this in mind if you don’t want to lose all text format information such as colors etc.

As the text is linked with data structures containing the format of the text, the cursor position inside the line is important if data is being changed. You select a word or a sequence of characters from the line and then issue commands to do actions on the selected data.

Replacing the word "Tom" with "Betty" in the line: Jim, Tom and Lucy are learning a new spell. This could be done with following script:

selectString("Tom",1)
replace("Betty")

Things get more complicated if there are two or more occurrences of "Tom" in the line e.g. Jim and Tom like magic. Jim, Tom and Lucy are learning a new spell.

The above example code would select the first occurrence of "Tom" in this line and ignore the second. If you want to work on the the second occurrence of "Tom" you have to specify the occurrence number in the call to select().

selectString( "Tom", 2 )
replace( "Betty" )

This code would change the second "Tom" and leave the first "Tom" alone. The function call

replaceAll( "Betty" )

will replace all occurrences of "Tom" with "Betty" in the line if "Tom" has been selected before. replaceAll() is a convenience function defined in LuaGlobal.lua.

Colorization example: You want to change to color of the words "ugly monster" to red on a white background.

You add a new trigger and define the regex: ugly monster In the script you write:

selectString("ugly monster", 1 )
setFgColor(255,0,0)
setBgColor(255,255,255)
resetFormat()

Another means to select text is to select a range of characters by specifying cursor positions. If we have following line: Jim and Tom like magic. Jim, Tom and Lucy are learning a new spell.

selectSection( 28, 3 )

This example would select the second Tom. The first argument to selectSection is the cursor position within the line and the second argument is the length of the selection.

selectCaptureGroup( number )

Deleting Text - Gagging

This function selects the captureGroup number if you use Perl regular expressions containing capture groups. The first capture group starts with index 1.

deleteLine()

This function deletes the current line - or any line where the cursor is currently placed. You can use repeated calls to this function to effectively erase the entire text buffer. If you want to delete or gag certain words only, you can select the text that you want to delete and then replace it with an empty string e.g:

If you get this line form the MUD: "Mary and Tom walk to the diner."

selectString( "Tom", 1 )
replace( "" )

Then the output will be changed to: "Mary and walk to the diner."

Cursor Movement and Cursor Placement

moveCursor( windowName, x, y ) This will move the user cursor of window windowName to the absolute (x/y) coordinates in the text.

moveCursor( "main", 20, 3950 ) will move the cursor on the 20th character from the left on line number 3950. To determine the current cursor position you can use getLineNumber() and getColumnNumber() as well as getLastLineNumber() to get the number of the last line in the text buffer. moveCursorEnd("main") will move the cursor of the main display to end of the buffer. This is always a new line of size 1 containing the character \n.

number_of_last_line_in_text = getLineCount()

returns the number of the last line of the text in the console buffer. This number will change as soon as a new \n is printed either by the user or

when a new line arrives from the MUD. All lines from the MUD are terminated with \n which is called line feed or the new line character. This control character ends the current line and move the cursor to the beginning of the next line, thus creating a new, empty line below the line that contains the \n.

line_number_of_the_current_cursor_position = getLineNumber()

column_number_of_the_current_cursor_position = getColumnNumber()

luaTable_containing_textlines = getLines( absolute_line_number_from, absolute_line_number_to )

this will return a Lua table containing all lines between the absolute positions from and to. NOTE: This function uses absolute line numbers, not relative ones like in moveCursor(). This little demo script shows you how to use cursor functions:

moveCursor() return true or false depending on whether the move was possible.

User defined dockable windows

You may want to use dock windows to display information you gathered in your scripts, or you may want to use them as chat windows etc. Adding a user defined window:

openUserWindow( string window_name )

echoUserWindow( string window_name, string text )

setWindowSize( int x, int y )

clearWindow( string window_name )

Dynamic Timers

tempTimer( double timeout, string lua code_to_execute, string/float/int timer_name ) disableTimer( name ) enableTimer( name )

Dynamic Triggers

triggerID = tempTrigger( regex, code ) creates a fast substring matching trigger triggerID = tempRegexTrigger( regex, code ) creates a regular expression matching trigger

Registering Event Handlers and Raising Events

raiseEvent( string name, args )

As an example, your prompt trigger could raise an onPrompt event if you want to attach 2 functions to it. In your prompt trigger, all you’d need to do is raiseEvent("onPrompt") Now we go about creating functions that attach to the event. Lets say the first one is check_health_stuff() and the other is check_salve_stuff(). We would like these to be executed when the event is raised. So create a script and give it a name of check_health_stuff. In the Add user defined event handler, type onPrompt, and press enter to add it to the list. In the script box, create: function check_health_stuff()blah blah end. When the onPrompt event comes along, that script catches it, and does check_health_stuff() for you.

Default events raised by Mudlet

Mudlet itself also creates events for your scripts to hook on. The following events are generated currently:

sysWindowResizeEvent

Raised when the main window is resized, with the new height and width coordinates passed to the event. A common usecase for this event is to move/resize your UI elements according to the new dimensions.

Example

This sample code will echo whenever a resize happened with the new dimensions:

function resizeEvent( event, x, y )
        echo("RESIZE EVENT: event="..event.." x="..x.." y="..y.."\n")
end

sysWindowMousePressEvent

Raised when a mouse button is pressed down anywhere on the main window (note that a click is composed of a mouse press and mouse release). The button number and the x,y coordinates of the click are reported.

Example
function onClickHandler( event, button, x, y )
        echo("CLICK event:"..event.." button="..button.." x="..x.." y="..y.."\n")
end

sysWindowMouseReleaseEvent

Raised when a mouse button is released after being pressed down anywhere on the main window (note that a click is composed of a mouse press and mouse release). See sysWindowMousePressEvent for example use.

ATCP events

Mudlets ATCP implementation generates events for each message that comes, allowing you to trigger on them easily. Since ATCP messages vary in name, event names will vary as well. See the atcp section on how to use them.

Handling Tables in Lua

Nick Gammon has written a very nice overview on how to deal with Lua tables. You can find it here: http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=6036.

How to use multilinematches[n][m]

(The following example can be tested on the MUD batmud.bat.org)

In the case of a multiline trigger with these 2 Perl regex as conditions:

^You have (\w+) (\w+) (\w+) (\w+)
^You are (\w+).*(\w+).*

The command "score" generates the following output on batMUD:

You have an almost non-existent ability for avoiding hits.
You are irreproachably kind.
You have not completed any quests.
You are refreshed, hungry, very young and brave.
Conquer leads the human race.
Hp:295/295 Sp:132/132 Ep:182/181 Exp:269 >

If you add this script to the trigger:

showMultimatches()

The script, i.e. the call to the function showMultimatches() generates this output:

 -------------------------------------------------------
 The table multimatches[n][m] contains:
 -------------------------------------------------------
 regex 1 captured: (multimatches[1][1-n])
           key=1 value=You have not completed any quests
           key=2 value=not
           key=3 value=completed
           key=4 value=any
           key=5 value=quests
 regex 2 captured: (multimatches[2][1-n])
           key=1 value=You are refreshed, hungry, very young and brave
           key=2 value=refreshed
           key=3 value=young
           key=4 value=and
           key=5 value=brave
 -------------------------------------------------------

The function showMultimatches() prints out the content of the table multimatches[n][m]. You can now see what the table multimatches[][] contains in this case. The first trigger condition (=regex 1) got as the first full match "You have not completed any quests". This is stored in multimatches[1][1] as the value of key=1 in the sub-table matches[1] which, in turn, is the value of key=1 of the table multimatches[n][m].

The structure of the table multimatches:

multimatches {
                1 = {
                       matches[1] of regex 1
                       matches[2] of regex 1
                       matches[3] of regex 1
                              ...
                       matches[n] of regex 1 },
                2 = {
                       matches[1] of regex 2
                       matches[2] of regex 2
                             ...
                       matches[n] of regex 2 },
                 ...         ...
                n = {
                       matches[1] of regex n
                       matches[2] of regex n
                             ...
                       matches[n] of regex n }
}

The sub-table matches[n] is the same table matches[n] you get when you have a standard non-multiline trigger. The value of the first key, i. e. matches[1], holds the first complete match of the regex. Subsequent keys hold the respective capture groups. For example: Let regex = "You have (\d+) gold and (\d+) silver" and the text from the MUD = "You have 5 gold and 7 silver coins in your bag." Then matches[1] contains "You have 5 gold and 7 silver", matches[2] = "5" and matches[3] = "7". In your script you could do:

myGold = myGold + tonumber( matches[2] )
mySilver = mySilver + tonumber( matches[3] )

However, if you’d like to use this script in the context of a multiline trigger, matches[] would not be defined as there are more than one regex. You need to use multimatches[n][m] in multiline triggers. Above script would look like this if above regex would be the first regex in the multiline trigger:

myGold = myGold + tonumber( multimatches[1][2] )
mySilver = mySilver + tonumber( multimatches[1][3] )

What makes multiline triggers really shine is the ability to react to MUD output that is spread over multiple lines and only fire the action (=run the script) if all conditions have been fulfilled in the specified amount of lines.

Scripting howtos

How to highlight my current target?

You can put the following script into your targetting alias:

if id then killTrigger(id) end
id = tempTrigger(target, [[selectString("]] .. target .. [[", 1) fg("gold") resetFormat()]])

Where target is your target variable. Note that you have to use the full name, capitalized. If you’d like the script to auto-capitalize for you, you can use this version*:

target = target:title()
if id then killTrigger(id) end
id = tempTrigger(target, [[selectString("]] .. target .. [[", 1) fg("gold") resetFormat()]])

^*requires Mudlet 1.1.0

ATCP

Since version 1.0.6, Mudlet includes support for ATCP. This is primarily available on IRE-based MUDs, but Mudlets impelementation is generic enough such that any it should work on others.

The latest ATCP data is stored in the atcp table. Whenever new data arrives, the previous is overwritten. An event is also raised for each ATCP message that arrives. To find out the available messages available in the atcp table and the event names, you can use display(atcp).

Note that while the typical message comes in the format of Module.Submodule, ie Char.Vitals or Room.Exits, in Mudlet the dot is removed - so it becomes CharVitals and RoomExits. Here’s an example:

room_number = tonumber(atcp.RoomNum)
echo(room_number)

Triggering on ATCP events

If you’d like to trigger on ATCP messages, then you need to create scripts to attach handlers to the ATCP messages. The ATCP handler names follow the same format as the atcp table - RoomNum, RoomExits, CharVitals and so on.

While the concept of handlers for events is to be explained elsewhere in the manual, the quick rundown is this - place the event name you’d like your script to listen to into the Add User Defined Event Handler: field and press the + button to register it. Next, because scripts in Mudlet can have multiple functions, you need to tell Mudlet which function should it call for you when your handler receives a message. You do that by setting the Script name: to the function name in the script you’d like to be called.

For example, if you’d like to listen to the RoomExits event and have it call the process_exits() function - register RoomExits as the event handler, make the script name be process_exits, and use this in the script:

function process_exits(event, args)
    echo("Called event: " .. event .. "\nWith args: " .. args)
end

Feel free to experiment with this to achieve the desired results. A ATCP demo package is also available on the forums for using event handlers and parsing its messages into Lua datastructures.

Aardwolf’s 102 subchannel

Similar to ATCP, Aardwolf includes a hidden channel of information that you can access in Mudlet since 1.1.1. Mudlet deals with it in the same way as with ATCP, so for full usage instructions see the ATCP section. All data is stored in the channel102 table, such you can do

display(channel102)

To see all the latest information that has been received. The event to create handlers on is titled channel102Message, and you can use the sendTelnetChannel102(msg) function to send text via the 102 channel back to Aardwolf.

db: Mudlet DB Frontend

The DB package is meant to provide easier access to a database, so you don’t have to know SQL or use the luasql module to set and get at your data. However, it does require that the luasql module be compiled and included in Mudlet to function - and this all is available since Mudlet 1.0.6.

Creating a Database

Before you can store anything in a database, you need to create one. You may have as many independent databases as you wish, with each having as many unique tables-- what we will call sheets in this package, so as to avoid confusion with Lua tables - think spreadsheets.

To create a database, you use the db:create() function, passing it the name of your database and a Lua table containing its schema configuration. A schema is a mold for your database - it defines what goes where. Using the spreadsheet example, ths would mean that you’d define what goes into each column. A simple example:

        db:create("people", {friends={"name", "city", "notes"}, enemies={"name", "city", "notes"}})

This will create a database which contains two sheets: one named friends, the other named enemies. Each has three columns, name, city and notes-- and the datatype of each are strings, though the types are very flexible and can be changed basically whenever you would like. It’ll be stored in a file named Database_people.db in your Mudlet config directory on the hard drive should you want to share it.

It’s okay to run this function repeatedly, or to place it at the top-level of a script so that it gets run each time the script is saved: the DB package will not wipe out or clear the existing data in this case. More importantly, this allows you to add columns to an existing sheet. If you change that line to:

        db:create("people", {friends={"name", "city", "notes"}, enemies={"name", "city", "notes", "enemied"}})

It will notice that there is a new column on enemies, and add it to the existing sheet-- though the value will end up as nil for any rows which are already present. Similarly, you can add whole new sheets this way. It is not presently possible to -remove- columns or sheets without deleting the database and starting over.

A note on column or field names: you may not create a field which begins with an underscore. This is strictly reserved to the db package for special use.

Adding Data

To add data to your database, you must first obtain a reference (variable) for it. You do that with the db:get_database function, such as:

        local mydb = db:get_database("people")

The database object contains certain convenience functions (discussed later, but all are preceded with an underscore), but also a reference to every sheet that currently exists within the database. You then use the db:add() function to add data to the specified sheet.

        db:add(mydb.friends, {name="Ixokai", city="Magnagora"})

If you would like to add multiple rows at once to the same table, you can do that by just passing in multiple tables:

        db:add(mydb.friends,
                {name="Ixokai", city="Magnagora"},
                {name="Vadi", city="New Celest"},
                {name="Heiko", city="Hallifax", notes="The Boss"}
        )

Notice that by default, all columns of every table are considered optional-- if you don’t include it in the add, then it will be set to its default value (which is nil by default)

For those familiar with databases: with the DB package, you don’t have to worry about committing or rolling back any changes, it will commit after each action automatically. If you would like more control then this, see Transactions below.

You also cannot control what is the primary key of any sheets managed with DB, nor do you have to create one. Each row will get a unique integer ID that automatically increments, and this field can be accessed as "_row_id".

Querying

Putting data in isn’t any fun if you can’t get it out. If you want every row from the sheet, you can do:

        db:fetch(mydb.friends)

But rarely is that actually useful; usually you want to get only select data. For example, you only want to get people from the city of Magnagora. To do that you need to specify what criteria the system should use to determine what to return to you. It looks like this:

        db:fetch(mydb.friends, db:eq(mydb.friends.city, "Magnagora"))

So the basic command is - db:fetch(_sheet_, _what to filter by_)

The following filter operations are defined:

        db:eq(field, value[, case_insensitive]) -- Defaults to case insensitive, pass true as the last arg to
                                                        reverse this behavior.
        db:not_eq(field, value[, case_insensitive) -- Not Equal To
        db:lt(field, value) -- Less Than
        db:lte(field, value) -- Less Than or  Equal to.
        db:gt(field, value) -- Greater Than
        db:gte(field, value) -- Greater Than or Equal To
        db:is_nil(field) -- If the column is nil
        db:is_not_nil(field) -- If the column is not nil
        db:like(field, pattern) -- A simple matching pattern. An underscore matches any single character,
                                        and a percent(%) matches zero or more characters. Case insensitive.
        db:not_like(field, pattern) -- As above, except it'll give you everything but what you ask for.
        db:between(field, lower_bound, upper_bound) -- Tests if the field is between the given bounds (numbers only).
        db:not_between(field, lower_bound, upper_bound) -- As above, only... not.
        db:in_(field, table) -- Tests if the field is in the values of the table. NOTE the trailing underscore!
        db:not_in(field, table) -- Tests if the field is NOT in the values of the table *

The db:in_ operator takes a little more explanation. Given a table, it tests if any of the values in the table are in the sheet. For example:

        db:in_(mydb.friends.city, {"Magnagora", "New Celest"})

It tests if city == "Magnagora" OR city == "New Celest", but with a more concise syntax for longer lists of items.

There are also two logical operators:

        db:AND(operation1, ..., operationN)
        db:OR(operation1, operation2)

You may pass multiple operations to db:fetch in a table array, and they will be joined together with an AND by default. For example:

        db:fetch(mydb.friends,
                {db:eq(mydb.friends.city, "Magnagora"), db:like(mydb.friends.name, "X%")}
        )

This will return every record in the sheet which is in the city of Magnagora, and has a name that starts with an X. Again note that in LIKE patterns, a percent is zero or more characters — this is the same effect as "X.*" in pcre patterns. Similarly, an underscore matches any single characters and so is the same as a dot in pcre.

Passing multiple expressions in an array to db:fetch is just a convenience, as its exactly the same as:

        db:fetch(mydb.friends, db:AND(db:eq(mydb.friends.city, "Magnagora"), db:like(mydb.friends.name, "I%")))

The db:OR operation only takes two arguments, and will check to see if either of the two is true. You can nest these logical operators as deeply as you need to.

You can also just pass in a string directly to db:fetch, but you have to be very careful as this will be passed straight to the SQL layer. If you don’t know any SQL then you want to avoid this… for example, in SQL there’s a very big difference between double and single quotes. If you don’t know that, then stick to the db functions. But an example is:

        db:fetch(mydb.friends, "city == 'Magnagora'")

Now, the return value of db:fetch() is always a table array that contains a table dictionary representing the full contents of all matching rows in the sheet. These are standard Lua tables, and you can perform all normal Lua operations on them. For example, to find out how many total items are contained in your results, you can simply do #results. If a request from the friends sheet were to return one row that you stored in the results variable, it would look like this if passed into the display() function:

    table {
        1: table {
            'name': 'Bob',
            'city': 'Magnagora',
            'notes': 'Trademaster of Tailoring'
        }
    }

And if you were to echo(#results), it would show 1.

The order of the returned rows from db:fetch is generally the same as the order in which you entered them into the database, but no actual guarantees are made to this. If you care about the order then you can pass one or two optional parameters after the query to db:fetch() to control this.

The first table array of fields that indicate the column names to sort by; the second is a flag to switch from the default ascending(smallest to largest) sort, to a descending(largest to smallest) sort. For example:

        db:fetch(mydb.friends, db:eq(mydb.friends.city, "Magnagora"), {mydb.friends.city})

This will return all your friends in Magnagora, sorted by their name, from smallest to largest. To reverse this, you would simply do:

        db:fetch(mydb.friends, db:eq(mydb.friends.city, "Magnagora"), {mydb.friends.city}, true)

Including more then one field in the array will indicate that in the case that two rows have the same value, the second field should be used to sort them.

If you would like to return ALL rows from a sheet, but still sort them, you can do that by passing nil into the query portion. For example:

        db:fetch(mydb.friends, nil, {mydb.friends.city, mydb.friends.name})

This will return every friend you have, sorted first by city and then their name.

Indexes and Types

The sheets we’ve defined thus far are very simple, but you can take more control over the process if you need to. For example, you may assign default values and types to columns, and the DB package will attempt to coerce them as appropriate. To do that, you change your db:create() call as:

        db:create("people", {
                friends={"name", "city", "notes"},
                enemies={
                        name="",
                        city="",
                        notes="",
                        enemied="",
                        kills=0
                }
        })

This is almost the same as the original definition, but we’ve defined that our first four fields are strings with a default value of blank, and the new kills field which is an integer that starts off at 0. The only way to set a datatype is to set a default value at this time.

Please note, beneath the DB package is SQLite, and SQLite is very data-type neutral. It doesn’t really care very much if you break those rules and put an integer in a string field or vice-versa, but the DB package will — to a limited degree — attempt to convert as appropriate, especially for the operations that work on numbers.

You may also create both standard and unique indexes. A unique index enforces that a certain criteria can only happen once in the sheet. Now, before you go and make indexes, pause and consider. There is no right or wrong answer when it comes to what to index: it depends on what kind of queries you do regularly. If in doubt, don’t make an index. Indexes will speed up reading data from the sheet, but they will slow down writing data.

To add an index, pass either the _index or _unique keys in the table definition. An example:

        db:create("people", {
                friends={"name", "city", "notes"},
                enemies={
                        name="",
                        city="",
                        notes="",
                        enemied="",
                        kills=0,
                        _index = { "city" },
                        _unique = { "name" }
                }
        })

You can also create compound indexes, which is a very advanced thing to do. One could be: _unique = { {"name", "city"} }

This would produce an effect that there could be only one "Bob" in Magnagora, but he and "Bob" in Celest could coexist happily.

Now, bear in mind: _index = { "name", "city"} creates two indexes in this sheet. One on the city field, one on the name field. But, _index = { {"name", "city"} } creates one index: on the combination of the two. Compound indexes help speed up queries which frequently scan two fields together, but don’t help if you scan one or the other.

The admonition against making indexes willy-nilly holds double for compound indexes: do it only if you really need to!

Uniqueness

As was specified, the _unique key can be used to create a unique index. This will make it so a table can only have one record which fulfills that index. If you use an index such as _unique = { "name" } then names must be unique in the sheet. However, if you use an index such as _unique = { {"name", "city"} } then you will allow more then one person to have the same name — but only one per city.

Now, if you use db:add() to insert a record which would violate the unique constraint, a hard error will be thrown which will stop your script. Sometimes that level of protection is too much, and in that case you can specify how the db layer handles violations.

There are three possible ways in which the layer can handle such violations; the default is to FAIL and error out.

You can also specify that the db layer should IGNORE any commands that would cause the unique constraint to be violated, or the new data should REPLACE the existing row.

For example:

        db:add(mydb.enemies, {name="Bob", city="Sacramento"})
        db:add(mydb.enemies, {name="Bob", city="San Francisco"})

With the name field being declared to be unique, these two commands can’t succeed normally. The first db:add() will create a record with the name of Bob, and the second would cause the uniqueness of the name field to be violated. With the default behavior (FAIL), the second db:add() call will raise an error and halt the script.

If you want the IGNORE behavior, the second command will not cause any errors and it will simply fail silently. Bob’s city will still be Sacramento.

With the REPLACE behavior, the second command will cause its data to completely overwrite and replace the first record. Bob’s city will now be San Francisco.

A word of caution with REPLACE, given:

        db:add(mydb.enemies, {name="Bob", city="Sacramento", notes="This is something."})
        db:add(mydb.enemies, {name="Bob", city="San Francisco"})

With the REPLACE behavior, the second record will overwrite the first-- but the second record does not have the notes field set. So Bob will now not have any notes. It doesn’t -just- replace existing fields with new ones, it replaces the entire record.

To specify which behavior the db layer should use, add a _violations key to the table definition:

        db:create("people", {
                friends={"name", "city", "notes"},
                enemies={
                        name="",
                        city="",
                        notes="",
                        enemied="",
                        kills=0,
                        _index = { "city" },
                        _unique = { "name" },
                        _violations = "IGNORE"
                }
        })

Note that the _violations behavior is sheet-specific.

Timestamps

In addition to strings and floats, the db module also has basic support for timestamps. In the database itself this is recorded as an integer (seconds since 1970) called an epoch, but you can easily convert them to strings for display, or even time tables to use with Lua’s built-in time support.

The most common use for the Timestamp type is where you want the database to automatically record the current time whenever you insert a new row into a sheet. The following example illustrates that:

        local mydb = db:create("combat_log",
           {
              kills = {
                 name = "",
                 area = "",
                 killed = db:Timestamp("CURRENT_TIMESTAMP"),
                 _index = { {"name", "killed"} }
              }
           }
        )


        db:add(mydb.kills, {name="Drow", area="Undervault"})

        results = db:fetch(mydb.kills)
        display(results)

The result of that final display would show you this on a newly created sheet:

        table {
          1: table {
            '_row_id': 1
            'area': 'Undervault'
            'name': 'Drow'
            'killed': table {
              '_timestamp': 1264317670
            }
          }

As you can see from this output, the killed fields contains a timestamp-- and that timestamp is stored as an epoch value. For your convienance, the db.Timestamp type offers three functions to get the value of the timestamp in easy formats. They are as_string, as_number and as_table, and are called on the timestamp value itself.

The as_number function returns the epoch number, and the as_table function returns a time table. The as_string function returns a string representation of the timestamp, with a default format of "%m-%d-%Y %H:%M:%S". You can override this format to anything you would like. Details of what you can do with epoch values, time tables, and what format codes you can use are specified in the Lua manual at: http://www.lua.org/pil/22.1.html for the Lua date/time functions.

A quick example of the usage of these functions is:

        results = db:fetch(mydb.kills)
        for _, row in ipairs(results) do
                echo("You killed " .. row.name .. " at: " .. row.killed:as_string() .."\n")
        end

Deleting

The db:delete function is used to delete rows from the sheet. It takes two arguments, the first being the sheet you are deleting and the second a query string built using the same functions used to build db:fetch() queries.

For example, to delete all your enemies in the city of Magnagora, you would do:

        db:delete(mydb.enemies, db:eq(mydb.enemies.city, "Magnagora"))

Be careful in writing these! You may inadvertantly wipe out huge chunks of your sheets if you don’t have the query parameters set just to what you need them to be. Its advised that you first run a db:fetch() with those parameters to test out the results they return.

As a convienence, you may also pass in a result table that was previously retrieved via db:fetch and it will delete only that record from the table. For example, the following will get all of the enemies in Magnagora, and then delete the first one:

        results = db:fetch(mydb.enemies, db:eq(mydb.enemies.city, "Magnagora"))
        db:delete(mydb.enemies, db:eq(mydb.enemies._row_id, results[1]._row_id))

That is equivelent to:

        db:delete(mydb.enemies, results[1])

You can even pass a number directly to db:delete if you know what _row_id you want to purege.

A final note of caution: if you want to delete all the records in a sheet, you can do so by only passing in the table reference. To try to protect you from doing this inadvertantly, you must also pass true as the query after:

        db:delete(mydb.enemies, true)

Updating

If you make a change to a table that you have received via db:fetch(), you can save those changes back to the database by doing:

        db:update(mydb.enemies, results[1])

A more powerful (and somewhat dangerous, be careful!) function to make changes to the database is db:set, which is capable of making sweeping changes to a column in every row of a sheet. Beware, if you have previously obtained a table from db:fetch, that table will NOT represent this change.

The db:set() function takes two arguments: the field to change, the value to set it to, and the db:fetch() like query to indicate which rows should be affected. If you pass true as the last argument, ALL rows will be changed.

To clear out the notes of all of our friends in Magnagora, we could do:

        db:set(mydb.friends.notes, "", db:eq(mydb.friends.notes, "Magnagora"))

Be careful in writing these!

Transactions

As was specified earlier, by default the db module commits everything immediately whenever you make a change. For power-users, if you would like to control transactions yourself, the following functions are provided on your database instance:

        local mydb = db:get_database("my_database")
        mydb._begin()
        mydb._commit()
        mydb._rollback()
        mydb._end()

Once you issue a mydb._begin() command, autocommit mode will be turned off and stay off until you do a mydb._end(). Thus, if you want to always use transactions explicitly, just put a mydb._begin() right after your db:create() and that database will always be in manual commit mode.

Migrating to Mudlet

From Nexus

Trigger patterns

Lets start with triggers. For translating Nexus patterns into Mudlet, use the following table below. Make sure to set the pattern type in Mudlet to be perl regex, because the default substring works differently.

Mudlet Nexus What it does

(\w+)

{w}

Match one or more non-whitespace characters (a 'word')

(\d+)

{d}

Match one or more numeric digits (a number)

([abc])

{[abc]}

Match one or more of either 'a', 'b' or 'c'. (a character class)

([^abc])

{[^abc]}

Match one or more of anything EXCEPT 'a', 'b', or 'c'

(.*)

{*}

Match zero or more characters (anything)

^

{<}

Match the beginning of a line (not the actual newline character)

$

{>}

Match the end of a line (not the actual newline character)

Note If you just have a pattern with a {<} and {>} and nothing else, copy the pattern over without the {<}{>} and select the exact match pattern type. For example {<}You sit down.{>} in Nexus would become You sit down. in Mudlet with the pattern type of exact match. This is easier to read and matches way faster!

Basic scripting

The Nexus way of doing a function is like this: #function stuff it does. In Mudlet, it’s function(stuff it does). Note that if you’d like to give text to a function, you put it inside double or single quotes.

Nexus
#send hi
#echo Hello to me!
Mudlet
send ("hi")
echo ("Hello to me!")

If you’d like to use a variable inside text, you’d put it outside the quotes and glue it together with two dots.

Nexus
#set number 1234
#echo My number is: $number
#echo the $number is in the middle of my text
Mudlet
number = 1234
echo ("My number is: " .. number)
echo ("the " .. number .. " is in the middle of my text")

wildcards in Mudlet

To use the wildcard variable in Nexus, you’d use $1 for the first match, $2 for the second and so on. In Mudlet, the matches[] table contains the matches - and it starts placing the matches from 2. So the Nexus $1 would be matches[2], $2 would be matches[3].

Nexus:
{w} kicks you.
#set badguy $1
#echo $badguy kicked me!

Mudlet:
(\w+) kicks you\.
badguy = matches[2]
echo(badguy .. " kicked me!")

Another example would be the targetting alias:

Nexus:
alias name: t
alias script: #set target $1

Mudlet:
alias pattern: ^t (.*)$
alias script: target = matches[2]

The reason the first match goes into matches[2] and not matches[1] is because matches[1] contains the part of the line that matched your trigger / alias.

#wait in Mudlet

In Nexus, a #wait will freeze the script while it’s executing - such that commands after it are delayed. In Mudlet, we use tempTimer which that works a bit differently - it doesn’t freeze the script, but instead makes it so commands a tempTimer is asked to do will get done in the future.

So the difference is that a tempTimer doesn’t freeze the commands following it at all, everything gets done at once as usual. Just things a tempTimer was asked to do get done in the future.

Nexus script:
#send hello
#wait 1000
#send hi
#wait 500
#send bye

Mudlet:
send("hello")
tempTimer(1, [[send("hi")]])
tempTimer(1.5, [[send("bye")]])

ifs in Mudlet

Next are the #if statements. Here’s a table comparing the syntaxes of Nexus and Mudlet:

Mudlet Nexus

if <condition> then <text> end

#if <condition> <text>

#if <condition> { <script> }

if <condition> then <script> else <script> end

#if <condition> { <script> } else { <script> }

#if <condition> { <script> } { <script> }

if <condition> then <script> elseif <condition> then <script> end

#if <condition> { <script> } elsif <condition> { <script> }

if <condition> then <script> elseif <condition> then <script> else <script> end

#if <condition> { <script> } elsif <condition> { <script> } else { <script> }

Here is the sample side-by-side comparison of how you’d use it:

Mudlet Nexus
-- If foo is true, echo that fact.
if foo then echo ("Foo is true!") end -- If foo is true, echo that fact.

-- A slight modification of the above that illustrates an 'else' clause.
-- Note that the 'then' is always necessary to avoid confusion.
if foo then echo ("Foo is true!") else echo ("Foo is false!") end

-- Illustration of putting more than one statement in a block (also
-- spans lines here). Note the if doesn't end until the last 'end' is
-- encountered.
-- We also add a \n at the end of multiple echos so each one is on it's own line.
if foo then
   echo ("Foo is true!\n")
   echo ("Isn't it grand?\n")
   echo ("These commands are all on separate lines too!\n")
end

-- This shows how ifs continue (on 'logical' lines) even though the
-- blocks in its constituent clauses may have multiple lines. The
-- following lines are all _one_ single if statement.
if foo > 50 then
   echo ("Foo is big!\nYes it is.")
elseif foo > 10 then
   echo ("Foo is pretty big...\n")
   echo ("I've seen bigger.")
else
   echo ("Foo is actually kind of small.")
end

-- Ifs can be nested too.
if foo then
   if bar then
      echo ("Both foo and bar are true.")
   else
      echo ("Foo's true, but bar isn't.")
   end
end
// If $foo is true, echo that fact.
#if $foo #echo Foo is true!          // If $foo is true, echo that fact.

// A slight modification of the above that illustrates an 'else' clause.
// The second form shows that the word 'else' is actually optional too.
// (The two lines are functionally the same.)
#if $foo { #echo Foo is true! } else { #echo Foo is false! }
#if $foo { #echo Foo is true! } { #echo Foo is false! }

// Illustration of putting more than one statement in a block (also
// spans lines here). Note the if doesn't end until the last '}' is
// encountered.
#if $foo {
   #echo Foo is true!
   #echo Isn't it grand?
   #echo These commands are all on separate lines too!
}

// This shows how ifs continue (on 'logical' lines) even though the
// blocks in its constituent clauses may have multiple lines. The
// following lines are all _one_ single if statement.
#if $foo > 50 {
   #echo Foo is big!
   #echo Yes it is.
} elsif $foo > 10 {
   #echo Foo is pretty big...
   #echo I've seen bigger.
} else {
   #echo Foo is actually kind of small.
}

// Ifs can be nested too.
#if $foo {
   #if $bar {
      #echo Both foo and bar are true.
   } else {
      #echo Foo's true, but bar isn't.
   }
}

Mudlet equivalents of Nexus functions

Now that we got the ifs covered, lets go over the Mudlet equivalents of other Nexus functions. Note that this is a direct Nexus→Mudlet comparison; certain functions in Mudlet have much more capability.

Mudlet Nexus
<variable> = <variablename> <+ - /  *> <number or variable>    
#add <variablename> <expression>
echo "text\n" or echo("text\n")        
#echo <text>
echo "text" or echo("text")            
#echo_ <text>
enableAlias("<group name>"), enableTrigger(), enableKey() or enableTimer() 
#groupon <group name>
disableAlias("<group name>"), disableTrigger(), disableKey() or disableTimer() 
#groupoff <group name>
selectString (line, 1) bg("<color>") resetFormat() 
#highlight <color>
deleteLine()                       
#gag
openUrl("<url>")*                  
#openurl <url>
send("<stuff>")                    
#send <text>
sendAll("hi", "hello", "bye")      
#send_ hi #send_ hello #send bye
<variable name> = "<text value>"   
#set <variable name> <text value>
<variable name> = <expression>     
#set <variable name> = <expression>
<variable name> = nil              
#unset <variable name>
tempTimer(<time in s>, [[<lua code to do once time is up>]])
#wait <milliseconds>

*Planned for Mudlet 1.0.6

How to call aliases in Mudlet

The typical workflow in Nexus is that you’d define the aliases for yourself to use, and also use aliases for system-related things (things like checking if you’d need to eat a herb, etc).

While the first use is good, the second isn’t - because the user can also run this alias at an inopportune time, because if you want to call an alias it has to match all alises again, and so on.

Now, while in Mudlet you can call another alias with the expandAlias() function, this is strongly discouraged. What you do instead if create a function (for example, send() and echo() are functions) that you can then call from your alias or trigger. This has many advantages - it’s faster, you can easily give your function values, and your function can return you values, too.

To make a function, you go to the Scripts section, create a new script, and write in your function:

function <name> ()
    <stuff it does>
end

For example, if we want to make a function that does two things for us at once, we’d do this:

function eat_bloodroot()
    send ("outr bloodroot")
    send ("eat bloodroot")
end

Then just do this in our alias or trigger to outr and eat bloodroot:

eat_bloodroot()

As mentioned, you can also give things to functions - in this case lets expand the function to eat any herb for us we tell it:

function eat (what)
    send ("outr " .. what)
    send ("eat " .. what)
end

[...]
eat ("bloodroot")
eat ("kelp")
eat ("ginseng")

Lastly, functions can also give you data back. One useful example for this is to break down tasks into functions, which will help your code readibility:

function can_i_stand()
    if not paralyzed and not prone and not stunned then
        return true
    else
        return false
end

[...]

if can_i_stand() and have_balance and have_equilibrium then
    send ("stand")
end

Notes to be aware of

To finish off, a couple of points that should be remembered:

  • Mudlet does profile snapshots (nexus archives) automatically - to load a different one, select it from the list when connecting

  • Mudlet can import and export xml

  • Mudlet and Nexus xml formats aren’t compatible

  • Mudlet can do nexus graphics, here is a package for - Achaea

  • Mudlet has a ton more features such are more speed, less bugs, copy from the normal window, replay logging, color html logging, more powerful scripting, and the list goes on.

Lua API

Mudlet defines several global Lua variables that are accessible from anywhere.

Table 1. Built-in Lua Variables
Variable Name Description

command

This variable holds the current user command. This is typically used in alias scripts.

line

This variable holds the content of the current line as being processed by the trigger engine. The engine runs all triggers on each line as it arrives from the MUD.

matches[n]

This Lua table is being used by Mudlet in the context of triggers that use Perl regular expressions. matches[1] holds the entire match, matches[2] holds the first capture group, matches[n] holds the n-th capture group.

If the trigger uses the Perl style /g switch to evaluate all possible matches of the given regex within the current line, matches[n+1] will hold the second entire match, matches[n+2] the first capture group of the second match and matches[n+m] the m-th capture group of the second match.

multimatches[n][m]

This table is being used by Mudlet in the context of multiline triggers that use Perl regular expression. It holds the table matches[n] as described above for each Perl regular expression based condition of the multiline trigger. multimatches[5][4] may hold the 3rd capture group of the 5th regex in the multiline trigger. This way you can examine and process all relevant data within a single script. Have a look at this example.

Alphabetical Function Index

Click on the respective function to get more information:

A

B

C

D

E

F

G

H

I

K

M

O

P

R

S

T

W

Most Important Functions

send( command, echo the value = true/false )

This sends "command" directly to the network layer, skipping the alias matching. The optional second argument of type boolean (print) determines if the outgoing command is to be echoed on the screen.

If you want your command to be checked if it’s an alias, use expandAlias() instead. example:

send( "Hello Jane" ) --echos the command on the screen
send( "Hello Jane", true ) --echos the command on the screen
send( "Hello Jane", false ) --does not echo the command on the screen

echo( windowName, text )

This function appends text at the end of the current line. The current cursor position is ignored. Use moveCursor() and insertText() if you want to print at a different cursor position.

If the first argument is omitted the main console is used, otherwise the mini console windowName. === Example 1:

echo( "Hello world\n" ) -- writes "Hello world" to the main screen.
echo( "info", "Hello this is the info window" ) -- writes text to the mini console named "info" if such a window exists

Echos a piece of text as a clickable link.

  • text - text to display in the echo. Same as a normal echo().

  • command - lua code to do when the link is clicked.

  • hint - text for the tooltip to be displayed when the mouse is over the link.

  • boolean - if true, then the link will use the current selection style (colors, underline, etc). If missing or false, it will use the default link style - blue on black underlined text.

Available since 1.1.0-pre1.

Example:

echoLink("hi", [[echo("hey bub!")]], "click me now")

selectString( text, number_of_match ) returns position in line or -1 on error (text not found in line)

Selects a substring from the line where the user cursor is currently positioned. You can move the user cursor with moveCursor(). When a new line arrives from the MUD, the user cursor is positioned at the beginning of the line. However, if one of your trigger scripts moves the cursor around you need to take care of the cursor position yourself and make sure that the cursor is in the correct line if you want to call one of the select functions. To deselect text, see deselect().

Note To prevent selection of random data use the error return if not found like this: if selectString( "big monster", 1 ) > -1 then setFgColor( 255,0,0) end

decho (text[, foreground color] [, background color] [, true/false] [, name of console])

  • text - the text that you’d like to echo with embedded color tags. Tags take the RGB values only, see below for an explanation.

  • foreground color - optional string containing value for foreground color as a named color.

  • background color optional string containing value for background color as a named color.

  • true/false - optional boolean that tells the function to use insertText() rather than echo(). Note that if sending to miniConsole, this has to be false.

  • name of console - optional name of the console to echo to. Defaults to main.

How text works

Color changes can be made using the format <FR,FG,FB:BR,BG,BB> where each field is a number from 0 to 255. The background portion can be omitted using <FR,FG,FB> or the foreground portion can be omitted using <:BR,BG,BB>. Arguments 2 and 3 set the default fore and background colors for the string using the same format as is used within the string, sans angle brackets, e.g. decho("test", "255,0,0", "0,255,0")

deselect()

Clears the current selection in the main window.

deselect(windowname)

Clears the current selection in the given miniConsole.

deleteLine()

deletes the current Line under the user cursor. Note: This is a high speed gagging tool and it is very good at this task. It is meant to be used when the line can be omitted entirely in the output. If you want to replace this line with something else have a look at the replace() functions below. Note that scripts such as: deleteLine(); echo("this line is gone"); will not work because lines that have been gagged with deleteLine() will not be rendered even if there is text in the buffer. → wrapLine() for details on how to force a re-render if this is necessary for some reason. This is not the recommended way of replacing text. → replace() and wrapLine()

tempTimer( seconds, lua code )

Creates a temporary single shot timer. You can use 2.3 seconds or 0.45 etc. After it has fired, the timer will be deactivated and killed. see here for details.

disableAlias (name)

Disables/deactivates an alias with the given name. This means that when you type in text that should match it’s pattern, it won’t match and will be sent to the MUD. If several aliases have this name, they’ll all be disabled.

enableTrigger( name )

Enables the Trigger name. → enableTrigger()

disableTrigger( name )

Use trigger name or the id returned by tempTrigger() to identify the timer that you want to disable. → disableTrigger( name )

insertText( text )

inserts text at the current cursor position in the main window; if the cursor has not been explicitly moved this function will always print at the beginning of the line whereas the echo() function will always print at the end of the line

insertText( windowName, text )

inserts text at cursor position in window windowName

Same as echoLink() but inserts the text at the current cursor position, while echoLink inserts at the end of the current line.

Available since 1.1.0-pre1.

insertPopup([window], text, {commands}, {hints}, [bool current or default format])

Same as echoPopup(), but inserts text at the current cursor position.

Available since 1.1.0-pre1.

suffix( text )

Suffixes text at the end of the current line when used in a trigger.

prefix( text )

Prefixes text at the beginning of the current line when used in a trigger.

Example:

-- Prefix the hours, minutes and seconds onto our prompt even though Mudlet has a button for that
prefix(os.date("%H:%M:%S "))

result = string:title(text)

Capitalizes the given text for you.

Examples:

test = "bob"
test = string.title(test)
-- test is now "Bob"

testname = string.title("anna")
-- testname is now Anna

permAlias(name, parent, regex, lua code)

Creates a persistent alias that stays after Mudlet is restarted and shows up in the Script Editor.

  • name is the name you’d like the alias to have.

  • parent is the name of the group, or another alias you want the trigger to go in - however if such a group/alias doesn’t exist, it won’t do anything. Use "" to make it not go into any groups.

  • regex is the pattern that you’d like the alias to use.

  • lua code is the script the alias will do when it matches.

Examples:

-- creates an alias called "new alias" in a group called "my group"
permAlias("new alias", "my group", "^test$", [[echo ("say it works! This alias will show up in the script editor too.")]])
Note

Note that Mudlet by design allows duplicate names - so calling permAlias with the same name will keep creating new aliases. You can check if an alias already exists with the exists() function.

permSubstringTrigger( name, parent, pattern, lua code )

Creates a persistent trigger with a substring pattern that stays after Mudlet is restarted and shows up in the Script Editor.

  • name is the name you’d like the trigger to have.

  • parent is the name of the group, or another trigger you want the trigger to go in - however if such a group/trigger doesn’t exist, it won’t do anything. Use "" to make it not go into any groups.

  • pattern table is a table of patterns that you’d like the trigger to use - it can be one or many.

  • lua code is the script the trigger will do when it matches.

Example:

-- Create a trigger to highlight the word "pixie" for us
permSubstringTrigger("Highlight stuff", "General", {"pixie"},
[[selectString(line, 1) bg("yellow") resetFormat()]])

-- Or another trigger to highlight several different things
permSubstringTrigger("Highlight stuff", "General", {"pixie", "cat", "dog", "rabbit"},
[[selectString(line, 1) fg ("blue") bg("yellow") resetFormat()]])
Note

Note that Mudlet by design allows duplicate names - so calling permSubstringTrigger with the same name will keep creating new triggers. You can check if a trigger already exists with the exists() function.

permRegexTrigger( name, parent, pattern, lua code )

Creates a persistent trigger with a regex pattern that stays after Mudlet is restarted and shows up in the Script Editor.

  • name is the name you’d like the trigger to have.

  • parent is the name of the group, or another trigger you want the trigger to go in - however if such a group/trigger doesn’t exist, it won’t do anything. Use "" to make it not go into any groups.

  • pattern table is a table of patterns that you’d like the trigger to use - it can be one or many.

  • lua code is the script the trigger will do when it matches.

Example:

-- Create a regex trigger that will match on the prompt to record your status
permRegexTrigger("Prompt", "", {"^(\d+)h, (\d+)m"}, [[health = tonumber(matches[2]; mana = tonumber(matches[3])]]
Note

Note that Mudlet by design allows duplicate names - so calling permRegexTrigger with the same name will keep creating new triggers. You can check if a trigger already exists with the exists() function.

permBeginOfLineStringTrigger( name, parent, pattern table, lua code )

Creates a persistent trigger with a begin of line substring pattern that shows up in the Script Editor andstays after Mudlet is restarted.

  • name is the name you’d like the trigger to have.

  • parent is the name of the group, or another trigger you want the trigger to go in - however if such a group/trigger doesn’t exist, it won’t do anything. Use "" to make it not go into any groups.

  • pattern table is a table of patterns that you’d like the trigger to use - it can be one or many.

  • lua code is the script the trigger will do when it matches.

Example:

-- Create a trigger that will match on anything that starts with "You sit" and do "stand".
-- It will not go into any groups, so it'll be on the top.
permBeginOfLineStringTrigger("Stand up", "", {"You sit"}, [[send ("stand")]])

-- Another example - lets put our trigger into a "General" folder and give it several patterns.
permBeginOfLineStringTrigger("Stand up", "General", {"You sit", "You fall", "You are knocked over by"}, [[send ("stand")]])
Note

Note that Mudlet by design allows duplicate names - so calling permBeginOfLineStringTrigger with the same name will keep creating new triggers. You can check if a trigger already exists with the exists() function.

permTimer(name, parent, seconds, lua code)

Creates a persistent timer that stays after Mudlet is restarted and shows up in the Script Editor.

Name is the timer name, parent is the name of the timer group you want the timer to go in. Seconds is a number specifying a delay after which the timer will do the lua code you give it as a string.

Examples:

permTimer("my timer", "first timer group", 4.5, [[send ("my timer that's in my first timer group fired!")]])
Note

Note that Mudlet by design allows duplicate names - so calling permTimer with the same name will keep creating new timers. You can check if a timer already exists with the exists() function.

selectSection( from, length_of_string )

Select text on the line under the current cursor position. Use absolute column number for start of selection and length of selection The function returns true on success and false if the selection is not possible.

selectSection( windowName, from, length )

just like selectSection() but does not operate on the main window, but on window windowName. See selectSection() for a detailed description

selectCaptureGroup( groupNumber ) with first group = 0

Selects the content of the capture group number in your Perl regular expression e.g. "you have (\d+) Euro". If you want to color the amount of money you have green you do: selectCaptureGroup(1); setFgColor(0,255,0)

replace( with )

Replaces the currently selected text with the new text. To select text, use selectString() and similar function.

If you’d like to delete/gag the whole line, use deleteLine().

example:

-- replace word "troll" with "cute trolly"
selectString("troll",1)
replace("cute trolly")

-- lets replace the whole line
selectString(line, 1)
replace("Out with the old, in with the new!")

replace( windowName, with )

Same as above, but replaces text in a window (miniConsole, label).

Example:

replaceAll( what, with )

replaces all occurrences of what in the current line with with → replace()

feedTriggers( text )

This function will have Mudlet parse the given text as if it came from the MUD - one great application is trigger testing. You can use \n to represent a new line - you also want to use it before and after the text you’re testing, like so:

feedTriggers("\nYou sit yourself down.\n")

The function also accept ANSI color codes that are used in MUDs. A sample table can be found here.

Example:

feedTriggers("\nThis is \27[1;32mgreen\27[0;37m, \27[1;31mred\27[0;37m, \27[46mcyan background\27[0;37m," ..
"\27[32;47mwhite background and green foreground\27[0;37m.\n")

fg( color_name )

Sets the current selection foreground color to color_name which is one of the pre defined colors in the table below.

Table 2. Color table (for use in the fg("colorname") and bg("colorname") functions)

snow

ghost_white

white_smoke

gainsboro

floral_white

old_lace

linen

antique_white

papaya_whip

blanched_almond

bisque

peach_puff

navajo_white

moccasin

cornsilk

ivory

lemon_chiffon

seashell

honeydew

mint_cream

azure

alice_blue

lavender

lavender_blush

misty_rose

white

black

dark_slate_gray

dark_slate_grey

dim_gray

dim_grey

slate_gray

slate_grey

light_slate_gray

light_slate_grey

gray

grey

light_grey

light_gray

midnight_blue

navy

navy_blue

cornflower_blue

dark_slate_blue

slate_blue

medium_slate_blue

light_slate_blue

medium_blue

royal_blue

blue

dodger_blue

deep_sky_blue

sky_blue

light_sky_blue

steel_blue

light_steel_blue

light_blue

powder_blue

pale_turquoise

dark_turquoise

medium_turquoise

turquoise

cyan

light_cyan

cadet_blue

medium_aquamarine

aquamarine

dark_green

dark_olive_green

dark_sea_green

sea_green

medium_sea_green

light_sea_green

pale_green

spring_green

lawn_green

green

chartreuse

medium_spring_green

green_yellow

lime_green

yellow_green

forest_green

olive_drab

dark_khaki

khaki

pale_goldenrod

light_goldenrod_yellow

light_yellow

yellow

gold

light_goldenrod

goldenrod

dark_goldenrod

rosy_brown

indian_red

saddle_brown

sienna

peru

burlywood

beige

wheat

sandy_brown

tan

chocolate

firebrick

brown

dark_salmon

salmon

light_salmon

orange

dark_orange

coral

light_coral

tomato

orange_red

red

hot_pink

deep_pink

pink

light_pink

pale_violet_red

maroon

medium_violet_red

violet_red

magenta

violet

plum

orchid

medium_orchid

dark_orchid

blue_violet

purple

medium_purple

thistle

Note that you may use the showColors() function in an alias to display these names with their respective colors in Mudlet.

Example:

-- in a trigger, lets color all words on the current line green
selectString(line, 1)
fg("green")
resetFormat()

-- or lets only color Anne's name in tomato color
selectString("Anne", 1)
fg("tomato")
resetFormat()

bg( color_name )

Same as fg(), but changes the background color of the text (ie, highlights).

See also: fg(), setBgColor().

Example:

-- in a trigger, lets highlight all words on the current line red
selectString(line, 1)
fg("red")
resetFormat()

-- or lets only color dogs in blue
selectString("dog", 1)
fg("tomato")
resetFormat()

resetFormat()

Resets the character format to default. This should be used after you have highlightd some text or changed the current foreground or background color, but you don’t want to keep using these colors for further prints. If you set a foreground or background color, the color will be used until you call resetFormat() on all further print commands.

setBgColor( r,g,b )

Sets the current text background color in the main window. Values are RGB: red, green, blue ranging from 0-255 e.g. for red: setFgColor(255,0,0) If you set a foreground or background color, the color will be used until you call resetFormat() on all further print commands.

setBgColor( windowName, r, g, b )

Sets the current text background color in window windowName. If you have selected text prior to this call, the selection will be highlightd otherwise the current text background color will be changed. If you set a foreground or background color, the color will be used until you call resetFormat() on all further print commands.

example 1 highlights the first occurrence of the string "Tom" in the current line with a red background color.

selectString( "Tom", 1 )
setBgColor( 255,0,0 )

example 2 prints "Hello" on red background and "You" on blue.

setBgColor(255,0,0)
echo("Hello")
setBgColor(0,0,255)
echo(" You!")
resetFormat()

setFgColor( r, g, b )

Sets the current text foreground color in the main window. Values are RGB: red, green, blue ranging from 0-255 e.g. for blue: setBgColor(0,0,255)

setFgColor( windowName, r, g, b )

Sets the current text foreground color in window windowName If the windowName parameters omitted, the main screen will be used.

setBold( windowName, bool )

Sets the current text font to bold (true) or non-bold (false) mode. If the windowName parameters omitted, the main screen will be used.

setItalics( windowName, bool )

Sets the current text font to italics/non-italics mode. If the windowName parameters omitted, the main screen will be used.

setUnderline( windowName, bool )

Sets the current text font to underline/non-underline mode. If the windowName parameters omitted, the main screen will be used.

openUserWindow( name )

Opens a user dockable console window for user output e.g. statistics, chat etc. If a window of such a name already exists, nothing happens. You can move these windows, dock them, make them into notebook tabs or float them.

openUrl ( url )

Opens the default OS browser for the given URL.

Examples:

openUrl("http://google.com")
openUrl("www.mudlet.org")

disableTimer( name )

Disables a timer from running it’s script when it fires - so the timer cycles will still be happening, just no action on them. If you’d like to permanently delete it, use killTrigger() instead.

Use timer name or the id returned by tempTimer() to identify the timer that you want to disable.

disableTrigger( name )

Use trigger name or the id returned by tempTrigger() to identify the timer that you want to disable.

disableKey( name )

Uses trigger name as id or the id returned by tempTrigger()

enableAlias(name)

Enables/activates the alias by it’s name. If several aliases have this name, they’ll all be enabled.

enableTimer( name )

enables or activates a timer that was previously disabled. The parameter "name" expects the timer ID that was returned by tempTimer() on creation of the timer or the name of the timer in case of a GUI timer

enableTrigger( name )

enables a Trigger. see enableTimer() for more details.

enableKey( name )

enable key or key group "name" (hot keys or action keys).

expandAlias( command, print=1 )

like send(), but without bypassing alias expansion. This function may lead to infinite recursion if you are not careful. This function can be used to make recursive alias expansion. expandAlias( "Hello Tom" ) echos the command on the screen expandAlias( "Hello Jane" ) sends the command visually unnoticeable by the user send

howmany = exists (name, type)

Tells you how many things of the given type exist. Type can be "alias", "trigger", or "timer".

Example:

echo("I have " .. exists("my trigger", "trigger") .. " triggers called 'my trigger'!")

You can also use this alias to avoid creating duplicate things, for example:

-- this code doesn't check if an alias already exists and will keep creating new aliases
permAlias("Attack", "General", "^aa$", [[send ("kick rat")]])

-- while this code will make sure that such an alias doesn't exist first
-- we do == 0 instead of 'not exists' because 0 is considered true in Lua
if exists("Attack", "alias") == 0 then
    permAlias("Attack", "General", "^aa$", [[send ("kick rat")]])
end

raiseEvent( event_name, arg-1, … arg-n )

Raises the event event_name. The event system will call the main function (the one that is called exactly like the script name) of all such scripts that have registered event handlers. If an event is raised, but no event handler scripts have been registered with the event system, the event is ignored and nothing happens. This is convenient as you can raise events in your triggers, timers, scripts etc. without having to care if the actual event handling has been implemented yet - or more specifically how it is implemented. Your triggers raise an event to tell the system that they have detected a certain condition to be true or that a certain event has happened. How - and if - the system is going to respond to this event is up to the system and your trigger scripts don’t have to care about such details. For small systems it will be more convenient to use regular function calls instead of events, however, the more complicated your system will get, the more important events will become because they help reduce complexity very much.

The corresponding event handlers that listen to the events raised with raiseEvent() need to use the script name as function name and take the correct number of arguments. NOTE: If you raise an event with 5 arguments but your event handlers functions only take 2,10 or 0 arguments, the functions will not be called. For example: raiseEvent("fight") a correct event handler function would be: myScript( event_name ). In this example raiseEvent uses minimal arguments, name the event name. There can only be one event handler function per script, but a script can still handle multiple events as the first argument is always the event name. So you can call your own special handlers for individual events. The reason behind this is that you should rather use many individual scripts instead of one huge script that has all your function code etc. Scripts can be organized very well in trees and thus help reduce complexity on large systems.

tempTimer( seconds, lua code ) returns timer ID, ID is a string and not a number.

Creates a temporary single shot timer and returns the timer ID for subsequent enableTimer(), disableTimer() and killTimer() calls. You can use 2.3 seconds or 0.45 etc. After it has fired, the timer will be deactivated and killed. example:

tempTimer( 0.3, [[send("kill monster")]] )

This script will send the command "kill monster" 0.3 seconds after this function has been called. Note that this function does not wait until 0.3 seconds have been passed, but it will start a timer that will run the Lua script that you have provided as a second argument after 0.3 seconds.

Note [[ ]] can be used to quote strings in Lua. The difference to the usual `" " quote syntax is that `[[ ]] also accepts the character ". Consequently, you don’t have to escape the " character in the above script. The other advantage is that it can be used as a multiline quote.

Also note that the Lua code that you provide as an argument is compiled from a string value when the timer fires. This means that if you want to pass any parameters by value e.g. you want to make a function call that uses the value of your variable myGold as a parameter you have to do things like this:

tempTimer( 3.8, [[echo("at the time of the tempTimer call I had ]] .. myGold .. [[ gold.")]] )

You can also use the global variable table, the so-called _G table, directly to get the value that is associated with the name of the variable. The _G table holds all global variables and all global function names as keys with the respective values as values.

Here is yet another way to expand variables in Lua, posted in the Mudlet user forum:

How-to: expand variables

Post by eraldo » Sat Aug 22, 2009 4:29 am
Example:

Global variable:
MyNumber = 10

function:

            testFunction(a, b)
                local VariableNameString = a .. b
                local value = tonumber( VariableNameString )
                echo( value )
            end


Function call:
testFunction( "My", "Number" )

Result:
10

tempTrigger( string, lua code ) returns trigger ID, ID is a string and not a number.

This function creates a temporary trigger using substring matching. Contrary to tempTimers, tempTriggers lives throughout the entire session until it is explicitly disabled or killed. Disabled tempTimers can be re-enabled with enableTrigger(). This is much faster than killing the trigger and creating a new one. This is the second fastest trigger (with begin of line substring patterns being the fastest) and should be used instead of regex triggers whenever possible.

tempLineTrigger( from, howMany, LuaCode ) returns trigger ID, ID is a string and not a number.

Temporary trigger that will fire on n consecutive lines following the current line. This is useful to parse output that is known to arrive in a certain line margin or to delete unwanted output from the MUD. Example: tempLineTrigger( 1, 3, ) will fire 3 times starting with the line from the MUD. tempLineTrigger( 20, 2, ) will fire 20 lines after the current line and fire twice on 2 consecutive lines. The function returns the ID of the newly created temporary trigger. You can use this ID to enable/disable or kill this trigger later on.

tempRegexTrigger( regex, lua code ) returns trigger ID

temporary trigger using Perl regex pattern matching <<tempTrigger, This function returns the ID of the newly created temporary trigger. ID is a string and not a number. You can use this ID to enable/disable or kill this trigger later on. → tempTrigger()

killAlias (name)

Deletes an alias with the given name. If several aliases have this name, they’ll all be deleted.

killTimer( id ) returns true or false

Deletes a tempTimer. Use the Timer ID returned by tempTimer() as name parameter. ID is a string and not a number. This function returns true on success and false if the timer id doesn’t exist anymore (=timer has already fired) or the timer is not a temp timer. Note that non-temporary timers that you have set up in the GUI cannot be deleted with this function. Use disableTimer() to turn them on or off.

killTrigger( id ) returns true or false

Deletes a tempTrigger according to trigger ID. ID is a string value, not a number.

See also: killTimer()

openUserWindow( name )

Opens a user dockable console window for user output e.g. statistics, chat etc. If a window of such a name already exists, nothing happens. You can move these windows, dock them, make them into notebook tabs or float them. Most often a mini console is more useful than a dockable window → createMiniConsole

x,y = calcFontSize(font_size)

This returns you the height and the length of letters for the given font size. As the primary intended usage is for calculating the needed dimensions of a miniConsole, it doesn’t accept a font argument - as the miniConsoles currently only work with the default font for the sake of portability.

Examples:

-- Create a miniConsole that is 45 letters in length and 25 letters in height for font size 7
font_size = 7

local x, y = calcFontSize( font_size )
createMiniConsole("map",0,0,0,0)
setMiniConsoleFontSize("map", font_size)
resizeWindow( "map", x*45, y*25 )

clearUserWindow( window_name )

Clears the user window or a mini console with the name given as argument.

copy()

Copies the current selection to the clipboard. This function operates on rich text, i. e. the selected text including all its format codes like colors, fonts etc. in the clipboard until it gets overwritten by another copy operation. example: This script copies the current line on the main screen to a user window (mini console) named chat and gags the output on the main screen.

selectString( line );
copy();
appendBuffer("chat");
replace("This line has been moved to the chat window!")

getLineNumber()

Gets the absolute line number of the current user cursor.

getColumnNumber()

Gets the absolute column number of the current user cursor.

getLineCount()

Gets the absolute amount of lines in the current console buffer

getAreaTable()

Returns a key-value table with all known areas and their IDs.

getAreaRooms(area id)

Returns a key-value table with all rooms for a given area ID.

timestamp = getTimestamp( optional console_name, lineNumber )

Returns the timestamp string as it’s seen when you enable the timestamps view (blue i button bottom right).

Available since 1.1.0-pre1

Example:

Echo the timestamp of the current line in a trigger:

echo(getTimestamp(getLineCount()))

getLines( from_line_number, to_line_number ) returns a Lua table with the content of the lines on a per line basis

Returns a section of the content of the screen text buffer. The form of the return value is: Lua_table[relative_linenumber, content] Absolute line numbers are used.

getLineCount() returns number

Returns the number of the last line in the buffer.

moveCursor( windowName, x, y ) returns true or false

Moves the user cursor of the window windowName to the absolute point (x,y). This function returns false if such a move is impossible e.g. the coordinates don’t exist. To determine the correct coordinates use getLineNumber(), getColumnNumber() and getLastLineNumber(). The trigger engine will always place the user cursor at the beginning of the current line before the script is run. If you omit the windowName argument, the main screen will be used.

Example:

-- set up the small system message window in the top right corner

-- determine the size of your screen
WindowWidth=0;
WindowHeight=0;
WindowWidth, WindowHeight = getMainWindowSize();

-- define a mini console named "sys" and set its background color
createMiniConsole("sys",WindowWidth-650,0,650,300)
setBackgroundColor("sys",85,55,0,255);

-- you *must* set the font size, otherwise mini windows will not work properly
setMiniConsoleFontSize("sys", 12);
-- wrap lines in window "sys" at 65 characters per line
setWindowWrap("sys", 60);
-- set default font colors and font style for window "sys"
setTextFormat("sys",0,35,255,50,50,50,0,0,0);
-- clear the window
clearUserWindow("sys")

moveCursorEnd("sys")
setFgColor("sys", 10,10,0)
setBgColor("sys", 0,0,255)
echo("sys", "test1---line1\n<this line is to be deleted>\n<this line is to be deleted also>\n")
echo("sys", "test1---line2\n")
echo("sys", "test1---line3\n")
setTextFormat("sys",158,0,255,255,0,255,0,0,0);
--setFgColor("sys",255,0,0);
echo("sys", "test1---line4\n")
echo("sys", "test1---line5\n")
moveCursor("sys", 1,1)

-- deleting lines 2+3
deleteLine("sys");
deleteLine("sys");

-- inserting a line at pos 5,2
moveCursor("sys", 5,2);
setFgColor("sys", 100,100,0)
setBgColor("sys", 255,100,0)
insertText("sys","############## line inserted at pos 5/2 ##############");

-- inserting a line at pos 0,0
moveCursor("sys", 0,0)
selectCurrentLine("sys");
setFgColor("sys", 255,155,255);
setBold( "sys", true );
setUnderline( "sys", true );
setItalics( "sys", true );
insertText("sys", "------- line inserted at: 0/0 -----\n");

setBold( "sys", true )
setUnderline( "sys", false )
setItalics( "sys", false )
setFgColor("sys", 255,100,0)
setBgColor("sys", 155,155,0)
echo("sys", "*** This is the end. ***\n");

moveCursorEnd( windowName ) returns true or false

Moves the cursor to the end of the buffer. "main" is the name of the main window, otherwise use the name of your user window. → moveCursor

paste(windowName)

pastes the previously copied text including all format codes like color, font etc. at the current user cursor position. The copy() and paste() functions can be used to copy formated text from the main window to a user window without losing colors e. g. for chat windows, map windows etc.

appendBuffer( name )

Pastes the previously copied rich text (including text formats like color etc.) into user window name.

Example:

selectString( line, 1 )
copy()
appendBuffer( "chat" )

printTable( table_name )

Lua debug function that prints the content of a Lua table on the screen, split up in keys and values. Useful if you want to see what the capture groups contain i. e. the Lua table "matches".

This function is defined in LuaGlobal.lua.

Note

This function is old. Use the newer display() function instead.

showCaptureGroups()

Lua debug function that highlights in random coolors all capture groups in your trigger regex on the screen. This is very handy if you make complex regex and want to see what really matches in the text. This function is defined in LuaGlobal.lua.

Example:

Make a trigger with the regex (\w+) and call this function in a trigger. All words in the text will be highlighted in random colors.

showColors()

Displays all color names that the bg() and fg() functions know about on the main screen along with their respective colors. Very handy for seeing which name is actually what color.

showMultimatches()

Lua helper function to show you what the table multimatches[n][m] contains. This is very useful when debugging multiline triggers - just doing showMultimatches() in your script will make it give the info.

table.load( file, table )

restores a Lua table from a data file that has been saved with table.save()

table.save( file, table )

Saves the given table into the given file.

sendAll( command1, command2, … )

Sends a list of commands to the MUD. You can use this to send some things at once instead of having to use multiple send() commands one after another.

Example:

sendAll("stand", "wield shield", "say ha!")

-- instead of:
send ("stand")
send ("wield shield")
send ("say ha!")

sendTelnetChannel102(msg)

Sends a message via the 102 subchannel back to the MUD (that’s used in Aardwolf). The msg is in a two byte format - see help telopts in Aardwolf on how that works.

Example:

-- turn prompt flags on:
sendTelnetChannel102("\52\1")

-- turn prompt flags off:
sendTelnetChannel102("\52\2")

replaceWildcard(what, replacement)

Replaces the given wildcard (as a number) with the given text. Equivalent to doing:

selectString(matches[2], 1)
replace("text")

Example:

replaceWildcard(2, "hello") -- on a perl regex trigger of ^You wave (goodbye)\.$, it will make

resetFormat (window)

Resets the user character format to the default. If you don’t specify a window, it’ll reset the main console - otherwise it’ll reset the specified miniConsole.

Example:

resetFormat() -- resets the format on the main window
resetFormat("chat console") -- resets the format on the specified window

playSoundFile( fileName )

This function plays a sound file. To make sound work on your operating system you may need to install additional packages:

Microsoft Windows: The underlying multimedia system is used; only WAVE format sound files are supported. (works out of the box) X11: The Network Audio System is used if available, otherwise all operations work silently. NAS supports WAVE and AU files. Mac OS X: NSSound is used. All formats that NSSound supports, including QuickTime formats, are supported by Qt for Mac OS X. (should work out of the box)

cecho (text[, foreground color] [, background color] [, true/false] [, name of console])

  • text - the text that you’d like to echo with embedded color tags. Tags take the color names only, see below for an explanation.

  • foreground color - optional string containing value for foreground color as a named color.

  • background color optional string containing value for background color as a named color.

  • true/false - optional boolean that tells the function to use insertText() rather than echo(). Note that if sending to miniConsole, this has to be false.

  • name of console - optional name of the console to echo to. Defaults to main.

How text works

Color changes can be made using the format <foreground:background> where each field is one of the colors listed by showColors(). The background portion can be omitted using <foreground> or the foreground portion can be omitted using <:background>. Arguments 2 and 3 to set the default colors take named colors as well.

Examples:

cecho("Hi! This text is <red>red, <blue>blue, <green> and green.")

cecho("<:green>Green background on normal foreground. Here we add an <ivory>ivory foreground.")

cecho("All of this text is green.", "green")

cecho("All of this text is navy on cyan.", "navy", "cyan")

cecho("This <green:red>text<light_gray:black> is sent to the map miniConsole.", nil, nil, false, "map"))

createConsole(consoleName, fontSize, charsPerLine, numberOfLines, Xpos, Ypos)

Make a new console window with ease. The default background is black and text color white.

This will create a miniconsole window that has a font size of 8pt, will display 80 characters in width, hold a maximum of 20 lines and be place at 200x400 of your mudlet window. If you wish to change the color you can easily do this when updating your text or manually somewhere, using setFgColor() and setBackgroundColor().

Example:

-- this will create a console with the name of "myConsoleWindow", font size 8, 80 characters per line and default height fr 20 lines at 200x, 400y coordinates.
createConsole("myConsoleWindow", 8, 80, 20, 200, 400)

createBuffer( name )

Creates a named buffer for formatted text, much like a user terminal window, but the buffer cannot be shown on the screen. Intended for temporary buffer work.

appendBuffer( name )

Like paste() but pastes at the end of the buffer/window and wraps new text automatically.

showWindow( name )

shows user window name.

hideWindow( name )

hides user window name.

getNetworkLatency() returns number

returns the last measured response time between the sent command and the server reply e.g. 0.058 (=58 milliseconds lag) or 0.309 (=309 milliseconds)

createStopWatch() returns number

This function creates a stop watch. It is high resolution time measurement tool. Stop watches can be started, stopped, reset and asked how much time has passed since the stop watch has been started. Returns the ID of a high resolution clock with milliseconds to measure time more accurately than what is possible with std. Lua routines → startStopWatch, stopStopWatch, resetStopWatch, getStopWatchTime example: In a global script you create all stop watches that you need in your system and store the respective stopWatch-IDs in global variables:

fightStopWatch = createStopWatch(); -- you store the watchID in a global variable to access it from anywhere

Then you can start the stop watch in some trigger/alias/script with:

startStopWatch( fightStopWatch );

To stop the watch and measure its time in e.g. a trigger script you can write:

fightTime = stopStopWatch( fightStopWatchID )
echo( "The fight lasted for " .. fightTime .. " seconds." )
resetStopWatch( fightStopWatchID );

You can also measure the elapsed time without having to stop the stop watch with getStopWatchTime

startStopWatch( watchID )

Starts the stop watch. → createStopWatch

stopStopWatch( watchID ) returns time as a number

Stops the stop watch and returns the elapsed time in milliseconds in form of 0.001. → createStopWatch

resetStopWatch( watchID )

This function resets the time to 0:0:0.0, but does not start the stop watch. You can start it with startStopWatchcreateStopWatch

getStopWatchTime( watchID ) returns number

Returns the time (milliseconds based) in form of 0.058 (= clock ran for 58 milliseconds before it was stopped) → createStopWatch

getTime(returntype, format)

returntype takes a boolean value (in Lua anything but false or nil will translate to true). If true, the function will return a table in the following format:

{ hour = #, min = #, sec = #, msec = # }

If false or nil, it will return the time as a string using a format passed to the second arg or the default of hh:mm:ss.zzz

Format expressions:

h               the hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display)
hh              the hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display)
H               the hour without a leading zero (0 to 23, even with AM/PM display)
HH              the hour with a leading zero (00 to 23, even with AM/PM display)
m               the minute without a leading zero (0 to 59)
mm              the minute with a leading zero (00 to 59)
s               the second without a leading zero (0 to 59)
ss              the second with a leading zero (00 to 59)
z               the milliseconds without leading zeroes (0 to 999)
zzz             the milliseconds with leading zeroes (000 to 999)
AP or A         use AM/PM display. AP will be replaced by either "AM" or "PM".
ap or a         use am/pm display. ap will be replaced by either "am" or "pm".

d               the day as number without a leading zero (1 to 31)
dd              the day as number with a leading zero (01 to 31)
ddd             the abbreviated localized day name (e.g. 'Mon' to 'Sun'). Uses QDate::shortDayName().
dddd            the long localized day name (e.g. 'Monday' to 'Qt::Sunday'). Uses QDate::longDayName().
M               the month as number without a leading zero (1-12)
MM              the month as number with a leading zero (01-12)
MMM             the abbreviated localized month name (e.g. 'Jan' to 'Dec'). Uses QDate::shortMonthName().
MMMM            the long localized month name (e.g. 'January' to 'December'). Uses QDate::longMonthName().
yy              the year as two digit number (00-99)
yyyy            the year as four digit number

All other input characters will be ignored. Any sequence of characters that are enclosed in singlequotes will be treated as text and not be used as an expression. Two consecutive singlequotes ("''") are replaced by a singlequote in the output.

*Available since Mudlet 1.0.6.

appendBuffer( name, text )

append text at the end of temp buffer name (fast function)

setWindowWrap( windowName, wrapAt )

sets at what position in the line the console or miniconsole will start word wrap

createGauge(name, width, height, Xpos, Ypos, gaugeText, color1, color2, color3)

Creates a gauge that you can use to express completion with. For example, you can use this as your healthbar or xpbar.

  • name is the gauge name. Must be unique - so don’t have two or more gauges with the same name.

  • width is the width (left to right dimensions) of the gauge.

  • height is the height (top to bottom dimensions) of the gauge.

  • Xpos is the X (so left to right) coordinate of the top-left corner of the gauge. It starts counting from the top-left corner of Mudlets window. So a coordinate of 5 will place it 5 pixels to the right from the top-left part of Mudlet.

  • Ypox is the Y coordinate of the top-left corner of the gauge, relative to the top-left corner of Mudlet.

  • gaugeText is optional text that you can have on the gauge. If you don’t want to have text, use nil in it’s place.

  • color# are the RGB color values to color the gauge with.

See also: moveGauge, setGauge, setGaugeText. Examples:

createGauge("healthBar", 300, 20, 30, 300, nil, 0, 255, 0)
createGauge("healthBar", 300, 20, 30, 300, nil, "green")
-- This would make a gauge at that's 300px width, 20px in height, located at Xpos and Ypos and is green.
-- The second example is using the same names you'd use for something like fg() or bg().

-- If you wish to have some text on your label, you'll change the nil part and make it look like this:
createGauge("healthBar", 300, 20, 30, 300, "Now with some text", 0, 255, 0)
-- or
createGauge("healthBar", 300, 20, 30, 300, "Now with some text", "green")

createMiniConsole( name, posX, posY, width, height ) returns true or false

opens a console window inside the main window of Mudlet at position posX/posY with size according to width/height (values depend on your own screen resolution usually between 0-1600 for x and 0-1024 for y). This console is the ideal fast colored text display for everything that requires a bit more text e.g. status screens, log windows, chat windows etc.. You can use clearWindow/moveCursor etc. functions for this window for custom printing as well as copy & paste functions for colored text copies from the main window or normal echoUserWindow( name, text) for normal printing. To set word wrap see setWindowWrap. To move the main window to make room for miniconsole windows on your screen (if you want to do this as you can also layer mini console and label windows) see setBorderTop, setBorderColor To have your screen layout adjusted after the window size of the main screen gets resized see handleWindowResizeEvent

Example 1:

-- set up the small system message window in the top right corner

-- determine the size of your screen
WindowWidth = 0;
WindowHeight = 0;
WindowWidth, WindowHeight = getMainWindowSize();

createMiniConsole("sys",WindowWidth-650,0,650,300)
setBackgroundColor("sys",85,55,0,255)
setMiniConsoleFontSize("sys", 8)
-- wrap lines in window "sys" at 65 characters per line
setWindowWrap("sys", 40)
-- set default font colors and font style for window "sys"
setTextFormat("sys",0,35,255,50,50,50,0,0,0)

echo("sys","Hello world!")

This script would create a mini text console called "sys" and write with yellow foreground color and blue background color "This is a test".

Example 2:

The next example is more advanced. The user Thylacine has written a tabbed chat widget on the basis of layered mini consoles and on screen buttons (labels). Each tab page holds the output of certain MUD channels e.g. All, Guild, Market, City etc.. You can click on the tab to change the tab page and read what was said in the respective channel. You can get the package here: http://mudlet.sourceforge.net/phpBB3/viewtopic.php?f=6&t=171

Note .

createLabel( name, posX, posY, width, height, fillBackground) returns true or false

labels are intended for very small variable or prompt displays or images. labels are clickable and if you specify a callback function with setLabelClickCallback( labelName, myLabelOnClickFunction ) your function will get called if the user clicks on the label with the mouse. If fillBackground = 0, the background will be hidden, if fillBackground = 1 the background will be shown i.e. you can see the background color. labels can be transparent. You can place labels anywhere within then main display, also als overlays over the main displays e.g. for on screen buttons, micro display, etc. DON’T use labels for larger text displays because they are a lot slower than the highspeed mini consoles

Labels accept some HTML and CSS code for text formating.

Example 1:

Note . screenshot example 1

This example creates a transparent overlay message box to show a big warning message "You are under attack!" in the middle of the screen. Because the background color has a transparency level of 150 (0-255, with 0 being completely transparent and 255 non-transparent) the background text can still be read through. The message box will disappear after 2.3 seconds.

local width, height = getMainWindowSize();
createLabel("messageBox",(width/2)-300,(height/2)-100,250,150,1);
resizeWindow("messageBox",500,70);
moveWindow("messageBox", (width/2)-300,(height/2)-100 );
setBackgroundColor("messageBox", 150,100,100,200);
echo("messageBox", [[<p style="font-size:35px"><b><center><font color="red">You are under attack!</font></center></b></p>]] );
showWindow("messageBox");
tempTimer(2.3, [[hideWindow("messageBox")]] ) -- close the warning message box after 2.3 seconds

Example 2:

Note . screenshot example 2

This example creates a mouse clickable compass (taken from the GUI demo package. You can download this package with the image data etc. → Mudlet forum → packages section http://mudlet.sourceforge.net/phpBB3/viewtopic.php?f=6&t=95

-- define the path to the location of the images
-- this needs to be adapted to your own system e.g. "c:\images\mudimages"
-- on my linux system the folder are in my home directory in the folder gfx
gfx_path = "/home/heiko/gfx";

-- determine the size of your screen
WindowWidth=0;
WindowHeight=0;
WindowWidth, WindowHeight = getMainWindowSize();

-- create clickable compass with clickable images = labels with on click callback functions
createLabel("north",82,WindowHeight-220,50,50,1);
setBackgroundImage("north",gfx_path .. "/n.png" );
setBackgroundColor("north", 0,0,0,0);
setLabelClickCallback("north", "walk_north" )

createLabel("south",82,WindowHeight-80,50,50,1);
setBackgroundImage("south",gfx_path .. "/s.png" );
setBackgroundColor("south", 0,0,0,0);
setLabelClickCallback("south", "walk_south" )

createLabel("west",0,WindowHeight-150,55,55,1);
setBackgroundImage("west",gfx_path .. "/w.png" );
setBackgroundColor("west", 0,0,0,0);
setLabelClickCallback("west", "walk_west" )

createLabel("east",150,WindowHeight-150,50,50,1);
setBackgroundImage("east",gfx_path .. "/e.png" );
setBackgroundColor("east", 0,0,0,0);
setLabelClickCallback("east", "walk_east" )

createLabel("northeast",125,WindowHeight-190,40,40,1);
setBackgroundImage("northeast",gfx_path .. "/ne.png" );
setBackgroundColor("northeast", 0,0,0,0);
setLabelClickCallback("northeast", "walk_northeast" )

createLabel("northwest",30,WindowHeight-190,40,40,1);
setBackgroundImage("northwest",gfx_path .. "/nw.png" );
setBackgroundColor("northwest", 0,0,0,0);
setLabelClickCallback("northwest", "walk_northwest" )

createLabel("southwest",30,WindowHeight-95,40,40,1);
setBackgroundImage("southwest",gfx_path .. "/sw.png" );
setBackgroundColor("southwest", 0,0,0,0);
setLabelClickCallback("southwest", "walk_southwest" )

createLabel("southeast",135,WindowHeight-95,40,40,1);
setBackgroundImage("southeast",gfx_path .. "/se.png" );
setBackgroundColor("southeast", 0,0,0,0);
setLabelClickCallback("southeast", "walk_southeast" )

createLabel("compass",64,WindowHeight-162,75,75,1)
setBackgroundColor("compass",0,0,0,0);
setBackgroundImage("compass",gfx_path .. "/compass.png");
setLabelClickCallback("compass", "callbackTest", 1, "score", 94, 104, 99, "lastParameter" );
--setLabelClickCallback("compass", "callbackTest", 948, "qw", 49, 52 )

function callbackTest( p1, p2, p3, p4, p5, p6  )
        echo("p1="..p1.." p2<"..p2.."> p3<"..p3.."> p4<"..p4..">p5="..p5.." p6<"..p6..">");
        if p1 == 1 then
                send( p2 );
        else
                send( p2 );
        end
end

function walk_north()
        expandAlias("n;map")
        compileRoomDescription()
end

function walk_south()
        expandAlias("s;map")
        compileRoomDescription()
end

function walk_west()
        expandAlias("w;map");
        compileRoomDescription()
end

function walk_east()
        expandAlias("e;map");
        compileRoomDescription()
end

function walk_northeast()
        expandAlias("ne;map");
        compileRoomDescription()
end

function walk_northwest()
        expandAlias("nw;map");
        compileRoomDescription()
end

function walk_southwest()
        expandAlias("sw;map");
        compileRoomDescription()
end

function walk_southeast()
        expandAlias("se;map");
        compileRoomDescription()
end

Example 3 (advanced)

Note . screenshot example 3

This is yet a more advanced demo of interactive graphical user interface programming in Mudlet on the basis of labels. Eraldo has implemented interactive icon buttons, vertical & horizontal gauges for status reports and on screen mini buttons to show status information of his character in-game that is extracted invisibly by his system and reported to the user via interactive or non-interactive icons or other graphical or textual representations. You can download the full package in the package section of the Mudlet forum at http://mudlet.sourceforge.net/phpBB3/viewtopic.php?f=6&t=865

hideWindow( name )

This function hides a mini console label. To show it again use showWindow. createMiniConsole, createLabel

showWindow( name )

This function shows a mini console or label. To hide it use hideWindow see also: createMiniConsole, createLabel

resizeWindow( name, width, height )

resizes a mini console or label see also: createMiniConsole, createLabel, handleWindowResizeEvent, resizeWindow, setBorderTop

moveWindow( name, x, y )

This function moves window name to the given x/y coordinate. The main screen cannot be moved. Instead you’ll have to set appropriate border values → preferences to move the main screen e.g. to make room for chat or information mini consoles, or other GUI elements. In the future moveWindow() will set the border values automatically if the name parameter is omitted. see also: createMiniConsole, createLabel, handleWindowResizeEvent, resizeWindow, setBorderTop

moveGauge (gaugeName, newX, newY)

Moves a gauge created with createGauge to the new x,y coordinates. Remember the coordinates are relative to the top-left corner of the output window.

Example:

moveGauge("healthBar", 1200, 400)
-- This would move the health bar gauge to the location 1200, 400

setMiniConsoleFontSize( name, fontSize )

Sets the font size of the mini console. see also: createMiniConsole, createLabel

setGauge(gaugeName, currentValue, maxValue, gaugeText)

Use this function when you want to change the gauges look according to your values. Typical usage would be in a prompt with your current health or whatever value, and throw in some variables instead of the numbers.

Examples:

setGauge("healthBar", 200, 400)

In that example, we’d change the looks of the gauge named healthBar and make it fill to half of its capacity. The height is always remembered.

If you wish to change the text on your gauge, you’d do the following:

setGauge("healthBar", 200, 400, "some text")

setGaugeText(gaugeName, gaugeText, color1, color2, color3)

Set the color and text on a custom gauge built by createGauge.

Example:

setGaugeText("healthBar", "HP: 100%", 40, 40, 40)
setGaugeText("healthBar", "HP: 100%", "red")

An empty gaugeText will clear the text entirely. Colors are optional and will default to 0,0,0(black) if not passed as args.

setTextFormat( windowName, r1, g1, b1, r2, g2, b2, bold, underline, italics )

sets current text format of window windowName: foreground color(r1,g1,b1), background color(r2,g2,b2), bold(1/0), underline(1/0), italics(1/0) A more convenient way to control the text format in a mini console is to use setFgColor( windowName, r,g,b ), setBold( windowName, true ), setItalics( windowName, true ), setUnderline( windowName, true ) etc.createMiniConsole, setBold, setBgColor, setFgColor, setItalics, setUnderline

Example:

createMiniConsole( "con1", 0,0,300,100);
setTextFormat("con1",0,0,255,255,255,0,1,1,1);
echo("con1","This is a test")

This script would create a mini text console and write with yellow foreground color and blue background color "This is a test".

getCurrentLine()

returns the content of the current line under the user cursor in the buffer. The Lua variable line holds the content of getCurrentLine() before any triggers have been run on this line. When triggers change the content of the buffer, the variable line will not be adjusted and thus hold an outdated string. line = getCurrentLine() will update line to the real content of the current buffer. This is important if you want to copy the current line after it has been changed by some triggers. selectString( line,1 ) will return false and won’t select anything because line no longer equals getCurrentLine(). Consequently, selectString( getCurrentLine(), 1 ) is what you need.

selectCurrentLine()

selects the content of the current buffer line

setBackgroundImage( labelName, imageFileName )

loads an image file (png) as a background image for a label. This can be used to display clickable images etc.

setBackgroundColor( window name, red, green, blue, transparency )

Sets rgb color values and the transparency for the given window. Colors are from 0 to 255 (0 being black), and transparency is from - to 255 (0 being completely transparent).

Note that transparency only works on labels, not miniConsoles for efficiency reasons.

setLabelClickCallback( labelName, luaFunctionName, optional any amount of arguments )

specify a Lua function to be called if the user clicks on the label/image e.g. setLabelClickCallback( "compassNorthImage", "onClickGoNorth" ) UPDATE: this function can now pass any number of string or integer number values as additional parameters. These parameters are then used in the callback. Thus you can associate data with the label/button. check the forum for more information on how to use this. * FIXME * example

Turns the selected text into a clickable link - upon being clicked, the link will do the command code. Tooltip is a string which will be displayed when the mouse is over the selected text.

Available since 1.1.0-pre1.

Example:

In a sewer grate substring trigger, the following code will make clicking on the words do the send("enter grate") command:

selectString(matches[1], 1)
setLink([[send("enter grate")]], "Clicky to enter grate")

setPopup(name, {lua code}, {hints})

Turns the selected text into a left-clickable link, and a right-click menu. The selected text, upon being left-clicked, will do the first command in the list. Upon being right-clicked, it’ll display a menu with all possible commands. The menu will be populated with hints, one for each line.

  • name - the name of the console to operate on. If not using this in a miniConsole, use "main" as the name.

  • {lua code} - a table of lua code strings to do. ie, \{[[send("hello")]], [[echo("hi!"]]\}.

  • {hints} - a table of strings which will be shown on the popup and right-click menu. ie, `{"send the hi command", "echo hi to yourself"}.

Available since 1.1.0-pre1.

Example:

In a Raising your hand in greeting, you say "Hello!" exact match trigger, the following code will make left-clicking on Hello show you an echo, while left-clicking will show some commands you can do.

selectString("Hello", 1)
setPopup("main", {[[send("bye")]], [[echo("hi!")]]}, {"left-click or right-click and do first item to send bye", "click to echo hi"})

echoUserWindow( windowName )

This function will print text to both mini console windows, dock windows and labels. This function is outdated. Use echo( windowName, text ) instead → echo

Same as setPopup() except it doesn’t require a selection. echoPopup creates a link from the given text that it echos.

Available since 1.1.0-pre1.

Example:

Create some text as a clickable with a popup menu:

echoPopup("activities to do", {[[send "sleep"]], [[send "sit"]], [[send "stand"]]}, {"sleep", "sit", "stand"})

getMainWindowSize() returns 2 numbers, width and height in pixels

return window width and window height ( function with 2 return values ) to calculate the window dimensions and placement of custom GUI toolkit items like labels, buttons, mini consoles etc. example:

mainWindowWidth, mainWindowHeight = getMainWindowSize();

table.size( tableName )

Gets the actual size of non-index based tables.

For index based tables you can get the size with the # operator: myTableSize = # myTable This is the standard Lua way of getting the size of index tables i.e. ipairs() type of tables with numerical indices. To get the size of tables that use user defined keys instead of automatic indices (pairs() type) you need to use the function table.size() referenced above.

result = table.union(tableA, tableB, tableC)

Returns a table that is the union of the provided tables. This is a union of key/value pairs. If two or more tables contain different values associated with the same key, that key in the returned table will contain a subtable containing all relevant values. See table.n_union() for a union of values. Note that the resulting table may not be reliably traversable with ipairs() due to the fact that it preserves keys. If there is a gap in numerical indices, ipairs() will cease traversal.

Example:

Given:
tableA = {
        [1] = 123,
        [2] = 456,
        ["test"] = "test",
        }

tableB = {
        [1] = 23,
        [3] = 7,
        ["test2"] = function() return true end,
        }

tableC = {
        [5] = "c",
        }

table.union(tableA, tableB, tableC) will return:
{
        [1] = {
                123,
                23,
                },
        [2] = 456,
        [3] = 7,
        [5] = "c",
        ["test"] = "test",
        ["test2"] = function() return true end,
}

result = table.n_union(tableA, tableB, tableC)

Returns a numerically indexed table that is the union of the provided tables. This is a union of unique values. The order and keys of the input tables are not preserved.

result = table.intersection(tableA, tableB, tableC)

Returns a table that is the intersection (or difference, ie elements only common to all tables) of the provided tables. This is an intersection of key/value pairs. See table.n_intersection() for an intersection of values. Note that the resulting table may not be reliably traversable with ipairs() due to the fact that it preserves keys. If there is a gap in numerical indices, ipairs() will cease traversal.

Example:

tableA = {
        [1] = 123,
        [2] = 456,
        [4] = { 1, 2 },
        [5] = "c",
        ["test"] = "test",
        }

tableB = {
        [1] = 123,
        [2] = 4,
        [3] = 7,
        [4] = { 1, 2 },
        ["test"] = function() return true end,
        }

tableC = {
        [1] = 123,
        [4] = { 1, 2 },
        [5] = "c",
        }

table.intersection(tableA, tableB, tableC) will return:
{
        [1] = 123,
        [4] = { 1, 2 },
}

result = table.n_intersection(tableA, tableB, tableC)

Returns a numerically indexed table that is the intersection of the provided tables. This is an intersection of unique values. The order and keys of the input tables are not preserved.

true/false = io.exists(file or folder)

Checks to see if a given file or folder exists. If it exists, it’ll return the Lua true boolean value, otherwise false.

Examples:

if io.exists("/home/vadi/Desktop") then
        echo("This folder exists!")
else
        echo("This folder doesn't exist.")
end

if io.exists("/home/vadi/Desktop/file.tx") then
        echo("This file exists!")
else
        echo("This file doesn't exist.")
end

table string:split( delimiter )

Splits a string into a table by the given delimiter. For example:

names = "Alice, Bob, Peter"
name_table = {}

name_table = names:split(", ")
display(name_table)

table {
  1: 'Alice'
  2: 'Bob'
  3: 'Peter'
}

complement = table.complement(set1, set2)

Returns a table that is the relative complement of the first table with respect to the second table. Returns a complement of key/value pairs.

complement = table.n_complement(set1, set2)

Returns a table that is the relative complement of the first table with respect to the second table. Returns a complement of values.

true/false = table.contains(t, value)

Determines if a table contains a value as a key or as a value (recursive)

state = getButtonState() with state = 2 button is checked and state = 1, button is not checked

this function can be used in checkbox button scripts (2-state buttons) to determine the current state of the checkbox. Example:

checked = getButtonState();
if checked == 1 then
    hideExits()
else
    showExits()
end;

showToolBar( name )

Shows toolbar with the given name name on the screen. Toolbars are the groups that buttons are contained in.

hecho (text[, foreground color] [, background color] [, true/false] [, name of console])

  • text - the text that you’d like to echo with embedded color tags. Tags take the hex color values only, see below for an explanation.

  • foreground color - optional string containing value for foreground color as a named color.

  • background color optional string containing value for background color as a named color.

  • true/false - optional boolean that tells the function to use insertText() rather than echo(). Note that if sending to miniConsole, this has to be false.

  • name of console - optional name of the console to echo to. Defaults to main.

How text works

Color changes can be made within the string using the format |cFRFGFB,BRBGBB where FR is the foreground red value, FG is the foreground green value, FB is the foreground blue value, BR is the background red value, etc., BRBGBB is optional. |r can be used within the string to reset the colors to default.

The colors in arg2 and arg3 replace the normal defaults for your console. So if you use cecho("|cff0000Testing |rTesting", "00ff00", "0000ff"), the first Testing would be red on black and the second would be green on blue.

hideToolBar( name )

Hides the toolbar with the given name name and makes it disappear. If all toolbars of a tool bar area (top, left, right) are hidden, the entire tool bar area disappears automatically.

disconnect()

close network connection to the server without a proper logout.

reconnect()

reconnects to the current server after a call to disconnect()

setTriggerStayOpen( name, number )

set for how many more lines a trigger script should fire or a chain should stay open after the trigger has matched. The main use of this function is to close a chain when a certain condition has been met.

number should be 0 to close the chain, or a positive number to keep the chain open that much longer.

homedir = getMudletHomeDir()

returns the current home directory of the current profile. This can be used to store data, save statistical information or load resource files.

datetime:parse(source, format, as_epoch)

parses the specified source string, according to the format if given, to return a representation of the date/time. If as_epoch is provided and true, the return value will be a Unix epoch — the number of seconds since 1970. This is a useful format for exchanging date/times with other systems. If as_epoch is false, then a Lua time table will be returned. Details of the time tables are provided in the Lua Manual.

Supported Format Codes
%b

Abbreviated Month Name

%B

Full Month Name

%d

Day of Month

%H

Hour (24-hour format)

%I

Hour (12-hour format, requires %p as well)

%p

AM or PM

%m

2-digit month (01-12)

%M

2-digit minutes (00-59)

%S

2-digit seconds (00-59)

%y

2-digit year (00-99), will automatically prepend 20 so 10 becomes 2010 and not 1910.

%Y

4-digit year.

display( myTable )

Generic pretty print function to display tables of any complexity. This is the preferred debug function to print the content of any kind of Lua table or variable to the main screen.

display( myVariable )

generic pretty print function to display the content of any Lua variable or table on the main screen

db:create(database name, schema table)

Creates and/or modifies an existing database. This function is safe to define at a top-level of a Mudlet script: in fact it is reccommended you run this function at a top-level without any kind of guards. If the named database does not exist it will create it. If the database does exist then it will add any columns or indexes which didn’t exist before to that database. If the database already has all the specified columns and indexes, it will do nothing.

The database will be called Database_<sanitized database name>.db and will be stored in the Mudlet configuration directory.

Database tables are called sheets consistantly throughout this documentation, to avoid confusion with Lua tables.

The schema table must be a Lua table array containing table dictionaries that define the structure and layout of each sheet.

Examples:

        local mydb = db:create("combat_log",
                {
                        kills = {
                                name = "",
                                area = "",
                                killed = db:Timestamp("CURRENT_TIMESTAMP"),
                                _index = { {"name", "area"} }
                        },
                        enemies = {
                                name = "",
                                city = "",
                                reason = "",
                                enemied = db:Timestamp("CURRENT_TIMESTAMP"),
                                _index = { "city" },
                                _unique = { "name" },
                                _violations = "IGNORE"
                        }
                }
        )

The above will create a database with two sheets; the first is kills and is used to track every successful kill, with both where and when the kill happened. It has one index, a compound inde tracking the combination of name and area. The second sheet has two indexes, but one is unique: it isn’t possible to add two items to the enemies sheet with the same name.

For sheets with unique indexes, you may specify a _violations key to indicate how the db layer handle cases where the unique index is violated. The options you may use are: * FAIL - the default. A hard error is thrown, cancelling the script. * IGNORE - The command that would add a record that violates uniqueness just fails silently. * REPLACE - The old record which matched the unique index is dropped, and the new one is added to replace it.

returns a reference of an already existing database. This instance can be used to get references to the sheets (and from there, fields) that are defined within the database. You use these references to construct queries.

If a database has a sheet named enemies, you can obtain a reference to that sheet by simply doing:

    local mydb = db:get_database("my database")
    local enemies_ref = mydb.enemies
    local name_ref = mydb.enemies.name

These references do not contain any actual data, they only point to parts of the database structure.

db:add(sheet reference, table1, …, tableN)

Adds one or more new rows to the specified sheet. If any of these rows would violate a UNIQUE index, a lua error will be thrown and execution will cancel. As such it is advisable that if you use a UNIQUE index, you test those values before you attempt to insert a new row.

Each table is a series of key-value pairs to set the values of the sheet, but if any keys do nto exist then they will be set to nil or the default value.

An example:

        db:add(mydb.enemies, {name="Bob Smith", city="San Francisco"})
        db:add(mydb.enemies,
                {name="John Smith", city="San Francisco"},
                {name="Jane Smith", city="San Francisco"},
                {name="Richard Clark"})

As you can see, all fields are optional.

db:fetch(sheet reference, query, order_by, descending)

returns a table array containing a table for each matching row in the specified sheet. All arguments but sheet are optional. If query is nil, the entire contents of the sheet will be returned.

Query is a string which should be built by calling the various db: expression functions, such as db:eq, db:AND, and such. You may pass a SQL WHERE clause here if you wish, but doing so is very dangerous. If you don’t know SQL well, its best to build the expression.

Query may also be a table array of such expressions, if so they will be AND’d together implicitly.

The results that are returned are not in any guaranteed order, though they are usually the same order as the records were inserted. If you want to rely on the order in any way, you must pass a value to the order_by field. This must be a table array listing the columns you want to sort by. It can be { "column1" }, or { "column1", "column2" }

The results are returned in ascending (smallest to largest) order; to reverse this pass true into the final field.

Examples:

        db:fetch(mydb.enemies, nil, {"city", "name"})
        db:fetch(mydb.enemies, db:eq(mydb.enemies.city, "San Francisco"))
        db:fetch(mydb.kills,
                        {db:eq(mydb.kills.area, "Undervault"),
                        db:like(mydb.kills.name, "%Drow%")}
        )

The first will fetch all of your enemies, sorted first by the city they reside in and then by their name.

The second will fetch only the enemies which are in San Francisco.

The third will fetch all the things you’ve killed in Undervault which have Drow in their name.

db:eq(field reference, value)

returns a database expression to test if the field in the sheet is equal to the value.

db:exp(string)

returns the string as-is to the database.

Use this function with caution, but it is very useful in some circumstances. One of the most common of such is incrementing an existing field in a db:set() operation, as so:

        db:set(mydb.enemies, db:exp("kills + 1"), db:eq(mydb.enemies.name, "Ixokai"))

This will increment the value of the kills field for the row identified by the name Ixokai.

But there are other uses, as the underlining database layer provides many functions you can call to do certain things. If you want to get a list of all your enemies who have a name longer then 10 characters, you may do:

        db:fetch(mydb.enemies, db:exp("length(name) > 10"))

Again, take special care with this, as you are doing SQL syntax directly and the library can’t help you get things right.

db:not_eq(field reference, value)

returns a database expression to test if the field in the sheet is NOT equal to the value.

db:lt(field reference, value)

returns a database expression to test if the field in the sheet is less than the value.

db:lte(field reference, value)

returns a database expression to test if the field in the sheet is less than or equal to the value.

db:merge_unique(sheet reference, table array)

merges the specified table array into the sheet, modifying any existing rows and adding any that don’t exist.

This function is a convenience utility that allows you to quickly modify a sheet, changing existing rows and add new ones as appropriate. It ONLY works on sheets which have a unique index, and only when that unique index is only on a single field. For more complex situations you’ll have to do the logic yourself.

The table array may contain tables that were either returned previously by db:fetch, or new tables that you’ve constructed with the correct fields, or any mix of both. Each table must have a value for the unique key that has been set on this sheet.

For example, consider this database:

        local mydb = db:create("peopledb",
                {
                        friends = {
                                name = "",
                                race = "",
                                level = 0,
                                city = "",
                                _index = { "city" },
                                _unique = { "name" }
                        }
                }
        );

Here you have a database with one sheet, which contains your friends, their race, level, and what city they live in. Let’s say you want to fetch everyone who lives in San Francisco, you could do:

        local results = db:fetch(mydb.friends, db:eq(mydb.friends.city, "San Francisco"))

The tables in results are static, any changes to them are not saved back to the database. But after a major radioactive cataclysm rendered everyone in San Francisco a mutant, you could make changes to the tables as so:

        for _, friend in ipairs(results) do
                friend.race = "Mutant"
        end

If you are also now aware of a new arrival in San Francisco, you could add them to that existing table array:

        results[#results+1] = {name="Bobette", race="Mutant", city="San Francisco"}

And commit all of these changes back to the database at once with:

        db:merge_unique(mydb.friends, results)

The db:merge_unique function will change the city values for all the people who we previously fetched, but then add a new record as well.

db:gt(field reference, value)

returns a database expression to test if the field in the sheet is greater than to the value.

db:gte(field reference, value)

returns a database expression to test if the field in the sheet is greater than or equal to the value.

db:is_nil(field reference, value)

returns a database expression to test if the field in the sheet is nil.

db:is_not_nil(field reference, value)

returns a database expression to test if the field in the sheet is not nil.

db:like(field reference, pattern)

returns a database expression to test if the field in the sheet matches the specified pattern.

LIKE patterns are not case-sensitive, and allow two wild cards. The first is an underscore which matches any single one character. The second is a percent symbol which matches zero or more of any character.

LIKE with "_" is therefore the same as the "." regular expression.

LIKE with "%" is therefore the same as ".*" regular expression.

db:not_like(field reference, pattern)

returns a database expression to test if the field in the sheet does not match the specified pattern.

LIKE patterns are not case-sensitive, and allow two wild cards. The first is an underscore which matches any single one character. The second is a percent symbol which matches zero or more of any character.

LIKE with "_" is therefore the same as the "." regular expression.

LIKE with "%" is therefore the same as ".*" regular expression.

db:between(field reference, lower_bound, upper_bound)

returns a database expression to test if the field in the sheet is a value between lower_bound and upper_bound. This only really makes sense for numbers and Timestamps.

db:not_between(field reference, lower_bound, upper_bound)

returns a database expression to test if the field in the sheet is not a value between lower_bound and upper_bound. This only really makes sense for numbers and Timestamps.

db:in_(field reference, table array)

returns a database expression to test if the field in the sheet is one of the values in the table array.

First, note the trailing underscore carefully! It is required.

The following example illustrates the use of in_:

        local mydb = db:get_database("my database")
        local areas = {"Undervault", "Hell", "Purgatory"}

        db:fetch(mydb.kills, db:in_(mydb.kills.area, areas))

This will obtain all of your kills which happened in the Undervault, Hell or Purgatory. Every db:in_ expression can be written as a db:OR, but that quite often gets very complex.

db:not_in(field reference, table array)

returns a database expression to test if the field in the sheet is not one of the values in the table array.

See link: db:in_

db:aggregate(field reference, aggregate function, query)

returns the result of calling the specified aggregate function on the field and its sheet. The query is optional.

The supported aggregate functions are:

  • COUNT - Returns the total number of records that are in the sheet or match the query.

  • AVG - Returns the average of all the numbers in the specified field.

  • MAX - Returns the highest number in the specified field.

  • MIN - Returns the lowest number in the specified field.

  • TOTAL - Returns the value of adding all the contents of the specified field.

    Examples:
    -----
           local mydb = db:get_database("my database")
           echo(db:aggregate(mydb.enemies.name, "count"))
    -----

db:AND(sub-expression1, …, sub-expressionN)

returns a compound database expression that combines all of the simple expressions passed into it; these expressions should be generated with other db: functions such as db:eq, db:like, db:lt and the like.

This compound expression will only find items in the sheet if all sub-expressions match.

db:OR(sub-expression1, sub-expression2)

returns a compound database expression that combines both of the simple expressions passed into it; these expressions should be generated with other db: functions such as db:eq, db:like, db:lt and the like.

This compound expression will find any item that matches either the first or the second sub-expression.

db:delete(sheet reference, query)

Deletes rows from the specified sheet. The argument for query tries to be intelligent:

  • if it is a simple number, it deletes a specific row by _row_id

  • if it is a table that contains a _row_id (e.g., a table returned by db:get) it deletes just that record.

  • Otherwise, it deletes every record which matches the query pattern which is specified as with db:get.

  • If the query is simply true, then it will truncate the entire contents of the sheet.

Some examples:

        enemies = db:fetch(mydb.enemies)

        db:delete(mydb.enemies, enemies[1])
        db:delete(mydb.enemies, enemies[1]._row_id)
        db:delete(mydb.enemies, 5)
        db:delete(mydb.enemies, db:eq(mydb.enemies.city, "San Francisco"))
        db:delete(mydb.enemies, true)

Those deletion commands will do in order:

  1. When passed an actual result table that was obtained from db:fetch, it will delete the record for that table.

  2. When passed a number, will delete the record for that _row_id. This example shows getting the row id from a table.

  3. As above, but this example just passes in the row id directly.

  4. Here, we will delete anything which matches the same kind of query as db:fetch uses-- namely, anyone who is in the city of San Francisco.

  5. And finally, we will delete the entire contents of the enemies table.

db:update(sheet reference, table)

This function updates a row in the specified sheet, but only accepts a row which has been previously obtained by db:fetch. Its primary purpose is that if you do a db:fetch, then change the value of a field or tow, you can save back that table.

For example:

        local mydb = db:get_database("my database")
        local bob = db:fetch(mydb.friends, db:eq(mydb.friends.name, "Bob"))[1]
        bob.notes = "He's a really awesome guy."
        db:update(mydb.friends, bob)

This obtains a database reference, and queries the friends sheet for someone named Bob. As this returns a table array containing only one item, it assigns that one item to the local variable named bob. We then change the notes on Bob, and pass it into db:update() to save the changes back.

db:set(field reference, value, query)

The db:set function allows you to set a certain field to a certain value across an entire sheet. Meaning, you can change all of the last_read fields in the sheet to a certain value, or possibly only the last_read fields which are in a certain city. The query argument can be any value which is appropriate for db:fetch, even nil which will change the value for the specified column for EVERY row in the sheet.

For example, consider a situation in which you are tracking how many times you find a certain type of egg during Easter. You start by setting up your database and adding an Eggs sheet, and then adding a record for each type of egg.

        local mydb = db:create("egg database", {eggs = {color = "", last_found = db.Timestamp(false), found = 0}})
        db:add(mydb.eggs,
                {color = "Red"},
                {color = "Blue"},
                {color = "Green"},
                {color = "Yellow"},
                {color = "Black"}
        )

Now, you have three columns. One is a string, one a timestamp (that ends up as nil in the database), and one is a number.

You can then set up a trigger to capture from the mud the string, "You pick up a (.*) egg!", and you end up arranging to store the value of that expression in a variable called "myegg".

To increment how many we found, we will do this:

        myegg = "Red" -- We will pretend a trigger set this.
        db:set(mydb.eggs.found, db:exp("found + 1"), db:eq(mydb.eggs.color, myegg))
        db:set(mydb.eggs.last_found, db.Timestamp("CURRENT_TIMESTAMP"), db:eq(mydb.eggs.color, myegg))

This will go out and set two fields in the Red egg sheet; the first is the found field, which will increment the value of that field (using the special db:exp function). The second will update the last_found field with the current time.

Once this contest is over, you may wish to reset this data but keep the database around. To do that, you may use a more broad use of db:set as such:

        db:set(mydb.eggs.found, 0)
        db:set(mydb.eggs.last_found, nil)

id = tempAlias(regex, lua code)

Creates a temporary (lasts only until the profile is closed) alias. This means that it won’t exist anymore after Mudlet restarts.

Example:

tempAlias("^hi$", [[send ("hi") echo ("we said hi!")]]

tempColorTrigger( foregroundColor, backgqroundColor, function )

Makes a color trigger that triggers on the specified foreground and background color. Both colors need to be supplied in form of these simplified ANSI 16 color mode codes: .Color Codes:

0 = default text color
1 = light black
2 = dark black
3 = light red
4 = dark red
5 = light green
6 = dark green
7 = light yellow
8 = dark yellow
9 = light blue
10 = dark blue
11 = light magenta
12 = dark magenta
13 = light cyan
14 = dark cyan
15 = light white
16 = dark white

example:

tempColorTrigger(9,2,[[selectString(matches[1],1); fg("red"); bg("blue");]] );

This script will re-highlight all text in blue foreground colors on a black background with a red foreground color on a blue background color until another color in the current line is being met. temporary color triggers do not offer match_all or filter options like the GUI color triggers because this is rarely necessary for scripting. A common usage for temporary color triggers is to schedule actions on the basis of forthcoming text colors in a particular context.

r,g,b = getFgColor( windowName )

This function returns the rgb values of the color of the first character of the current selection on mini console (window) windowName. If windowName is omitted Mudlet will use the main screen. example:

local r,g,b;
selectString("troll",1)
r,g,b = getFgColor()
if r == 255 and g == 0 and b == 0 then
    echo("HELP! troll is written in red letters, the monster is aggressive!\n");
end

r,g,b = getBgColor( windowName )

get the rgb values of the first character of the current selection → getFgColor() for more information

wrapLine( windowName, lineNumber )

wrap line lineNumber of mini console (window) windowName. This function will interpret \n characters, apply word wrap and display the new lines on the screen. This function may be necessary if you use deleteLine() and thus erase the entire current line in the buffer, but you want to do some further echo() calls after calling deleteLine(). You will then need to re-wrap the last line of the buffer to actually see what you have echoed and get you \n interpreted as newline characters properly. Using this function is no good programming practice and should be avoided. There are better ways of handling situations where you would call deleteLine() and echo afterwards e.g.:

selectString(line,1);
replace("");

This will effectively have the same result as a call to deleteLine() but the buffer line will not be entirely removed. Consequently, further calls to echo() etc. sort of functions are possible without using wrapLine() unnecessarily.

isAnsiFgColor( ansiFgColorCode )

This function tests if the first character of the current selection has the foreground color specified by ansiFgColorCode. Codes are: .Color Codes:

0 = default text color
1 = light black
2 = dark black
3 = light red
4 = dark red
5 = light green
6 = dark green
7 = light yellow
8 = dark yellow
9 = light blue
10 = dark blue
11 = light magenta
12 = dark magenta
13 = light cyan
14 = dark cyan
15 = light white
16 = dark white

example:

selectString( matches[1], 1 )
if isAnsiFgColor( 5 ) then
    bg("red");
    resetFormat();
    echo("yes, the text is light green")
else
    echo( "no sorry, some other foreground color" )
end

Note that matches[1] holds the matched trigger pattern - even in substring, exact match, begin of line substring trigger patterns or even color triggers that do not know about the concept of capture groups. Consequently, you can always test if the text that has fired the trigger has a certain color and react accordingly. This function is faster than using getFgColor() and then handling the color comparison in Lua.

isActive (name, type)

You can use this function to check if something, or somethings, are active. Type can be either "alias", "trigger", or "timer", and name is the name of the said item you’d like to check.

Example:

echo("I have " .. isActive("my trigger", "trigger") .. " currently active triggers called 'my trigger'!")

isAnsiBgColor( ansiBgColorCode )

This function tests if the first character of the current selection has the background color specified by ansiBgColorCode. → isAnsiFgColor for more details

true/false = isPrompt()

Returns true or false depending on if the current line being processed is a prompt. This infallible feature is available for MUDs that supply GA events (to check if yours is one, look to bottom-right of the main window - if it doesn’t say <No GA>, then it supplies them).

Example use could be as a Lua function, making closing gates on a prompt real easy.

path = invokeFileDialog (boolean file_or_folder, dialog_title)

Opens a file chooser dialog, allowing the user to select a file or a folder visually. The function returns the selected path or nil if there was none chosen.

  • boolean - true for file selection, false for folder selection.

  • dialog title - name that the dialog (file chooser window) will have

Example:

local path = invokeFileDialog(false, "Find me the gfx folder, please")
echo(path)
Note If the user clicks Cancel in the dialog, the path returned will then be "" (blank string).

setBorderTop( size )

Sets the height of the top border to size pixel and thus effectively moves down the main console window by size pixels to make room for e.g. mini console windows, buttons etc..

setBorderBottom( size )

Sets the height of the bottom border to size pixels. → setBorderTop

setBorderLeft( size )

Sets the width of the left border. → setBorderTop

setBorderRight( size )

Sets the width of the right border. → setBorderTop

setBorderColor( r, g, b )

Sets the color of the border in RGB color e.g. setBorderColor( 255, 0, 0 ) sets the border to red.

bool = hasFocus()

Returns true or false depending on if the main Mudlet window is in focus. By focus, it means that the window is selected and you can type in the input line and etc. Not in focus means that the window isn’t selected, some other window is currently in focus.

handleWindowResizeEvent()

The standard implementation of this function does nothing. However, this function gets called whenever the main window is being manually resized. You can overwrite this function in your own scripts to handle window resize events yourself and e. g. adjust the screen position and size of your mini console windows, labels or other relevant GUI elements in your scripts that depend on the size of the main Window. To override this function you can simply put a function with the same name in one of your scripts thus overwriting the original empty implementation of this function.

function handleWindowResizeEvent()
   -- determine the size of your screen
   WindowWidth=0;
   WindowHeight=0;
   WindowWidth, WindowHeight = getMainWindowSize();

   -- move mini console "sys" to the far right side of the screen whenever the screen gets resized
   moveWindow("sys",WindowWidth-300,0)
end

setConsoleBufferSize( consoleName, linesLimit, sizeOfBatchDeletion )

Set the scrollback buffer size to linesLimit and determine how many lines are deleted at once in case the lines limit is reached. The lower the limit the less memory being used. On machines with low RAM you should consider limiting the size of buffers that don’t need a lot of scrollback e.g. system notification windows, chat windows etc.. Default values are linesLimit = 100000 lines with 10000 lines of batch deletion. Minimum buffer size is 100 lines with 10 lines batch deletion.

searchRoom (room name)

Searches for rooms that match (by case-insensitive, substring match) the given room name. It returns a key-value table in form of roomname = roomid, ie:

table {
  'office of the guildmaster': 2004
  'Hall of Cultural Masterpieces': 10361
  'the Grand Master's Chambers': 21546
  'the chambers of Master Blasterson': 10210
  'Exhibit of the Techniques of the Master Artisans': 6324
  'the master's gallery': 19067
  'office of the Guildmaster': 5340
  'the master's salon': 18978
  'The Enlightened Master': 11313
  'hall before the Master Chambers': 6592
  'central hall in the beastmaster apartments': 7712
  'before the Master Ravenwood Tree': 1337
}

If no rooms are found, then an empty table is returned.