Lua

From PZwiki
(Redirected from Lua)
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Lua is a language heavily used in Project Zomboid. Its knowledge is handy if one plans to do any modding or just understand how the game works.

What is Lua?

Lua is a powerful, fast, surprisingly lovely and simple scripting language. It is easily embedded into Java code (which Project Zomboid is written in) and thus was an ideal choice for the Indie Stone to introduce to the modding community. Lua appeared from the 0.2.0 test versions onwards, resulting in some fantastic mods. Some of the initial mods have gone on to be implemented as part of the vanilla game (Stormy's Reloading Mod, RobertJohnson's Farming Mod and Camping Mod).

How do I write and edit Lua code?

In order to create code, you will first need a program to write in. You could do this in Notepad, but this would make it significantly more difficult than it needs to be. We recommend you choose one of the programs listed below. There is a variety of programs, all of which have advantages:

  • Notepad++ - this is popular amongst many coders. You can download a Lua language file which highlights Lua keywords and helps make code more readable.
  • SciTE - this program highlights Lua keywords and datatypes for easy reading. Most importantly, it also contacts a Lua IDE, meaning that you can run Lua code inside the program. For this reason, this is the best tool for when you are learning Lua.
  • IntelliJ IDEA (with a Lua plugin) - this is an excellent tool that highlights syntax errors and suspicious globals. It was recommended by lemmy101 and makes a great tool for making actual mods.

Learning Lua

Due to its simplicity, Lua is a language easily picked up by both programmers and non-programmers alike. These early tutorials are written with the assumption the reader has no previous knowledge of programming, i.e., the absolute beginner. For those with experience who might get impatient with the pace of these tutorials, it is recommendeded reading Programming in Lua, with the first edition available for free.

The tutorials here are intended to be done in order, so that the techniques and complexity of examples increase as you become for comfortable with the language. They first introduce you to the Lua language (with PZ in mind), before moving on to specifically using Lua for Project Zomboid. Like any skill, it will take time and practice to learn, so don't be put off if you don't master something immediately.

Variables assignments datatypes

For SciTE users: To run code, choose 'save as' from the file menu and save a file with the extension '.lua'. The play 'Run Program' button should then become available to use. Any output from your code will be displayed in the box at the bottom (if this is missing, press F8 to bring it up).

Hello World

In Lua this is easily done:

print("Hello World!")

print is a built-in Lua function. Functions can be identified by the parentheses, these things: (). Anything between the parentheses are called arguments. In this case, we passed it the string 'Hello World!' which it took and printed in the console. Note that Lua is case-sensitive, so print() is not same as Print()
Also note, that unlike many other languages, we do not need a semicolon at the end of each line (although you can still use semicolons with no ill effect!).

Variables and assignment

Variables are basically named storage containers. You can put any value inside a variable, then refer to it easily by using the variable's name. Let's take a look at the Hello World example to illustrate this:

a = "Hello World!"
print(a)

Here, we placed the string 'Hello World' inside the variable a, then printed a. This should have produced the same results as before. Before we put the string into the variable a, it was empty (contained nil). By assigning it a value, we initialized it. Note we used the single equals sign, =, to assign the string to the variable. This is basically a command to make the item on the left to be equal to that on the right. We can place practically anything inside a variable:

a = print
a("Hello World!")

b = "This works too."
a(b)

We can also assign multiple variables at once:

a, b, c = "This is called", "multiple", "assignment!"
print(a, b, c)

Note if we have more values than variables, the extra variables are discarded. Likewise, any extra variables remain unchanged (i.e., containing nil in the example below).

a, b, c = "Data ", "missing"
print(a, b, c)

Multiple assignment is particular useful for swapping variables around with having to use a third variable to hold data on the transition:

a, b = 1, 2
print(a, b)
a, b = b, a
print(a, b)

