PT-BR/Introdução ao Scripting
O que será abordado neste artigo são os conceitos básicos de scripting, ou seja, não iremos fazer nada além do que escrever um arquivo em uma linguagem que o computador entenda. Para deixar mais claro, a linguagem de programação utilizada pelo MTA é a Lua. Antes de começar a entender como esta linguagem funciona, é preciso esclarecer o que é um recurso (resource, em inglês).
Um recurso é um componente essencial de um servidor de MTA. Este pode ser caracterizado por uma pasta ou um arquivo comprimido, no qual há um conjunto de arquivos. Dentre eles, está presente o meta.xml, que por sua vez, informa ao servidor como/quais arquivos devem ser carregados e o que estes representam dentro do jogo. Em outras palavras, podemos comparar o recurso como se fosse um "programa de computador", o qual é iniciado ou interrompido sob demanda, além disso, o servidor tem a funcionalidade de rodar não só um recurso, mas vários ao mesmo tempo.
Obs: Os recursos são comprimidos em .zip e não em .rar
Tudo relacionado a scripting tem relação com os recursos, afinal, os recursos são nada mais que, geralmente, um conjunto de scripts destinados a realizar alguma tarefa. Um recurso pode ser classificado, via o meta.xml, como um gamemode, um mapa ou qualquer outra coisa. O MTA já vem por padrão com alguns recursos interessantes que você pode, além de reaproveitá-los, adaptá-los às suas necessidades. Por exemplo, você pode editar as texturas do gamemode race para deixar-lo com a cara do seu servidor ou mudar o comportamento do modo de jogo perante a mudança de mapa.
Dica: Para facilitar seus primeiros passos na programação em linguagem Lua, é recomendado utilizar um editor de textos com highlight, ou seja, o programa vai destacar cada tipo de instrução com uma cor distinta. Isso facilita a leitura e escrita de qualquer código independente da linguagem. Muito utilizado e recomendado por nós é o Notepad++ ou LuaEdit. Há também um editor de códigos (criado por fãs do MTA) com foco na linguagem Lua para o MTA: MTA Script Editor (ainda em fase de desenvolvimento, mas você já pode testá-lo). |
Criando nosso primeiro script
Neste capítulo, vamos aprender a criar um script para permitir o jogador explorar o mundo do San Andreas.
Onde estão todos os scripts?
Vamos dar uma olhada na estrutura dos scripts. Vá até a pasta de instalação do seu MTA, que por padrão é C:/Arquivo de Programas/MTA San Andreas 1.x.
- Navegue até a pasta /server/mods/deathmatch/resources/
- Dentro desta há várias outras pastas no formato [nome_da_pasta]. O motivo disto está na necessidade de organizar os recursos em certas categorias.
- Como criaremos um modo de jogo totalemente novo, abriremos a pasta [gamemodes]
- Para que o MTA possa reconhecer nossa pasta como um recurso, criemos outra dentro de [gamemodes] com um nome de sua preferência e sem os colchetes. Nós iremos usar "myserver" neste artigo.
- No final, o seu resultado deve ser o seguinte:
/server/mods/deathmatch/resources/[gamemodes]/myserver
Identificando seu recurso
Para que o servidor reconheça os arquivos de um determinado recurso (para carregá-los), um arquivo meta.xml deve ser criado, contendo uma lista de todo o conteúdo do recurso. O arquivo meta.xml deve ser salvo na pasta principal (nesse caso, na pasta "myserver"). Então, abra um editor de textos (recomenda-se o Notepad++) e salve-o com o nome de "meta.xml".
Entre com as seguintes linhas no arquivo meta.xml:
<meta> <info author="Seu_Apelido" type="gamemode" name="Meu_jogo" description="Meu primeiro recurso" /> <script src="script.lua" /> </meta>
Na tag <info />, existem:
- type - indica que o recurso criado é um gamemode. Pode também ser um map (mapa), que iremos explicar depois
- name - o próprio nome já diz: o nome do recurso
- description - uma breve descrição a fim de outros jogadores e/ou donos de servidores possam saber do que se trata seu recurso
A tag <script /> indica o caminho dos arquivos (escritos em Lua) presentes no seu recurso. Criaremos no próximo passo o exemplo (script.lua).
Dica: Um gamemode é o que você precisa para criar um servidor independente, pois é um recurso que envolve toda a programação principal do jogo online. |
Criando um script simples
Observe que na tag <script /> anterior, o arquivo .lua está presente na pasta principal do recurso, e não em uma subpasta. Então precisamos salvar o script na pasta principal, assim como indicado na tag.
- Salve o seguinte código no arquivo script.lua:
local spawnX, spawnY, spawnZ = 1959.55, -1714.46, 10 function joinHandler() spawnPlayer(source, spawnX, spawnY, spawnZ) fadeCamera(source, true) setCameraTarget(source, source) outputChatBox("Bem-vindo ao meu servidor!", source) end addEventHandler("onPlayerJoin", getRootElement(), joinHandler)
Este script criado faz parte da esfera do servidor. Ou seja, ele é executado somente no servidor. Caso ele seja definido no cliente, cada um dos jogadores receberá o arquivo contendo o código. Mas neste exemplo, por enquanto, não necessitamos usar funções específicas do cliente. Vamos ao que interessa:
O script irá gerar o seu personagem (seu boneco) nas coordenadas (x, y, z) especificadas assim que você entrar no jogo. Note que a função fadeCamera() precisa ser usada ou, do contrário, a tela ficará preta (e você não verá nada). Outra função é a setCameraTarget(), que foca a câmera do jogo no seu personagem (do contrário, a câmera estaria virada para o céu).
A variável source representa o elemento responsável pela chamada do evento (haverá um tópico adiante sobre isso). Assim, quando um jogador entra no jogo, o evento "onPlayerJoin" é chamado e, em seguida, chama a função. O evento automaticamente define o jogador na variável source.
Se olharmos bem para o addEventHandler, veremos 3 argumentos:
- onPlayerJoin - define o evento que chamará a função. No caso, esse evento é chamado assim que o jogador entra no jogo, logo, a função é executada assim que o jogador entra no jogo
- getRootElement() - indica quem poderá chamar o evento. Neste caso, usamos a função getRootElement() para nos retornar um elemento padrão do MTA denominado RootElement. Ele, por sua vez, representa todos os outros elementos existentes no servidor; ou seja, todos os jogadores estão representados nele. Consequentemente, todo jogador que entrar no servidor, causará o evento e a função será executada.
- joinHandler - indica o nome da função a ser executada quando o evento ocorrer.
Vamos agora rodar nosso servidor e testar o script.
Dica: A variável source armazena sempre o elemento que chamou determinado evento. Ou seja, nem sempre source será o jogador. |
Executando o script
Para iniciar o servidor, execute o arquivo "MTA Server.exe" situado na pasta ../server/. Primeiramente, é mostrada uma lista com os principais status do seu servidor; observe o número da porta, da qual você irá precisar quando entrar no jogo. Logo após, o servidor carrega todos os recursos presentes no diretório ../server/mods/deathmatch/resources/, caso estejam corretos. E finalmente "fica pronto para aceitar conexões" (ready to accept connections!).
Antes de se conectar ao seu servidor, você precisa executar o gamemode criado. Para isso, digite no console (a janela do MTA Server.exe) o comando gamemode myserver e pressione Enter. O servidor irá carregar o gamemode e mostrar os erros do seu script, caso existam.
obs: "myserver" é o nome da pasta criada no início deste tutorial e, consequentemente, a identificação do seu recurso)
Agora você pode se conectar ao seu servidor de duas maneiras:
- Clique em Quick Connect > Insira o endereço IP do servidor e o número da porta no seguinte formato - IP:Porta
- Navegue até Server Browser > Aba Local > Clique duas vezes no seu servidor.
Se tudo correr bem, seu personagem será criado nas coordenadas especificadas.
No próximo tópico iremos criar um comando para que o jogador possa gerar um veículo ao seu lado. Se preferir, embora não recomendado caso seja iniciante, você pode visitar alguns scripts mais avançados clicando aqui. Outra parte interessante da seção "Scripting", na qual obviamente você está, é Conhecendo a Interface Gráfica. Nela, se discute a criação de interfaces gráficas para seus scripts.
Criando um comando simples
Como foi mencionado anteriormente, criaremos agora um comando que irá gerar um carro ao lado do seu personagem. Antes de tudo, precisamos adicionar uma função e um evento específico, chamado de addCommandHandler() ao nosso script.lua.
obs: Um comando é como se fosse uma função chamada pelo jogador quando ele está no chat. Ele digita seu nome e envia-o argumentos especificando o que ele quer. Sempre no formato: /[nome do comando] <argumentos> Exemplo: /cv 410
-- adiciona a função a ser executada pelo comando. function createVehicleForPlayer(thePlayer, command, vehicleModel) -- aqui colocaremos, mais tarde, o código para gerar o veículo end -- adiciona um gerenciador de comandos addCommandHandler("createvehicle", createVehicleForPlayer)
Dica: As funções nativas do MTA são clicáveis em todo o Wiki e lhe redirecionam à páginas sobre elas. |
Sobre os gerenciadores de comandos
Esse é um ponto interessante a ser discutido, já que envolve eventos e seus derivados. No MTA, para se criar um comando, usamos um tipo específico de evento que ocorre quando um jogador entra com um comando. Usamos a função addCommandHandler para cria-los. Como um comando é necessariamente um evento, vale a teoria de eventos para ele:
- Um evento é criado por uma função para determinar suas propriedades
- Todo evento ativa quando algo acontece no jogo e este sempre envia à função determinadas informações sobre o que ocoreu
- A relação evento-função é nada mais que uma troca de argumentos, de propriedades
Deve-se prestar atenção nesta troca, pois envolve conceitos fundamentais de Lua. Se você já teve alguma experiência com programação, deve se lembrar como funciona os argumentos de uma função:
nomeDaFuncao(argumento1, argumento2, argumento3, ..)
addCommandHandler("createvehicle", createVehicleForPlayer, ..) createVehicleForPlayer(thePlayer, commandName, vehicleModel, ..)
Como você observa no exemplo acima, nas duas funções se define quem são os argumentos para indicar como o código deve-se comportar:
- addCommandHandler é uma na qual se define que quando o jogador digitar o comando createvehicle, a função createVehicleForPlayer irá ser executada.
- Em createVehicleForPlayer há o recebimento de certas informações enviadas pelo evento, na sequência:
- thePlayer - Qual é o jogador que ativou o evento?
- commandName - Qual foi o comando digitado?
- vehicleModel - Qual foi o ID digitado pelo jogador?
Nota-se nesse caso uma clara troca de informações entre o evento e a função determinada. Quem define como será essa troca é o próprio tipo de evento, que neste caso é um comando. Como ele está sendo executado no servidor, ele sempre enviará na ordem: Quem Digitou? Qual Comando? Quais Argumentos digitados por este jogador? E aí será sua função quem decidirá o que fazer com essas informações; você, programador, dá o nome a esses argumentos e pode até não querer recebê-los, caso elas não forem necessárias.
Vamos a um exemplo prático: alguém digita /createvehicle 468 no chat para gerar uma Sanchez (ID dela é 468. Veja outros neste link), então o gerenciador de comandos chama a função determinada de uma forma como se o jogador tivesse feito o script dele e digitado no arquivo:
createVehicleForPlayer(thePlayer,"createvehicle","468") -- thePlayer seria o próprio jogador
É claro que ele não vai querer seguir este tutorial para fazer isso, e desta forma você está aqui para fazer um comando pra ele. Simplesmente ele digita no chat e seu script cria o veículo desejado.
obs: Os dois primeiros argumentos colocados na função addCommandHandler sempre serão do mesmo tipo: quem foi? o que digitou? Em diante, você pode definir quantas variáveis quiser, de acordo com sua necessidade. Se o comando precisar criar um cavalo (pouco provável) e precisar que o jogador diga qual é a cor dele, a raça, a altura, o humor, poderá definir quantas quiser. Algo nesse estilo: addCommandHandler("criarcavalo" criarCavaloParaJodador(), cor, raça, altura, humor)
Dica: A função sempre deverá existir antes de você criar o comando (vale para eventos também). O computador é uma coisa linear, portanto se você pedir pra ele definir algo que não existe ainda, vai dar erro! Então sempre coloque a função em cima (lógico, é lido de cima pra baixo) do addEventHandler() |
Elaborando a função
É hora de escrever nossa função; Sempre que for fazer isso é necessário pensar como faremos um veículo ser criado de forma mais simples o possível:
- Obter a posição do jogador, para sabermos onde desovar o veículo
- Calcular essa posição favorável para que a máquina de, em média, 450 quilos não cair em cima do coitado
- Pedir ao jogo gerar este veículo
- Verificar se foi gerado com sucesso, ou informá-lo um erro
Para fazer isso, teremos que usar várias funções. Portanto, nós nos direcionamos à página contendo uma Lista de funções de servidor para achá-las. Já que precisamos obter a posição do jogador e como jogador é um Elemento, clicamos primeiro em Element functions onde haverá a função getElementPosition para tal fim. Ao clicar no link, uma página contendo detalhes sobre essa função será mostrada. Nela contém a sintaxe a ser usada, acompanhada de um exemplo. Como a sintaxe do português, a de programação indica como/quais argumentos se pode usar ou omitir.
Para getElementPosition, a sintaxe é:
float, float, float getElementPosition ( element theElement )
Os três itens na frente do nome da função indica o tipo de informação que ela retorna. Neste caso, são float (números com decimais). Em outras palavras, é retornado ao programador três números decimais indicando a posição (x, y, z). Depois do nome, sempre em parênteses, é o que você precisa mandar pra função funcionar. Neste caso, só é preciso de um elemento do qual a posição será retornada e no nosso exemplo, este elemento é o jogador:
function createVehicleForPlayer(thePlayer, command, vehicleModel) -- retorna a posição do jogador nas variáveis (x, y, z) -- o termo "local" significa que as três variáveis só existirão dentro do bloco - este é a função local x,y,z = getElementPosition(thePlayer) end
Neste passo precisamos garantir que o veículo não esmagará o jogador, então adicionamos algumas unidades no eixo x; fazendo este gerar a leste dele
function createVehicleForPlayer(thePlayer, command, vehicleModel) local x,y,z = getElementPosition(thePlayer) -- retorna a posição do jogador x = x + 5 adiciona 5 unidades na variável x end
Agora precisamos de outra função para gerar o veículo. Novamente, procuramos na Lista de funções de servidor. Só que como estamos falando de veículos, procuramos na seção Vehicle functions; onde encontraremos createVehicle. Na sintaxe desta função, temos somente um tipo de retorno (o qual é mais comum): um elemento do tipo veículo criado pela própria função. Além do mais, percebemos alguns argumentos inseridos em colchetes ([]), indicando opcionalidade.
Oras, já temos todos os argumentos necessários para a função funcionar: os (x, y, z) calculados anteriormente e o ID do veículo fornecido pelo jogador através do comando; na variável (vehicleModel). É só digitar:
function createVehicleForPlayer(thePlayer, command, vehicleModel) local x,y,z = getElementPosition(thePlayer) -- get the position of the player x = x + 5 -- add 5 units to the x position -- create the vehicle and store the returned vehicle element in the ''createdVehicle'' variable local createdVehicle = createVehicle(tonumber(vehicleModel),x,y,z) end
É claro que esse código pode melhorar em vários aspectos, mas pelo menos adicionaremos uma condição para verificar o sucesso na criação do veículo. Como foi lido (e infelizmente para uns, era pra ter sido) na página do createVehicle; em baixo de Returns, a função retorna false quando não é possível criar o veículo. Portanto, verificaremos o valor retornado pela variável createdVehicle
Agora temos o script completo:
function createVehicleForPlayer(thePlayer, command, vehicleModel) local x,y,z = getElementPosition(thePlayer) -- retorna a posição do jogador x = x + 5 adiciona 5 unidades na variável x local createdVehicle = createVehicle(tonumber(vehicleModel),x,y,z) -- verifica se o retorno foi falso if (createdVehicle == false) then -- Se sim, retorne uma mensagem de erro somente para o jogador outputChatBox("Não foi possível criar o veículo. A sintaxe é: /createvehicle [id]",thePlayer) end end addCommandHandler("createvehicle", createVehicleForPlayer)
Deve-se destacar os seguintes pontos:
- Quando tentamos usar qualquer função que crie algum elemento no MTA e esta criação falha, uma bool é retornada como falso. Isso quer dizer: ou há um erro de sintaxe/programação no seu script, ou realmente o servidor não conseguiu computar essa informação
- Foi introduzida outra função denominada outputChatBox, a qual simplesmente escreve no chat a mensagem de erro
A partir de agora, você pode dar uma olhada na documentação sobre as funções aqui no Wiki. Se desejar, há a página do Map Manager só falando de scripting avançado.
Importante Saber
Já foi dito anteriormente algumas coisas sobre os recursos, gerenciadores de comandos e funções, porém há muita coisa ainda pra aprender. Essa seção lhe fornecerá outras coisas importantes sobre o funcionamento do MTA, ao mesmo tempo linkando à páginas relacionadas se possível.
Lado do Cliente e Servidor
É notório a semelhança dos termos (Cliente/Servidor) em várias partes do site, até mesmo nas funções. O MTA não suporta scripts somente rodando no servidor e oferecendo aos seus jogadores comandos como aquele do exemplo acima ou algo parecido. Ele também oferece a possibilidade de rodar um script no computador de cada jogador logado. Isso porque algumas vantagens oferecidas pelo programa precisam rodar no computador deles. Imagine um servidor mandar o todo tempo informações para desenhar a interface gráfica, sendo esta necessitar uma renderização em uma faixa de 30 vezes por segundo. Não faz o menor sentido, e nem é viável! Ainda mais em PC's antigos com ping alto. Há outras razões além da performance, como o fato de alguns funcionarem melhor em um dos lados ou justamente porque não rola.
A maioria dos scripts (gamemodes ou mapas) provavelmente serão no lado do servidor, como foi feito anteriormente. Se caso acontecer de algo não poder ser resolvido com script do servidor, vá tranquilo pro cliente. Um exemplo de fazer isso seria criar um arquivo pacato chamado client.lua e defini-lo no meta.xml, como a seguir:
<script src="client.lua" type="client" />
O atributo type define automaticamente o arquivo como servidor, então só precisa especificar se for rodado no cliente. Quando terminar, este arquivo será baixado para o computador do brother quando ele conectar ao servidor. Para mais informações, segue o link (Em inglês).
Recursos Complicados
A última seção explicou brevemente como adicionar um script no cliente, mas há muita coisa possível a ser feita. Como foi dito nesta página, recursos praticamente são a alma do jogo. Sua função é definida pelo que eles podem fazer. Então vão aí uns exemplos de recursos hipotéticos retirados da instalação, mostrando suas distintas funções:
Primeiro exemplo - Um utilitário
/admin_commands /meta.xml /commands.lua /client.lua
<meta> <info author="Someguy" description="admin commands" /> <script src="commands.lua" /> <script src="client.lua" type="client" /> </meta>
- O commands.lua oferece alguns comandos de administrador, como banir algum pateta ou algo útil para se administrar o servidor
- O client.lua cria janelas para executar-los de forma muito mais clara e rápida
Este exemplo pode estar rodando o tempo todo (talvez até auto-carregado quando o servidor iniciar) se útil durante a jogatina, e claro, não interferir no processo; só se o administrador decidir intervir.
Segundo exemplo - Um gamemode
/counterstrike /meta.xml /counterstrike.lua /buymenu.lua
<meta> <info author="Someguy" description="Counterstrike remake" type="gamemode" /> <script src="counterstrike.lua" /> <script src="buymenu.lua" type="client" /> </meta>
- O counterstrike.lua oferece:
- A possibilidade dos jogadores escolherem o time e nascerem
- Lhe dá armas, alvos e instruções (talvez tiradas do Mapa, veja adiante)
- Define as regras do jogo; exemplos: o que acontece no fim da partida ou quando um jogador falece?
- E muita coisa que não dá ser citada aqui...
- O buymenu.lua é um script do cliente e adiciona um menu para adquirir armas
Esse exemplo pode ser denominado um gamemode, pois não só complementa a partida, como também define as regras dela. O atributo type indica que este recurso funciona com o Map manager ou outro escrito pelos desenvolvedores com finalidade de gerenciar os gamemodes e carregamento de mapas. É bem recomendado começar seu gamemode a partir do diferencial que este apresenta.
E mais, um gamemode provavelmente não rodará sem um mapa. Gamemodes sempre precisarão ser o mais genérico possível. Um exemplo de mapa pode ser visto no próximo exemplo.
Terceiro exemplo - Um mapa
/cs-airport /meta.xml /airport.map /airport.lua
<meta> <info author="Someguy" description="Counterstrike airport map" type="map" gamemodes="counterstrike" /> <map src="airport.map" /> <script src="airport.lua" /> </meta>
- O airport.map no arquivo XML acima oferece diversas informações sobre o mapa do gamemode, podendo incluir:
- Onde os jogadores deverão nascer, com quais armas, em qual time
- Quais são os alvos
- Clima, horário e limite de tempo da partida.
- Veículos espalhados pelo local
- O airport.lua também pode conter objetos específicos, como:
- Abrir alguma porta/fazer algo explodir quando uma coisa acontece
- Criar ou mover objetos customizados, ou manipular objetos criados no arquivo .map
- .. ou infinitas coisas limitadas pela sua imaginação
Nota-se a presença do atributo type classificando o recurso como um mapa, consequentemente avisando o Map manager disso; Enquanto o atributo gamemodes indica com qual o gamemode ele é compatível. Neste caso é o próprio gamemode do exemplo acima. O que pode incomodar é a presença de um script em um mapa. É claro, não há a necessidade de haver isso nele. Entretanto, isso abre uma gama de possibilidades para criadores de mapas concretizarem seus próprios mundos nas regras do gamemode pra ele criado.
O arquivo airport.map contém algo parecido com isso:
<map mode="deathmatch" version="1.0"> <terrorists> <spawnpoint posX="2332.23" posY="-12232.33" posZ="4.42223" skins="23-40" /> </terrorists> <counterterrorists> <spawnpoint posX="2334.23443" posY="-12300.233" posZ="10.2344" skins="40-50" /> </counterterrorists> <bomb posX="23342.23" posY="" posZ="" /> <vehicle posX="" posY="" posZ="" model="602" /> <vehicle posX="" posY="" posZ="" model="603" /> </map>
Quando um gamemode é iniciado com um mapa, este é automaticamente iniciado pelo Map manager e a informação contida é lida pelo gamemode. Quando um mapa muda durante a partida, o atual é fechado e o escolhido, iniciado. Para mais detalhes e exemplos sobre como os mapas são utilizados na programação principal, visite a página Escrevendo Gamemodes (Em inglês).
Eventos
Foi dada uma breve explicação no inicio da página, mas um conceito mais formal (criado pelo pessoal do MTA) nunca é demais:
- Eventos são a forma como o MTA diz aos scripts sobre as coisas que acontecem. Por exemplo, quando um jogador morre, o evento onPlayerWasted é ativado. Com o objetivo de providenciar ações caso isso aconteça, deve ser adicionado um evento, como mostrado no primeiro capítulo.
Este exemplo irá escrever uma mensagem no chat com o apelido do jogador falecido:
function playerDied(totalAmmo, killer, killerWeapon, bodypart) outputChatBox(getPlayerName(source).." feleceu!") end addEventHandler("onPlayerWasted",getRootElement(),playerDied)
Em vez de mostrar aqui todos os tipos de eventos, uma página específica foi criada no Wiki só para especificar este tipo de coisa; o mesmo serve para os gerenciadores de comandos. Um outro ponto importante é com relação a variável source. Ela é automaticamente declarada na hora de a função ser executada. Ela agrega um diferente valor para cada tipo de evento, como no exemplo acima: para eventos relacionados a jogadores, ele retorna um elemento do tipo jogador. Uma outra forma de como a variável source é usada está no script básico de fazer o jogador nascer no topo da página.
Para onde ir a partir daqui
Agora você deve estar bem mais familiarizado com os aspectos básicos de scripting para o MTA e também com o Wiki. A Página Inicial contém todos os links para mais informações, tutoriais e referências para lhe propiciar todas as fontes necessárias a um estudo mais aprofundado.
Dica: Recomendamos ler o tutorial de debugging a partir daqui. Um bom treinamento em tirar os bugs de seus scripts é fundamental. Também é recomendado dar uma olhada nas variáveis pré-definidas para ajudá-lo com certas tarefas, e claro "scriptar" de forma mais rápida e prática. |
Veja mais:
- Tópicos Avançados (Em inglês)