Skip to content

Instantly share code, notes, and snippets.

@androksi
Last active January 13, 2024 21:09
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save androksi/10b49e114a972084bc631d1c1bcf31c6 to your computer and use it in GitHub Desktop.
Save androksi/10b49e114a972084bc631d1c1bcf31c6 to your computer and use it in GitHub Desktop.

Este tutorial é uma tradução. Você pode encontrar o original, em Inglês, aqui neste link: NanoBob - luatutorial.md

Tradução por: androksi

Tutorial Lua para iniciantes

Este tutorial tem como objetivo ensinar a linguagem Lua voltado para scripts, àqueles com 0 experiência prévia com programação / scripting. Este guia começará explicando alguns conceitos da linguagem Lua, e posteriormente coisas específicas do MTA:SA.

Conteúdo:

  • Lua
    • Scripts
    • Variáveis
    • Tipos de Dados
    • Operadores
    • Condições if
    • Funções
    • Retorno de valores
    • Escopos & local
    • Repetições (for)
    • Tabelas
    • Iteradores (pairs/ipairs)
    • Callbacks
    • Funções anônimas
  • MTA
    • Server & Resources
    • Server vs client
    • Funções do MTA & Wiki
    • Elementos (userdata)
    • Comandos
    • Eventos
    • Variáveis pré-definidas
    • Comunicação entre server <-> client
  • O que fazer agora?

Lua

Esta parte do tutorial discute Lua propriamente dita. Nenhum conceito do MTA será discutido aqui.

Scripts

A linguagem Lua é uma linguagem cujo é interpretada por um "Interpretador Lua". Os resources do MTA:SA usam-o para rodar os códigos. Para o começo deste tutorial, você pode usar o interpretador Lua online, disponível em https://www.lua.org/demo.html. Qualquer código dentro deste tutorial pode ser executado nele.

Lua é escrito em texto. Isso significa que existem caracteres normais, como qualquer outro texto que você esteja escrevendo. Para editar arquivos Lua você vai precisar de um editor de texto. Você pode até usar o Bloco de Notas, mas há muitas opções melhores. Meu favorito é o Visual Studio Code, mas há outras alternativas, tais como: Atom, Sublime Text, Notepad++

Variáveis

O primeiro conceito que nós discutiremos é variáveis. Variáveis te permitem armazenar um valor no seu código. Por exemplo:

x = 10

print(x)

print(x) irá mostrar o valor da variável x. Nós iremos entender o que exatamente a função print faz, no decorrer do tutorial.

Variáveis podem ter o nome que você deseja, contanto que você siga algumas regras específicas

  • o nome de uma variável DEVE começar com uma letra (minúscula ou maiúscula) ou um underline (_)
  • o nome de uma variável pode conter letras (minúsculas e maiúsculas), números e underlines.
x = 10
y = 20
z = 30

print(x)
print(y)
print(z)

A convenção em Lua é nomear suas variáveis em "camelCase". Isso significa que, se uma variável possui várias palavras, você as começa com letra maiúscula, exceto a primeira palavra.

camelCaseVariable = 5

Tipos de Dados

Até agora vimos variáveis usadas para armazenar valores numéricos, mas há muitos tipos de dados diferentes que a linguagem Lua pode lidar. São eles:

  • number
    Qualquer valor numérico
  • string
    Um pedaço de texto, uma string é cercada por aspas duplas " ou simples '. Por exemplo "Hello world" ou 'Hello world'
  • boolean
    Um boleano é um tipo de dado que tem apenas 2 opções, true e false.
  • nil
    nil é um valor que indica nada. É algo sem valor. (Não confunda com 0!)
  • table
    Tabelas serão discutidas no decorrer deste tutorial.
  • userdata
    Userdatas serão discutidas no decorrer deste tutorial.
  • function
    Funções serão discutidas no decorrer deste tutorial.
  • thread
    Threads estão fora de assunto para este tutorial e não serão discutidos.

Então, podemos usar estes diferentes tipos de dados and armazená-los em variáveis:

numberVariable = 10
stringVariable = "Hello world"
booleanVariable = true
nilVariable = nil

Operadores

Operadores são símbolos em Lua cujo podem ser usados para realizar "coisas" com variáveis. Aqui uma lista de operadores e um exemplo para cada um deles:

