Difference between revisions of "Introduction to lua"

From Mudlet
Jump to navigation Jump to search
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.
 
=== 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.]]
 

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 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 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 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 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 for loop, that starts at 1 (i is 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 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 Note: Function can also return multiple values. You can see this at work using a built in function like string.find.

Note Note: Functions can also have scope (global or local) just like variables.