Lua / Guide / March 2, 2020

Lua Basics

An introduction to the Lua scripting language used in FiveM: syntax, data types, functions, flow control, tables, metatables, OOP, error handling and code standards.

GuideBlogLuaCodingMarch 2, 2020/7 min read/1,585 words/24 code samples

Basic Syntax and Data Types

Lua is a lightweight and easy to learn programming language. In this section, we will cover the basic structure of a Lua script, as well as the different data types available and how to manipulate them.

Script Structure

A Lua script is a plain text file with the .lua file extension. The script is executed from top to bottom, and it can contain a combination of statements, comments, and functions.

Statements

Statements are commands that perform a specific action, such as assigning a value to a variable or calling a function.

lua

2 lines

-- This is a statement that assigns the value "hello" to the variable "greeting"
greeting = "hello"

Comments

Comments are used to add notes or explanations to the code and are ignored by the interpreter. In Lua, comments are created using double dashes (--).

lua

2 lines

-- This is a comment and will be ignored by the interpreter
greeting = "hello"

Tip: It's a great practice to leave comments around your code to explain what it does, for not only yourself but others.

lua

7 lines

-- Function to check pal's health
function checkPalHealth()
    -- Get pal using getPal function
    local pal = getPal("anubis")
    -- Return pal's health
    return pal.health
end

Variables and Functions

In Lua, variables are used to store and manipulate data, and functions are used to organize and reuse code.

Variables

In Lua, variables are declared using the local keyword or without it. It's recommended to use the local keyword when declaring a variable, as it limits the scope of the variable to the current function or file, and prevents accidental global variable assignments.

lua

5 lines

-- Declaring a variable
local x = 5

-- Declaring a variable without the `local` keyword
y = 10

Lua supports several data types, including numbers, strings, booleans, and tables.

lua

11 lines

-- Declaring a number variable
local age = 25

-- Declaring a string variable
local name = "John"

-- Declaring a boolean variable
local isValid = true

-- Declaring a table variable
local myTable = {1, 2, 3}

Functions

In Lua, functions are declared using the function keyword and can be called using the function name followed by parentheses.

lua

7 lines

-- Declaring a function
function sayHello()
    print("Hello!")
end

-- Calling a function
sayHello()

Functions can also take parameters and return values.

lua

7 lines

-- Declaring a function with parameters
function add(x, y)
    return x + y
end

-- Calling a function with arguments
result = add(5, 10)

Functions can be stored in variables and passed as arguments to other functions.

lua

7 lines

-- Storing a function in a variable
local myFunction = function()
    print("Hello!")
end

-- Passing a function as an argument
otherFunction(myFunction)

Flow Control

In Lua, flow control statements are used to control the flow of execution of a script. These include if, else, elseif, for, while, and repeat statements.

If-Else Condition

The if statement is used to check if a certain condition is true and execute a block of code if it is. The else statement is used as an alternative if the condition is not true.

lua

7 lines

-- If-else statement
local x = 5
if x > 10 then
    print("x is greater than 10")
else
    print("x is less than or equal to 10")
end

Elseif Condition

The elseif statement is used to check multiple conditions. If the first condition is not true, the script will check the next one, and so on, until a true condition is found or the end of the script is reached.

lua

9 lines

-- Elseif statement
local x = 5
if x > 10 then
    print("x is greater than 10")
elseif x < 0 then
    print("x is less than 0")
else
    print("x is between 0 and 10")
end

For Loop

The for loop is used to iterate over a range of numbers or elements in an array or table. The loop variable is declared and initialized before the loop, and the loop will continue until the end condition is met.

lua

4 lines

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

While Loop

The while loop is used to execute a block of code repeatedly as long as a certain condition is true.

lua

6 lines

-- While loop
local x = 5
while x > 0 do
    print(x)
    x = x - 1
end

Repeat Until

The repeat statement is used to execute a block of code repeatedly until a certain condition is met. The code block is executed at least once before the condition is checked.

lua

6 lines

-- Repeat until
local x = 5
repeat
    x = x - 1
    print(x)
until x == 0

Tables and Metatables

In Lua, tables are a fundamental data structure used to store and organize data. A table is a collection of key-value pairs, where each key is unique within the table. Tables can be used as arrays, dictionaries, objects, or a combination of these.

Creating a Table

To create a table, you can use curly braces { } and separate the key-value pairs with commas. Keys can be either numbers or strings, and values can be any Lua data type, including other tables.

lua

10 lines

-- Creating a table
local myTable = {
    ["name"] = "John",
    ["age"] = 25,
    [3] = "hello",
    -- Tables can be used as values of other tables
    ["nestedTable"] = {
        ["key"] = "value"
    }
}

Accessing Table Elements

You can access the elements of a table using the table name followed by the key in square brackets [].

lua

5 lines

-- Accessing table elements
print(myTable["name"]) -- Output: John
print(myTable.name)     -- Output: John
print(myTable[3])      -- Output: hello
print(myTable.nestedTable.key) -- Output: value