Note there are some rules on what variable names can be. It cannot contain anything other than letters, underscores, or digits, and it must start with an underscore or letter (i.e., not a digit). It also cannot be any of Lua's keywords (such as and, or, function, local etc.), but we will go into these more later.

Datatypes

So far 'strings' and 'functions' were mentioned, these are both examples of different datatypes found in Lua. Datatypes are the classifications of a value. A useful function to discover the datatype of a value is type(). In order to see the result of this, we also need to use print(). Try some of these examples:

print(type(a))
print(type("Hello!"))
print(type(345))
print(type(print))
print(type(true))
print(type(0))
print(type(false))

You probably guessed it, the main ones are:

  • numbers - any digit, including zero
  • nil - the absense of value
  • function - a function, like print() or type()
  • string - a sequence of characters
  • boolean - true or false
  • table - a list of values (covered in a later tutorial)

Other types include userdata and thread, but these won't be needed at this stage.

To determine a string, we use quotes, either "double" or 'single'. As a special case, we can also use double square brackets: [[ ]]

Characters inside square brackets are treated literally, so backslash escaping (more on this in another tutorial!) doesn't work, and a new line is determined by simply using an actual new line. See below for more clarification:

print("A string")
print("A 'string'")
print([["Square brackets" have
unique properties, that can
very useful later. You also cannot
use 'escaping': \n, \a \\]])

Using quotes will suffice for now when using strings, but it is worth remembering square brackets if you want to write a long string with separate lines, or your strings contain backslashes you just want to print.

Summary

  • Lua is case-sensitive
  • print() can be used to print values to the console
  • type() can be used to discover the datatype of a value
  • Variables are containers which can be assigned pretty much any value
  • You can do multiple assignments on one line
  • Variables without assigned values are not initialized and contain nil
  • There are eight datatypes: nil, number, string, function, table, boolean, userdata and thread

Test yourself

1) Swap the following variables around without using multiple assignment:

a = "A"
b = "B"
a = "A"
b = "B"
temp = a
a = b
b = temp
print(a, b)

This requires you to use a third variable to hold the contents of one of the original variables. This is avoided when using multiple assignment.

2) This code contains errors, identify and fix them:

Hello = a
b = Print
c = type
b(c(a))
a = "Hello"
b = print
c = type
b(c(a))

When assigning a variable, the contents need to appear on the right of the equal sign, and the variable name to the left. Strings also require quotation marks " " or ' '. Remember that Lua is case-sensitive, meaning Print is not a real function, unlike print. While variables that contain just a function is ugly (and fairly pointless), it does highlight that any piece of code can be held in a variable.

3) Without running it, what would the outcome of this code?

a = type(type(1))
print(a)

The type function always returns a string as its result, regardless of the argument's datatype. Therefore, type of type is a string.

Arithmetic operators

This short tutorial will cover the basic arithmetic operators used within Lua.

So in a nutshell, the math operators include:

  • + Addition
  • - Subtraction
  • * Multiplication
  • / Division
  • ^ Power of / Exponentiation

We can see these in action:

print(2 + 3)
print(2 - 3)
print(2 * 3)
print(2 ^ 3)
print(2 - 3 + 4 * 5 ^ 6)

Precedence

With the last example we should also mention precedence. Precedence refers to the order in which operations are performed in. This is standardised for consistency:

^ (Exponentiation, right associative)
* (Multiplication), / (division)
+ (Addition), - (Subtraction)

Everything except exponentiation is left associative, meaning that any operations with the same precendence are done from left to right, while exponentiation is done from right to left. Of course you can override precedence simply by placing anything you want done first within parentheses. Like this:

print(4 + 5 * 10)
print((4 + 5) * 10) --This will have a different result

In the last example I placed a comment by using two dashed lines (--). This signifies something you want the Lua interpreter to ignore, and is often used to mark code so yourself and others can understand it more easily. It is considered good programming etiquette to comment your code, and is invaluable when trying to learn.

Here are some more examples using variables from the first tutorial:

b = 5
a = b + 2*3
b = a / b
print(b, a)

Note that you cannot use arithmetic operators on anything other than the numbers datatype. However, if a string consists entirely of numbers, then Lua is smart enough to convert the datatype to avoid errors:

print("200" + 25)

a = "Even if it contained a non-number once"
a = 25
print("200" + a)

a = "25"
print(type(a))
a = a + a
print(a)
print(type(a))

You might have noticed that it was still considered a string before being used by the arithmetic operator. As a general rule, you should avoid storing numbers as strings.

Summary

  • There are five arithmetic operators: Addition (+), Subtraction (-), Multiplication (*), Division (/), Exponentiation (^)
  • These are performed in order of precedence
  • Precedence can be overriden with parentheses
  • Math operators can only be used on the number datatype, but Lua converts number only strings to the number datatype when used
  • Comments can be placed in code using two dashes: --

Relational and boolean operators

Relational operators

Relational operators are used to compare two values and return a boolean (true or false) as a result. They consist of:

  • > More than
  • < Less than
  • == Equal to
  • >= More than or equal to
  • <= Less than or equal to
  • ~= Not equal to

Notice that when comparing values we use two equal signs ==, rather than when we are assigning a variable, when we use just one.

They are fairly self-explanatory when comparing numbers:

print(1 > 2)
print(1 < 2)
print(1 >= 1)
print(5 <= 10)
print(1 == 2)
print(1 ~= 1)

We can also use the to compare strings, although this is slightly more complicated. For single character strings, it compares the collation (the order it appears in that locale):

print("a" < "b")
print("a" == "a")
print("c" >= "d")
print("A" < "a")
print("A" < "B")
print("Z" > "a")

For multiple line strings, it compares the first character that is different:

print("abcd" < "abce")
print("abc" < "abcd")
print("ab" > "Abcdefghijk")

Note that == and ~= can be used to compare values of any datatype:

print(false == nil)
print(false ~= 0)
print(1 == "1")

Notice that two different datatypes will never be considered equal. The rest of the relational operators can only be used when comparing two strings or two numbers, otherwise Lua will throw up an error.

Boolean operators

This consists of three operators: and, or and not.

and

The and operator returns true if both it's operands (the values used by an operator) are true, otherwise it returns false. Have a look at these examples to get a better understanding:

print(true and true)
print(true and false)
print(1 < 2 and "a" > "A")
print(false and nil)
print(nil and false)
print(true and 10)
print(10 and true)

Notice that if the first operand is false or nil, then the first operand is returned, otherwise the second operand is returned.

or

The or operand returns true if one of it's operands is true, otherwise it returns false:

print(1 > 1 or 1 == 1)
print(1 ~= 1 or nil)
print(nil or false)
print(true or false)
print(1 < 2 or 1 ~= 2)

In contrast to and, or returns it's first operand if it is something other than false or nil, otherwise it returns the second operand. This can be useful when writing a function that uses variables that may be uninitialized, for example:

print(FavouriteFilm or "Die Hard")
 FavouriteFilm = "Pan's Labyrinth"
 print(FavouriteFilm or "Die Hard")

As FavouriteFilm == nil, the or operator returned the string instead. When we assigned it a value, being the first operand it was returned first.

not

not is the only unary operator on our list, as it only takes one operand. The rest are considered binary, as they take two operands. When using not, the operand goes to the right. If the operand is false or nil, the result is true, otherwise it returns false:

print(not false)
print(not nil)
print(not 0)
print(not "Noooo")
print(not (1 < 2))

Notice that I used brackets around the relational operator in the last example. This is because not has high precedence than <, resulting in 'false < 2', which throws up an error.

Updated precedence

This means the precedence list from the last tutorial needs to be updated:

^ (Eponentiation, right associative)
not, # (length operator, explained next tutorial)
* (Multiplication), / (Division)
+ (Addition), - (Subtract)
.. (String concatenation, explained next tutorial)
<, >, ~=, ==, >=, <= (Relational operators)
and
or

