IT/Introduzione allo scripting

From Multi Theft Auto: Wiki

Le Resources (Risorse) sono la parte principale di MTA. Essenzialmente, una risorsa è una cartella o un archivio zip che contiene una serie di files ed un file meta che contiene informazioni su come il server deve gestire la risorsa e cosa contengono i suoi componenti. Una risorsa può essere vista come uno dei programmi che girano su un sistema operativo: può essere avviata e fermata, e più risorse possono essere eseguite contemporaneamente.

Tutto ciò che ha a che fare con lo scripting avviene nelle risorse, ciò che una risorsa fà dipende da se si tratta di una gamemode, una mappa o qualsiasi altra cosa.

Creare un semplice script

Innanzitutto, mettiamo caso volessimo creare uno script per aggiungere un comando 'createvehicle' che i giocatori possano usare per far spawnare un veicolo alla loro attuale posizione.

Preparazione

Come detto sopra, una risorsa è una cartella o un archivio zip, quindi prima di tutto dovremo creare una cartella. Il nome della cartella è il nome della risorsa, che viene usato per avviarla o fermarla o come riferimento negli script. Nel nostro esempio la chiameremo commands.

Ciò di cui qualsiasi risorsa ha bisogno è il file meta.xml. Nel nostro caso, vogliamo creare uno script che fornisca determinati comandi ad un giocatore, dobbiamo quindi dire al server di caricare il nostro script, che chiameremo script.lua.

Ecco il contenuto del nostro file meta.xml:

<meta>
     <info author="TuoNome" description="Alcuni semplici comandi" />
     <script src="script.lua" />
</meta>

Creiamo ora il file che conterrà il nostro script: script.lua, nella stessa cartella di meta.xml. Adesso abbiamo i seguenti files:

/commands/meta.xml
/commands/script.lua

Scrivere uno script

Iniziamo a scrivere il contenuto del file script.lua. Come sopra, vogliamo creare un comando per spawnare un veicolo nella posizione di chi esegue il comando stesso. Prima di tutto dobbiamo creare una funzione che possa essere eseguita all'esecuzione dello script, ed un command handler che dica al gioco di eseguire la nostra funzione quando il nostro comando viene chiamato.

-- Creiamo la funzione per spawnare un nuovo veicolo, con gli argomenti "thePlayer", "command" e "vehicle"
function createVehicleForPlayer(thePlayer, command, vehicle)
   -- Qui metteremo le funzioni per creare il veicolo, ottenere la posizione del giocatore eccetera
end

-- Creiamo quindi il "command handler"
addCommandHandler("createvehicle", createVehicleForPlayer)

Nota: Negli esempi di script i nomi delle funzioni sono dei link che portano alla relativa pagina per informazioni sulla funzione stessa.

A proposito dei command handlers

Il primo argomento della funzione addCommandHandler è il nome del comando che il giocatore dovrà eseguire, il secondo argomento è invece la funzione che il comando attiverà, nell'esempio sopra createVehicleForPlayer.

Se hai già un'esperienza di base nello scripting o nella programmazione, saprai già che una funzione si chiama nel seguente modo:

nomeFunzione(argomento1, argomento2, argomento3, ...)

Abbiamo infatti chiamato la funzione addCommandHandler in questo modo, e dato che anche createVehicleForPlayer è una funzione, può essere anche lei chiamata in tale modo. Ma stiamo invece utilizzando un command handler, che la chiama in modo simile internamente ad MTA.

Ad esempio: se qualcuno durante il gioco scrivesse "createvehicle 468" nella console per spawnare una Sanchez, il command handler chiamerebbe la funzione createVehicleForPlayer, come se avessimo avuto questa riga di codice nello script:

createVehicleForPlayer(thePlayer,"createvehicle","468") -- thePlayer è l'identificatore del giocatore che ha chiamato la funzione