Metatables

Metatables are used to define the behavior of a table when certain operations are performed on it. A metatable is a table that can be set as the metatable of another table. The metatable contains metamethods, which are functions that define the behavior of the table when certain operations are performed on it.

lua

25 lines

-- Creating a metatable
local myMetatable = {
    __add = function(table1, table2)
        local newTable = {}
        for k, v in pairs(table1) do
            newTable[k] = v
        end
        for k, v in pairs(table2) do
            newTable[k] = v
        end
        return newTable
    end
}

-- Setting the metatable
setmetatable(myTable, myMetatable)

-- Using the metamethod
local myOtherTable = {
    ["city"] = "New York",
    ["country"] = "USA"
}
local myCombinedTable = myTable + myOtherTable
print(myCombinedTable.name)     -- Output: John
print(myCombinedTable.city)     -- Output: New York

Object-Oriented Programming

While Lua doesn't have built-in class support, we can implement OOP patterns using tables and metatables. This creates a powerful and flexible object system.

Creating Classes

lua

19 lines

-- Define a class
local Player = {}
Player.__index = Player

function Player.new(name, health)
    local self = setmetatable({}, Player)
    self.name = name
    self.health = health
    return self
end

function Player:takeDamage(amount)
    self.health = self.health - amount
end

-- Creating an instance
local player = Player.new("Hero", 100)
player:takeDamage(10)
print(player.health) -- Output: 90

Inheritance

lua

19 lines

-- Base class
local Entity = {}
Entity.__index = Entity

function Entity.new(name)
    local self = setmetatable({}, Entity)
    self.name = name
    return self
end

-- Derived class
local Monster = setmetatable({}, {__index = Entity})
Monster.__index = Monster

function Monster.new(name, damage)
    local self = setmetatable(Entity.new(name), Monster)
    self.damage = damage
    return self
end

Error Handling and Debugging

Lua provides robust error handling mechanisms to help you write more reliable code and track down issues effectively.

Protected Calls

lua

9 lines

-- Using pcall for safe execution
local success, result = pcall(function()
    -- Potentially dangerous code
    error("Something went wrong!")
end)

if not success then
    print("Error caught: " .. result)
end

Custom Error Handling

lua

12 lines

-- Creating and handling custom errors
function divide(a, b)
    if b == 0 then
        error("Division by zero!", 2)
    end
    return a / b
end

local success, result = pcall(divide, 10, 0)
if not success then
    print("Error in division: " .. result)
end

Debug Library

lua

14 lines

-- Using the debug library
local function trace()
    local level = 2
    local info = debug.getinfo(level, "Sl")
    while info do
        print(string.format("%s:%d", info.short_src, info.currentline))
        level = level + 1
        info = debug.getinfo(level, "Sl")
    end
end

function someFunction()
    trace() -- Print stack trace
end

Code Standards and Organization

Writing clean, maintainable Lua code involves following consistent patterns and conventions. Here's how to structure your code professionally.

File Organization

lua

21 lines

-- config.lua
local Config = {
    VERSION = "1.0.0",
    MAX_PLAYERS = 10,
    DEBUG_MODE = true
}
return Config

-- player.lua
local Player = {}
local Config = require("config")

function Player.new(name)
    return {
        name = name,
        health = 100,
        maxPlayers = Config.MAX_PLAYERS
    }
end

return Player

Naming Conventions

Follow these naming patterns for clear, readable code:

  • PascalCase for modules and classes (PlayerManager, GameState)
  • camelCase for functions and variables (getCurrentPlayer, playerCount)
  • UPPERCASE for constants (MAX_HEALTH, DEFAULT_SPEED)
  • Use descriptive, meaningful names (calculateDamage, isPlayerAlive)

lua

19 lines

-- Module/Class names (PascalCase)
local PlayerManager = {}
local GameState = {}

-- Variables and functions (camelCase)
local playerCount = 0
local function getCurrentPlayer()
    return activePlayer
end

-- Constants (UPPERCASE)
local MAX_HEALTH = 100
local DEFAULT_SPEED = 5

-- Clear, descriptive names
local isPlayerAlive = true
local function calculateDamage(baseDamage, armor)
    return baseDamage * (1 - armor)
end

Documentation Standards

lua

24 lines

--- Player class representing a game character
-- @class Player
-- @field name string The player's display name
-- @field health number Current health points
local Player = {}

--- Creates a new Player instance
-- @param name string The player's name
-- @param health number Initial health value
-- @return Player The new player instance
function Player.new(name, health)
    local self = setmetatable({}, Player)
    self.name = name
    self.health = health
    return self
end

--- Applies damage to the player
-- @param amount number The amount of damage to apply
-- @return number The remaining health
function Player:takeDamage(amount)
    self.health = math.max(0, self.health - amount)
    return self.health
end
Keep reading
Other guides and references in the archive.