Introdução ao scripting
Os scripts do MTAX são escritos em Lua 5.4. Cada resource roda em uma VM própria: variáveis globais de um resource não vazam para outro, e reiniciar um resource limpa tudo o que ele criou.
A API segue uma convenção previsível: verbo primeiro, domínio em seguida — createVehicle, setElementPosition, getPlayerName. Quem lê o nome de uma função sabe o que ela faz e onde procurar as vizinhas.
Servidor e cliente
Seção intitulada “Servidor e cliente”Cada script é declarado no mtaxmanifest.lua em uma das três listas:
| Lista | Onde executa |
|---|---|
server_files | Só no servidor. O arquivo nunca sai da máquina. |
client_files | Baixado e executado na máquina do jogador. |
shared_files | Executado nos dois lados. |
O servidor é a autoridade. Tudo que precisa ser confiável — dinheiro, inventário, permissões — vive no servidor.
Nem toda função existe nos dois lados. A referência de funções é dividida em três grupos — compartilhadas (485), do servidor (130) e do cliente (569) — e é esse grupo que determina em qual lista do manifesto você pode usar cada função.
O que está disponível
Seção intitulada “O que está disponível”O ambiente Lua é restrito. Estas bibliotecas padrão estão abertas no servidor:
base · math · string · table · coroutine · utf8 · os · debug
Os scripts do lado do cliente abrem as mesmas bibliotecas com as mesmas restrições, mais uma: no cliente string.dump foi removido, enquanto o servidor mantém a biblioteca string inteira.
A biblioteca os está disponível de forma parcial: os.time, os.date, os.clock e os.difftime funcionam, mas os.execute, os.remove, os.rename, os.exit, os.getenv, os.tmpname e os.setlocale foram removidos.
A biblioteca debug está reduzida a duas funções: debug.traceback e debug.getinfo. Elas existem para que o relato de erros consiga nomear de onde algo veio; todo o resto que a biblioteca normalmente oferece — os hooks, os leitores de upvalues e locais, o registry — foi retirado, porque passaria por cima do sandbox.
Utilitários incluídos
Seção intitulada “Utilitários incluídos”Além da API do servidor, todo script recebe um conjunto de helpers prontos:
local pos = Vector3(10, 20, 30)local outra = Vector3(0, 0, 5)
print(pos + outra) -- vector3: { x = 10.000, y = 20.000, z = 35.000 }print(pos:getLength())print(pos:getNormalized())| Recurso | O que oferece |
|---|---|
Vector2, Vector3, Vector4 | Vetores com operadores (+, -, *, /), getLength, getNormalized, dot, cross. |
Matrix | Matriz de transformação com position, rotation, forward, transformPosition, inverse. |
bitAnd, bitOr, bitXor, bitNot, bitTest | Operações bit a bit em 32 bits sem sinal. O bitTest responde se o AND de todos os argumentos é diferente de zero. |
bitLShift, bitRShift, bitArShift, bitLRotate, bitRRotate | Deslocamentos e rotações. O bitArShift preserva o bit de sinal; as rotações limitam o deslocamento a 0–31. |
bitExtract(var, campo [, largura]) / bitReplace(var, valor, campo [, largura]) | Lê e escreve um campo de bits. A largura padrão é 1. |
split(texto, separador) | Divide um texto em uma tabela. |
getDistanceBetweenPoints2D / getDistanceBetweenPoints3D | Distância euclidiana entre dois pontos. Ambas aceitam números soltos ou Vector2/Vector3. |
getColorFromString("#RRGGBBAA") | Converte hexadecimal em r, g, b, a. |
tocolor(r, g, b, a) | Empacota uma cor em um único inteiro. Os canais são limitados a 0–255 e o padrão é 255; um canal que não seja número faz a função devolver false e avisar. |
interpolateBetween / getEasingValue | Interpolação com curvas de easing (Linear, InOutQuad, OutBounce, …). |
utfLen, utfSub, utfChar, utfCode, utfSeek | Manipulação de texto UTF-8. |
inspect(valor) / iprint(valor) | Imprime tabelas de forma legível, seguro contra ciclos. |
ref / deref | Guarda um valor em um registro e recupera depois por número. |
Um conjunto específico de funções aceita um Vector3 no lugar das coordenadas soltas:
createVehicle(411, 0, 0, 5)createVehicle(411, Vector3(0, 0, 5)) -- equivalenteSão elas: attachElements, createBlip, createBuilding, createExplosion, createMarker, createObject, createPed, createPickup, createVehicle, getElementsWithinRange, moveObject, removeWorldModel, restoreWorldModel, setCameraMatrix, setCameraTarget, setElementAngularVelocity, setElementAttachedOffsets, setElementInterior, setElementPosition, setElementRotation, setElementVelocity e setMarkerTarget.
Nas demais, passe os componentes separadamente (pos.x, pos.y, pos.z).
No cliente, setCameraMatrix também aceita um Matrix direto. Como no MTA, só a posição sai de fato dele: o ponto de mira repassado é a rotação da matriz convertida para radianos, o que na prática é um ponto perto da origem do mundo. Para olhar na direção da matriz, passe você mesmo a posição e um ponto à frente dela:
setCameraMatrix(minhaMatriz) -- câmera na posição da matriz, olhando para a origem
local p, f = minhaMatriz.position, minhaMatriz.forwardsetCameraMatrix(p.x, p.y, p.z, p.x + f.x, p.y + f.y, p.z + f.z) -- olhando na direção da matrizElementos
Seção intitulada “Elementos”Quase tudo no mundo é um elemento: jogadores, veículos, peds, objetos, marcadores, blips. Elementos formam uma árvore.
Dois globais já vêm prontos em todo script:
| Global | Conteúdo |
|---|---|
root | A raiz da árvore — o ancestral de todos os elementos do servidor. |
resourceRoot | A raiz deste resource. |
Eles equivalem a getRootElement() e getResourceRootElement(), mas dispensam a chamada.
Elementos criados por um resource são filhos da raiz dele e são destruídos automaticamente quando o resource para.
local veiculo = createVehicle(411, 0, 0, 5)
setElementPosition(veiculo, 10, 20, 5)setElementData(veiculo, "dono", "Gabriel")
local x, y, z = getElementPosition(veiculo)API orientada a objetos
Seção intitulada “API orientada a objetos”As mesmas funções nativas também podem ser chamadas como métodos e propriedades no próprio elemento. As duas formas estão sempre disponíveis, nos dois lados, sem precisar habilitar nada:
local veiculo = Vehicle(411, 0, 0, 5)
veiculo.health = 750veiculo:setData("dono", "Gabriel")
print(veiculo.position) -- Vector3São 32 classes de elemento e 10 namespaces estáticos cobrindo a API inteira. Veja o guia de OOP para as regras e a referência de classes para cada membro.
Eventos
Seção intitulada “Eventos”Eventos são o principal meio de reagir ao que acontece no mundo.
addEventHandler("onPlayerJoin", root, function() outputDebugString(getPlayerName(source) .. " entrou")end)Dentro de um manipulador, cinco globais estão disponíveis:
| Global | Conteúdo |
|---|---|
source | O elemento que originou o evento. |
client | O jogador que enviou o evento, quando ele veio do cliente. |
eventName | O nome do evento em execução. |
sourceResource | O resource cujo script disparou o evento; nil quando quem disparou foi a engine. |
sourceResourceRoot | O elemento raiz desse resource, pela mesma regra. |
Elas só existem durante a execução do manipulador — depois voltam a ser nil.
A página de eventos traz a lista completa e os parâmetros de cada um.
Comandos
Seção intitulada “Comandos”addCommandHandler("curar", function(player, cmd, quantidade) if player == false then return end -- veio do console do servidor if not getElementData(player, "podeCurar") then return end setElementHealth(player, tonumber(quantidade) or 100)end)O manipulador recebe (source, nomeDoComando, ...). Quando a linha vem do console do servidor, source é false; quando um jogador a digita no F8, source é esse jogador.
Uma linha digitada no F8 executa primeiro os manipuladores do cliente e o onClientConsole, depois é encaminhada automaticamente: onPlayerCommand → manipuladores do servidor → onConsole. O executeCommandHandler do cliente, em contraste, só executa manipuladores locais e não atravessa a rede. A página de comandos detalha os quatro caminhos.
Por padrão os comandos diferenciam maiúsculas de minúsculas. Para desligar isso:
addCommandHandler("curar", handler, false, false)-- ^ ^-- restricted caseSensitiveComunicação entre resources
Seção intitulada “Comunicação entre resources”Declare no manifesto o que o resource oferece:
-- mtaxmanifest.luaexports = { "getDinheiro" }E chame de outro resource:
local dinheiro = exports.banco:getDinheiro(player)Chamar um resource que não está rodando não gera erro: você recebe false e um aviso no debug.
Depuração
Seção intitulada “Depuração”outputDebugString("valor: " .. tostring(x))outputDebugString("algo deu errado", 1) -- 1 = erro, 2 = aviso, 3 = informaçãoiprint(minhaTabela) -- imprime tabelas de forma legívelErros dentro de um manipulador de evento não derrubam o servidor: eles são registrados no console com o nome do resource e do evento.