Come possiamo vedere, contiene diversi argomenti: il giocatore che ha chiamato il comando, il comando che ha chiamato e tutto ciò che ha scritto dopo di esso, in questo caso "468" come ID per il veicolo da spawnare, una Sanchez. I primi due parametri sono gli stessi in tutti i command handlers, come puoi vedere nella pagina addEventHandler. Per questo, devi per forza definire almeno questi due parametri prima che MTA possa riconoscerne altri aggiuntivi(ad esempio, in questo caso, l'ID del veicolo).

Nota: Puoi creare un command handler solo DOPO aver creato la funzione che l'handler chiamerà, altrimenti MTA non troverà la funzione e non potrà quindi eseguirla.

Programmare la funzione

Prima di creare la funzione che utilizzeremo per il comando, dobbiamo pensare a cosa abbiamo in mente di fare:

  • Memorizzare la posizione del giocatore, in modo da sapere dove far spawnare il veicolo (vogliamo che gli appaia vicino);
  • Calcolare la posizione in cui far spawnare il veicolo (non vogliamo che appaia addosso al giocatore);
  • Spawnare il veicolo;
  • Controllare se l'operazione ha avuto successo, altrimenti inviare un messaggio di errore.

Per raggiungere i nostri obiettivi dovremo usare molte funzioni. Per capire quali funzioni usare, andiamo alla Lista funzioni Server-side. Innanzitutto ci serve una funzione per ottenere la posizione del giocatore. Dato che i giocatori sono conteggiati come Elements(Elementi), andiamo alla sezione Funzioni sugli elementi dove troveremo la funzione getElementPosition. Cliccando il nome della funzione nella lista raggiungeremo la pagina sui dettagli, dove potremo vedere la sintassi, un esempio di uso e il valore che la funzione ritorna. La sintassi ci mostra quali parametri possiamo o dobbiamo inserire nella funzione.

Per la funzione getElementPosition, la sintassi è:

float, float, float getElementPosition ( element theElement )

I tre float prima del nome della funzione sono i tipi di valore che essa ritorna, in questo caso tre numeri decimali. Dentro le parentesi possiamo invece vedere gli elementi che la funzione accetta, in questo caso solo l'elemento del quale vogliamo conoscere la posizione.

function createVehicleForPlayer(thePlayer, command, vehicleModel)
	-- Ottiene le coordinate del giocatore e le memorizza nelle variabili x,y,z.
	-- (local indica che le variabili esisteranno solo all'interno dello ''scope'' corrente, in questo caso, la funzione)
	local x,y,z = getElementPosition(thePlayer)
end

Ora, vogliamo assicurarci che il veicolo non compaia direttamente sopra o nel giocatore, aggiungeremo quindi qualche unità alla variabile x, in modo da farlo comparire un pò più ad est del giocatore.

function createVehicleForPlayer(thePlayer, command, vehicleModel)
	local x,y,z = getElementPosition(thePlayer) -- otteniamo la posizione del giocatore
	x = x + 5 -- aggiungiamo 5 unità alla variabile x
end

Ora abbiamo bisogno di un'altra funzione, che ci permetta di spawnare il veicolo. Cerchiamola nuovamente nella Lista funzioni Server-side, questa volta tra le Funzioni per veicoli, dove sceglieremo createVehicle. Nella sintassi di questa funzione abbiamo solo un valore di ritorno (più comune rispetto alle funzioni che ritornano più di un valore), cioé un elemento veicolo che indica il veicolo che abbiamo appena creato con la funzione stessa. Inoltre, vediamo che alcuni elementi si trovano tra parentesi quadre, ad indicare che essi sono opzionali.

Abbiamo già tutti gli elementi che ci servono per chiamare createVehicle nella nostra funzione: le coordinate che abbiamo memorizzato nelle variabili x,y,z e l'ID del veicolo che vogliamo chiamare, ottenuto tramite il comando ("createvehicle 468") ed a cui possiamo accedere tramite la variabile locale vehicleModel

function createVehicleForPlayer(thePlayer, command, vehicleModel)
	local x,y,z = getElementPosition(thePlayer) -- otteniamo la posizione del giocatore
	x = x + 5 -- aggiungiamo 5 unità alla variabile x
	-- creiamo il veicolo e lo memorizziamo nella variabile ''createdVehicle''
	local createdVehicle = createVehicle(tonumber(vehicleModel),x,y,z)
end

Ovviamente questo codice otrebbe essere migliorato in molti modi, ma per il momento ci limiteremo ad aggiungere un controllo per verificare che il veicolo sia stato creato correttamente. Come possiamo leggere nella pagina createVehicle sotto Valori di ritorno, la funzione restituisce false quando non riesce a creare il veicolo. Controlleremo quindi il valore della variabile createVehicle.

Ecco il nostro script completo:

function createVehicleForPlayer(thePlayer, command, vehicleModel)
	local x,y,z = getElementPosition(thePlayer) -- otteniamo la posizione del giocatore
	x = x + 5 -- aggiungiamo 5 unità alla variabile x
	local createdVehicle = createVehicle(tonumber(vehicleModel),x,y,z)
	-- controlliamo se la funzione ha restituito valore "false"
	if (createdVehicle == false) then
		-- se si, inviamo un messaggio nella chat, ma solo al giocatore che ha chiamato il comando.
		outputChatBox("Fallimento nella creazione del veicolo.",thePlayer)
	end
end
addCommandHandler("createvehicle", createVehicleForPlayer)

Come possiamo vedere, abbiamo inserito una nuova funzione: outputChatBox. Adesso dovresti essere in grado di navigare tra le funzioni e documentarti su di esse da te.

Cosa c'è da sapere

Abbiamo già letto qualcosa sulle resources, sui command handlers e sulle funzioni nel primo paragrafo, ma c'è ancora molto da imparare. Questa sezione ti darà qualche delucidazione su alcuni altri elementi del LUA, e dove possibile ti verranno linkate le pagine sugli argomenti in questione.

Scripts client-side e scripts server-side

Avrai probabilmente già sentito i termini Server e Client da qualche parte in questa wiki, soprattutto parlando delle funzioni. MTA non supporta solo funzioni o comandi (come quello visto sopra) che funzionano su server, ma anche scripts che vengono eseguiti dal client di MTA. Il motivo di ciò è che alcune funzioni che MTA fornisce (come l'interfaccia grafica, la GUI - Graphical User Interface) possono essere eseguite solo dal client.

La maggior parte degli scripts che creerai (gamemodes, e mappe) saranno probabilmente server-side, come quello che abbiamo creato nella sezione precedente. Se però ti imbatterai in qualcosa che non funzionerà sul server, dovrai ricorrere agli script client-side. Per creare uno script client-side devi creare un nuovo file .lua (ad esempio client.lua), come per quelli server-side e specificarlo nel meta.xml, in questo modo:

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

L'attributo type di default è impostato sempre a server, quindi avrai bisogno di specificarlo solo per gli script client-side. Quando fai ciò, lo script viene scaricato nel PC del client quando esso si connette al server. Per saperne di più vedi la Lista funzioni Client-side.

Script più complessi

La sezione precedente spiegava in breve come aggiungere script client-side alla Resource, ma si può fare molto di più. Come è spiegato a inizio pagina, con le resource si può fare praticamente tutto. Il loro scopo dipende semplicemente da ciò che fanno. Prendiamo alcune resource d'esempio, esaminando i file che contengono, il meta.xml e cosa dovrebbero fare:

Primo esempio - Un utility script

/comandi_admin
	/meta.xml
	/comandi.lua
	/client.lua
<meta>
	<info author="Qualcuno" description="Comandi admin" />
	<script src="comandi.lua" />
	<script src="client.lua" type="client" />
</meta>
  • comandi.lua contiene dei comandi che un admin potrà usare nel suo server per bannare i players, mutarli, o qualsiasi altra cosa
  • client.lua contiene una finestra GUI per rendere tutto più gradevole alla vista e, soprattutto, agevole

Questo script di esempio potrebbe restare sempre attivo (magari anche auto-avviato allo startup del server) perchè è utile durante ogni fase di gioco e non interferisce con la gamemode, a meno che un admin decida di togliere alcune azioni, ovviamente.

Secondo esempio - Una gamemode

/counterstrike
	/meta.xml
	/counterstrike.lua
	/buymenu.lua
<meta>
	<info author="Qualcuno" description="Counterstrike remake" type="gamemode" />
	<script src="counterstrike.lua" />
	<script src="buymenu.lua" type="client" />
</meta>
  • counterstrike.lua contiene features simili a queste:
    • Far scegliere ai players il loro team e spawnarli
    • Dargli delle armi, e assegnarli degli obiettivi, magari con una mappa (vedi sotto)
    • Definire le regole del gioco, per esempio quando finisce un round o cosa succede quando un player muore
    • .. e magari qualcos'altro
  • buymenu.lua è uno script client-side e crea un menu GUI per comprare le armi

Questo esempio può essere chiamato gamemode perchè non solo interferisce con il gioco, ma ne detta anche le regole. L'attributo type indica che questo esempio funzionerà con il Map manager, un'altra resource ancora creata dal QA team per gestire le gamemode e le mappe a loro associate. È fortemente raccomandato basare la gamemode sulle tecniche che il Map Manager fornisce.

Ciò significa anche che la gamemode probabilmente non girerà senza una mappa. Le gamemode, infatti, dovrebbero essere più generiche possibile. Un esempio per una mappa si può trovare qui sotto.

Terzo esempio - Una mappa

/cs-airport
	/meta.xml
	/airport.map
	/airport.lua
<meta>
	<info author="Qualcuno" description="Counterstrike airport map" type="map" gamemodes="counterstrike" />
	<map src="airport.map" />
	<script src="airport.lua" />
</meta>
  • airport.map è un file XML che fornisce alla mode informazioni sulla mappa, come:
    • I punti di spawn dei players, le loro armi, i team
    • Gli obiettivi
    • Il tempo atmosferico, l'ora del giorno, il limite di tempo
    • I veicoli e gli oggetti
  • airport.lua potrebbe contenere alcune feature specifiche di una mappa:
    • Aprire una porta/fare qualcosa di speciale quando accade un evento particolare
    • Creare e muovere oggetti personalizzati, o manipolare gli oggetti creati con il file .map
    • .. qualunque altra cosa specifica di una mappa

Come potete vedere, l'attributo type è diventato 'map', per avvertire il Map manager che questa resource è una mappa, mentre l'attributo gamemodes imposta le gamemode compatibili con questa mappa, in questo caso la gamemode dell'esempio precedente. Ciò che potrebbe sembrare strano è uno script LUA in una mappa. Ovviamente non sempre serve, ma offre a chi crea una mappa la possibilità di creare un vero e proprio mondo con le regole imposte dalla gamemode.

Il file airport.map potrebbe somigliare a questo:

<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 una gamemode con una mappa compatibile è avviata, la resource della mappa viene automaticamente avviata dal mapmanager, che permette alla gamemode di leggere le informazioni che contiene. Quando la mappa cambia, la resource della mappa precedente viene fermata automaticamente e viene avviata quella successiva.

Eventi

Gli eventi sono il mezzo con cui MTA comunica agli script ciò che sta accadendo nel gioco. Per esempio, quando un player muore, viene chiamato l'evento onPlayerWasted. Per eseguire qualsiasi azione quando un player muore, devi imparare a creare degli event handlers, com'è spiegato nel primo capitolo.

Questo esempio scriverà nella chatbox il nome del player che è morto:

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

Al posto di mostrare quali argomenti servono, la pagina di documentazione per gli eventi mostra quali parametri sono passati alla funzione a cui un evento è legato (come playerDied nell'esempio di prima), come fa un command handler, solo che è diverso da evento a evento. Un altro punto importante è la variabile source, che si trova nelle funzioni chiamate da un event handler. Non ha bisogno di essere aggiunto nella lista di parametri della funzione, ma esiste lo stesso. Ha valori differenti da evento a evento, per gli eventi che riguardano un player (come nell'esempio di sopra) di solito è l'elemento player.

Cosa fare dopo

Ora dovresti essere pratico con le basi dello scripting per MTA e anche un po' con la documentazione. La Pagina principale ti fornisce link a informazioni, tutorial e riferimenti che permettono di scoprire più a fondo gli argomenti che vuoi imparare.