Difference between revisions of "Introduction to lua"
| Line 285: | Line 285: | ||
''Functions'' are pieces of code that perform a certain task, much like a response to a trigger or a button press. Functions are the basis for reusable code and allow you to write some code once and reuse it many times over. Functions are ''called'' whenever they need to be ''run''. You are already familiar with send and echo (if not read the [[Manual:Introduction|introduction]] first), those are both functions being called when we want them to run. | ''Functions'' are pieces of code that perform a certain task, much like a response to a trigger or a button press. Functions are the basis for reusable code and allow you to write some code once and reuse it many times over. Functions are ''called'' whenever they need to be ''run''. You are already familiar with send and echo (if not read the [[Manual:Introduction|introduction]] first), those are both functions being called when we want them to run. | ||
| + | |||
| + | === In-built Functions === | ||
| + | |||
| + | Mudlet has a load of in-built function to make your life easier. See the [[Manual:Lua API|Lua API for the entire list.]] But if you can't find what you're looking for, you might need to write your own function which is covered next. | ||
| + | |||
| + | === Custom Functions === | ||
Functions use the following syntax; | Functions use the following syntax; | ||
| Line 354: | Line 360: | ||
{{note}} Functions can also have scope (global or local) just like variables. | {{note}} Functions can also have scope (global or local) just like variables. | ||
| − | |||
| − | |||
| − | |||
| − | |||
Revision as of 23:04, 1 November 2025
Introduction to Lua
Lua is the programming language used by Mudlet to perform various tasks. Lua is used in all sorts of ways from game engines to home automation. The Lua version Mudlet uses is 5.1 and the official manual can be found here. This page aims to describe the syntax (grammar and structure) Lua uses so you can become more familiar with the language and write useful scripts to help you in game. It will introduce various programming concepts such as variables, conditions, loops, etc.. with an eye towards how this can be used in everyday MUDding.
Mudlet users tend to call snippets of code written in Mudlet scripts. These scripts can be used throughout the Script Editor; in triggers, aliases, keybindings, timers and a dedicated script area where you might store your functions which are discussed later.
Note: Have a read through the Introduction to Mudlet page if you haven't already, it provides a good walkthrough of the features and terminology Mudlet uses.
Variables
Variables are containers that store values; they hold a piece of information in memory so it can be obtained later. A variable might, for example, contain the name of an enemy or your current amount of gold. To define (or create) a variable we simply think of an appropriate name and assign a value to it.
gold = 100
or
income = 10
The variable gold now contains the value 100.
enemy = "ogre"
or
location = "under the bridge"
The variable enemy now contains the value ogre. Note that when using strings (words or sentences) you need to provide quotes. This is because we can actually assign variables the value of another variable and this keeps them differentiated. For example;
gold = 100 income = 10 total = gold + income
Here we define two variables, gold and income which contain the values 100 and 10 respectively. We then define a third total variable which is the sum of gold plus income; total now contains the value 110.
You can use +, -, *, / and ~; addition, subtraction, multiplication, division and unary (negation).
Variables can be reused; we could have reassigned gold;
gold = 100 income = 10 gold = gold + income
gold started off as 100 but then later was assigned the sum; it now holds 110.
To add (or concatenate) strings together;
enemy = "ogre" location = "under the bridge" target = enemy .. location
target will now contain the value ogreunder the bridge. Oops, we forgot to add a space between enemy and location. Let's rewrite the concatenation slightly to this;
target = enemy .. " " .. location
Now target will hold "ogre under the bridge".
The same line above can also be written;
target = f"{enemy} {location}"
This is easier to read and is called string interpolation. Note the f and curly braces around the variable names needed to work correctly.
Variable Scope
Variables can also contain scope. A variable's scope defines where (or not) it can be seen or accessed. There are two types of scope;
- global scope - a variable can be accessed from anywhere within the Mudlet profile in which it is defined.
- local scope - a variable can only be accessed from within the script in which it is defined.
It is good programming practice to keep your variables as local as possible - this prevents accidental overwrites from other areas of your code when your scripts starts to grow and you forget which variables are called what.
To define a local variable we simply add the keyword local when defining it. Variables without the local keyword are global; it has no associated keyword.
local enemy = "ogre"
Now when we run an alias, or match a trigger the enemy variable will be created (and removed) locally in that script. Now we can write other scripts using the same variable name without the fear of enemy being set to the wrong value from another trigger or alias.
nil
The value nil is special. It represents the absence of a useful value. It can be used to delete a variable;
ogre = nil
ogre no longer exists.
Tables
Tables are also variables. Tables just provide a more structured way to contain values, or more commonly, multiple values.
local myStats = {} -- creates an empty table
local friends = { "Bob", "Sally", "Jeff" } -- creates a table containing three separate values
To create a key/value type table you might use something like;
local myStats = { hp = 500, class = "cleric", gold = 100 }
or
local enemies = { [1] = "ogre", [2] = "troll", [3] = "goblin" }
Accessing tables requires some special syntax by using a period or square brackets.
heal = friends[1] -- heal variable now contains the first value from friends (see disclaimer below regarding table order) hp = myStats.hp -- hp now contains 500 hp = myStats["hp"] -- is also valid, hp contains 500 target = enemies[3] -- target now contains goblin
Tables can also be added to later;
myStats.mana = 50 enemies[4] = "ghost"
and removed from using the nil keyword;
enemies[2] = nil
enemies will now look like;
{ [1] = "ogre", [3] = "goblin", [4] = "ghost" }
Notice how the numeric keys are not reassigned. This is important and is covered further in loops.
To get the current size of a table use the # operator;
local enemyNumber = #enemies -- enemyNumber now contains 3
Note: Tables do not retain order in which they are created. You need to search or sort your tables to guarantee this. More about this in loops.
Conditions - if/then statements
Conditions are a test. They will provide a yes/no, true/false answer (also called a boolean). The syntax looks like this;
if <true condition> then
<true action>
end
if, then and end provide the structure of the condition statement and are required. Condition and action are placeholders. Let's write a real world example to explain better;
if gold == 100 then
print("I have exactly 100 gold.")
end
We are saying; if the gold variable contains the value 100 then print "I have exactly 100 gold." to the screen. What happens if we have more than or less than 100 gold? Nothing. The print line will be skipped because it doesn't match the true condition required to run.
We can expand this further for a false condition by adding an else keyword;
if <true condition> then
<true action>
else
<false action>
end
if gold == 100 then
print("I have exactly 100 gold.")
else
print("I have more or less than 100 gold.")
end
We can expand this further by chaining together multiple if/then statements with the elseif keyword;
if <true condition> then
<true action>
elseif <true_condition> then
<true action>
else
<false action>
end
if gold == 100 then
print("I have exactly 100 gold.")
elseif gold > 100 then
print("I have more than 100 gold!")
else
print("I have less than 100 gold.")
end
If the first if condition is not true then the next one will be tested. Each if check will be tested until a match is found, otherwise the false condition check will run. You can chain together as many of these checks as you like, as long as you follow the keyword structure.
Lua provides the following operators for conditions that always result in true or false;
- < less than
- > greater than
- <= less than or equal to
- >= greater than or equal to
- == is exactly the same
- ~= is not the same
We can test using these operators on strings also.
if enemy ~= "ogre" then
send("cast blind")
end
if the enemy variable does not exactly equal ogre then cast blind.
Note: Functions can also return true/false values for testing conditions, we will touch more on that in the Functions section.
Iteration - for loop
Loops allow for repetitive or repeating code, known as iterations. While Lua has more methods available for iterative coding, we will only touch on two flavours of one type; the for loop.
Numeric For Loop
A for loop iterates (repeats) the action a number of times defined by it's starting value, ending value and increments.
for <start value, end value, increments> do
<action>
end
for, do and end provide the structure of the for statement and are required. Start value, end value, increments and action are placeholders. Let's write a real world example to explain better;
for index = 1, 10, 1 do
send("cast heal")
end
Starting at 1, up to 10, in increments of 1, cast heal. index (a variable, you can call it anything) is initially assigned 1 which is then incremented by 1 every time the loop finishes a cycle. So this script will cast heal 10 times. index can also be used inside the for loop itself. See the example below.
Note: Increments defaults to 1 if not specified so can be left out, but is useful in certain scenarios (e.g., only use even numbers for some action (i = 2, 10, 2), or want to go in reverse (i = 10, 1, -1)).
Here's an example to heal all your friends;
local friends = { "Bob", "Sally", "Jeff" }
for i = 1, #friends do
send(f"cast heal {friends[i]}")
end
We've brought together a number of concepts from above here. Let's walk through it.
- create a variable that contains a list of our friends
- write a
forloop, that starts at 1 (iis short of index) and continues until the size of friends (3 in this case) is reached. - use string interpolation to cast heal, calling out each value in the table
The result?
cast heal Bob cast heal Sally cast heal Jeff
Generic For Loop
A generic for loop iterates just like a numeric one. Being more general in it's approach it can traverse all sorts of data with the help of an iterator function. The chief ones we will examine here are pairs iterator and it's friends.
for <values> in <iterator> do <action> end
for, in, do and end provide the structure of the generic for statement and are required. Values, iterator and action are placeholders. Let's write a real world example to explain better;
local myStats = { hp = 500, class = "cleric", gold = 100 }
for key, value in pairs(myStats) do
print(f"{key} is {value}")
end
pairs returns the key and value (normally shortened to k,v but these can be called whatever you like) from the table myStats, and loops through until the end is reached. For each step in the script, key gets assigned the key (hp, class or gold), while value gets the value associated with that key (500, cleric or 100). The result is;
class is cleric hp is 500 gold is 100
Notice how they aren't in the original order defined when creating the variable. Tables do not necessarily keep their order. If order matters, sort the table first. Using spairs as the iterator function can return a sorted table (default is alphanumeric sorting by key).
for k, v in spairs(myStats) do
print(f"{k} is {v}")
end
will result in this alphabetically sorted output;
class is cleric gold is 100 hp is 500
ipairs is also available to return a key when one doesn't exist already, as in our friends table.
for k, v in ipairs(friends) do
print(f"{key} is {friends[key]}")
end
will result in
1 is Bob 2 is Sally 3 is Jeff
Note: A table can contain tables are values!
Functions
Functions are pieces of code that perform a certain task, much like a response to a trigger or a button press. Functions are the basis for reusable code and allow you to write some code once and reuse it many times over. Functions are called whenever they need to be run. You are already familiar with send and echo (if not read the introduction first), those are both functions being called when we want them to run.
In-built Functions
Mudlet has a load of in-built function to make your life easier. See the Lua API for the entire list. But if you can't find what you're looking for, you might need to write your own function which is covered next.
Custom Functions
Functions use the following syntax;
function <name>(<optional arguments>) <action> <optional return> end
function, ( ) and end provide the structure of the function statement and are required. Name, arguments, action and return are placeholders. Functions are quite powerful; let's build up a function that casts heal to ourself, or a friend if that is supplied to explain better;
function heal()
send("cast heal self")
end
This is a function in its very basic form. A function is called via heal() (say from a trigger or alias) then sends "cast heal" on oneself.
Arguments
Arguments provide information to the function and allows us to reuse the same code over and over with different targets each time it is called. Let's add in a single argument;
function healTarget(target)
send("cast heal {target}")
end
We have added an argument to the cast heal function to target our heals. To call it use heal("Sally") or call it with a variable like heal(friends[1]).
But we can combine these two functions together to make things simpler, and as arguments are optional we can just heal ourselves if a target isn't supplied.
function heal(target)
if target then
send("cast heal {target}")
else
send("cast heal self")
end
end
This function first checks if target is present (i.e., not nil) and uses it, otherwise just heals self instead. This is useful when calling it from an alias where the target might be optional. Let's heal all our friends using a for loop and calling the function.
for k, v in ipairs(friends) do heal(friends[v]) end
While this is a fairly simplistic idea imagine expanding the function to include casting heal, bless, fly and invis all from the one spellup function. We only need to change what our function does, all other parts of the code (triggers, aliases, etc) remain the same.
Return Values
Functions can also return information to the caller. To do this we use the return keyword.
function add(x, y) local sum = x + y return sum end
A simple example which takes two numbers as arguments and adds them together, returning the result. Let's make a more interesting example that searches a list of enemies to see if one exists.
function tableSearch(table, target)
for k, v in pairs(table) do
if v == target then
return true
end
end
return false
end
This function searches a supplied table looking for the target. Call it like tableSearch(enemies, "ogre"). It will return true or false depending on whether the ogre value exists in the enemies table.
Note: Function can also return multiple values. You can see this at work using a built in function like string.find.
Note: Functions can also have scope (global or local) just like variables.