PT-BR/Introdução ao Scripting

From Multi Theft Auto: Wiki
Jump to navigation Jump to search

Os recursos (resources) são uma "chave" do MTA. Um recurso é uma pasta ou um arquivo comprimido (em .zip, e não em .rar) contendo um conjunto de arquivos, juntos a um meta.xml que informa ao servidor como/quais recursos devem ser carregados e quais arquivos estes possuem. Um recurso pode ser visto como um programa de computador, onde pode ser iniciado/interrompido a qualquer momento, além de poder rodar junto a diversos outros recursos ao mesmo tempo.

Tudo relacionado a scripting (programação em linguagem Lua, neste caso) tem relação com os recursos, afinal, os recursos nada mais são do que, normalmente, um conjunto de scripts escritos em Lua destinados a realizar alguma tarefa. O que um recurso define é se determinados arquivos definem um gamemode (estilo de jogo, como corrida, deathmatch, entre outros), 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 (como por exemplo, um limitador de áreas que impede que um jogador se afaste de um determinado local, ou scripts que criam objetos "pegáveis" no mapa, como colete e armas). {{tip|Para facilitar seus primeiros passos na programação em linguagem Lua, é recomendado que se utilize um editor de textos com highlight, ou seja, "cores" para cada comando. 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

Nós primeiro vamos aprender a criar um script que permita que o jogador ande pela cidade, passo-a-passo.

Onde estão todos os scripts?

Vamos dar uma olhada na estrutura dos scripts. Vá até a pasta de instalação do seu MTA (padrão: C:\Arquivo de Programas\MTA San Andreas 1.x\) e siga os passos abaixo:

/server/mods/deathmatch/resources/

Você irá ver diversos arquivos .zip, que são os simples scripts que vêm por padrão junto ao MTA. Cada arquivo é um "recurso", e eles serão todos descompactados e carregados pelo servidor quando ele for iniciado. Para criar seu próprio resource, simplesmente crie uma pasta com um nome de sua preferência. Nós iremos usar o nome "myserver" para este tutorial.

Após a criação da pasta (myserver), acessamos-a:

/server/mods/deathmatch/resources/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_Nome" type="gamemode" name="Meu_Servidor" description="Meu primeiro recurso" />
     <script src="script.lua" />
</meta>

Na tag <info />, existe o campo "type", que indica que o recurso criado é um gamemode. Pode também ser um map (mapa), que iremos explicar depois. Um gamemode é o que você precisa para criar um servidor independente.

A tag <script /> indica o caminho dos arquivos (escritos em Lua) presentes no seu recurso. O do exemplo (script.lua), criaremos no próximo passo.

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)

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 (você verá mais sobre eventos mais adiante). 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 (obs: a variável source armazena sempre o elemento que chamou determinado evento. Ou seja, nem sempre source será o jogador. Cuidado com isso.)

Se olharmos bem para o addEventHandler, veremos 3 argumentos: 'onPlayerJoin', que indica o evento que chamará a função (no caso, esse evento é chamado assim que o jogador entra no jogo, logo, a função é chamada assim que o jogador entra no jogo); 'getRootElement()', que indica quem poderá chamar o evento (ou seja, todos os elementos, incluindo todos os jogadores); 'joinHandler', que indica o nome da função que será chamada quando o evento for chamado. Maiores detalhes serão explicados mais adiante. Vamos agora simplesmente rodar nosso servidor e testar nosso script.

Executando o script

Para iniciar o servidor, simplesmente execute o arquivo "MTA Server.exe" (presente na pasta de instalação do MTA:SA). Primeiramente é mostrada uma lista com os principais status do seu servidor; observe o número da porta, que você irá precisar quando entrar no jogo. Logo após, o servidor carrega todos os resources (caso todos estejam corretos) que estão presentes no diretório /resource/ e então "fica pronto para aceitar conexões" (ready to accept connections!).

Antes de se conectar ao seu servidor, você precisa executar o gamemode que criou. Para isso, digite no console (a janela do MTA Server.exe) o comando gamemode myserver (obs: "myserver" é o nome da pasta que você criou no início deste tutorial e, consequentemente, o nome do seu recurso) e pressione Enter. O servidor irá carregar o gamemode que você criou e irá mostrar os erros (caso existam) do seu script. Agora você pode se conectar ao seu servidor de duas diferentes maneiras: clicando em "Quick Connect" e inserindo o endereço IP do seu servidor e o número da porta, ou clicando em "Server Browser", acessando a aba "Local" e clicando 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, ou prossiga com este tutorial. Outra parte interessante deste tutorial é Iniciando com GUIs, no qual aborda a criação de interfaces gráficas (janelas, botões, etc) para seus scripts.

Criando um comando simples

Vamos voltar ao arquivo script.lua (abra-o novamente). Como mencionado anteriormente, vamos criar agora um comando (para executá-lo, digite no chat /seucomando) que irá gerar um carro ao lado do seu personagem. Antes de tudo precisamos criar uma função que iremos chamar posteriormente e um addCommandHandler(), responsável por criar o comando.

-- cria a funcao que sera chamada pelo comando. Os argumentos sao: thePlayer, command, vehicleModel
function createVehicleForPlayer(thePlayer, command, vehicleModel)
   -- cria um veiculo
end

-- cria um lancador de comandos
addCommandHandler("createvehicle", createVehicleForPlayer)

Nota: Você pode clicar nas funções dos códigos para obter uma explicação de cada uma delas.

Sobre os lançadores de comandos (responsáveis por criar os comandos)

O primeiro argumento do addCommandHandler é o nome do comando que o jogador precisará digitar (no exemplo, ele precisará digitar no chat /createvehicle <argumento>. O segundo argumento é a função que será chamada quando o comando for digitado (nesse caso, a função createVehicleForPlayer().

Se você já possui alguma experiência em programação, sabe que você chama uma função desta forma:

nomeDaFuncao(argumento1, argumento2, argumento3, ..)
nomeDaFuncao(thePlayer, commandName, argumento3, ..)

Se observarmos bem o exemplo acima, podemos ver que o argumento1 é o jogador (quem digita o comando) e o argumento2 é o nome do comando (nesse caso, createvehicle). O argumento3 é o argumento digitado depois do comando (por exemplo, se o jogador digitou /createvehicle Infernus, então a variável argumento3 irá armazenar o texto "Infernus"). Nunca se esqueça que os dois primeiros argumentos são padrões (ou seja, precisam existir), mas você pode nomeá-los com outro nome (mas isto não é necessário agora).

Nós chamamos a função addCommandHandler, que chama a função createVehicleForPlayer.

Por exemplo: alguém digita "createvehicle 468" no chat para gerar uma Sanchez (468 é o ID da Sanchez. Veja mais aqui), então o lançador de comandos (quando dizemos "lançador de comandos", é o mesmo que "criador de comandos") chama a função createVehicleForPlayer(), como se nós tivéssemos a seguinte linha no script:

createVehicleForPlayer(thePlayer,"createvehicle","468") -- thePlayer e o jogador que digitou o comando

As we can see, it provides several parameters: the player who called the command, the command he entered and whatever text he had after that, in this case "468" as vehicle id for the Sanchez. The first two parameters are the same with all command handlers, which you can read on the addEventHandler page. For this fact, you always have to define at least those two parameters to use any after that (for example to process text that was entered after the command, like in our example the vehicle model id).

Note: You have to add the command handler AFTER you defined the handler function, else it can't find it. The order of execution matters.

Writing the function

In order to fill the function we created, we need to think about what we have to do:

  • Get the players position, so we know where to spawn the vehicle (we want it to appear right beside the player)
  • Calculate the position we want to spawn the vehicle at (we don't want it to appear in the player)
  • Spawn the vehicle
  • Check if it has been spawned successfully, or output a message

In order to achieve our goals, we have to use several functions. To find function we need to use, we should visit the Server Functions List. First we need a function to get the players position. Since players are Elements, we first jump to the Element functions where we find the getElementPosition function. By clicking on the function name in the list, you get to the function description. There we can see the syntax, what it returns and usually an example. The syntax shows us what arguments we can or have to submit.

For getElementPosition, the syntax is:

float, float, float getElementPosition ( element theElement )

The three float in front of the function name are the return type. In this case it means the function returns three floating point numbers. (x, y and z) Within the parentheses, you can see what arguments you have to submit. In this case only the element whose position you want to get, which is the player in our example.

function createVehicleForPlayer(thePlayer, command, vehicleModel)
	-- get the position and put it in the x,y,z variables
	-- (local means, the variables only exist in the current scope, in this case, the function)
	local x,y,z = getElementPosition(thePlayer)
end

Next we want to ensure that the vehicle won't spawn directly in the player, so we add a few units to the x variable, which will make it spawn east from the player.

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
end

Now we need another function, one to spawn a vehicle. We once again search for it on the Server Functions List, this time - since we are talking about vehicles - in the Vehicle functions section, where we will choose createVehicle. In this function's syntax, we only have one return type (which is more common), a vehicle element that points to the vehicle we just created. Also, we see that some arguments are enclosed within [ ] which means that those are optional.

We already have all arguments we need for createVehicle in our function: The position we just calculated in the x,y,z variables and the model id that we provided through the command ("createvehicle 468") and can access in the function as vehicleModel variable.

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

Of course this code can be improved in many ways, but at least we want to add a check whether the vehicle was created successfully or not. As we can read on the createVehicle page under Returns, the function returns false when it was unable to create the vehicle. Thus, we check the value of the createVehicle variable.

Now we have our complete script:

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
	local createdVehicle = createVehicle(tonumber(vehicleModel),x,y,z)
	-- check if the return value was ''false''
	if (createdVehicle == false) then
		-- if so, output a message to the chatbox, but only to this player.
		outputChatBox("Failed to create vehicle.",thePlayer)
	end
end
addCommandHandler("createvehicle", createVehicleForPlayer)

As you can see, we introduced another function with outputChatBox. By now, you should be able to explore the function's documentation page yourself. For more advanced scripting, please check out the Map Manager.

What you need to know

You already read some things about resources, command handlers and finding functions in the documentation in the first paragraph, but there is much more to learn. This section will give you a rather short overview over some of these things, while linking to related pages if possible.

Clientside and Serverside scripts

You may have already noticed these or similiar terms (Server/Client) somewhere on this wiki, mostly in conjunction with functions. MTA not only supports scripts that run on the server and provide commands (like the one we wrote above) or other features, but also scripts that run on the MTA client the players use to connect to the server. The reason for this is, that some features MTA provides have to be clientside (like a GUI - Graphical User Interface), others should be because they work better and still others are better off to be serverside or just don't work clientside.

Most scripts you will make (gamemodes, maps) will probably be serverside, like the one we wrote in the first section. If you run into something that can't be solved serverside, you will probably have to make it clientside. For a clientside script for example, you would create a ordinary script file (for example called client.lua) and specify it in the meta.xml, like this:

<script src="client.lua" type="client" />

The type attribute defaults to 'server', so you only need to specify it for clientside scripts. When you do this, the clientside script will be downloaded to the player's computer once he connects to the server. Read more about Client side scripts.

More complex resources

The previous section showed briefly how to add clientside scripts to the resource, but there is also much more possible. As mentioned at the very top of this page, resources can be pretty much everything. Their purpose is defined by what they do. Let's have some theoretical resources, by looking at the files it contains, the meta.xml and what they might do:

First example - A utility script

/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>
  • The commands.lua provides some admin commands, like banning a player, muting or something else that can be used to admin the server
  • The client.lua provides a GUI to be able to perform the mentioned actions easily

This example might be running all the time (maybe even auto-started when the server starts) as it's useful during the whole gaming experience and also wont interfere with the gameplay, unless an admin decides to take some action of course.

Second example - A 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>
  • The counterstrike.lua contains similiar to the following features:
    • Let players choose their team and spawn them
    • Provide them with weapons, targets and instructions (maybe read from a Map, see below)
    • Define the game's rules, e.g. when does the round end, what happens when a player dies
    • .. and maybe some more
  • The buymenu.lua is a clientside script and creates a menu to buy weapons

This example can be called a gamemode, since it not only intereferes with the gameplay, but actually defines the rules of it. The type attribute indicates that this example works with the Map manager, yet another resource that was written by the QA Team to manage gamemodes and map loading. It is highly recommended that you base your gamemodes on the techniques it provides.

This also means that the gamemode probably won't run without a map. Gamemodes should always be as generic as possible. An example for a map is stated in the next example.

Third example - A Map

/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>
  • The airport.map in a XML file that provides information about the map to the gamemode, these may include:
    • Where the players should spawn, with what weapons, what teams there are
    • What the targets are
    • Weather, World Time, Timelimit
    • Provide vehicles
  • The airport.lua might contain map-specific features, that may include:
    • Opening some door/make something explode when something specific happens
    • Create or move some custom objects, or manipulate objects that are created through the .map file
    • .. anything else map-specific you can think of

As you can see, the type attribute changed to 'map', telling the Map manager that this resource is a map, while the gamemodes attribute tells it for which gamemodes this map is valid, in this case the gamemode from the above example. What may come as a surprise is that there is also a script in the Map resource. Of course this is not necessarily needed in a map, but opens a wide range of possibilities for map makers to create their own world within the rules of the gamemode they create it for.

The airport.map file might look similiar to this:

<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>

When a gamemode is started with a map, the map resources is automatically started by the mapmanager and the information it contains can be read by the gamemode resource. When the map changes, the current map resource is stopped and the next map resource is started. For a more in-depth explanation and examples of how map resources are utilized in the main script, please visit the Writing Gamemodes page.

Events

Events are the way MTA tells scripts about things that happen. For example when a player dies, the onPlayerWasted event is triggered. In order to perform any actions when a player dies, you have to prepare yourself similiar to adding a command handler, as shown in the first chapter.

This example will output a message with the name of the player who died:

function playerDied(totalAmmo, killer, killerWeapon, bodypart)
	outputChatBox(getPlayerName(source).." died!")
end
addEventHandler("onPlayerWasted",getRootElement(),playerDied)

Instead of showing what arguments are needed, the documentation page for Events shows what parameters are passed to the handler function, similiar to the way a command handler does, just that it is different from event to event. Another important point is the source variable, that exists in handler functions. It doesn't have to be added to the parameter list of the function, but it still exists. It has a different value from event to event, for player events (as in the example above) it is the player element. As another example, you can take a look at the basic spawning player script in the first section to get an idea how source is used.

Where to go from here

You should now be familiar with the most basic aspects of MTA scripting and also a bit with the documentation. The Main Page provides you with links to more information, Tutorials and References that allow a deeper look into the topics you desire to learn about.

[[{{{image}}}|link=|]] Note: From here we recommend reading the debugging tutorial. Good debugging skills are an absolute necessity when you are making scripts. We also recommend you to use the predefined variables list to help you with certain tasks and make scripting easier and faster.

See also: