IT/Introduzione allo scripting
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 fannp. 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.
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.
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(getClientName(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.
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.