Operador +

Soma dois valores

x = 10 + 10
print(x)

y = 20
print(y)

z = x + y
print(z)

Operador -

Subtrai um valor de outro

x = 10 - 10
print(x)

Operador *

Multiplica dois valores

x = 10 * 10
print(x)

Operador /

Divide o valor por um outro valor

x = 10 / 10
print(x)

Operador %

Este é o operador chamado de módulo. Irá dividir um valor pelo outro e retornar o resto.

x = 10 % 4
print(x)

O resultado da operação acima é 2.

Operador and

O operador and irá retornar true se ambas variáveis são verdadeiras. Do contrário, retorna false (Um valor verdadeiro é qualquer coisa, exceto false e nil)

x = true and false
print(x)

y = true and true
print(y)

Operador or

O operador or irá retornar true se uma das variáveis é verdadeira. Do contrário, retorna false

x = true or false
print(x)

y = false or false
print(y)

Operador ==

O operador == (igual a) irá retornar true se ambas variáveis forem a mesma coisa. Do contrário, retorna false

x = "hey there" == "hello there"
print(x)

y = 150 == 150
print(y)

Operador ~=

O operador ~= (diferente de) irá retornar true se ambas variáveis forem distintas. Do contrário, retorna false

x = "hey there" ~= "hello there"
print(x)

y = 150 ~= 150
print(y)

Operador >

O operador > (maior que) irá retornar true se o primeiro valor é maior que o segundo valor. Do contrário, retorna false

x = 10 > 5
print(x)

y = 10 > 15
print(y)

y = 10 > 10
print(y)

Operador >=

O operador >= (maior ou igual a) irá retornar true se o primeiro valor é maior ou igual ao segundo valor. Do contrário, retorna false

x = 10 > 5
print(x)

y = 10 > 15
print(y)

y = 10 > 10
print(y)

Operador <

O operador < (menor que) irá retornar true se o primeiro valor é menor que o segundo valor. Do contrário, retorna false

x = 10 < 5
print(x)

y = 10 < 15
print(y)

y = 10 < 10
print(y)

Operador <=

O operador <= (menor ou igual a) irá retornar true se o primeiro valor é menor ou igual ao segundo valor. Do contrário, retorna false

x = 10 < 5
print(x)

y = 10 < 15
print(y)

y = 10 < 10
print(y)

Operador ..

O operador .. (concatenação de string) permite a você juntar duas strings.

x = "Hello"
z = "World!"

combined = x .. " " .. z
print(combined)

Condições if

Uma condição if permite seu código decidir alguma coisa ou não, dependendo do valor. Majoritariamente as condições if são usadas junto com algum daqueles operadores acima.

Uma condição if é escrita assim: if <expressão> then <bloco de código> end

x = 10
if x > 5 then
    print("X is higher than 5")
end

Qualquer código entre then e end será apenas executado quando a expressão for verdadeira. Você talvez tenha notado que o código entre o then e and está movido um pouco à direita. Isso é chamado "indentação". Sempre que nós abrimos um novo escopo (escopos serão discutidos no decorrer deste tutorial) nós movemos nosso código à direita. Geralmente, isso é feito usando tab ou vários espaços . Muitos editores de código irão converter um tab em espaços.

Else

Dentro de uma condição if, você pode também adicionar um else. O código dentro dele será executado quando o código no if não é executado.

x = 10
if x > 5 then
    print("X is higher than 5")
else
    print("X is not higher than 5")
end

Elseif

Caso você queira fazer várias condições if, use um elseif:

x = 15
if x > 10 then
    print("X is higher than 10")
end
if x > 5 then
    print("X is higher than 5")
end
x = 15
if x > 10 then
    print("X is higher than 10")
elseif x > 5 then
    print("X is higher than 5")
end

A diferença entre o primeiro e o segundo exemplo é que se o x for maior que 10, no primeiro exemplo, ambas linhas "X is higher than 10" e "X is higher than 5" serão mostradas. Enquanto no segundo exemplo, apenas "X is higher than 10" será mostrado.

E uma condição if deve sempre começar com um if, pode conter vários elseif e deve apenas ter um else.

name = "NanoBob"
if name == "NanoBob" then
    print("Hello world!")
elseif name == "Brophy" then
    print("Black 123")
elseif name == "Tombaa" then
    print("Stupid")
else
    print("I have no idea")
end

Funções

Funções te permitem escrever menos código, reusando pedaços de código.

O modo no qual criamos uma função é function <nome>(<parâmetros>) <bloco de código> end

function foo()
    print("Hello world #1")
    print("Hello world #2")
end

Para estar executando um código na função, você "chama" a função. Você faz isso escrevendo o nome da função, seguido de ().

function foo()
    print("Hello world #1")
    print("Hello world #2")
end

foo()
foo()
foo()

Funções também te permitem enviar uma variável para elas, para estar processando algo com isso. Isso é o que chamamos de parâmetro da função. Os parâmetros da função são definidos dentro do (), depois do nome da função.

function foo(x)
    print(x)
end

foo(10)
foo("50")

Você talvez note que isso se parece bastante com a função print() que nós estamos usando. Isso porque, print, é uma função Lua.

Retorno de valores

Uma função não apenas pode executar código, como também retornar algo para onde foi chamada. Isso é chamado de retorno de valor. Para estar retornando alguma coisa de uma função, você usa a palavra-chave return.

function foo()
    return 10
end

x = foo()
print(x)

print(foo())

Assim como em uma condição if, todo código dentro de uma função é indentada.

Agora, bora combinar tudo o que aprendemos até agora:

function foo(x)
    if x > 10 then
        return "X is higher than 10"
    elseif x > 5 then
        return "X is higher than 5"
    else
        return "X is not higher than 5"
    end
end

y = foo(15)
print(y)

print(foo(10))
print(foo(0))

Escopos & local

Anteriormente, encontramos escopos e foi dito que indentamos nosso código sempre que há um novo escopo. Mas escopos te permitem fazer muito mais que isso. Mais importante, variáveis "local".

Uma variável local é apenas disponível e vista no escopo que foi definida (ou escopos que foram criados dentro daquele escopo)

Você pode criar um novo escopo usando do (funções e condições if também têm seu próprio escopo).

do
    local x = 5
    print(x)
end
print(x)
do 
    local x = 5
    do 
        local y = 10
        print(x)
        print(y)
    end
    print(y)
end

Repetições (for)

Repetições (ou loop) (for) são formas na programação / scripting para executar um código várias vezes, sem ter que escrever a mesma coisa diversas vezes. Um exemplo de repetição (for):

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

A primeira parte é: i = 1, 10 define uma variável chamada i, cujo começa em 1. Será somado 1 para cada repetição, até que alcance o valor 10. O código dentro do loop, então, pode usar essa variável.

Você pode também estar somando com um número diferente de 1 (incluindo valores negativos) usando esta estrutura.

for i = 20, 0, -2 do
    print(i)
end

Esse exemplo de código começará com i tendo o valor 20, e irá se manter adicionando -2 (o mesmo que subtraindo 2), até alcançar 0

Tabelas

Tabelas são um tipo de dado em Lua cujo permite criar listas de coisas. Aqui um exemplo:

x = {
    [1] = 100,
    [2] = 200,
    [3] = 300
}

print(x[1])
print(x[2])
print(x[3])

Tabelas consistem em um par de chave/valor. No exemplo acima, a chave 1 possui o valor 100; a chave 2 possui o valor 200 e a chave 3 possui o valor 300. Você pode obter o valor em uma tabela, colocando a chave entre colchetes []. Como em print(x[1]).

x = {
    100,
    200,
    300
}

print(x[1])
print(x[2])
print(x[3])

Você pode escolher não incluir as chaves na tabela. Fazendo isso, automaticamente serão adicionados números como sendo chaves, começando em 1. Sendo assim, o exemplo acima seria a mesma coisa que o primeiro exemplo.

As chaves e valores de uma tabela podem ser qualquer tipo de dado, incluindo outras tabelas. Isso te permite criar estruturas de tabelas (bem) complexas.

t = {
    [1] = {
        100,
        200,
        300
    },
    ["x"] = 100,
    [true] = "something"
}
print(t["x"])
print(t[true])

print(t[1][1])
print(t[1][2])
print(t[1][3])

Quando estiver usando uma string como a chave de uma tabela, pode deixar sem os colchetes e sem aspas. Isso vale tanto para definir uma tabela e para indexar (obter o valor dela). Por exemplo:

t = {
    x = 100,
    y = 200,
    z = 300
}

print(t.x)
print(t.y)
print(t.z)

Os valores de uma tabela podem ser modificados / definidos depois da criação da tabela também.

t = {}

t[1] = 10
t[2] = 20
t[3] = 30

t["x"] = "banana"
t[true] = false
t.x = "banana"

Quando estiver usando tabelas, você irá querer frequentemente adicionar alguma coisa ao final da tabela. Isso é mais comum quando você está usando tabelas com chaves numéricas. Para fazer isso, você pode usar # para obter a quantidade de itens que a tabela atualmente possui.

t = {
    10,
    20,
    30
}

t[#t + 1] = 40

Isso irá armazenar o valor 40 na chave 4, pois #t é 3.

Iteradores (pairs/ipairs)

Iteradores são mecanismos que te permitem criar um loop, para um conjunto de valores. Não será discutido aqui neste tutorial. Mas há duas funções cujo são frequentemente usadas para criar um iterador. São eles pairs e ipairs.

t = {
    10, 20, 30, 40, 50
}

for key, value in ipairs(t) do
    print(key, value)
end

A diferença entre pairs e ipairs é a ordem na qual os pares de chave/valor serão iterados, e qual dos pares de chave/valor estão sendo iterados. Onde ipairs usará sempre valores numéricos como chaves, começando em 1 e somando mais 1 toda vez, até não haver mais nenhum valor na tabela. Significa, também, que não irá iterar sobre nenhuma chave que não seja numérica.

t = {
    ["x"] = 5,
    [1] = 10,
    [2] = 20,
    [3] = 30,
    [5] = 50,
}

for key, value in ipairs(t) do
    print(key, value)
end

Um loop pairs irá iterar sobre qualquer valor na tabela, mas a ordem não é garantida. Isto é, distintas execuções de código podem devolver uma ordem diferente.

t = {
    ["x"] = 5,
    [1] = 10,
    [2] = 20,
    [3] = 30,
    [5] = 50,
}

for key, value in pairs(t) do
    print(key, value)
end

Callbacks

Callbacks é quando você passa uma função como um argumento para uma outra função, para chamar a função que você passou, depois. Isso é bastante usado dentro do MTA.

Um exemplo de uma função em Lua cuja usa callback, é table.sort. table.sort irá ordenar os valores de uma tabela. Por padrão, esses valores são ordenados numericamente, mas você pode usar uma callback para mudar o comportamento.

values = {
    5, 4, 3, 6, 8, 1, 2, 9, 7
}

function sortFunction(a, b)
    return b > a
end

function reverseSortFunction(a, b)
    return a > b
end

table.sort(values, sortFunction)
print("Sorted: ")
for _, v in ipairs(values) do
    print(v)
end


table.sort(values, reverseSortFunction)
print("\nReverse sorted: ")
for _, v in ipairs(values) do
    print(v)
end

Na primeira chamada do table.sort (table.sort(values, sortFunction)) você pode ver a função sortFunction sendo passada como segundo argumento. Note que nós não escrevemos sortFunction() aqui (veja os parênteses), pois isso chamaria a função sortFunction e passaria o valor retornado para table.sort.

table.sort irá, então, chamar essa função quando comparar dois valores diferentes. Essa função deve retornar true ou false, dependendo se o seu segundo argumento (b nesse caso) é maior que o primeiro argumento (a), no contexto de ordenação.

Funções anônimas

É possível usar uma "função anônima" também, quando estiver passando uma callback para uma função.

values = {
    5, 4, 3, 6, 8, 1, 2, 9, 7
}

table.sort(values, function(a, b)
    return b > a
end)

print("Sorted: ")
for _, v in ipairs(values) do
    print(v)
end


table.sort(values, function(a, b)
    return a > b
end)

print("\nReverse sorted: ")
for _, v in ipairs(values) do
    print(v)
end

MTA

Esta parte do tutorial discute coisas específicas do MTA. Qualquer código nesta parte do tutorial não irá rodar naquele Interpretador Lua. Você irá precisar criar um servidor local para isso.

Server & Resources

Por padrão, quando instalamos o MTA:SA, um servidor é instalado também. Esse servidor fica localizado na pasta "server" no seu diretório do MTA. Geralmente fica em C:\Program Files (x86)\MTA San Andreas 1.5\server. Esse diretório contém um arquivo chamado MTA Server.exe, clicando duas vezes nele, irá iniciar o seu servidor.

Os scripts de um servidor são um grupo de resources. Um resource pode ter vários arquivos Lua e outras coisas, como imagens, sons, fontes, modificações (txd, dff, col) e mais.

Os resources estão localizados em mods\deathmatch\resources, na pasta do seu servidor. Um resource está sempre no seu próprio diretório. Um resource deve sempre ter apenas um arquivo meta.xml. Esse arquivo diz ao servidor (entre outras coisas) quais arquivos devem ser carregados. Um meta.xml normal é basicamente assim:

<meta>
    <script src="vehicleSystem.lua" type="server"/>
    <script src="vehicleMods.lua" type="client"/>
</meta>

Você precisará de uma entrada para cada arquivo Lua que você quer ter executado no servidor.

Você pode iniciar um resource escrevendo start <nome do resource> no Console do servidor (aquela janela que abriu quando você executou o MTA Server.exe). O nome do resource é o nome da pasta que o arquivo meta.xml está. Iniciando um resource, começará a executar os arquivos Lua. Se você tem alterado o seu script, você precisará reiniciá-lo para as mudanças acontecerem. (restart <nome do resource>)

Server vs client

Código Lua pode ser executado em dois locais. No servidor ou no cliente. Scripts que são server-side, serão executados na máquina onde o servidor está rodando o MTA Server.exe. Script que são client-side, serão executados no computador de cada jogador que entra no seu servidor.

Scripts server-side e client-side têm uma diferença bem distinta no quesito responsabilidade e possibilidade. Algumas funções, por exemplo, são disponíveis apenas no client-side, enquanto outras são apenas server-side (e muitas outras disponíveis em ambos).

Nota: Algumas dessas funções que são disponíveis em ambos, server-side e client-side, são diferentes, dependendo do lado que está sendo executada

Funções do MTA & Wiki

Em Lua propriamente dita, não há muito a se fazer ao ponto de afetar o jogo como o MTA:SA. É por isso que o MTA:SA oferece uma longa lista de funções para você estar usando em seus scripts, cujo interage com o mundo do GTA:SA. Você pode encontrar uma lista de todas elas - o que fazem e como usá-las, na Wiki do MTA

Um exemplo de função é createObject(). Essa função irá criar um objeto no jogo. A página da Wiki contém informações de como usá-la (quais argumentos são esperados e em que ordem). Na maioria das vezes, há um exemplo demonstrando o seu uso.

createObject(1337, 0, 0, 3)

Elementos (userdata)

No começo deste tutorial, nós mencionamos o tipo de dado userdata. Esses tipos de dados são configuráveis pela implementação da linguagem Lua. Nesse caso, configurados pelo MTA. O MTA usa userdata para representar "elementos". Várias coisas no MTA são elementos, como objetos, markers, pedestres, jogadores, interface de usuário (GUI), veículos etc. Na Wiki do MTA você irá notar que muitas funções retornam um elemento ou pedem um elemento como argumento. Uma lista de diferente tipos de elementos pode ser encontrada na Wiki do MTA.

Elementos possuem também uma estrutura hierárquica. Elementos podem conter um "pai" e vários "filhos". Isso irá resultar numa árvore de elementos. O elemento do topo dessa árvore (e também o "pai superior" de todos os elementos) é chamado de root.

Comandos

Por muitas vezes no MTA, você irá querer que certas coisas aconteçam quando o jogador digita um comando. Isso é feito usando comandos, eles usam callback, na qual discutimos anteriormente. A wiki do MTA contém uma página para a função addCommandHandler(). Para este exemplo, iremos usar a versão server-side.

function handler(player, command, argument)
    outputChatBox("You entered " .. command)
    if argument ~= nil then
        outputChatBox("You used the argument " .. argument, player)
    end
end

addCommandHandler("banana", handler)

Esse exemplo também usa a função outputChatBox(), ela irá mostrar um pedaço de texto no chat. (nesse caso, apenas para o jogador que executou o comando)

A callback passada ao addCommandHandler será chamada toda vez que um jogador usa o comando /banana no jogo.

Eventos

Além de coisas sendo feitas ao digitar um comando, você com certeza irá querer que o jogo responda à diversos tipos de coisas que acontecem no jogo. Por exemplo, jogadores levando dano, chegando perto de alguma coisa etc, algo entrando ou saindo de um marker etc. Essas coisas são chamadas de eventos. Sempre que um evento é acionado, você pode rodar um pedaço de código. Você faz isso usando eventos. Eventos são criados usando a função addEventHandler().

O primeiro argumento para a função addEventHandler é a string do nome do evento. Elas podem ser encontradas na Wiki do MTA também.

Um evento é sempre acionado para um elemento específico. Por exemplo o evento "onPlayerWasted" é acionado no jogador que morreu. Você pode anexar um evento para um elemento único, para apenas sua função callback estar sendo chamada quando aquele elemento específico realizar alguma ação. Mas você até que pode usar o elemento root aqui e a sua função callback será chamada para cada elemento que o evento está ligado.

function handlePlayerDeath()
    outputChatBox("You died!!", source)
end
addEventHandler("onPlayerWasted", getRootElement(), handlePlayerDeath)

A função getRootElement() usada nesse exemplo retorna o elemento root, no qual discutimos mais cedo. Você pode também ver que source é usado nesse trecho de código. Nós iremos falar sobre isso na próxima seção.

Variáveis pré-definidas

O MTA tem algumas variáveis globais pré-definidas para você usar no seu código. Uma lista delas pode ser encontrada na Wiki do MTA.

Aqui estão algumas que você pode notar em códigos e qual é o uso delas

  • root O elemento root, o mesmo retornado pela função getRootElement()
  • source
    O elemento anexado ao evento que foi chamado. Um evento na página da Wiki do MTA sempre descreve qual será o source do evento.
  • localPlayer O elemento do jogador onde o script está rodando. (Disponível somente client-side)
  • client Será discutido na próxima seção

Comunicação entre server <-> client

Como dito mais cedo neste tutorial, scripts podem tanto rodar no servidor ou em um dos clientes que estão conectados. Porém, frequentemente você irá querer realizar alguma ação no servidor, vindo de algum cliente - ou vice-versa.

Um exemplo disso seria quando o usuário clica no botão de login numa interface gráfica (GUI) e o servidor deve tentar realizar o login daquele usuário, e manda novamente para o usuário uma resposta (feedback) se foi ou não feito com sucesso.

Isso pode ser feito usando eventos. O MTA te permite criar e acionar os seus próprios eventos. Esses eventos podem ser acionados do servidor ao cliente, e vice-versa. Você pode fazer isso usando a função addEvent(). Uma vez que o evento tem sido adicionado (e marcado como remoto, passando true como segundo parâmetro), você pode chamá-lo do servidor ou do cliente. Você faz isso usando triggerClientEvent() e triggerServerEvent() respectivamente.

Server side:

function handlePlayerLogin(username, password)
    outputChatBox("You tried to log in with the username " .. username, client)
end
addEvent("LuaTutorial.Login", true)
addEventHandler("LuaTutorial.Login", root, handlePlayerLogin)

Client side:

function pretendLogin()
    triggerServerEvent("LuaTutorial.Login", localPlayer, "username", "password")
end

pretendLogin()

Esse exemplo irá acionar o evento "LuaTutorial.Login" do servidor e passa os argumentos "username" e "password". O servidor, então, lida com esse evento na função handlePlayerLogin(), no qual, em nosso caso, só mostra algo no chat.

Nesse exemplo, você pode ver a variável global client, que foi mencionada anteriormente. Essa variável é definida ao elemento do jogador correspondente ao cliente que o evento foi acionado. (Entretanto, ela só é disponível quando um evento é chamado por algum cliente)

O que fazer agora?

Com essa informação, você deve estar apto para começar a fazer coisas no MTA! Se você não entendeu tudo ou se você tem mais algumas dúvidas, não tem porque ter vergonha. Você pode me encontrar e também muitos outros querendo ajudar você com suas dúvidas no canal de scripting no discord do MTA. Uma outra boa fonte de questões relacionadas à programação e/ou scripting é o Stack Overflow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment