Chapter 1: Getting Up to Speed with Roblox and Luau Basics

In this chapter, we will start by looking at what Roblox is. Once we know this, we will learn about Roblox’s programming language. Then, we will proceed to understand the basics of this programming language, such as the data types and variables. We will learn how to perform unique operations on each of these data types, such as math operations. Once we know this, we can slowly increase the complexity of our scripts. We will then use conditionals to take our scripts to another level. Conditionals allow us to change the behavior of our code depending on data. Finally, we will learn all about scopes. We will learn what they are and why it is crucial to keep them in mind while coding.

The following topics are covered in this chapter:

  • Understanding Roblox and Luau
  • Understanding and using data types in Luau
  • Introducing variables
  • Using conditionals
  • Understanding scopes
  • Exercises

By the end of this chapter, you will have learned how to make scripts in Roblox. In addition, you will know how to make simple systems in your experience.

Technical requirements

To start programming with Luau, you need access to a device with internet access. This can either be a Windows or a Mac device.

You need to download the following software:

  • Roblox Player
  • Roblox Studio

To install Roblox Player and Roblox Studio, please follow the steps in the following article:

https://en.help.roblox.com/hc/en-us/articles/204473560

All the code examples for this book can be found on GitHub at https://github.com/PacktPublishing/Mastering-Roblox-Coding.

The CiA video for this chapter can be found at https://bit.ly/3ORIwT7.

Understanding Roblox and Luau

In this section, we will start by looking at what Roblox is and what makes it unique. Then, once we understand the fundamentals of Roblox, we will learn about the programming language that Roblox uses for its games.

Roblox is a really popular platform for children around the world. This is because they can play almost anything they want. With over 24 million different experiences, as Roblox likes to call their games, you can be almost anything. You can become a pirate, be the king of a castle, escape prison, or stop players from escaping. The possibilities are endless. And the best part is that it is completely free to play. According to Roblox, in September 2021, 47 million unique users played Roblox daily. Once you have created your account in a few clicks, you are ready to join an almost unlimited and constantly increasing supply of experiences.

Creating an account

To make an account on Roblox, visit this link:

https://www.roblox.com/signup

Roblox is unique because other players make all these experiences that you can join. Anyone that creates a Roblox account can create a game. Besides Roblox Player, which allows you to join experiences, there is an additional application called Roblox Studio. With it, you can create your very own experiences.

At the time of writing this book, there are over 9.5 million developers on the platform. These developers can unleash their creativity and make anything they want. Roblox also takes care of all the complex and technical tasks involved in hosting and maintaining games. All you have to focus on is creating the game. Roblox takes care of the rest. The platform ensures that your game shows up for your friends, and makes sure that you and your friends can join the game. They make sure that you can report people who misbehave and bans them from the platform. These systems, among many more, are provided by Roblox. If you make games using other game engines, these systems all have to be made and operated by you. This is pricey and very time-consuming.

However, this is not even the best thing that Roblox has to offer. You are also able to monetize the experiences! Roblox has a virtual currency called Robux. Players can purchase this with real-life money. When they convert their money into Robux, they can use it throughout the entire site to buy different things. This varies from purchasing clothing for your avatar to purchasing in-game perks for any game you like. When someone purchases something in your game, you get 70% of the sale. You can convert this earned Robux currency back to real-life money. This is called the Developer Exchange program. Some developers on the platform make over 1 million USD yearly!

Most of the time, these developers do not work alone. There is usually an entire team of developers that work on a game. These developers have different roles. The most common developers on a team are programmers and builders. However, other developer roles can be added if teams start to grow. In addition, other developer roles exist, such as animators, modelers, UI designers, music composers, and possibly even more. As previously stated, this book is about the programming aspect of Roblox. So, let us learn more about the programming language that is used to code Roblox experiences.

Introducing Luau

Roblox uses a programming language named Luau. Luau is an easy-to-learn yet powerful programming language. Luau is a heavily modified version of the programming language called Lua.

Because Roblox Luau has a relatively low learning curve compared to other programming languages, it is an ideal programming language to start with, especially for younger developers or aspiring developers. Even if you have no interest in creating experiences on Roblox, this is still a great starting point. In general, programming in any language teaches you how to think logically. This is useful even when you are not programming. By the end of this book, you will be able to analyze coding problems, split them into smaller tasks, and overcome them.

If you are hoping to program in other languages in the future, this is an excellent place to start. You might argue that if you know how to program in one programming language, you can program in all of them. This is because programming is basically instructing a computer on what to do. When nothing is specified, the computer does nothing. The only real difference between most programming languages is syntax. The syntax is basically what words you type in your script. Most programming languages use the same words. However, the syntax may differ.

Becoming a master in Roblox game development gives you a head start when programming in other languages. Regardless, before switching to another language, you first have to master Luau. It takes a lot of practice to understand everything in this programming language fully. This will most certainly be very stressful and confusing. Therefore, this book slowly progresses in complexity. A basic understanding of Roblox is highly recommended.

Roblox is a unique platform where people can play millions of experiences for free. The amazing thing about these experiences is that other users on the platform make them. There are a lot of different roles that you can have as a developer on the platform. You can be a modeler, animator, graphics artist, and even a programmer.

In the next section, we make our very first Luau script.

Understanding and using data types in Luau

In this section, we will learn everything about data types in the Luau programming language. Let us start with the absolute basics. Programming is all about data. This data can be anything:

  • The player’s in-game money
  • The spot the player is in during a race
  • The number of criminals that they have arrested, or anything else

All of the mentioned examples are numbers. The in-game money can be 100, the race position can be 1, and the number of criminals arrested can be 3. In programming languages, we have numbers too. Numbers are our first data type.

Internal usage of the number data type

Sometimes, a difference between numbers and integers is made in the documentation. In Luau, integers are a part of the number data type. Internally, Luau changes between doubles, floats, and integers. If the documentation specifies an integer, you can expect the number to be stored as an integer internally. When a number is documented, it is internally stored as float or long. Do not worry if this does not make sense. This knowledge is not required in Luau.

We can have more than just numbers. We can also have sentences. Examples of sentences can be the player’s username, a roleplay name, or a chat message. These sentences are called strings in programming. Strings are our second data type.

You might have heard of our third data type, a Boolean. A Boolean is true or false. People often say programming is about ones and zeros, and true or false. There are no other possibilities for Booleans.

