The Lua language is a fast, procedural programming language used by the GIANTS Engine and many other applications. Lua was originally created in 1993 due to software trade barriers in Brazil, which prevented many from buying specialized software from outside the country. Because of this, the language was designed to be highly customizable with a simple API in the C programming language so that programmers could easily make changes to fit their needs.

The goal of this chapter is to give you the knowledge you need to make your first programs in Lua so that you’re ready to become a fully proficient programmer. No prior programming experience is assumed, so we will start by covering the concept of variables and other universal programming constructs. Then, within a few chapters, you will be able to create full programs to interact with your Farming Simulator mods. If you find some material difficult, don’t be worried! You’re taking a crash course in a skill that you will build upon for the rest of your life, and this is just the beginning. If you feel the need to move between different sections or review some earlier ones, you are encouraged to do so!

Technical Requirements

In this chapter, you will be working entirely in the GIANTS Editor and must meet the requirements mentioned in the “Technical Requirements” section of Chapter 2, “Getting Started with the GIANTS Editor.” While releases are infrequent, make sure you are always using the most recent version of the GIANTS Editor. This will ensure that you are able to take advantage of any new features. You can find all the code used in this chapter in the book’s code repository on the GDN at the following link:

A Q R scan code.

https://gdn.giants-software.com/lp/scriptingBook.php

Learning About Data Types and Creating Variables

In programming, a variable is a way for your code to hold a piece of information; different types of information are called data types. Variables are convenient because when you create one, you can give it an identifier (a name) so that it may be easily referenced and used later. In many programming languages, these variables are typed; this means that the type of the variable, be it a number or a word, must be decided when it is created. In Lua, variables are untyped which means you may assign any type of data to a variable without stating what that type is.

It is important that you know what the most common data types are before you begin programming.

Data Types

In Lua, the number data type can hold both whole and fractional portions of numeric values. Examples of a number can include 1, 0.33, 1.2, etc. This is the only numeric type used in Lua, but it may be helpful to remember that an integer is simply a whole number, positive, negative, or zero. The number data type is actually named a double-precision floating-point number, though it is more commonly called a double, which is a type of float. Some values cannot be perfectly represented by this system because of how computers work; such inaccuracies are referred to as floating-point errors. An example of how one of these errors might look is when the value 0.2 is instead shown as 0.20000000000000018. It’s important to be aware of these errors when processing data, and you may run into them in a game development environment.

Booleans are a simple data type with a binary true or false value; when stored in a variable, they are more frequently called bools. It is important to note that in Lua, 1 and 0 only have numeric values and do not have any uses in Boolean logic, as seen in other languages. That is to say, 1 and 0 only represent numbers, not true or false or any other value.

Strings, as a data type, are best explained as any kind of text. Strings can represent words, sentences, letters, and more. In most languages, strings are an array or sequence of individual characters; in Lua, however, characters are not a data type.

In Lua, tables are a data structure that can be used in many different ways. We will discuss two uses of tables in this section: arrays and dictionaries. Unlike arrays in other programming languages, tables when used as an array can act more like lists, as they are not limited to an initialized size, nor do they require one; as a result, additional table positions do not need to be preemptively reserved. Elements contained in these tables are indexed from 1 to n, with n being how many elements are in your table. It should be noted that in most other languages, the starting index value of a table is 0, whereas it is 1 in Lua; these are called zero-based and one-based indexing styles, respectively. Additionally, tables are not typed by default; it could be said that this has many advantages, as in other languages you would be restricted to only adding values of the same type into a single array. However, you should still keep your tables organized and not loosely throw any type of data into them like a virtual colander.

The greatest benefit of using a table as a dictionary is the ability to index anything with a convenient key instead of numbered indices. For example, if you had a farm in your experience and you wanted all the apples in your experience to have one functionality and all bananas to have another, you could use the name of the fruit as a dictionary key that has an associated function; you could even use the physical fruit itself as the key since keys are not limited just to strings. As before, dictionaries have an infinite capacity, and elements within them can be of any type.

Setting and Manipulating Variables

Initializing and changing variables in Lua is a process that utilizes a variety of different operators to achieve a desired result. A convenient way to check the value of your variable is by using the print() function. You learned this function in Chapter 2, “Getting Started with the GIANTS Editor,” where you used it to output Hello world! in the Console. The print() function is a vaLuable tool when following along in your program and for observing what your code produces when it would otherwise not be visible. In this section, we will begin writing new lines of code that hold and manipulate data. To get started, open the GIANTS Editor and create a new script. Once you write a program, you will need to click Save and then Execute. Output from your program will be seen in the console as shown in the “Scripting” section of Chapter 2, “Getting Started with the GIANTS Editor.”

Numbers

Number variables are easy to set and change. We initialize variables using the “local” keyword followed by its name and then its value which determines its type. If you do not use the “local” keyword, then the variable is declared globally for the entire script which can lead to problems later on. After writing local, you put the name of your variable. A variable name cannot start with nonalphabetical characters and cannot contain any characters that are not alphanumeric except for underscores. For example, if you wanted to have a variable that simply held the value of 99, your code would look like this:

local myNumber = 99

There are many different operators we can use to change the variable, and libraries of special functions, but for this example, we simply want the value to increment by 1 to reach a total value of 100. To accomplish this, we can think of setting the variable to itself plus 1 by using the addition operator (+):

myNumber = myNumber + 1

When we are referencing a variable, we simply use its name, so stating “myNumber = “, we are saying we want to update the existing variable we declared earlier. The local keyword is only used when we first initialize the variable. Depending on the scenario, it may be more practical to simply set the variable to 100 directly. In this case, you would simply set the variable to the value, similar to what you did when initializing it (without the local statement, of course).

Lua supports the arithmetic operators that are standard across most languages, those being for addition (+), subtraction (-), multiplication (*), division (/), and modulo (%).

While you are likely familiar with most of these operations, the concept of modulo may be new to you. A modulo operation simply returns the remainder when a is divided by b. For example:

  • 7 % 4 = 3

  • 8 % 4 = 0

For more advanced operations, Lua provides a library with the same functionality as the standard math library in the C language. This library provides trigonometric functions, value conversions, and specific values with convenience and accuracy. To utilize this library, we can use the math keyword.

Here is an example of getting an accurate approximation of pi by using the math library:

myNumber = math.pi

Moving forward, we will discuss the Boolean data type.

Booleans

Setting a Boolean is simple as there are only two initialization options: using the true or false keyword. An initialization statement for a Boolean value may look something like this:

local myBool = true

To change the value of this variable, you simply need to set the bool as true or false. For example, we will set myBool equal to false with the following line of code:

myBool = false

There is a trick for setting a bool to its opposite value in one line, as opposed to using a conditional. We can do this by making use of the not operator, which will be covered more once we get to conditional statements. The not operator serves to simply return the opposite of the input following it. For example, if we wanted to change the preceding myBool variable from false back to true, we could simply use the following line of code:

myBool = not myBool print(myBool) Output: true

Continuing, we will cover another primitive data type, strings.

Strings

To declare a string, you should use the same variable initialization style and write your text inside double quotes. We use double quotes to describe strings, for example, “My String.” If the string itself contains double quotes, then we can use a single quote instead:

local myString = "Hello"

If you are using double quoted strings, Lua uses the backslash (\) as an escape character. This means that any character that would normally be special is treated as text within a string. For example, if someone in some game dialog is speaking from the third person, you could create double quote marks, like this:

myString = "He said \"I don't like apples!\""

Conversely, this backslash operator makes some normal text special. Two characters that are made special by the backslash character are the letters n and t.

\t: A tab will be added in that place.

\n: A new line is inserted at that point.

The following code shows the usage of both special chars:

myString = "Separated\tby\ttabs" print(myString) Output: "Separated by tabs" myString = "Separated\nby\nlines" print(myString) Output: "Separated by lines"

If you have multiple lines in your string, you do not necessarily need to utilize the \n operator. Lua, unlike some other languages, supports the use of multiline strings. Aside from being able to simply press your Enter key to create new lines, you can more conveniently format paragraph-sized strings in your programs. To initialize a paragraph string, you must capture your text within double brackets, as shown here:

myString = [[This string can span multiple lines.]]

One of the most common ways string variables can be changed is by concatenating them. By following any string with .. and providing another string, the latter string will be attached at that position:

myString = "Hello" myString = myString.. " World!" print(myString) Output: "Hello World!"

The ability to concatenate is particularly useful when you’re presenting different information to a player via a UI element. For example, if you wanted to announce that some event has come to an end, you can append the name of that event to a string:

local eventName = "rain" myString = "Weather event over! The ".. eventName.. " has ended." print(myString) Output: "Weather event over! The rain has ended."

Similar to how numeric data has a library for mathematical operations, there exists a library of string functions for more complex manipulations, as well as data management. This library can be accessed by writing the string keyword. Some functions include the ability to change the case of all letters within a string, the ability to split strings at certain points, and even to find all strings within a string that match a certain pattern, which is useful for systems such as in-game search bars. For example, all the letters in the following string will be converted into uppercase using one of the string library’s functions:

myString = "iT iS wARm tOdaY." print(string.upper(myString)) Output: "IT IS WARM TODAY."

Using strings in numeric arithmetic should be avoided when possible, but there may be situations where this happens. Whenever a string is used where a number is required, Lua will attempt to automatically convert that string into a number. For example, if you try to add the string “50” to the number 100, it will function correctly, as shown here:

print("50" + 100) Output: 150

However, if the string you are attempting to perform an operation on contains nonnumeric characters, the string-to-number conversion will fail. To prevent this, you can check if a string is fully numeric by using the tonumber() function. If the string that’s been passed to the function cannot be converted into a number, the value that’s returned will be nil; nil is a value that represents something nonexistent. If we attempt to add the string “Hello” to the number 100, an error will occur:

myString = "Hello" print(tonumber(myString)) Output: nil local myNumber = 100 + myString Output:  "local myNumber = 100 + myString:3: attempt to perform arithmetic (add) on number and string"

Next, you will learn about tables, which have a wide variety of applications.

Tables

Tables are straightforward but less intuitive to set and manipulate than the other data types we have covered so far, as you must make use of a library for some operations. If you are already familiar with other programming languages, you might compare tables with an array or a list.

To create a new, empty table, you must set your variable to a set of braces ({}), as shown here:

local myTable = {}

When initializing a new table, you do not need to have it start out empty; you can include elements within your table when it is first created. It is important to note that elements in tables require a separating character; in this case, that character is a comma (,). For example, if a player was tasked with retrieving items from a grocery list, you could initialize a table of predetermined foodstuffs this way:

local myTable = {"Tofu", "Milk", "Bacon"}

Once you’ve created your table, you will need to be able to index what items exist within your list. Without loops, which will be covered later in this chapter, you can only index items individually. Remember that tables use one-based numeric indexing, so indexing items is just done with a number within brackets ([]). All items from the grocery list could be either assigned to a variable or acquired directly, as seen in the following print line of code:

local myTable = {"Tofu", "Milk", "Bacon"} local firstItem = myTable[1] print(firstItem, myTable[2], myTable[3]) Output: "Tofu Milk Bacon"

To add or remove elements from a table, you can use the table library, which can be accessed by using the table keyword. This library allows you to alter the structure of tables by changing how they are sorted, what their contents are, and where existing table entries are located. In order to add new elements to a table, you should use the table.insert() function. The function requires a minimum of two arguments, with the first being the table being targeted and the second being the value that you wish to add to the table. If three arguments are provided, the first argument is the targeted table, the second is the desired table position, and the third is the value to be added. When using the function with three arguments, it is important to remember that all the elements following or at the desired position are shifted to the right. Furthermore, there are no restrictions to the provided index, meaning the index can be negative or be an element that hasn’t been reached yet by the length of the table, though you should avoid this as it internally converts the array-like table to a dictionary. Here is an example of adding an element to the beginning of a table and an element without a position specified, which will, by default, go to the end of the table:

local items = {"Elephant", "Towel", "Turtle"} table.insert(items, 1, "Rock") table.insert(items, "Cat") -> items = {"Rock", "Elephant", "Towel", "Turtle", "Cat"}

To remove an item from the list, you need to know the exact index of the item within the table. For example, if the list is only supposed to contain living things, we would want to remove the Rock and Towel items. We can do this by using the table.remove() function. It is important to note that removing an element from a table will shift all the elements that follow it to the left. So, if the rock was removed from the table first, the indices of all the other items in the table would be one less than they were before. This can be seen in the following code:

items = {"Rock", "Elephant", "Towel", "Turtle", "Cat"} table.remove(items, 1) -> items = {"Elephant", "Towel", "Turtle", "Cat"} table.remove(items, 2) -> items = {"Elephant", "Turtle", "Cat"}

To confirm that the correct number of elements is within your table at any given time, you can preface a table or table variable with the # operator to return the number of elements within it. Additionally, you can use the table.getn() function to return the same result, though this is longer to write. You can prove these techniques return the same result by making the following comparison:

print(#items == table.getn(items)) Output: true

We’ll cover how to make more comparisons when we learn about conditional statements. In the following section, you will learn about dictionaries.

Dictionaries

As we mentioned previously, dictionaries are tables that use custom, key-based indexing as opposed to sorted numeric indexes. Conceptually, you can think of entering values into a dictionary as declaring a variable, except the local keyword is not applicable here. While elements in a dictionary can be laid out like a table, it is more common for each entry to have its own line; the separating character for elements is a comma. If you had a restaurant’s menu within your experience, you could arrange the items within a dictionary, with the key being the name of the meal’s course and the value being the name of the dish:

local menu = {       appetizer = "House salad",       entree = "Ham sandwich",       dessert = "Ice cream", }

Indexing these declared elements is quite intuitive as you must simply follow the path to the desired value. In this case, let’s say you wanted to capture what dish was being served as the entrée on the menu with a new variable:

local meal = menu.entree print(meal) Output: "Ham sandwich"

Setting elements is equally as straightforward; by following the path, you can set or alter the element based on its data type like any other variable:

menu.entree = "Turkey sandwich"

One of the advantages of using these keys in Lua is that they are not restricted to only string indexes. By using brackets ([]), you can use any data type as an index of your value. This is particularly useful if you want one data type to have a direct association with another at a given value. For example, if you wanted to set a list of threshold prices that correlated with a describing string, you could use a number as an index. Bear in mind that in order to index non-string keys, you must also use brackets:

local prices = {       [0] = "Free",       [5] = "Cheap",       [20] = "Average",       [50] = "Expensive", } print(prices[0]) Output:  "Free"

Something to note is that tables can have another table as a value; whenever something exists within another entity of the same type, we call this nesting. You can create structures by nesting tables within each other and fetching them with the same key-based style. When we discuss classes and specializations, you’ll see that nesting tables is a somewhat common practice for organizational and functional purposes. For instance, if you wanted to maintain sets of information where you can retrieve data with a key, you can format your dictionary like so:

local configInfo = {       ["Peach Tree"] = {             spawnRate = 16,             health = 50,       },       Chicken = {             eggsPerMinute = 3,             health = 15,       }, }

Now that you know how to set and alter these data types once they have been assigned to variables, you will learn how to check the values of them to determine what type of behavior should occur as a result.

Conditional Statements

Conditional statements or conditional expressions are used in code when you want different behaviors to occur only when some requirement is met. These are important for determining different information about data and what your program should do to handle that data accordingly.

The if statement is the core component of conditional expressions. These statements consist of three elements: the if keyword, the case that must be met for the contained code to be executed, and the then keyword, which serves as an identifier for the end of your case. To give you a direct example of this, the following code shows a conditional where the condition is simply true, meaning the contained instructions will always be executed:

if true then       print("Executed") end Output: Executed

Here, you can see that the conditional closes with the end keyword. In Lua, anything that acts as a single block of code (defines a scope) will have end designate the conclusion of that block.

Returning to the condition portion of if statements, determining whether a condition has been met is based on Boolean logic, a system of evaLuating any type of data. We end up with a true or false value as the result. To do this, evaLuations are made using a system containing various logical operators and relational operators. Like many languages, Lua uses two equal signs (==) to check equality between values; this is a relational operator. As an example, let’s say you want to print the string “Play motor sound” only if another string representing the state of an object is equal to “motorOn”:

if ignitionState == “motorOn” then       print(“Play motor sound”) end

For this operator, there exists an opposite: the not equal to expression (~=). Like the use of its counterpart, this relational operator is used to make comparisons of an explicit value.

To check finite or infinite ranges of numbers, you can use the relational operators of greater than (>) and less than (<). These operators have variations that include the value they are being compared to in the form of greater than or equal to (>=) and less than or equal to (<=). A practical application of these operators could be only playing a sound when the engine of a vehicle is revved:

if motorRPM >= 750 then       print("Play loud motor sound") end

The not operator serves to negate whatever value is provided to it. As we saw previously when we switched the state of a bool variable, the not operator returned the opposite of what was given to it. In terms of conditional statements, this can be used in similar situations as the inequality operator (~=):

if movementDirection ~= -1 then       print("Moving forward or standing") end

The logical and operator is used to compare two values and requires that the values provided to it are both true. In the following example, we want to ensure that both variables hold Fruit as their value. When this condition is not met because one is defined as Vegetable, we will not see any output since a true value is not present on both sides of the operator:

local item1 = "Fruit" local item2 = "Vegetable" if item1 == "Fruit" and item2 == "Fruit" then       print("Both fruit.") --No output as requirements not met. end Output:

The logical or operator only requires that at least one of the values it receives is true. We can see in this instance that the item is defined as Vegetable. The condition says that the item must be defined as either Fruit or Vegetable, meaning we will see output since one of the values on either side of the operator is true:

local item = "Vegetable" if item == "Fruit" or item == "Vegetable" then       print("Is produce.") --Prints as one requirement is met. end Output: "Is produce."

As we mentioned previously, you can check multiple cases using one conditional expression, though this does not require the use of multiple if statements. The else keyword grants additional functionality to these expressions by executing an alternate case if the first condition was not passed. Let’s look at an example where lifting a heavy object requires 100 strength, but an object that is not heavy requires only 50 strength. Notice that our heavy variable, being a bool, will only be true or false, and, consequently, we do not need to use the equality operator (==):

local heavy = true local strengthRequired = 0 if heavy then       strengthRequired = 100 else       strengthRequired = 50 end print(strengthRequired) Output:  100

While this has great uses, it does not allow us to explicitly check additional cases – it merely gives us an idea of what to do if the previous condition was not satisfied. The elseif keyword is used when you want to check additional cases that may occur under different conditions. You can have as many elseif statements as desired, allowing you to create a chain of various conditions and cases. When using elseif statements, you can still utilize an else expression, but it must be at the end of the overall conditional statement. Let’s look at an example where a machine has been supplied random produce and we must count fruits, vegetables, as well as any other item that managed to find its way into the supply:

local numFruits = 0 local numVeggies = 0 local notProduce = 0 local item = "Fruit" if item == "Fruit" then       numFruits = numFruits + 1 elseif item == "Vegetable" then       numVeggies = numVeggies + 1 else       notProduce = notProduce + 1 end

Lastly, there exist implicit conditional statements, which are expressions where, through the use of logical operators, you can set a condition and alternate cases without ever explicitly writing an if statement. In most languages, this behavior is called a ternary expression.

In the following code, the goal is to assign a string to the isEven variable based on whether some value assigned to a variable called number is, in fact, even. While this could be accomplished with an if-else statement, it is shorter to use this new expression here instead:

local isEven = number % 2 == 0 and "Even" or "Odd"

As you can see, if number is even, that side of the and operator will be true when assigning “Even” to the variable by using short-circuit logic, meaning conditional evaLuation stops after one condition is met or violated. If number is not even, it will go to an alternate case, which is “Odd”. You may have observed that the or operator acts similarly to the else keyword in this instance because of the nature of this implicit expression.

Like in mathematics, certain operators take precedence over others; this means that they are evaLuated first. In Lua, mathematical operators have the same precedence as they do in the real world, and relational operators typically take precedence over logical operators. Because of this, the use of parentheses in your conditions can help make your code more readable and help ensure it executes as you intend it to. You can see the full order of operator precedence from the linked page from the Lua documentation website:

A Q R scan code.

https://www.Lua.org/pil/3.5.html

Conditional statements are a core component in programming and, as you have seen, have a multitude of applications for even the most basic programs. In the next section, we will cover loops. Loops often go hand in hand with conditional statements, since they can feed whole sets of data into a conditional expression or repeat manipulation as needed to accomplish a desired behavior.

Declaring and Using Loops

Loops are vaLuable components when it comes to programming, especially when working with sets of data. It would, of course, be quite unrealistic to expect a programmer to index and assign all 1000 elements of a hypothetical table to variables in order to perform some sort of operation. To accomplish behaviors like this, loops are key. They function by jumping back to the beginning of their code block if a condition is still met, executing until they reach their terminating case.

for Loops

for loops are a type of loop that are primarily used for iterating over datasets. In Lua, those datasets are typically related to tables or numbers. In Lua, there are two types of for loops: numeric for loops and generic for loops. The primary difference between these is what determines how they are executed. For numeric for loops, a variable is assigned to a defined start value, end value, and optionally an increment value; if the increment is not included, Lua sets the increment to 1. The numeric for loop will then execute the contained code a specified number of times, treating the endpoints of the number range inclusively. Additionally, the assigned variable serves to tell you what the current value of the loop is. Much like the use of if and then in conditionals, for loops use for and do as their declaration keywords.

The following example prints numbers going from 0 to 10, incrementing by 1 with each loop completion. Note that the increment in this case did not need to be specified but has been shown to aid with your understanding:

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

Let’s create a more practical example using a numeric for loop where we find the sum of all integers ranging from 1 to n. Additionally, we can test that the for loop works correctly by plugging in the same value for n into the theorem for this operation seen in Figure 3-1.

Figure 3-1
An equation reads I = 1 N I = 1 + 2 + 3 + dot dot dot + N = N open bracket N + 1 close bracket 2.

Theorem for the sum of the first n natural numbers

In this demonstration, you will notice the use of tostring(), which functions much like the aforementioned tonumber(). This is used because while print() will automatically convert other data types into strings, you cannot append other data types, except for numbers to strings. In this example, we will find the sum, print it, and then print whether the sum that was found by the for loop matches the value expected by the theorem:

local n = 17 local theoremValue = (n * (n + 1)) / 2 local sum = 0 for i = 1, n, 1 do       sum = sum + i end print(sum) print("Function working = ".. tostring(sum == theoremValue)) Output: 153 "Function working = true"

Returning to the for loop types, we have generic for loops. While the name seems to imply that they are not useful or special, you will likely utilize them more than numeric for loops – or most other types of loops for that matter. Generic for loops allow you to traverse all indices and values returned by an iterator function.

Iterator Function

In programming languages, an iterator function is designed to allow programmers to process every element of a data structure while making those returned values isolated from the data structure itself except when the element is passed by reference, rather than by value. We’ll cover what this means in the “Recursion” section of this chapter. For now, keep in mind that things like tables and instances are passed in directly where values like numbers or strings are copied. Modifying the former types in the loop will modify them directly. This isolation of copied types, which can be seen when defining a variable in a code block, relates to the concept of scope. This means that something that’s declared in a block cannot be referenced outside of that block. In the following example, we have a dictionary called items that contains three strings. By providing this data structure to the pairs() iterator function, you can nicely display every index and value being provided by the iterator:

local items = {       Animal = "Elephant",       Food = "Egg",       Plant = "Flower", } for index, value in pairs(items) do       print(index, value) end

As mentioned previously, the index and value provided by the iterator are not components of the actual data structure that is passed to pairs(). This means you are free to manipulate these as desired without the risk of affecting the elements currently being processed. In the following code, the goal is to double any odd numbers to make them even. While we could assign the number that results from using modulo to a new variable, it is alright in this case to simply use the value variable directly. Notice that in order to actually change the element of the table that value corresponds to, you must use the index provided by the iterator with the table itself. In this example, we use the ipairs() iterator function. ipairs() should be used with tables being used as arrays as it ensures elements are processed in order, whereas pairs() does not. While you could continue to use pairs(), elements may be processed in a nonsequential order, for example, the second element (60) before the first element (37):

local values = {37, 60, 59, 20, 4, 10, 100, 75, 83} for index, value in ipairs(values) do       value = value % 2       if value == 1 then --Odd number             values[index] = values[index] * 2       end end print(values) Output: {74, 60, 118, 20, 4, 10, 100, 150, 166}

You will now learn about a different type of loop that will always run, so long as a condition is met.

while Loops

while loops are loops that run continuously, as long as some specified condition is met. Though they can be used for similar purposes as for loops, it is best to think of them as a repeating conditional statement. In the following example, the while loop increments a value by 1 only if that value is less than 10:

local num = 0 while num < 10 do       num = num + 1 end print(num) Output:  10

As long as the condition is not false (false or nil in Lua), the loop will execute, and if the condition itself is a function, that function will execute and the loop will also run if the function returns a value. This is also true of the condition of conditional statements. You should remember to use good style when doing this, which we will discuss more in the “Demonstrating Programming Style and Efficiency” section.

Another variation of the while loop is the while true loop. This type of loop will always execute as the condition is always true. This variation of a while loop can be useful or simply a preference over the previously shown way of creating one; however, it can cause a script to crash as the loop stacks on top of itself infinitely. To avoid this, we can use a break statement which terminates the loop. The break statement is usually wrapped in some conditional statement as it would otherwise immediately terminate the loop after running once. You can see this loop is equivalent to the previous loop example but makes use of the new syntax:

local num = 0 while true do       num = num + 1       if num >= 10 then             break       end end print(num) Output: 10

In the next section, you will learn about a similar type of loop that can be used in slightly different applications.

repeat Loops

repeat loops execute their contents until a condition is met. While this may seem much like a while loop, the difference is that while loops run only if a condition is met, checking the condition before running. Unlike other loop types, repeat loops always run at least once, checking the terminating condition only after execution, much like a do-while loop in other languages. The keywords for repeat loops are repeat and until, where the code to be executed follows the repeat keyword, closed by until, and ends with the condition to leave the loop. The following loop shows a number variable being decremented until its value is equal to 0:

local num = 12 repeat       num = num - 1 until num == 0 print(num) Output:  0

With loops now at your disposal, you can process large sets of data and make systems that require consistent, repetitive behavior. Next, you will learn about a new way of feeding data to loops, as well as condensing them if they are frequently used.

Learning About Functions

In programming, a function is a code block that is able to be called repeatedly, typically designed to accomplish a single task. Functions are important for abbreviating common jobs being done and help reduce the amount of redundancy within your programs. In this section, you will learn different ways to format functions, as well as when you should be using them.

Functions in Programming

We primarily use functions to define code that can be easily referenced and executed repeatedly. For the sake of terminology, many programming languages distinguish functions from procedures or subroutines; the difference here is that a function executes code to compute some data that is returned, whereas a procedure simply accomplishes a task without returning a value to where it was called. The following function has been designed to create a new table and fill it with fruits, vegetables, or a nonproduce item based on a randomly generated value. See that the function is locally defined, much like a variable, followed by the function keyword and the name of the function, and ends with a set of parentheses (()); this part of a function is called the header. To select a random item, we need to generate a random index using the random function of the math library. This function will generate a random integer between the min and max value that’s provided to it inclusively. This output of the function can then be used with one of the examples seen in the “Conditional Statements” section. Notice how we declare a new variable item but do not assign it a value. This is proper syntax and the variable will hold a value of nil by default. If you feel comfortable, try making a produce counter using the conditional statement from the previous section, a loop, and this function:

local function fillStoreSupply()       local storeSupply = {}       for i = 1, 10 do             local ranVal = math.random(1,3)             local item             if ranVal == 1 then                   item = "Fruit"             elseif ranVal == 2 then                   item = "Vegetable"             else                   item = "Shoe"             end             table.insert(storeSupply, item)       end       return storeSupply end local supplyTable = fillStoreSupply()

One of the main aspects of using functions is providing information to them when you call them for a task. To do this, values need to be added to the call statement of the function and defined in the line where the function is declared. When a value is being provided to a call, it is referred to as an argument. However, when referring to this data inside a function, it is referred to as a parameter. The following function creates a factorial from the provided number, n. A factorial is the result of multiplying all whole numbers less than a number, by that number. See how a number is provided as the argument in the function call. When the function runs, that value is automatically assigned to n, which can then be manipulated as needed:

local function factorial(n)       assert(n == math.floor(n), "n must be a whole number.")       local factorial = 1 --Empty product should be 1       while n > 1 do             factorial = factorial * n             n = n - 1       end       return factorial end print(factorial(12)) Output:  479001600

You may have noticed the use of the assert() function. Much like the use of throw() in the Java programming language, you can use this to throw an error and terminate a process if some condition is not met. The second argument is a string that is sent to the output and will look like any other naturally occurring error message. Do note that while this is useful for testing, you should not include assert statements in your final production code.

In the case that you do not know how many arguments are going to be passed to a function, you can create a variadic function. Variadic functions are like regular functions, though they possess the ability to take any number of arguments in a tuple state. The following variadic function returns the sum of all numbers provided to it. Notice the use of the three dots (...); these dots represent whatever arguments are passed to the function and are most often put directly into a table for processing. In the following code block, you can see a random amount of number arguments being passed to the sum function. The parameters are added and returned as a single value:

local function sum(...)       local args = {...}       local sum = 0       for _, number in pairs(args) do             sum = sum + number       end       return sum end local num = sum(7, 9, 12, 3, 2, 6, 13) print(num) Output:  52

As you may have found out on your own, all the loops we have covered have the ability to yield; that is to say that when they run, they pause the current thread, meaning that the loop must finish before any code following it can be executed. Make certain when writing your loops that they reach a terminating case; otherwise, the program will crash.

You may also notice that we use the special character “_” in the for loop definition. It is common practice to use the “_” character for unused elements. In the sample, the pairs iterator function returns two values in each loop. But in the loop block, we only use the second one (“number”). The first return value is unused so we could mark or replace it with “_” and clearly define we don’t use it in the following code path.

The next section will cover recursion, a useful technique for solving some types of problems.

Recursion

One of the invaLuable features of functions is their ability to call themselves. When properly structured, this can create what you might think of as a loop in a process called recursion. The difference between something like a while loop and a recursive process is that a loop jumps back to its beginning, whereas recursion actually stacks upon itself. In programming, a stack can be a data structure or, as in the case of recursion, simply the state of something in your program. Like a stack of plates or pancakes, the one that was most recently added will be the first one to be removed.

To demonstrate this stacking, let’s return to the factorial function we created earlier in the “Functions in Programming” section. Though a while loop was able to accomplish the goal, you could also achieve the same result by using recursion. In the following function, notice that the call and header of the function remain unchanged; the recursive elements exist in the return statements. For the if statement, the first case simply returns 1 if n is less than 1, because we cannot create a factorial from any values less than this; in the case of n being 0, its factorial would also be 1 by convention. The next case is the most important: if n is greater than 1, then it is multiplied by the value that’s returned by the factorial function, where n is one less than the current value of n. As you may begin to see, this causes the function to stack until n has been decreased down to 1. This is the base case, where no function call is made, and we work back down the stack:

local function factorial(n)       assert(n == math.floor(n), "n must be a whole number.")       if n <= 1 then             return 1       else             return n * factorial(n - 1)       end end print(factorial(6)) Output:  720

To give a better visualization of how this process is being executed, let’s look at a mapped-out example of the previous call to the factorial function, using 6 for n. Observe that each call, n, is set to be multiplied by the returned value of the function and continues to be stacked until a case without a function call is reached. Then, each function stops and is taken off the stack, returning the value to the return statement that called it. Once this process finishes, our original function returns the final value to wherever it was called from:

factorial(6) 6 * factorial(5) 6 * (5 * factorial(4)) 6 * (5 * (4 * factorial(3))) 6 * (5 * (4 * (3 * (factorial(2)))) 6 * (5 * (4 * (3 * (2 * factorial(1))))) 6 * (5 * (4 * (3 * (2 * 1)))) 6 * (5 * (4 * (3 * 2))) 6 * (5 * (4 * 6)) 6 * (5 * 24) 6 * 120 720

Now that you have a firmer grasp of the concept of recursion, let’s look at another practical example. When working with tables, setting a variable to an already existing table will not follow normal behavior and simply copy that value to the new variable; instead, tables use references. References work to save resources, essentially causing new variables to act only as pointers to a previously declared table; the pointer can actually be seen by printing the table. With this behavior, assigning a table to a variable and changing anything within that table would change it everywhere it is referenced. You can test this with the following code. Here, you can see that when a variable’s value is set to a table that has already been created, the variables contain the same reference:

local function checkEquality(table1, table2)       print("Variable 1: ".. tostring(table1))       print("Variable 2: ".. tostring(table2))       print("First and second variable same table = ".. tostring(table1 ==  table2)) end local group = {"Manuel", "Christian", "Zander"} local groupRef = group checkEquality(group, groupRef) Output: Variable 1: table: 0x0000020215384838 Variable 2: table: 0x0000020215384838 First and second variable same table = true

If a table were cloned every time it was assigned to a variable, that would make indexing libraries or any other large table structure extraordinarily expensive. There are scenarios, however, where you may need to clone a table or dictionary. While for loops may be viable in some cases, the presence of nested tables, as seen at the end of the “Dictionaries” section, could potentially require that you use any number of for loops to accomplish your task. To get around this, we can once again use recursion. The following example creates a copy of our items table by creating a new table and adding each element to it by index and value. In the case that the value to be cloned is a table, the function recurses with the nested table as the argument. Once it’s done this, the completely new table is returned to where it was called from, and the checkEquality() function from the previous example is used to verify that the tables are unique:

local items = {       Egg = {fragile = true},       Water = {wet = true}, } local function recursiveCopy(targetTable)       local tableCopy = {}       for index, value in pairs(targetTable) do             if type(value) == "table" then                   value = recursiveCopy(value)             end             tableCopy[index] = value       end       return tableCopy end local itemsClone = recursiveCopy(items) local areEqual = checkEquality(items, itemsClone) print(areEqual) Output:  false

Like loops, calls to recursive functions can yield, but typically, they finish in a short enough amount of time to where nothing in your thread is affected. If, for some reason, your recursive function runs long enough to cause a noticeable pause for the rest of your program, consider reviewing the efficiency of your function.

We will next look at how to create classes and what purpose they serve.

Classes

Classes are a convenient way in programming to organize code into templates with fields, methods, and events easily defined. For your mods, you will need to create classes to define the functionality and attributes of new items you create. The following code shows a sample class without any functionality:

SampleClass = {} function SampleClass.new()       local self = {}        setmetatable(self, {__index=SampleClass})        return self end

That’s all. Quite simply right. We only need a table and a constructor function. But as stated earlier, this class has no functionality at all.

So in a more practical example, you may want to create a vehicle for your mod. You will need to define behavior for when it is turned on, turned off, and how fast it is able to go. Let us say you want to make a new tractor – you can see how to create a class for the vehicle called Tractor in the following example:

Tractor = {} function Tractor.new(name, maxSpeed, maxPower)       local self = {}       setmetatable(self, {__index=Tractor})       self.name = name       self.maxSpeed = maxSpeed       self.maxPower = maxPower       return self end function Tractor:turnOn()       print(string.format("Turned on tractor '%s'", self:getName())) end function Tractor:turnOff()       print(string.format("Turned off tractor '%s'", self:getName())) end function Tractor:getMaxPower()       return self.maxPower end function Tractor:getMaxSpeed()       return self.maxSpeed end function Tractor:getName()       return self.name end

With the class now created, we can create new Tractor objects. In programming, an object is a single instance of a class. Using objects as opposed to functions and procedural logic to create programs is called object-oriented programming (OOP). Languages that use objects and OOP as the primary means of doing tasks are called object-oriented languages. While Lua is not an object-oriented language, creating classes and objects can still be convenient in certain environments as their creation, properties, and methods are uniform and neatly put together. To create a new object from the class, we simply call the new constructor of the class and assign what it returns to a variable. We can then call the methods of our class and use the returned values as needed as seen in the example:

local tractor1 = Tractor.new("Fendt Vario 700", 50, 280) local tractor2 = Tractor.new("New Holland T8", 50, 381) tractor1:turnOn() -> Turned on tractor 'Fendt Vario 700' tractor2:turnOn() -> Turned on tractor 'New Holland T8' print(tractor1:getMaxPower()) print(tractor2:getMaxPower()) Output: 280 381

We will now look at how to write programs with proper style and what to be aware of to ensure they remain efficient.

Demonstrating Programming Style and Efficiency

Writing code with good style not only improves the quality of your work, but it also prepares you to pursue programming in more professional environments or when working with other people. We will cover these universal programming style rules in this section.

General Programming Style Rules

Readability is an important aspect of maintaining good style. Not only do others who may read your code need to understand what is happening, but being able to easily follow your own code will greatly increase your workflow. Having a clean coding style will also enable you to be more conscious of other style factors you should be implementing. The two ways you can make your code the most readable are to use proper indentation and observe appropriate line length. For line length, most college programming courses will likely suggest that you limit your lines of code to 80–100 characters.

In the Script Editor of the GIANTS Editor, there are line numbers but not an indicator for which column you are on for a line. Generally, your line length should not exceed your viewport size, which means that you should not require the use of a horizontal scrollbar to see the entirety of your line. Out of all the readability rules, you should arguably observe indentation style the most carefully. Make sure what you write follows the code examples in this chapter until you feel confident with your ability to follow this rule. Alternatively, you can use the GIANTS Studio which provides a more dedicated programming environment with more quality-of-life features. We will cover the IDE in Chapter 4, “The GIANTS Studio.”

Thinking again about working with multiple programmers, comments are an invaLuable part of letting others, as well as your future self, know what your code is doing. While readability is also needed to make others aware of what a script’s purpose is, comments can be used to more explicitly tell fellow programmers where a code block is being used, what it requires, or what behavior to expect from it.

Looking back at implicit conditionals, there are situations where it may make more sense to use logic within a variable declaration than to use an explicit conditional statement. This decision is ultimately up to you, but only use it when it’s practical. If you need to create more than two or three cases, you should likely just use a conditional. Additionally, remember to consider previously mentioned style points, such as line length.

As mentioned briefly previously, you should ensure that you are not grouping random data together in tables or dictionaries. For organizational purposes, you should be using tables for a significant purpose, and the elements of data within them should be at least loosely associated with each other. If necessary, there is no harm in creating additional tables to accommodate different sets of data being used in your code.

A naming convention refers to how you format the names of your variables, functions, constants, and more when programming. There are many conventions – the first two you’ll likely encounter being Pascal case and camel case. Camel case is where the first letter of the variable is lowercase with the first letter of any other words in the variable being capitalized, for example, camelCase. Pascal case is where the first letter of all words in the variable name are capitalized, such as PascalCase. The convention you use can depend on what you’re defining, but for the purpose of this book, it is sufficient to say you should stylistically use camel case when programming in Lua.

Lastly, you should optimize your code when possible. Optimizing a program generally means writing it so that it accomplishes its goal while taking up the least amount of computational or memory resources. For example, if you are using a value in multiple places, consider using one variable instead of defining another. If you are using repeated lines of code in your program, consider using a single function – while this will have little impact on your program, it will help greatly with readability.

Summary

In this chapter, you learned about programming constructs that exist in a wide variety of languages, such as variables, data types, loops, and some data structures, as well as those that are exclusive to Lua. With this knowledge, you can begin making your own mental connections by experimenting with the examples from this chapter and making your own programs.

In the next chapter, you will learn about the GIANTS Studio to help you find errors and fix your programs. Following that, you will utilize the new information you have learned about to start making your first mod-oriented systems, which will lead to you making full mod creations in the following chapters.