Don't worry about memorising this list, it is just a useful way to introduce the different operators you will most commonly use.

Summary

  • The relational operators are: <, >, ~=, ==, >=, <=
  • These can be used to compare two values
  • and operator returns true if both its operands are true
  • or operator returns true if one of its operands are true
  • not returns true if its operand is false or nil

Concatenation and length operators

This short tutorial will cover two useful operators that haven't been mentioned so far.

Concatenation operator

Concatenation is the process of splicing together two values (strings or numbers) into a string, and is represented by two dots: ..
For example:

a = "Very " .. "handy"
print(a)

You can concatenate variables too, if they contain strings or numbers:

a = "This also "
b = "works"
c = a .. b
print(c)

d = 25 .. 25
print(d)
print(type(d))

Note that the output is always a string. Although remember Lua can convert strings containing only numbers into a number if is attempted to be used as a number:

d = "25" .. 25
print(d, type(d))
d = d * d
print(d, type(d))

Within PZ, this is useful for menus or any seen text that uses a value that may vary by player. For example:

playerName = "Baldspot"
daysSurvived = 7
zedsKilled = 15

print(playerName .. " survived for " .. daysSurvived .. " days. He killed " .. zedsKilled .. " zombies.")

Length operator

The length operator is self-explanatory; it returns the length of its operand. It is unuary and represented with: #