In the following sections, we will learn about all the things that we can do with these three data types. We will start by making a script to test out these data types.

Creating a script

Well, great to know about these three data types, but what can we do with them? Let us start Roblox Studio and follow these steps:

  1. First, create a new baseplate. Because the book assumes basic knowledge of Roblox and Roblox Studio, this is not explained.
  2. Once created, open Explorer. It should be visible by default. Search for something named ServerScriptService. You can see this in Figure 1.1.
  3. Right-click on ServerScriptService, select Insert Object, and add a new script. Make sure you did not accidentally make a LocalScript instance. The difference is explained later in the book. Once added, the Explorer window should look like this:
Figure 1.1 – Your created script

Figure 1.1 – Your created script

  1. Double-click on the script you just created and you should see the following code in the script:

    print("Hello world!")

Now we know how to create a new script. In the next section, we will run this script and see its output. Besides that, we dive deeper into what this piece of code means.

Hello, world!

In Roblox, all scripts start when the game begins. We can start the game by pressing the Play button in Roblox Studio. Once pressed, you will see your character appear in a running game. The game still looks empty, as if nothing has happened. There are no scripts in a baseplate game by default, and the one we just added does not do much. However, it definitely does something. It just cannot be seen by a regular player. We have to open the Output frame to see this. To open the Output frame, click on the Output button under the View section, as shown here:

Figure 1.2 – Opening the Output frame

Figure 1.2 – Opening the Output frame

Shortcut

Instead of pressing the Play button, you can press the F5 key on your keyboard.

Once we open the Output frame, we see Hello world!. Looking back at the script we created, we see the Hello world! part. However, one part is missing in Output; this is the print() part. That is because print() is a function in Luau. Functions are explained later in this book. For now, remember that print() prints the text that we passed to it in Output.

We should zoom in on the Hello world! part of the script. We previously learned that programming languages have sentences. They are called strings. “Hello world!” is a sentence; therefore, it is a string. We can see that this is a string because it is surrounded by quotation marks ("). Roblox Studio also recognizes this as a string and gives it a particular color in your script.

Strings can be anything; go ahead, change the Hello world! part to something else, your name, for instance. Your script could look like this:

print("James")

If we run the script that you just edited and open Output, you should see your name appear.

We now know what Hello world! means and what the print() function does. Consider the print() function as your trusted friend that will keep you informed on what your script is doing. This will help us to understand what is going on and come up with solutions for possible errors. In the following section, we will look at printing numbers.

Numbers

There are more data types than just strings. You can print all of these data types. Let us start with numbers. Change your name to any number, for instance, the number eight. Your code might look like this:

print("8")

If we run the game and open the Output frame, we can see that the number you chose was printed instead of your name. Did you notice how the number is the same color as the string in the Roblox Script Editor? As mentioned before, something that defines a string is quotation marks. The quotation marks are still there in the preceding code, but the correct number was printed. This is because the programming language still sees your number as a string. This is possible because numbers can be stored in strings too.

If we want to make sure our number is recognized as an actual number, we have to remove the quotation marks around the number. So, for example, your code could look like this now:

print(8)

Notice how the color of the number changed in the Script Editor?

In this section, we learned how to print numbers and how to differentiate numbers from strings in our code. In the following section, we dive deeper into the unique things we can do with numbers, such as math operations.

Math operations

If we run the code, we see the same line in Output. So, why bother making it a number instead of using strings? We previously mentioned that they are different data types. Different data types also serve different purposes. Numbers can do things that strings cannot do. Let us take a look at it from an easy perspective. In school, you used numbers during math. What did you use numbers for? Most likely to calculate things. You were adding, subtracting, dividing, multiplying, and whatnot. These are all things that you can do with numbers, whereas you probably had to write many sentences in English class. The chance is meager that you used multiplication on sentences.

Now that we have a number in our script, let us do some math. We start by adding the number three to the already present number eight. We do this by using the + operator. Try doing this for yourself. The script should look something like this:

print(8 + 3)

If we run the script, it should say 11 in Output. Congratulations, you have now made your first math operation in a programming language. However, we can do many more math operations. Try subtracting (-), multiplication (*), and dividing (/).

Tip

You can add multiple print statements below each other to create a larger script that performs multiple math operations at once.

Your code should look something along these lines:

print(8 + 3)
print(8 - 3)
print(8 * 3)
print(8 / 3)

We have two more math operations that you can do on numbers: exponentiation (^) and the modulus (%). For exponentiation, you can do something like this:

print(8 ^ 3)

The result of this operation would be 512. What happens when using this operator is that the number before the caret (^) gets taken. The number gets multiplied against itself as many times as stated behind the exponentiation operator. To make this easier, the operation 8 ^ 3 translates to 8 * 8 * 8. If we switched the numbers and made the operation 3 ^ 8, the operation would be translated to 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3.

The last operator is the modulus. This operator is commonly forgotten, especially in Roblox programming. The modulus operator (%) looks like a percentage; however, it is absolutely not a percentage. Do not let it fool you. The modulus operator takes the number behind the % and multiplies itself as many times as it fits into the number before the operator. Once it reaches its limit, it subtracts its total multiplication from the first number and gives back the difference. This operator is not easy to understand.

To visualize it, we take the operation 8 % 3. The number three fits twice into the number eight, because three times two is six, and six is lower than eight. If we multiplied it once more, we would do it three times, resulting in nine. Nine does not fit in eight. Then, we subtract six from eight. What is left is two. The operation 8 % 3 would return two. This is probably the most confusing operator so far. It is confusing because it is never taught during math in school.

Tip

You can try to make a few operations in your script to test this operator yourself.

We learned how to program math operations. In the next section, we will learn how to combine these math operations in one statement.

Combining math operations

Something that the modulus operator can do is combine different operators in one statement. We can do this too. Execute the following code:

print(8 + 3 * 2)

Notice how the result of this execution is 14? During math, you probably learned about the order of operations. The order of operation tells you in which order you have to execute each operation. In programming languages, we do the same. Luckily, it is the same order of execution taught in school. You probably heard of PEMDAS to help you remember this order. PEMDAS stands for Parentheses (( and )), Exponents (^), Multiplication (*), Division (/), Addition (+), and Subtraction (-).

In the previous code example, the three times two is executed first. If we wish to execute 8 + 3 first, we can surround it with parentheses. Your code would look like this:

print((8 + 3) * 2)

The outcome of this math operation changed from 14 to 22. Do you see that there are two opening parentheses directly behind print? Having multiple parentheses is allowed because the programming language understands that the first closing parenthesis, which is the one behind the three, is the closing one for the last opening parenthesis.

We learned that we could do math operations with the number data type in the previous two sections. Previously, we learned about multiple data types. The following section explores the unique things that we can do with strings.

String concatenation

Now that we know how to use numbers, we can look at some of the cool things that you can do with strings. We cannot do math with strings, but we can combine them. When combining strings, we use an operator called concat, short for concatenate. This operator is two dots directly after each other (..). Let us try to concatenate two strings in a print function.

First, we need two strings. As shown in the following code snippet, this is done by enclosing the message with quotation marks ("). Then, you can choose the sentence that you wish to insert into your string yourself. For this example, two strings that say “Hello” and “Laura” are combined:

print("Hello " .. "Laura ")

Hello Laura appears as one string in Output when you start your game. This operator is the only thing that you must use to concatenate two strings into one. Now, try to concatenate three strings into one by using what we just learned. As you might have guessed, it looks like this:

print("Hello " .. "Laura " .. "! ")

Now we know how to concatenate different strings into one. The following section teaches you about another unique thing with strings: escape characters.

Escape characters

If you want to display a string on the next line, we can add another print. Most of the time, this is not the ideal solution. There is a simple alternative for this. It is called a new line escape character. The name sounds more complicated than it is. This new line escape character looks like this: . In the same script, suppose that we want to greet someone and tell them it is Monday on a new line. Your code should look something like this:

print("Hello Peter!
Today is Monday! ")

If you run this script, Hello Peter! appears on the first line, and Today is Monday! appears on the second one.

Another practical escape character is for a horizontal tab. This escape character mimics the same thing that happens when you press the Tab button on your keyboard in your preferred text editor. This horizontal tab escape character looks like this: . This escape character comes in handy when creating lists. For example, printing a shopping list can look something like this:

print("Shopping List:
	- Bread,
	- Butter,
	- Milk.")

Tip

Try to play around and create a few strings yourself. Practicing with strings is the best way to ensure you know how to use them.

We now know how to use the new line escape character and the horizontal tab escape character. Previously, we also learned how to concatenate two strings into one, and before that, we learned about the unique things that we can do with numbers. But what if we wanted to combine both data types into one print statement? The following section teaches you how to do this.

Casting data types

What if we tried to combine a string and a number in the same print statement? For example, we have a string that contains the number four and the number five. If we want to concatenate them, our script looks like this:

print("4" .. 5)

For some reason, this statement prints 45 in Output. Behind the screen, the system sees a string trying to concatenate with a number. We know that strings can contain numbers. The system knows it too. So, Luau helps you out and turns the number 5 from your statement into a string that contains the number 5. Then, the two strings merge into one. As a result, we get an output of 45.

Instead of letting the programming language figure out how to solve this issue, we can specify that we want to do it this way. We can use more functions besides the print() function. One of them is tostring(). This function turns any data given between the parentheses into a string:

print("4" .. tostring(5))

If we change the operator from a concatenation (..) into an addition (+), do we get an error?

print("4" + 5)

This code gives an error. This error occurs because you used an operator used for numbers on a string. But did we not use an operator used for strings on a number too? Yes, however, we know that strings can contain any character, including non-numbers. Therefore, Luau cannot change a string into a number unless we specifically tell it to do so.

We can tell Luau to change a string into a number using the tonumber() function. For example, if we put the string that contains the number four into this function, our operation should work:

print(tonumber("4") + 5)

As expected, the result of this operation is 9 and not 45. However, what if we wanted to use the same string and number and get the result 45, while having both pieces of data as numbers and not strings? We can combine the tonumber() and tostring() functions to achieve this:

print(tonumber("4" .. tostring(5)))

One disclaimer when using the tonumber() function: this function does not always work. For example, if your string contains anything other than numbers, this function returns something called nil. Nil means nothing. If you are not careful and your function returns nil, and your script does not expect this, an error occurs. Try it for yourself:

print(tonumber("a5")) -- This will return nil.
print(tonumber("a5") + 6) -- This will give an error.

Now we know how to cast strings into numbers and the other way around. We also learned how to combine numbers and strings in one statement. In the next section, we look at Booleans, our final data type.

Booleans

Last but not least, we can also print Booleans. Printing Booleans is not very difficult on its own. However, there are some operations for Booleans as well. For now, let us start with printing true and false. Your script should look something like this:

print(true)
print(false)

When printing Booleans, there are no quotation marks (") required. The reason for this is because Booleans are not strings. Instead, they are a unique data type. In the next section, we look at logical operators.

Logical operators

Similar to the other data types, there are some operators that we can use to create operations with Booleans. These are the and (and) and the or (or) operators. There are no special characters for these operators; they are just text. However, these operators do have a particular name. They are called logical operators.

Let us start with the and operator. To keep it simple, let us use a real-life example of asking your parents for permission to purchase a new game. The answers your parents give are Booleans, yes (true) or no (false). Because we are talking about the and operator, you need both your parents to agree on purchasing a new video game.

If both of your parents allow you to purchase the video game, you have two true Booleans. Our expected result is true if we put this into our print script:

print(true and true)

Roblox Studio might warn you when you enter this script because it already knows that the result is always true. Therefore, the statement is redundant. However, to help you understand these operations, you can ignore the warning and execute the script.

Now, one of your parents says no. Make a script where one of the Booleans is false, and execute it. Your script can look like this:

print(true and false)
print(false and true)

Both of these statements return false. As mentioned before, we are using the and operator. Using this means both parents in our scenario have to agree. If either does not agree, the deal is off.

As you might have guessed, when both your parents do not agree, and both Booleans are false, the statement always returns false. However, for the sake of understanding, you should try the script regardless. Your script can look something like this:

print(false and false)

Besides the and operator, Booleans also have the or (or) operator. For the or operator, we use a different example. Imagine you are selling something online. You only need one buyer that wants to pay the price you have set. In the best-case scenario, both customers are interested in purchasing your item. Your script looks something like this:

print(true or true)

The result of this is true. The only problem is picking which person you sell your product to. This problem, however, is not something you have to solve through coding.

Now, only one person agrees. So, the code looks like this:

print(true or false)
print(false or true)

Unlike with the and operator, these both return true. When selling an item, you do not need multiple sellers. One is enough to sell your item to. However, both would be false when there are no sellers at all. The sale cannot go through. Your code looks like this:

print(false or false)

We have now seen the logical operators that we can use on Booleans. It might seem pointless, but we will see why this is important in the Using conditionals section.

In this section, we learned all about data types. We learned about three different data types: numbers, strings, and Booleans. We learned how to print them to Output and about the special operations that we can do on them. We learned how to do math operations on numbers, how to combine strings, and how to use logical operators on Booleans. In the next section, we take these data types to the next level by using variables.

Introducing and using variables

Now that we know about the essentials of programming and data, we can start using this data to start doing things. In this section, we will learn about variables. We will learn what they are, what they are helpful for, how to update them, and how to improve the quality and readability of our code by using variables. Variables are all about temporarily storing your data somewhere. In Luau, the variables for all data types look the same.

To create a variable, follow these steps:

  1. First, you define a variable by putting local into your script.
  2. Then, you put the name of your variable. The name of the variable can be anything. Try to make your variable names as logical as possible, as it helps when the size of your script starts to increase.
  3. Once your name is defined, you put an equal sign (=).
  4. Finally, you can specify the data that you wish to store in this variable. You can see an example of a variable structure in the following code:

    local your_name_here = your data

You can consider variables as temporary boxes that store any data that you specify behind the equal sign (=) under a specific name. Now, if we want to store a name, for instance, Emma, in a variable, it looks like this:

local name = "Emma"

We learned that the quotation marks (") around a sentence define a string. You can see this is the case for the preceding example, which means we just stored a string in a variable that is named name.

Now that we know how to make a variable, let us continue to learn about some of the best practices when creating a variable in the following sections.

Lower and upper camel case

Variables can be named anything. However, it is custom to start your variable name with a lowercase letter. If your variable name consists of multiple words, the first letter of the first word is a lowercase character, and the first letter of each word after that should be a capital letter. There is no practical reason for this, and it is just for the readability of your code. Here are some examples of correct variable names:

local firstName = "Emma"
local randomNumbers = 125
local isThePlayerAFK = false

This way of naming variables has a name. It is called lowerCamelCase. This method of naming variables is not unique to Roblox or Luau. Many tech companies around the world use it. It is a good habit to teach yourself to do this from the start.

Besides the lower camel case, another “camel case” is good to know. This other camel case is UpperCamelCase. The first character is capitalized with this naming method instead of using a lowercase character. You primarily use the upper camel case method when naming scripts.

We now know how to name our variables correctly. In the next section, we will learn how to update the value of our variables.

Updating variables

Besides storing data, what can we do with it? Everything that you can do with the data you learned about. The only difference is that you can now use the variable’s name instead of direct data, as shown here:

local firstName = "Alexander"
print(firstName)

Order of execution

Notice how we defined the variable before the print? The reason for this order is that the system reads the script line by line when the script gets executed. When it arrives at the line of the variable, it puts the variable into your computer’s memory. If it gets to the print first, it tries to find the firstName variable in your memory. If the variable does not exist, it cannot be printed and gives an error. It is custom to put all of your variables at the top of the script to prevent this from happening.

The same output appears when you insert the string directly into the print function. However, when you have 10 different print statements that would all print Alexander and you wish to change Alexander to William, you only have to change it in one spot. The variable’s value is the only thing that has to be changed for all prints to be automatically updated.

Let us take a better example. We have a number that starts at zero. We updated it five times. Each time we update the number, the number gets printed. We only use one variable for the number that we have. Your code can look something like this:

Disclaimer

The code you are about to see is imperfect, and we will improve it later. Methods for removing duplicate code are taught in Chapter 2, Writing Better Code.

local currentNumber = 0
print(currentNumber)
currentNumber = currentNumber + 1
print(currentNumber)
currentNumber = currentNumber + 1
print(currentNumber)
currentNumber = currentNumber + 1
print(currentNumber)
currentNumber = currentNumber + 1
print(currentNumber)
currentNumber = currentNumber + 1
print(currentNumber)

Let us take a look at the preceding code. First, we see a currentNumber variable that starts at 0. The first print() puts the variable, with the value 0, in the Output frame. Then, it does something that we have not seen before. It takes the variable’s name, puts an equal sign (=) behind it, states its name again, and does a math operation that adds one.

We start with what we know. We know how math operations work, and we know how variables work. currentNumber + 1 is a math operation with a variable. We can assume that this works the same way as any other math operation. It takes a look at the variable’s value, which is 0 for our current scenario, and then adds 1 to it. The result of this math operation would be 1. What Luau does behind the screen is change the line that we are executing to this:

currentNumber = 1

Hopefully, the statement that we have right now looks a bit familiar. It is not an exact match. However, you might recognize it from how we create variables. The only thing missing from creating a variable and our current code is the local part. We know that the local part is used when creating a variable. What if the statement that we have right now updates an existing variable to a new value? It would make sense. Just translate our current statement to something you can say out loud: “Current Number Equals One.” The currentNumber variable now has the value of one.

Hopefully, analyzing the code helps you realize what to do when you do not understand the code that you just read. First, try to look for the parts that you understand and figure out the rest from there.

Now we know how to update the value of a variable. In the next section, we learn about a best practice when updating variables.

Removing magic numbers

Now that we understand the script from the previous section, we know how to make variables and update them. But, as we said previously, the code is not ideal. Previously, we had an example where we incremented the number by 1. However, what if for our current example we do not want to increment a variable by 1 but by 2? We have to change our code in so many places. We could have prevented this by making a second variable that would determine the amount by which we would increment our variable. An improved version of the code would look something like this:

local currentNumber = 0
local incrementValue = 2
print(currentNumber)
currentNumber = currentNumber + incrementValue
print(currentNumber)
currentNumber = currentNumber + incrementValue
print(currentNumber)
currentNumber = currentNumber + incrementValue
print(currentNumber)
currentNumber = currentNumber + incrementValue
print(currentNumber)
currentNumber = currentNumber + incrementValue
print(currentNumber)

This code does what we initially wanted to do. Now, if we want to change our increment value from 2 to 3, there is only one spot we have to change it in. Using a variable makes it much easier to maintain for the future. Besides that, the code is a lot more readable as well. Before, there was just a number in your code; we had no idea what the purpose of this number was.

The number we just removed from our script has a name in programming. It is called a magic number. Magic numbers are pieces of data used multiple times in your script and do not explain what they do or what their purpose is, just like the example we just had. To prevent the usage of magic numbers, we can introduce a variable. Using magic numbers when programming is considered a code smell and should be avoided.

There is an even shorter way of writing this code. Notice how you were writing the current number variable multiple times per line? We do this because we have to define the variable that we are updating and also to get the current value of this variable. We can change the equals sign (=) to an operator that assigns and adds (+=) as follows:

local currentNumber = 0
local incrementValue = 2
print(currentNumber)
currentNumber += incrementValue
print(currentNumber)
currentNumber += incrementValue
print(currentNumber)
currentNumber += incrementValue
print(currentNumber)
currentNumber += incrementValue
print(currentNumber)
currentNumber += incrementValue
print(currentNumber)

Our code works the same, and the only difference is that we no longer have to write the variable’s name twice; we used it twice per line. Currently, we are using an operator that assigns and adds (+=); however, there are other operators like this for different math operations. For example, we also have one for assigning and subtracting (-=), assigning and multiplying (*=), and assigning and dividing (/=). Try using these new operators in a script similar to the one previously.

We just learned about a best practice to make our code more efficient while removing our magic number code smell. However, there is another best practice we can use to improve the readability of our code. The following section explains how to use constants in Luau.

Introducing constants

So far, we have made an immaculate script. However, we can do one more thing to improve the readability of this script. Most programming languages have something called constants. Constants are a special type of variable. The value of these variables is set once and can never be changed while running the script.

Previously, we said that the final thing was to optimize the script’s readability. This is only for readability purposes because Luau does not have constants. However, constants have a unique method of naming. You learned to name variables by using the lower camel case method. For constants, you have to write the full name of the variable in capitals. When the name of your constant consists of multiple words, an underscore (_) is added to separate the different words; this is something we can do in Luau. This way, other programmers know that this variable never changes, even though it is technically possible.

In the example we used, we can find a candidate for a constant. The currentNumber variable gets constantly updated with a new value. This variable is not a candidate to become a constant. On the other hand, our other variable is an excellent candidate to become a constant. The variable only defines the amount that the currentNumber variable is incremented by. If we turn this variable into a constant, our script looks like this:

local INCREMENT_VALUE = 2
local currentNumber = 0
print(currentNumber)
currentNumber += INCREMENT_VALUE
print(currentNumber)
currentNumber += INCREMENT_VALUE
print(currentNumber)
currentNumber += INCREMENT_VALUE
print(currentNumber)
currentNumber += INCREMENT_VALUE
print(currentNumber)
currentNumber += INCREMENT_VALUE
print(currentNumber)

If our script had been a bit different, our constant might have changed back to a standard variable. If, halfway through the script, we wished to change our increment value from two to three, it would have to be a variable again. As you can see, introducing a constant depends on what you wish to achieve with your script, and you have to look into the future as well. Do you wish to allow your script to be modified so this might be possible in the future? If so, it might be a good idea to keep it as a variable.

As you can see, when and when not to introduce a constant is a vague area. It is perfectly fine if you wish to never use constants in Luau. After all, Luau does not even have the implementation of a constant, and it is just about readability.

Tip

When in doubt about introducing a constant, do not do so. In Luau, it is better to keep a standard variable than to have an incorrectly implemented constant. This book, however, uses constants when required from now on.

Now that we know how to use variables and constants, we are ready to take our coding skills to an even higher level. The next section teaches us how to make our code perform different actions depending on our data.

Using conditionals

In this section, we will level up our code and learn everything about conditionals. Conditionals allow us to perform different actions depending on the provided data. These different actions can be anything. We are allowed to program these actions ourselves. Here is an example script that prints something based on the player’s position during a race:

local playerPosition = 1
if playerPosition == 1 then
    print("You are in the first place!")
end

The following text is printed in the Output frame when running this script: “You are in the first place!” However, when we change the value of the playerPosition variable to any other number, nothing appears in Output. The reason nothing appears is because of our if statement. To clear up any possible confusion, an if statement is a conditional.

Everything you place between the if and the then part gives a Boolean. Everything between then and end is executed when this Boolean gives true. If this is not the case, the code continues after the end statement.

Two things were just said to explain the if statement. We test to see whether this is correct in the following two sections.

Relational operators

We first said that everything between the if and the then statements has to return a true Boolean. We can confirm this by making a print statement. Execute the following code:

local playerPosition = 1
print(playerPosition == 1)

Notice how the result of this script prints true in Output? Now, if we change the value of our variable to two and execute the script, the result is false. It is false because we use the equal to (==) operator, a relational operator. When using a relational operator, the outcome is always a Boolean.

Equal to (==) is not the only relational operator that we have. The following is a list of all the relational operators that we have in Luau:

Table 1.1 – Relational operators explained

Table 1.1 – Relational operators explained

Now that we know how to make a simple if statement, we can increase the complexity by adding an else statement in our conditional. The next section explains how to do this.

if-else conditionals

The second thing that we said was that if the result of this relational operator comes back false, everything between then and end is skipped, and the script continues behind end. We can test this as well. We can put another print behind end and test whether it is executed, even when the result is false. Execute the following code:

local playerPosition = 1
if playerPosition == 1 then
    print("You are in the first place!")
end
print("Script completed.")

When we have our variable set to 1, both prints appear in Output. However, if we change the value of our variable to 2, only Script completed. ends up in the console.

Let us change our program. If a racer is in the top three, their position must be printed. If the racer is not in the top three, a motivational text is printed to support them. Let us try to implement this:

local playerPosition = 1
if playerPosition <= 3 then
    print("Well done! You are in spot " .. playerPosition 
    .. "!")
end
print("You are not in the top three yet! Keep going!")

This code works, sort of. When we are in the fourth spot, the motivating text is printed. However, both messages are printed when we are in the top three. We can prevent this by making two if statements below each other. So, your code would look like this:

local playerPosition = 1
if playerPosition <= 3 then
    print("Well done! You are in spot ".. playerPosition .. 
    "!")
end
if playerPosition > 3 then
    print("You are not in the top three yet! Keep going!")
end

This code works. It does what we want. However, there is a better way to do this. We also have an else option. Remember when we said that the code continues after end? There is an exception. If we add else before end, this part is executed first. When the code is false, the else section is always executed. Visualized, an if-else statement looks like this:

Figure 1.3 – An if-else statement visualized

Figure 1.3 – An if-else statement visualized

Now that we know how an else statement works, let us refactor our code to use this instead. Refactoring code is changing how it looks while keeping the same functionality. Our refactored code looks like this:

local playerPosition = 1
if playerPosition <= 3 then
    print("Well done! You are in spot " .. playerPosition 
    .. "!")
else
    print("You are not in the top three yet! Keep going!")
end

We have learned how to use the else statement. In the next section, we will learn about another statement that we can use in conditionals. This other statement is elseif.

Using elseif

This code looks a lot better. If we want another message for the players in spots four and five, this is possible. After all, they are almost there. We face another issue. We cannot add another if statement in an already existing if statement. Or can we? Let us take a look at this code:

local playerPosition = 1
if playerPosition <= 5 then
    if playerPosition <= 3 then
        print("Well done! You are in spot ".. 
        playerPosition .. "!")
    else
        print("You are almost there!")
    end
else
    print("You are not in the top three yet! Keep going!")
end

This code does exactly what we want. However, it looks confusing. Our first if statement checks whether the player’s position is below five. Then, it checks whether the position is below three. Right, we already had this. There are just two if statements to get this now. Then, we get to the first else statement. The number has to be either four or five to even get here. Then, we reach our final else. This final else is where all the numbers larger than five end up. As you can see, the system works. It just requires a bit more time to understand what is going on.

There is a better alternative. We already know about if and else. There is a combination of this, elseif. An elseif statement could be placed after if and before else. Because elseif is also if, we can add another expression.

Tip

The expression is the part between if/ elseif and then.

Our code with elseif would look like this:

local playerPosition = 1
if playerPosition <= 3 then
    print("Well done! You are in spot " .. playerPosition 
    .. "!")
elseif playerPosition <= 5 then
    print("You are almost there!")
else
    print("You are not in the top three yet! Keep going!")
end

This looks much better! At a simple glance, you can see exactly what this code does.

Before we came up with the solution to use an elseif statement, we tried using a nested if statement. In the following section, we take a deeper dive into these nested if statements.

Nested if statements

There is still a slight issue with our code. What if you accidentally change the player’s position to zero? Or a negative number? As of right now, the system cannot handle this.

First, we must determine how many players there can be in a race to fix this problem. Let us say a race has a minimum of 1 player and a maximum of 8 players. This means that the player position has to be between those numbers. These numbers can be variables. As a matter of fact, they can even be constants. The minimum and the maximum number of players never change while the script runs.

Besides the new constants, there are multiple ways of implementing this feature. Here are two correct methods of implementing constants:

local MINIMUM_PLAYERS = 1
local MAXIMUM_PLAYERS = 8
local playerPosition = 1
-- Checking player's position
if playerPosition >= MINIMUM_PLAYERS and playerPosition <= 3 then
    print("Well done! You are in spot " .. playerPosition 
    .. "!")
elseif playerPosition >= MINIMUM_PLAYERS and playerPosition <= 5 then
    print("You are almost there!")
elseif playerPosition >= MINIMUM_PLAYERS and playerPosition <= MAXIMUM_PLAYERS then
    print("You are not in the top three yet! Keep going!")
else
    warn("Incorrect player position [" .. playerPosition .. 
    "]!")
end

Let us take a look at this code. The first thing that we notice is the text behind --. This is something that has not been mentioned yet. By using --, you can make comments in your code. These comments do not get executed, but they help other developers when reading your code.

The second new thing is the warn() function used in the else statement. The warn() function is almost identical to the print() function. The only difference is that the color in Output turns orange. This is because you use this method to warn, as the name implies. When you see a warning in your Output, usually someone did something that the system prevented, such as having a wrong playerPosition in our case.

Something else that we have not seen before is the and operator in an if statement. When we learned that the true and true expression gives true, it probably did not feel like something substantial. However, we have the same thing in our if statement right now. So, it is essential to understand.

Tip

If you have forgotten how the and or the or operator works, it might be wise to reread the Logical operators section in this chapter.

The only thing that could be improved is the expression, which checks whether the position is higher than the minimum multiple times. Therefore, in this scenario, it is not an ideal solution. We can change this by having an if statement inside another one. Your code would look like this:

local MINIMUM_PLAYERS = 1
local MAXIMUM_PLAYERS = 8
local playerPosition = 1
-- Checking if the player's position is a valid number
if playerPosition >= MINIMUM_PLAYERS and playerPosition <= MAXIMUM_PLAYERS then
    -- Getting correct message based on player's position
    if playerPosition <= 3 then
        print("Well done! You are in spot ".. 
        playerPosition .. "!")
    elseif playerPosition <= 5 then
        print("You are almost there!")
    else
        print("You are not in the top three yet! Keep 
        going!")
    end
else
    -- The position of the player is not valid
    warn("Incorrect player position [" .. playerPosition .. 
    "]!")
end

This example uses an if statement within an if statement. if statements within if statements are perfectly allowed. There is even a name for this. The if statement within the other statement is called a nested if statement. It is good practice not to have more than three if statements within each other. Later in the book, we will learn ways to reduce the amount of nested if statements, such as by using functions and loops. You can find this information in Chapter 2, Writing Better Code.

We now know how to use conditionals in our code properly. We can use if, elseif, and else statements. If you practiced yourself, you might have run into issues where certain variables were not accessible throughout your entire script. These variables were not accessible because of scopes. In the next section, we take a look at what these scopes are.

Understanding scopes

In this section, we will learn what scopes are and why you have to keep them in mind when coding. First, we will start by looking at how a new scope is made. Scopes are something you have already worked with; you just did not realize it. Every time we had an if statement, did you notice how the code before the end was spaced to the right? The reason for this spacing is because this section is a new scope. A quick note: spacing your code to the right does not create a new scope.

But what can we do with scopes? Scopes are not necessarily something that you can use but rather something you have to keep in mind. We have already seen a new scope being created when using an if statement. There are a lot more situations where scopes are created. The following code is something you can do for the sole purpose of creating a scope:

do
    print("New Scope")
end

When a scope starts, it executes the code within it. If there is another scope within an already existing scope, all of the data from the scope above is for the scope below. If there is data in a scope that the current scope is not in, you cannot access that data.

Scopes accessing certain data sounds really confusing. Here is an example that demonstrates it:

local outsideScopeData = "this is accessible everywhere"
do
    -- New Scope
    print(outsideScopeData)
    -- New data in this scope
    local insideScopeData = "This data is accessible in 
    this scope."
    -- Printing in scope data
    print(insideScopeData)
end
-- Printing data outside of the scope
print(outsideScopeData)
print(insideScopeData) – This data does not exist

Our first variable is not inside of a scope. Therefore, the scope it is in is called the global scope. This section of your code is accessible everywhere in your code.

Next, we start our first scope. To test whether we can access the variable from the global scope, we have a print statement that prints the outsideScopeData variable. We can access this variable because your new scope is inside the global scope. Because our new scope is inside the global scope, all of the data that the global scope has, our new scope has as well.

Then, we create a new variable inside of our new scope. This data does not exist in the global scope. Inside our new scope, we print this new data using the print() function. We can do this because this data is inside the current scope and thus exists.

Our scope comes to an end. We can see this because of the end line. After this, we are back in the global scope. First, we try to print outsideScopeData. We can do this because this variable exists in the global scope. Then, we try to print a variable created inside a scope. However, printing this variable does not work. Printing this does not work because our current scope, the global scope, does not exist inside the scope where this variable has been made. Therefore, we cannot access the data from here.

What if we wanted to make the variable inside the scope and still print it inside the global scope? We can make a variable with no data inside the global scope. Then, we can set this empty variable to our desired value inside the new scope. This works because the variable is made in the global scope and set inside another scope. You can see this in practice in the following code snippet:

local outsideScopeData = "this is accessible everywhere"
local dataSetInScope
do
    -- New Scope
    print(outsideScopeData)
    -- Setting data in this scope
    dataSetInScope = "This data is accessible in this 
    scope."
    -- Printing in scope data
    print(dataSetInScope)
end
-- Printing data outside of the scope
print(outsideScopeData)
print(dataSetInScope)

As you can see, we have created a variable missing the = your_data part of a variable. If we do this, Luau automatically turns this into a nil variable. We could have done this ourselves as well. Our variable would have looked like this:

local dataSetInScope = nil

Then, inside the scope, we set the variable with new data. Because the variable exists in the global scope, the data is not thrown away when the scope ends. This is why we can print the correct result in the last line of this script.

Now that we know how scopes work, we have finished everything that the first chapter offers. In the following section, we put into practice the learned information.

Exercise 1.1 – Changing properties on a part

For this exercise, we use something we have not explained before. Therefore, this exercise starts with a short introduction to what is required. Please note that both this new information and all of the information explained before are required for these exercises. Therefore, it is highly recommended to do the exercise once you understand everything explained before.

In your Explorer menu in Roblox Studio, the same menu that you make scripts in, find something called the Workspace. Inside the Workspace, you can find everything that your players can see in-game. This includes the part that your avatar spawns on. This part is called the baseplate.

If you open the Properties frame and click on the baseplate, you can see that this instance exists by combining a lot of data. In Figure 1.4, you can see the Properties frame. One of the things that you can see is the name of the part. For the baseplate, this is set to Baseplate. However, a name is just a string. There are other properties, such as Anchored. This property determines whether the part is locked in its location or whether it is moveable in the game. There is a checkbox next to this property. This checkbox can be on or off. Sounds familiar? This is basically a Boolean.

Figure 1.4 – Baseplate properties

Figure 1.4 – Baseplate properties

Then, there are also numbers. For instance, the Transparency property is a number between 0 and 1. This determines the visibility of the part. Finally, some properties have multiple numbers separated by a comma. These are special data types that we have not yet seen. For now, there are two of these new data types that you can find on a part: Color3 and Vector3. A Color3 data type determines the color and a Vector3 data type determines the 3D size. This can be a position, size, rotation, and a lot more.

Both of these data types contain the number data type. That is why you see three numbers separated by a comma in these locations. For example, to make a new color, you use the following code:

Color3.fromRGB(0, 0, 0) -- Black Color
Color3.fromRGB(255, 255, 255) -- White Color

RGB, which stands for RedGreen, and Blue, is three numbers that each range from 0 to 255. Therefore, a combination of three of these numbers makes a color.

We also mentioned Vector3 data types, and you can make these using the following code:

Vector3.new(25, 25, 25)
Vector3.new(100, 25, 50)

A Vector3 data type also contains three different numbers. These resemble the x, y, and z axes. The x and the z axes determine the width, and the y axis determines the height. In our first example, we made a Vector3 data type with a width of 25, a length of 25, and a height of 25. This is basically a cube.

Tip

By using the Properties window, you can manually change the values of each property. Try playing around with some of the properties that we mentioned before. This gives you a better understanding of how they work, especially the new data types.

You need to start the game for some properties to go into effect.

Now that we know how these properties work, we can change them using a script. Before we can do this, we need to tell the script somehow which part we are trying to change. We need a reference for this. This reference directly points to a part somewhere in the game.

Before we can make a reference, we need to be able to tell what service we can find it in. For the baseplate, this is in the Workspace. So, to get the Workspace, we can do the following:

local workspaceService = game:GetService("Workspace")

However, because the Workspace is used a lot, there is a shorter way to reference it. We can simply use the workspace keyword to get a reference without having to use the :GetService() function, as shown here:

local workspaceService = workspace

Now that we know how to reference the Workspace, we can look through all of its children. In programming, children are instances inside of another instance. The parent is the instance that the current instance is inside of. This parent is a property that we can see within the Properties frame. If we open the properties, select Baseplate, and look for a property called Parent, we can see that the parent of this instance is the Workspace. This makes sense because, when we opened the Workspace, one of the first children we saw was the Baseplate part. To finish the reference toward the baseplate, our reference would look like this:

local baseplate = workspace.Baseplate

Now that we have the reference to our part, we can change properties. Changing properties is similar to updating a variable. First, you put what you want to change. This is our reference and the name of the property. Then, we put an equal to (=) and the new value. For example, if we want to change the reflectance of our part, our code looks like this:

local baseplate = workspace.Baseplate
baseplate.Reflectance = 1

Exercise:

  1. Open a new baseplate in Roblox Studio.

Create a new script in ServerScriptService.

  1. Create a new variable named baseplate to reference the baseplate.
  2. Print the name of the baseplate using the following:
    • The baseplate variable
    • The Name property of the baseplate
    • The print() function

Execute the script and confirm that Baseplate shows up in the Output frame.

  1. In your script, change the Name property of the baseplate to something else.

Execute the script and ensure that your new name appears in the Output frame.

  1. Change your print() statement, so that it prints the following:

The name of the baseplate is: ‘name of your baseplate here’

  1. Execute the script by using the following:
    • The baseplate variable
    • The Name property of the baseplate
    • String concatenation

Confirm that the correct string shows up in the Output frame.

  1. Change the CanCollide property of the baseplate to false.

Execute the script and confirm that your avatar falls through the baseplate.

  1. Change the Color property of the baseplate to a new RGB color with the RGB code: Red: 51, Green: 88: Blue: 130 (Storm Blue).

Execute the script and confirm that the baseplate has another color.

  1. Create a variable named terrain that references the Terrain object in the Workspace.
  2. Change the Parent property of the baseplate to reference the Terrain object by using the following:
    • The variable named baseplate
    • The variable named terrain
    • The property of the baseplate with the name Parent

Tip for 10: The Parent property does not have a value as a data type but as a reference. The current reference is to the Workspace parent. Set the value of this property to reference Terrain.

An example answer to this exercise can be found on the GitHub page for this book: https://github.com/PacktPublishing/Mastering-Roblox-Coding/tree/main/Exercises.

We combined the knowledge that we learned about programming with this exercise to make a visual change in our game. In the next exercise, we will make another system.

Exercise 1.2 – Police system part I

This exercise creates a simple police system that calculates a ticket price based on input.

System description

The police want a system where they can set a variable for the speed that a driver was going at, and a variable where they can set whether the driver had a license with them. There should be variables that determine the height of the ticket for each crime. There should also be two variables that state the maximum speed the driver is allowed to go and whether it is required for them to have a license. Combining this data should give one ticket price even if multiple crimes were committed. If there were no crimes committed, the ticket price would be 0. The ticket price should be displayed in Output with the following text: Ticket Price: 0. The number depends on the height of the ticket.

Try to conclude what variables you need based on the system description. Again, analyzing a problem helps you to create a correct system.

Based on the system description, we can conclude the following facts:

  • There should be two variables that the police can set. These variables are for the speed (speed) and whether the driver had a license (hasLicense) with them.
  • There should be two variables (constants?) that determine the ticket price for each crime.
  • There should be two variables (constants?) that determine the maximum allowed speed and whether it is required to have a driver’s license.
  • There should be a variable that holds the height of the ticket (ticketPrice).

Now that we know this, let us start programming our system. Follow these steps:

  1. Open a new baseplate in Roblox Studio.
  2. Create a new script in ServerScriptService.
  3. Create the variables we concluded from the system description.
  4. Create an if statement that checks whether the driver was going over the speed limit and applies the following:
    • If the driver was going over and not at the speed limit, increase the ticket price
    • If the driver was not going over the speed limit, do nothing
  5. Create an if statement that checks whether the driver was violating the driving license rule and applies the following:
    • If it is required to have a driver’s license and the driver has a driver’s license, nothing happens
    • If it is required to have a driver’s license and the driver does not have a driver’s license, increase the ticket price
    • If it is not required to have a driver’s license and the driver has a driver’s license, do nothing
    • If it is not required to have a driver’s license and the driver does not have a driver’s license, do nothing
  6. Use the print() function to print the correct sentence. Refer to the software description for the required sentence.

Execute your script and confirm that it works as described in the software description. Try to fix any errors that could show up in the Output frame. An example answer to this exercise can be found on the GitHub page for this book:

https://github.com/PacktPublishing/Mastering-Roblox-Coding/tree/main/Exercises

Exercise 1.3 – Understanding a script

In this exercise, we will insert a script into our game to understand what it does. This script will be given to you in this exercise. It is recommended that you look at this script and recreate it. Try not to copy and paste the script from the GitHub page for this book, as you will learn less by doing this.

Follow these steps:

  1. Open a new baseplate in Roblox Studio.
  2. Create a new script in ServerScriptService.
  3. Inside the previously made script, insert the following code:

    local spawnLocation = workspace.SpawnLocation

    if

        spawnLocation.Position.X == 0

        and

        spawnLocation.Position.Z == 0

    then

        print("Spawn is in the center!")

    else

        print("Spawn is not in the center.")

    end

  4. Start playing the game. If you do not move SpawnLocation, a Spawn is in the center! message should appear.
  5. Next, move SpawnLocation by using the Move tool in the Home section. Did the message change?
Figure 1.5 – Moving SpawnLocation

Figure 1.5 – Moving SpawnLocation

  1. What happens when you place SpawnLocation in the center and change the height? Why does this happen?

The code used in this exercise can be found on the GitHub page for this book: https://github.com/PacktPublishing/Mastering-Roblox-Coding/tree/main/Exercises.

Summary

Roblox is a unique platform where people can play millions of games for free. The amazing thing about these experiences, as Roblox likes to call their games, is that other users make them on the platform. There are a lot of different roles that you can have as a developer on the platform. You can be a modeler, animator, graphics artist, and even a programmer.

Programmers on Roblox use a language called Luau. Programming is all about manipulating data to change something in the game. There are different data types in programming. The most common and essential data types are strings, numbers, and Booleans. These basic data types can make completely new data types, such as Color3 and Vector3. We learned how to use these data types and what purpose each has. We learned about how to do math operations on numbers, string concatenation, and relational operators using Booleans.

This data can be stored in variables. We learned how to update these variables. We saw that these variables are very similar to properties on instances such as parts. We learned that these properties are what make an instance unique. You can change these properties, and something changes. For example, if you change the color of a part using the Color3 data type, you will see the color of the part change.

Because not all data is the same, we can take different actions depending on the data provided. For this, we have conditionals. We saw how conditionals go hand-in-hand with Booleans. We learned that conditionals have something called expressions to determine whether certain data is what we are looking for. If this is the case, a particular scope gets executed. If this is not the case, we can choose to do nothing or execute another scope of code by using the elseif and else statements.

We learned that these scopes have all the data that all the scopes above them have. Furthermore, we saw how each scope can also make its own data. The scopes above it do not know about it. Only the scope itself and the nested scopes know about it.

In the first exercise, we also learned how to make visual changes in our game. We learned how references to instances in the game work and how to change the properties on these instances. Besides this, we also learned how to create a system based on a given system description.

In the next chapter, we will start improving the quality of our code. We will learn ways to minimize duplicate code and make more advanced systems using functions, tables, loops, and modules.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.17.40