a = "Baldspot was here"
print("The sentence is " .. #a .. " characters long")

It can only be used for strings and tables.

Summary

  • Concatenation operator splices together two numbers or strings into a string
  • Length operator returns the length of either a string or table

If statements and functions

If statements

If statements allow the user to control the conditions for when code is run. It tests its conditions and then completes then or else statements accordingly, with else being optional. In full, the format is as follows:

a = 1
if a > 5 then
  print("More than 5!")
  elseif a < 5 then
  print("Less than 5!")
  else
  print("Number is 5!")
end

As the value of a changes, potentially so will the output. Both else and elseif are optional:

a = 1
 if a > 5 then
   print("More than 5!")
 end

elseif is a useful alternative to putting separate if statements under else - meaning only one end is needed for the whole if statement.
Like most things in Lua, if statements can be nested:

a = 3
 if a > 5 then
    if a/2 == math.floor(a/2) then
      print("a is more than 5 and is even")
      else
      print("a is more than 5 and is odd")
    end
   elseif a < 5 then
    if a/2 == math.floor(a/2) then
      print("a is less than 5 and is even")
      else
      print("a is less than 5 and is odd")
    end
   else
   print("a is 5!")
 end

math.floor returns the nearest lower integer (whole number). The math library will be explored in later tutorials. Although, it should be mentioned this can be written using one if statement:

a = 9
 if a > 5 and a/2 == math.floor(a/2) then
  print("a is more than 5 and is even")
  elseif a > 5 and a/2 ~= math.floor(a/2) then
  print("a is more than 5 and is odd")
  elseif a < 5 and a/2 == math.floor(a/2) then
  print("a is less than 5 and is even")
  elseif a < 5 and a/2 ~= math.floor(a/2) then
  print("a is less than 5 and is odd")
  else
  print("a is 5!")
 end

Functions

Functions, like print, allow the same piece of code to be used repeatdedly without having to re-write it every time. They can be creating using this format:

function increment(a)
--statements go here
end

a here represents any arguments that could be used by the function - like strings entered into print.
Here is an example of a function:

function incrementPrint(a)
print(a)
a = a+1
print(a)
end

incrementPrint(5)

Note that while a has been used to indicate where an argument goes, a variable by the same name won't be used unless entered as an argument. For example:

function increment(a)
print(a)
a = a+1
print(a)
end

a = 5
increment(a)
print(a)

The a referred to inside the function is different to the variable a - this is the difference between local and global variables and will be explained in a later tutorial. What this does mean is that you don't have to worry about arguments conflicting with variable names - although equally variables of the same name can't be accessed outside the function.
If you try the following code, you may notice that there is no output:

function increment(a)
a = a+1
end

print(increment(1))

This is because no value is returned from the function. This can be rectified using return - however, return is the last statement to be read in the function:

function increment(a)
a = a+1
return a
end

print(increment(1))

When calling a function with a single argument which is either a literal string or table constructor, then you do not need the parenthesis:

print"Hello World!"

Summary

  • If statements check their condition to see if it is true and runs the appropriate statement
  • They can include elseif and or else
  • You only need one end per if statement
  • Functions allow the same piece of code to be run again
  • Arguments are local variables inside a variable
  • return can be used to return anything inside the function
  • Functions with a single argument that is a string or table constructor do not need parentheses

Loops

Loops allow the user to control how many times a piece of code is run. There are three main types of loops used in Lua: for, while and repeat. while loops are not supported in PZ, so will only be mentioned very briefly.

For loops

For loops have the following format:

for local_variable = start_number, end_number, step do
--statements go here
end

For example:

for i = 1, 10 do
print(i)
end

In the above example 'step' is not specified, so Lua assumes it is +1. However, we can specifiy step to anything numerical, including decimals and negative numbers:

for i = 10, 1, -0.5 do
print(i)
end

If Lua detects that the loop will never reach the end number, it simply won't run. Note that i used in the example is a local variable, so any changes made to it are only done inside the chunk:

s = 10
e = 15

for i = s, e do
print("Start i: ".. i, "Start s: " .. s)
i = i+i
s = s+s
print("End i: " .. i, "End s: " .. s)
end

print("Final: " .. tostring(i), s)

While loops

while loops are not supported by PZ (although this isn't strictly true), however it is worth knowing if you want to use Lua outside PZ. A while loop tests a condition to see whether it is true, and repeats its statements until it is. They have the following format:

while condition_is_true do
--statements
end

For example:

i = 1

while i < 5 do
print(i)
i = i+1
end

Repeat loops

Repeat loops are similar to while loops, except the condition is tested at the end of the loop, meaning it always runs at least once. They have this format:

repeat
--statements
until condition_is_true

Note that unlike for and while loops it does not need an end or do. Using the same example as the while loop:

i = 1

repeat
print(i)
i = i+1
until i > 5

As already stated, repeat loops always run at least once regardless:

i = 6

repeat
print(i)
i = i+1
until i > 5

print(i)

Summary

  • for loops can numerically dictate how many times code is run
  • The step of a for loop is assumed to be +1 if not specified
  • while loops repeat while its condition is not true
  • while are not properly supported in PZ
  • repeat loops are similar to while loops, but always run at least once

Local and global variables

There are two types of variables in Lua: local and global. So far, these tutorials have used only global variables (unless otherwise specified). Global variables are available to the whole program. Local variables are available inside the chunk they were created. See this example for further clarification:

for i  = 1, 3 do
local l = i*i
g = i*i
print(l, g)
end

print(l, g)

The global variable 'g' continues to exist outside the loop, while local 'l' is discarded. As can be seen, local variables can be specified using local. Local varibales only exist inside their 'chunk' or lower chunks, such as nested if statements. Chunks refer to a sequence of statements, such as inside an if statement, loop or function. See this example:

i = 4

if i > 3 then
 local v = "V"
 print(v .. " is available")
       if i/2 == math.floor(i/2) then
       local v2 = "v2"
       print(v  .. " is available in lower chunks too")
       print(v2 .. " is available as it was created in this lower chunk")
       end
 print(tostring(v2) .. " but is not available in a higher chunk")
end

It is considered good practice to avoid using global variables where possible. It makes code is easier to read (as you can see the scope of each variable used, i.e. the chunks it is used in), it clutters namespace and makes it easier to confuse common ones (such as 'player', 'x' or y') and could create problems where different parts of the program try to access a global simultaneously. Local variables allow you to bypass this problem.

One useful thing you can do with local variables is to make a copy of a global variable inside a chunk where it is needed. This preserves the global variable from any changes.

playerHealth = 100

function damageRecieved(damage)
local playerHealth = playerHealth
playerHealth = playerHealth - damage
print(playerHealth)
end

damageRecieved(25)
print(playerHealth))

The 'local v = v' can be a useful snippet to have.

Summary

  • Global variables scope is the whole program
  • Local varaible scope is only inside the chunk it was created (and any lower chunks)
  • Where possible, local variables should always be used
  • local v = v is a useful way to use the value of global variables while preserving them

Tables

Tables are the last datatype to be introduced in these tutorials. In Lua they are very flexible and extremely useful. They are basically lists of values held in one place. To create one, we use a table constructor, represented by curly brackets, like so:

week = {}
print(type(week))

This creates a table called 'week'. We can now start adding values into the table. There are multiple ways of doing this. Tables that are arrays, i.e. tables with the indexes being numbers, we can simply write as so:

week = {"Monday", "Tuesday", "Wednesday"}
print(week[2])

Table fields can be understood is terms of table[key] = value
The above is the equivalent of writing:

week = {[1] = "Monday", [2] = "Tuesday", [3] = "Wednesday"}

We can also specify individual positions:

week[4] = "Thursday"
week[5] = "Friday"

The other type of table is called an associative table (also known as an associative array) - this is one where we use other datatypes for indexes instead of numbers, or the array has obvious gaps. With these types of tables you can add new fields using only a dot:

week = {}
week.LastDay = "Sunday"
print(week.LastDay)

Note that tables are always anonymous - there is no fixed relationship between the variable pointing to the table and the table itself:

week = {}
week.LastDay = "Sunday"

days = week
print(days.LastDay)

Also note that that week.x is not the same as week[x]. week.x is a field indexed by "x", while week[x] is a field indexed by the variable x. Tables can contain any datatype, including tables:

characters = {Baldspot  = {}, Kate = {} }
bald = characters.Baldspot
kate = characters.Kate

bald.health = 100
kate.health = 25

print(characters.Baldspot.health, characters.Kate.health)

bald.weapons = {"Knife", "Frying Pan", "Baseball Bat"}

print(characters.Baldspot.weapons[2])

Tables can also include functions. These functions can actually affect the table contents itself, but this will be covered in a later OOP tutorial.

Summary

  • Tables are created using the table constructor, {}
  • Arrays are tables with logical numerical indexes
  • Associative tables are tables with indexes that are not numbers
  • Tables can contain any datatype

Looping through tables

As covered in the last tutorial, you can access individual fields easily. However, there may be times when you want to use all the key-value pairs stored in table. For this, we use loops.

Firstly, let's create a table:

week  = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}

If you try print(week) you'll notice all you get is Lua's reference for that table. To view all the values in arrays, we can use a special for loop called ipairs:

for i,v in ipairs(week) do
print(i, v)
end

As you can see, i is assigned all the keys, and v all the values. Like mentioned in the loops tutorial, these are local variables discarded each cycle.
ipairs only works for arrays without any gaps. It works by iterating over the key-value pairs until key=nil and key+1=nil. Note that you can also use a regular loop to go through an array:

for i = 1, #week do
print(i, week[i])
end

For associative tables we can use a slight variation, in pairs:

Bob = {}
Bob.health = 50
Bob.weapon = "Axe"
Bob.hunger = 100

for i,v in pairs(Bob) do
print(i, v)
end

This returns all key-value pairs, but in an unpredictable order.

Summary

  • You can use an ipairs to loop through arrays
  • Or use a regular for loop and the length operator
  • You can use pairs loop for associative tables, but the order of returned values is unpredictable

See also

External links