Apprendre le Pawn avec C far
From SA-MP Wiki
Le langage Pawn : Débutant
Introduction
Le Pawn a été développé par Thiadmer Riemersma de l'entreprise individuelle ITB Compuphase. C'est en 1998, le 18 Août, que le Pawn a été partagé au public pour la première fois.
Au fil des années plusieurs versions sont sorties. Lors du début du développement de SAMP, Kalcor et ses collègues ont décidé de choisir le Pawn pour communiquer avec le serveur. A cette époque, la version du Pawn était la 3.0.3367. En ce qui concerne l'Abstract Machine (le coeur du Pawn), rien n'a été modifié, seul le compilateur a subit quelques modifications de dernière minute.
Le langage Pawn en lui-même est un C-like 32 bits (64 bits pour l'Abstract Machine). Il a quelques différences avec son cousin le C. Il n'y a pas de d'histoire de pointeurs, pas de structures ni de typedef, bref il y manque quelques éléments, mais c'est tout de même un langage robuste quand on le maîtrise.
Ce tutoriel est là pour vous former à ce langage petit à petit, si vous ne comprenez pas une partie, n'hésitez pas à la relire, tester et modifier les exemples qui seront donnés. Il ne vous expliquera pas l'étape sur l'utilisation d'un IDE ou de la compilation.
Un commencement à tout
Les impératifs
Nous avons deux principales choses impératives pour que la totalité de votre code fonctionne correctement. SA:MP est un mode permettant de communiquer entre le serveur et le client de chaque joueur. Lorsque le client envoie des informations, elles sont traitées par le serveur, en commençant par la partie architecturale en langage C++, qui appellera la fonction publique liée à l'action du joueur. Il existe énormément de fonctions publiques, elles portent toutes un nom différent en anglais, mais sont facilement compréhensibles et vous saurez dans quel cas elles seront appelées.
La fonction principale et impérative de votre code s'appelle main. Elle n'est appelée qu'une fois, en Pawn c'est la première fonction appelée quand votre programme est exécuté. Enfin la première...sur SA:MP, pas vraiment, mais vous comprendrez pourquoi, dites-vous tout simplement qu'elle est indispensable. Une fonction se reconnait grâce à ses parenthèses qui suivent son nom ainsi que ses accolades sur les lignes suivantes.
Avant de vous montrer le premier code, il faut que vous sachiez que SA:MP intègre énormément de fichiers pour que votre code compile si vous utilisez des fonctions propres au mode, c'est pourquoi nous utiliserons la directive de pré-processeur (ne retenez pas ce nom pour le moment) #include. Elle permet d'indiquer à la suite un nom de fichier dont nous souhaitons inclure son contenu. Dans notre cas, nous inclurons le fichier a_samp.
Voici donc le premier code :
#include <a_samp> main() { }
Et voilà, la base de tout code lorsque vous développerez quelque chose lié au mode SA:MP.
Hello world!
Cette étape est importante, car elle sera à comprendre par cœur et qu'on se servira de ce qui suivra, durant tout le tutoriel. Sachez que peut importe le langage de programmation que vous apprendrez, cette phrase "Hello world!" est universelle pour apprendre à afficher un texte, nous l'utiliserons donc sans exception. Si vous avez gardé le code d'avant, vous avez bien fait. Nous allons donc afficher "Hello world!" dans notre console, pour ça il faudra utiliser la fonction print.
#include <a_samp> main() { print("Hello world!"); }
Essayez ce code, vous aurez dans votre console, la phrase "Hello world!". Cette ligne ajoutée porte un nom, retenez-le bien, on appelle ça une instruction et une instruction finie toujours par un point-virgule ';'. (En Pawn il y a une méthode pour ne pas obliger les parenthèses et les points-virgules, mais habituez-vous à cette syntaxe, car beaucoup de langages de programmation l'utilisent).
Un print formaté
Il y a une seconde fonction qu'il faut que vous connaissiez et ce sera celle que j'utiliserai le plus pour les exemples, elle se nomme printf, oui print avec un 'f'. Le 'f' désigne "format", car on peut y formater du texte. Pour que vous compreniez mieux, le texte que j'ai rentré dans la fonction print, en prenant en compte les guillemets, s'appelle un argument. Une fonction peut contenir 1 argument, comme elle peut en contenir 2, 3, 4, 5... Pour ça, il faut connaître sa structure. print est fonction n'ayant d'un seul paramètre, on ne peut donc y passer qu'un argument. Son argument a un type, mais ça vous comprendrez plus tard.
Je vous parlais donc de printf, c'est une fonction similaire à print, mais vous pouvez entrer autant d'arguments que vous voulez entre ses parenthèses. Ces arguments seront formatés dans le premier argument qui sera notre texte qui sera affiché, mais il faudra indiquer quel type d'argument nous mettons dans notre fonction, pour ça nous utiliserons des spécificateurs : %d : un nombre ('d' pour décimal) %i : un nombre ('i' pour integer, qui veut dire en anglais "entier") %f : un nombre rationnel ('f' pour float) %s : une chaîne de caractères ('s' pour string, qui veut dire en anglais "chaîne")
Il y en a d'autres, mais retenez bien ces principaux. Là je vous vois venir en me disant "%d et %i c'est la même chose ?", oui ils sont identiques, vous pouvez utiliser un des deux. Personnellement, j'ai tendance à utiliser %d, donc ne soyez pas timide, si vous voulez favoriser %i plutôt que %d.
A l'application, si nous voulons afficher un nombre en argument, par exemple "Bonjour j'ai 19 ans", le nombre 19, nous le passerons en argument et nous utiliserons un spécificateur dans notre premier argument pour signaler que nous voulons le remplacer par l'argument qui le suit :
#include <a_samp> main() { printf("Bonjour j'ai %d ans"; 19); }
Testez ce code et vous verrez, que ça vous affichera "Bonjour j'ai 19 ans". Vous allez me dire c'est débile et qu'on aurait pu directement écrire 19 dans les guillemets, vous avez raison, mais le but est de comprendre comment fonctionne les spécificateurs avec printf.
Maintenant que se passe-t'il si nous voulons mettre 2 nombres dans la phrase ? Et bien, on passe en argument les 2 nombres et dans le texte, on utilise deux fois le spécificateurs %d/%i où nous voulons placer nos nombres :
#include <a_samp> main() { printf("%d + %d = 5"; 2, 3); }
Et autant d'arguments que vous voulez (y a une limite, mais bon j'préfère pas vous en parlez maintenant).
A l'assaut des variables
Qu'est-ce qu'une variable ?
En Pawn on appelle ça un symbole portant un nom dans lequel nous stockons une valeur. Une variable est impérative à tout programme informatique, sur SAMP, il n'y a pas d'exception, vous en rencontrez tout le temps durant votre apprentissage et il est important que vous compreniez son but, même si au départ, ce ne sera pas facile (excepter à ceux qui ont déjà programmé).
Quand je vous dis que l'on peut y stocker des valeurs, il y a quelque chose à retenir et d'important. Contrairement à un humain, il est capable d'identifier n'importe quel nombre, malheureusement, un langage de programmation a ses limites. En Pawn la limite est 32 bits.
Une variable 32 bits ?
32 bits ? Vous devez vous dire que je vous parle chinois à ce moment. Pour que vous compreniez au maximum, je vais faire une parenthèse sur les bits.
Un bit c'est ça : 1
Deux bits c'est ça : 11
La notation mathématique de 32 bits s'écrit 2^31 (2 puissance 31), sauf qu'en Pawn, nous devons compter le 0 comme un nombre positif, donc 2^31-1.
Donc, 32 bits c'est ça : 1111111111111111111111111111111
Si vous avez compté le nombre de '1', il y en a 31. Jusque là, vous devez vous dire "pourquoi il nous explique ça ?" et bien c'est parce qu'un bit peut avoir 2 valeurs, 0 ou 1 et que si on aligne chaque bit, on obtient une notation binaire, appelée aussi notation base de 2.
Si nous convertissons notre valeur binaire 1111111111111111111111111111111, en nombre entier/décimal, nous obtiendrons 2 147 483 647, c'est la valeur maximale que peut stocker une variable en Pawn. Cette valeur porte un nom, on l'appelle cellmax.
Evidemment puisqu'il y a un cellmax, vous vous doutez bien qu'il y a surement un cellmin ? Et bien c'est le cas, mais pour ne pas vous perturber avec cette histoire de bits et de notation binaire plus longtemps, cellmin vaut -2 147 483 648.
Déclarer une variable
Nous allons maintenant apprendre à déclarer une variable, classique et sans type. (n'ayez pas peur avec tous ces mots héhé). Pour ça nous aurons besoin du mot-clé new, il sera suivi du nom de votre variable et se finira par un point-virgule.
Mais avant de vous montrer en application ce que je viens de vous dire, il faut savoir différencier la portée des variables. Une variable globale sera déclarée en dehors d'une fonction (donc pas dans main par exemple), tandis qu'une variable locale sera déclarée dans une fonction. La différence, c'est qu'une variable globale peut être accédée depuis n'importe quelle fonction, une variable locale ne sera utilisable que dans son "block".
Qu'est-ce qu'un block ? Un block est définie quand vous ouvrez des accolades à la suite de certains mots-clés spécifiques.
Déclarons donc une variable globale et une variable locale :
#include <a_samp> new variable_globale; main() { new variable_locale; }
Si vous compilez ce code, vous rencontrerez 2 warnings, c'est normal, car nous n'utilisons pas nos variables, mais elles sont bien là ! Vous pouvez déclarer vos variables de plusieurs manières :
new a; new b; new c; new a, b, c; new a, b, c;
J'utiliserai personnellement la troisième méthode dans tout le tutoriel et je vous invite à faire de même, question d'organisation.
Attribuer une valeur à une variable
L'attribution d'une valeur à une variable peut se faire dans n'importe quel cas et c'est très facile. Pour ne pas s'éterniser dans cette partie, je vous propose de passer à la pratique :
#include <a_samp> main() { new variable = 5; variable = 10; }
Ici variable vaudra 5 lorsque nous la déclarons, puis le code continu d'être exécuté et on rencontre une nouvelle attribution, variable vaut 10.
Nom d'une variable
Ce qu'il faut savoir, c'est que le nom d'une variable doit obligatoirement commencer par une lettre, minuscule ou majuscule ou un underscore "_" (tiret du 8). Vous ne pouvez pas non plus déclarer deux fois une variable avec le même nom, mais celui-ci n'est pas sensible à la casse, cela signifie que "variable", "Variable" et "VARIABLE" sont trois noms différents.
new
variable,
Variable,
VARIABLE;
Taille d'une variable
Lorsque vous déclarez une variable, ce qu'il faut savoir c'est que vous allouez de la mémoire. Pour chaque variable déclarée, 4 octets (bytes en anglais, ne pas confondre avec les bits) sont alloués peut importe le type de la variable, c'est pourquoi il est important de limiter le nombre de variables que vous utilisez dans certains cas. Par exemple ici :
new
a,
b,
c,
d,
e;
J'ai déclaré 5 variables, donc j'alloue 20 octets (5 * 4).
Remarque : 4 octects s'appelle un cell.
Type d'une variable
Il existe plusieurs de types de variables :
Les entiers/décimaux :
Ils peuvent contenir une valeur de 32 bits.
new variable = 21475;
Les caractères :
Ils peuvent contenir un caractère. Cela fonctionne comme un entier, mais vous devez mettre le caractère entre des apostrophes. Voici un exemple :
new variable = 'g';
Les booléens :
Ils peuvent contenir seulement 2 valeurs, true ou false, 1 ou 0. Il faut par contre l'indiquer à la déclaration de la variable à l'aide du tag bool: :
new bool:variable = true;
Les floats/nombres rationnels :
Ils peuvent contenir un nombre rationnels, il faut l'indiquer à la déclaration de la variable à la du tag Float: :
new Float:variable = 5.5;
Une histoire d'opérateurs
Qu'est-ce qu'un opérateur ?
Il existe en Pawn, 4 types d'opérateurs :
Les opérateurs de comparaison sont utilisés uniquement pour comparer deux valeurs, la comparaison retourne une valeur booléenne (true/false).
Les opérateurs logiques permettent la multi-comparaisons.
Les opérateurs mathématiques permettent de faire des calculs basiques.
Les opérateurs de gestions de bits permettent... la gestion de bits (ouaw !) et aussi la comparaison.
Nous apprendrons dans cette partie, seulement les trois premiers types, car comprendre le fonctionnement des bits relève d'un autre niveau auquel vous n'êtes pas encore prêt.
Les opérateurs de comparaison
Opérateur de comparaison » '==':
Cet opérateur permet de savoir si les valeurs comparées sont identiques, je parle bien évidemment d'un entier ou d'un nombre à virgule. Si les valeurs sont identiques cela retournera true, dans le cas contraire false.
#include <a_samp> main() { printf("%d", 18 == 18); // Si 18 est égal à 18 }
Retournera 1. (true)
#include <a_samp> main() { printf("%d", 18 == 16); // Si 18 est égal à 16 }
Retournera 0. (false)
Opérateur de comparaison » '!=':
Cet opérateur est tout bêtement l'inverse de '=='.
#include <a_samp> main() { printf("%d", 71 != 71); // Si 71 est différent de 71 }
Retournera 0. (false)
#include <a_samp> main() { printf("%d", 71 != 54); // Si 71 est différent de 54 }
Retournera 1. (true)
Opérateur de comparaison » '<' et '>':
Si vous avez un niveau moyen en mathématique et vous avez compris comment fonctionne un opérateur de comparaison, alors vous aurez aussi compris que ces opérateurs signifient "supérieur à" et "inférieur à".
#include <a_samp> main() { printf("%d", 21 > 15); // Si 21 est supérieur à 15 }
Retournera 1. (true)
#include <a_samp> main() { printf("%d", 21 < 15); // Si 21 est inférieur à 15 }
Retournera 0. (false)
Opérateur de comparaison » '<=' et '>=':
Pareil que ce qu'on vient de voir juste avant mais on traduirait ça par "supérieur ou égal à" et "inférieur ou égal à".
#include <a_samp> main() { printf("%d", 48 >= 15); // Si 48 est supérieur ou égal à 15 }
Retournera 1. (true)
main() { printf("%d", 21 <= 15); // Si 21 est inférieur ou égal à 15 }
Retournera 0. (false)
Les opérateurs logiques
Opérateur logique » '&&':
Imaginons que je souhaite faire 2 comparaisons et demander si ces deux comparaisons retournent true. Cet opérateur ne compare pas réellement 2 valeurs mais permet la multi-comparaisons. Je m'explique :
#include <a_samp> main() { printf("%d", 5 == 5 5 == 7); }
On pourrait penser que cela fonctionnerait et que les deux comparaisons sont exactes, mais non. Il nous faut un opérateur logique pour que nos comparaisons soient correctes.
#include <a_samp> main() { printf("%d", 5 == 5 && 5 == 7); // Si 5 est égal à 5 ET 5 est égal à 7 }
Retournera 0. (false)
Une règle à savoir que vous devez retenir pour cet opérateur logique :
1 && 1 = 1 1 && 0 = 0 0 && 1 = 0 0 && 0 = 0
Cela signifie donc que dans le cas où vous aurez 2 comparaisons et que vous utiliserez l'opérateur logique "ET", ces comparaisons devront retourner true pour que votre condition soit correcte.
Opérateur logique » '||':
Similaire à notre opérateur '&&', sauf qu'il signifie "OU" dans pour une multi-comparaisons. Reprenons le code juste avant et mettons-y notre opérateur logique "OU".
#include <a_samp> main() { printf("%d", 5 == 5 || 5 == 7); // Si 5 est égal à 5 OU 5 est égal à 7 }
Retournera 1. (true)
La règle pour cet opérateur logique est la suivante :
1 || 1 = 1 1 || 0 = 1 0 || 1 = 1 0 || 0 = 0
Cela signifie qu'il faut au minimum qu'une des deux comparaisons retourne true pour que notre condition soit correcte.
Les opérateurs mathématiques
La suite concerne les opérateurs mathématiques, je pars du principe que vous saurez les reconnaître et les comprendre sans difficulté.
Opérateurs mathématiques » '+', '-', '*' '/', '%':
#include <a_samp> main() { printf("%d", 5 + 2); // Résultat : 7 printf("%d", 5 - 2); // Résultat : 3 printf("%d", 5 * 2); // Résultat : 10 printf("%d", 5 / 2); // Résultat : 2 printf("%d", 5 % 2); // Résultat : 1 }
Logiquement, vous les avez tous compris, sauf un !
Le modulo, ce fameux '%' et bien si vous vous rappelez des divisions euclidiennes, le modulo permet d'obtenir le reste d'une division.
Opérateurs mathématiques » '+=', '-=', '*=' '/=', '%=':
Lorsque vous souhaitez ajouter une valeur à une variable vous feriez comme ceci logiquement :
a = a + 5;
Et bien en Pawn, vous pouvez utiliser le raccourci '+=' :
a += 5;
Les codes sont similaires.
Les symboles valides :
+= -= *= /= %=
Opérateurs mathématiques » '++', '--':
Leurs utilisations portent des noms, mais que font ces opérateurs ?
L'opérateur '++' permet d'incrémenter, c'est à dire d'augmenter la valeur d'une variable de 1.
A la place de faire ceci :
a += 1;
Vous pouvez faire ceci :
a++;
On appelle ça donc l'incrémentation
Et si vous avez un minimum de logique, vous aurez compris que l'opérateur '--' fait le contraire, on l'appelle la décrémentation (et pas désincrémentation !!).
a--;
Un exemple d'utilisation :
#include <a_samp> main() { new a = 5; printf("%d", a++); printf("%d", a--); }
Maintenant, pour rentrer un peu plus dans le sujet, les exemples que je vous ai donnés sont en réalité une post-incrémentation et une post-décrémentation.
Pourquoi ? Car vous chargez la valeur de la variable, on utilise la valeur, puis on l'incrémente.
Si il y a donc une post-incrémentation, il y a donc une pré-incrémentation, hé oui ! Mais en Pawn comment pourrait-on écrire ça ?
Et bien comme ça :
++a; --a;
Ici, vous chargez la valeur de la variable, on incrémente cette valeur puis on l'utilise.
Dans quel genre de cas est-ce utile ? Lors d'une comparaison ou pour indiquer la case d'un tableau (nous verrons les tableaux plus tard).
Dans notre exemple avec mes printf, les valeurs seront chargées/utilisées puis incrémentées.
A condition que...
Dans cette partie, nous attaquons maintenant les conditions. Vous en utiliserez énormément, donc soyez attentifs !
En programmation, les conditions permettent d'exécuter du code seulement dans certains cas, cela demande donc une acquisition des opérateurs de comparaisons et des opérateurs logiques.
Mots-clés » if, else if et else:
Mot-clé » if:
Vous avez certainement déjà vu en mathématique le principe d'une condition par la théorie écrite, mais sans vous en rendre compte. Mais avant, je vais vous expliquer comment fonctionne ce mot-clé, la suite sera plus simple à comprendre.
La structure de if :
if(/* condition */) { // Nos instructions }
Tout simplement, si notre comparaison à l'intérieur des parenthèses suivies de notre if est correcte/vraie alors le code entre les accolades sera exécuté. (Le code entre les accolades suivi d'un mot-clé, s'appelle un bloc)
Un exemple :
#include <a_samp> main() { new variable = 5; if(variable == 5) { // Le code entre les accolades sera exécuté car la condition est correcte } if(variable == 4) { // Le code entre les accolades ne sera jamais exécuté car la condition est incorrecte } }
Vous devez surement avoir compris, donc on revient à notre théorie mathématique : SI la variable vaut ça, ALORS fais ceci. Petit mémo : "if" en anglais signifie "si".
Mot-clé » else if:
Contrairement à un anglais correct, on ne peut pas traduire ce mot-clé mot à mot, car il désigne en programmation "sinon si" ("sinon" se traduit par "otherwise"). Alors pourquoi "else" ? Par rapport à l'adverbe "or else" signifiant "ou autrement", donc similaire à la conjonction "sinon".
Voilà pour la petite étymologie de ce mot-clé.
Sa structure est la même que if :
if(/* condition */) { // Nos instructions } else if(/* condition */) { // Nos instructions }
Il permet d'être appelé lorsque la première condition n'est pas correcte, mais en y rajoutant tout de même une condition pour ne pas exécuter le code à l'intérieur du else if par défaut.
Vous pouvez en mettre autant que vous le souhaitez à la suite.
Mot-clé » else:
A l'inverse de if et else if, le mot-clé else n'est pas suivi de parenthèses, car il est utilisé lorsque une condition qui le précède est incorrecte pour exécuter le code à l'intérieur par défaut sans condition(s).
if(/* condition */) { // Nos instructions } else { // Nos instructions }
Mots-clés » switch, case et default:
Concrètement switch, case et default sont tout bêtement une condition, dont la structure est un petit peu différente de if, else if et else.
Mots-clés » switch + case:
Je vous parlais de condition juste avant et c'est bien le cas, switch et case sont une condition à eux deux. Ce sont deux mots-clés qui fonctionnent ensemble obligatoirement. Nous l'utilisons de cette manière :
#include <a_samp> main() { switch() { case: } }
Bien évidemment là, cela ne fonctionnera pas, il faut y mettre des valeurs:
#include <a_samp> main() { new variable = 2; switch(variable) { case 2: print("Coucou !"); } }
Ici dans cet exemple, vous remarquerez que ça nous affiche "Coucou !", donc ça fonctionne ! Mais quelle différence y a-t-il entre (if, else if, else) et switch ? On y vient! Tout simplement, imaginez, vous devez réaliser une action différente en fonction de la valeur d'une variable, vous vous dites "bah j'vais faire plusieurs else if" :
#include <a_samp> main() { new variable = 3; if(variable == 0) print("Coucou, moi c'est Pierre !"); else if(variable == 1) print("Coucou, moi c'est Jacques !"); else if(variable == 2) print("Coucou, moi c'est Paul !"); else if(variable == 3) print("Coucou, moi c'est Tony !"); else if(variable == 4) print("Coucou, moi c'est Mireille !"); else if(variable == 5) print("Coucou, moi c'est Alex !"); }
Ce code nous enverra "Coucou, moi c'est Tony !". Vous allez me dire "oui ben ça fonctionne, tant mieux non ?" Et c'est là que je vous dis NON! Pourquoi ? Parce que switch et case sont là pour ce genre de cas. Oui car, que c'est-il passé dans notre code juste au-dessus ? Et bien il a fait au total 4 calculs (et à charger 4 fois la même variable) pour comparer la valeur de variable à nos valeurs. switch fonctionne autrement, il chargera la valeur de la variable et ira directement à la case de cette valeur si elle existe. Donc si on reprend le code en utilisant switch et case :
#include <a_samp> main() { new variable = 3; switch(variable) { case 0: print("Coucou, moi c'est Pierre !"); case 1: print("Coucou, moi c'est Jacques !"); case 2: print("Coucou, moi c'est Paul !"); case 3: print("Coucou, moi c'est Tony !"); case 4: print("Coucou, moi c'est Mireille !"); case 5: print("Coucou, moi c'est Alex !"); } }
Le résultat est le même, mais est plus rapide que l'autre code.
Maintenant imaginez que vous souhaitiez exécuter une même instruction pour différentes valeurs, avec if et else if vous feriez comme ça:
#include <a_samp> main() { new variable = 3; if(variable == 0) print("Coucou, moi c'est Pierre !"); else if(variable == 1) print("Coucou, moi c'est Jacques !"); else if(variable == 2) print("Je suis mal poli, j'te dis pas coucou."); else if(variable == 3) print("Je suis mal poli, j'te dis pas coucou."); else if(variable == 4) print("Je suis mal poli, j'te dis pas coucou."); else if(variable == 5) print("Coucou, moi c'est Alex !"); // Ou comme ça : if(variable == 0) print("Coucou, moi c'est Pierre !"); else if(variable == 1) print("Coucou, moi c'est Jacques !"); else if(2 <= variable <= 4) print("Je suis mal poli, j'te dis pas coucou."); else if(variable == 5) print("Coucou, moi c'est Alex !"); }
Et bien avec case vous pouvez aussi "encadrer" vos valeurs pour exécuter le même code et ça en une seule ligne :
#include <a_samp> main() { new variable = 3; switch(variable) { case 0: print("Coucou, moi c'est Pierre !"); case 1: print("Coucou, moi c'est Jacques !"); case 2 .. 4: print("Je suis mal poli, j'te dis pas coucou."); case 5: print("Coucou, moi c'est Alex !"); } }
Ici le code affichera "Je suis mal poli, j'te dis pas coucou.", car la valeur de la variable est de 3 et que notre code s'exécute si la valeur se trouve entre 2 et 4.
Ce qu'il faut savoir, c'est vous ne pouvez comparer qu'une seule variable avec switch, mais il n'en reste pas moins très utile dans certains cas!
Je vais tout de même préciser un dernier truc pour case, car j'ai peur que ça en perturbe plus d'un. Vous l'aurez peut-être deviné, mais vous pouvez exécuter autant d'instructions que vous souhaitez dans votre case, il suffit de mettre des accolades :
#include <a_samp> main() { new variable = 3; switch(variable) { case 2: { print("Ce texte ne sera jamais affiché"); print("Quel dommage :("); } case 3: print("Coucou, moi c'est Tony !"); } }
Mot-clé » default:
Je vous ai montré qu'il était possible de faire des conditions plus rapide que if+else if avec switch+case. Mais que se passe t-il quand switch ne trouve pas la case par rapport à la variable fournie ? Absolument rien, la suite du code sera exécuté comme si rien ne s'était passé. Avec if+elseif+else, vous pouvez indiquer les instructions que vous souhaitez si aucuns résultats ne correspondent à la valeur de la variable de cette manière :
#include <a_samp> main() { new variable = 8; if(variable == 0) print("Coucou, moi c'est Pierre !"); else if(variable == 1) print("Coucou, moi c'est Jacques !"); else if(variable == 2) print("Coucou, moi c'est Paul !"); else if(variable == 3) print("Coucou, moi c'est Tony !"); else if(variable == 4) print("Coucou, moi c'est Mireille !"); else if(variable == 5) print("Coucou, moi c'est Alex !"); else print("Coucou, moi c'est... je connais pas mon prénom :("); }
Ici, ça vous affichera "Coucou, moi c'est... je connais pas mon prénom " car aucunes des conditions n'étaient vraies. Et bien avec switch c'est possible aussi en utilisant le mot-clé default, il sera appelé si aucunes cases ne correspondent à la valeur de la variable. Il s'utilise de cette manière :
#include <a_samp> main() { new variable = 4; switch(variable) { case 0: print("Coucou, moi c'est Pierre !"); case 1: print("Coucou, moi c'est Jacques !"); case 2: print("Coucou, moi c'est Paul !"); default: print("Coucou, moi c'est... je connais pas mon prénom :("); } }
Ici la valeur de variable est 4, mais dans notre switch, il y a pas de case 4, switch appellera alors default. Pareil que case, vous pouvez utiliser des accolades pour y introduire plusieurs instructions.
Conditions ternaires et symboles:
Les conditions ternaires reprennent le même fonctionnement de if, else if et else mais en utilisant deux symboles. Ils permettent d'encadrer une condition sur une seule ligne, ça peut être pratique si vous savez vous relire facilement et si vous voulez quelque chose de compacte. La structure d'une condition comprend par défaut un if et un else désigné par les symboles '?' et ':'.
Un exemple avec une explication vous fera comprendre :
#include <a_samp> main() { new t = 5, a = (t > 6 ? 7 : 4); printf("%d", a); }
Ceci affichera 4, mais pourquoi ?
Je vous traduis cette ligne : (t > 6 ? 7 : 4) :
SI t est supérieur à 6
ALORS (= '?') on insert la valeur 7 dans la variable a
SINON (= ':') on insert la valeur 4 dans la variable a
Si vous n'avez pas compris, je vous laisse modifier le code de l'exemple pour comprendre par vous-même.
Pour poursuivre, il y a un else if en ternaire ? La réponse est oui ! Par contre, au delà de deux, ça devient très bordélique. Il suffit de placer une seconde condition ternaire dans la partie "sinon", donc après le symbole ':'.
#include <a_samp> main() { new t = 5, a = ((t > 6) ? 7 : (t == 5) ? 6 : 4); printf("%d", a); }
Je vous traduis :
SI t est supérieur à 6
ALORS (= '?') on insert la valeur 7 dans la variable a
SINON SI (= ':') t est égal à 5
ALORS (= '?') on insert la valeur 6 dans la variable a
SINON (= ':') on insert la valeur 4 dans la variable a
Là pour vous dire, ça peut paraître bordélique si vous ne savez pas vous organiser, donc n'en abusez pas (du moins pas comme moi aha).
Une boucle d'oreille ?
Qu'est-ce qu'une boucle ? Cela fonctionne sur le même principe que les conditions, hé oui !
Concrètement, une boucle permet de répéter des instructions plusieurs fois. En clair, c'est un gain de temps, c'est très pratique, et bien souvent indispensable.
Les différentes boucles:
Boucle » while:
La structure d'une boucle while ressemble comme "deux gouttes d'eau" à une condition "if".
#include <a_samp> main() { while() { } }
Mais comment l'utiliser ? Comme je vous l'ai dit juste au-dessus, c'est le même principe qu'une condition. A l'intérieur de nos parenthèses, vous y placerez votre condition, si elle retourne vraie, l'instruction sera exécutée.
#include <a_samp> main() { new i = 0; while(i < 10) { print("Coucou !"); } }
Dans cet exemple, notre boucle fonctionne, mais elle renvoie le message "Coucou !" en continu sans s'arrêter.
Si je traduis la condition : Si i est inférieur à 10 alors on exécute les instructions.
Le problème c'est qu'à aucun moment la valeur de i n'est changée et donc notre boucle est dite "infinie".
Pour y remédier, il va falloir fixer un summum de répétitions à notre boucle. Pour ça nous allons incrémenter notre variable i dans notre bloc.
#include <a_samp> main() { new i = 0; while(i < 10) { print("Coucou !"); i++; } }
Miracle! Vous n'avez reçu le message que 10 fois, c'était le but.
Boucle » for:
Voici la structure de for :
#include <a_samp> main() { for(;;) { } }
La boucle for est identique à while, simplement vous avez la possibilité d'y ajouter 3 étapes en une ligne.
Des étapes ? Oulà vous devez être perdus ! Pourtant vous les connaissez !
La première étape consiste à déclarer notre variable.
La seconde étape c'est la condition.
Et la troisième étape c'est l'incrémentation / décrémentation, ou autres calculs. (vous pouvez y mettre ce que vous voulez).
L'avantage de for par rapport à while, c'est de pouvoir y ajouter ces 3 étapes en une seule ligne.
#include <a_samp> main() { for(new i = 0; i < 10; i++) { print("Coucou !"); } }
Si vous avez testé, vous remarquerez que c'est le même résultat du dernier exemple de while.
Boucle » do...while:
Aaaaaaah enfin quelque chose que beaucoup d'entre-vous ne comprennent pas !
Quoi ? Vous connaissez déjà while, ça vous suffit ? Non attendez !
Avant de vous expliquer son utilisation, voici sa structure :
#include <a_samp> main() { do { } while(); }
do...while s'utilise de la même façon que while à une seule différence : l'instruction qui sera exécutée à chaque passage de la boucle se trouvera entre les accolades de do.
#include <a_samp> main() { new i = 0; do { print("Coucou !"); i++; } while(i < 10); }
L'avantage à utiliser do...while c'est que si la condition n'est pas bonne la première fois, les instructions se trouvant dans do seront exécutées au moins une fois.
Vous n'avez pas compris ? Voici un exemple :
#include <a_samp> main() { new i = 10; do { print("Coucou !"); i++; } while(i < 10); }
Ici, on peut constater que la variable i est égale à 10 et que la condition pour exécuter les instructions est fausse.
Et pourtant, si vous avez testé ce code, vous constaterez que "Coucou !" s'est affiché une fois!
Leurs mots-clés d'états:
Mot-clé » continue:
Ce mot-clé s'utilise seulement dans une boucle. Il permet lorsqu'il est appelé, d'arrêter d'exécuter la suite du code qui le suit et de re-passer à la condition de la boucle.
#include <a_samp> main() { for(new i; i < MAX_PLAYERS; i++) { if(!IsPlayerConnected(i)) continue; /* code à exécuter */ } }
Dans cet exemple, on vérifie si i n'est pas connecté, si la condition est bonne alors on appelle continue qui ordonnera de passer au suivant.
L'utilité d'utiliser continue pour moi c'est de ne pas avoir de code comme celui-ci :
#include <a_samp> main() { for(new i; i < MAX_PLAYERS; i++) { if(IsPlayerConnected(i)) { if(GetPlayerMoney(i) >= 500) { GivePlayerMoney(i, 500); } } } }
Imaginez que vous ayez beaucoup plus de conditions ? Ce serait l'anarchie ! C'pourquoi je vous conseille d'utilise continue pour ce genre de cas, car cela donnerait certainement un code un peu plus propre :
for(new i; i < MAX_PLAYERS; i++) { if(!IsPlayerConnected(i)) continue; if(GetPlayerMoney(i) >= 500) continue; GivePlayerMoney(i, 500); }
Mot-clé » break:
Au même titre que continue, break ne s'utilise que dans les boucles. Il permet lorsqu'il est appelé d'arrêté la boucle et d'en sortir.
for(new i = 0; i < 10; i++) { if(i == 3) break; print("Coucou !"); } print("C'est déjà fini ? :o");
Ce code n'affichera que 3 fois "Coucou !" et le message "C'est déjà fini ? ".
Son utilisation peut être utile dans plusieurs cas. Par exemple, nous souhaitons récupérer l'ID du premier joueur qui ne possède même pas 500$ et de lui donner 100$.
for(new i = 0; i < MAX_PLAYERS; i++) { if(!IsPlayerConnected(i)) continue; if(GetPlayerMoney(i) < 500) { GivePlayerMoney(i, 100); break; } }
Pourquoi avoir utilisé break ? Parce que ça permet de ne pas laisser la boucle tourner alors qu'on a eu l'id que l'on souhaitait et j'avais précisé juste au-dessus que nous voulions "l'ID du premier joueur qui ne possède même pas 500$".
Le tableau devint-ci
Un tableau réunit plusieurs "grosses variables" pouvant contenir tout type de nombres.(entiers, décimaux, rationnels, caractères. (les caractères sont lus comme des nombres/valeurs cf: ASCII)). En Pawn comme en C(++), les tableaux peuvent avoir plusieurs dimensions; un tableau en Pawn peut avoir 3 dimensions maximum, tandis qu'en C(++) un tableau peut contenir jusqu'à 5 dimensions.
Pour ceux qui ont déjà utilisé ou vu des tableaux à plusieurs dimensions, appelés aussi tableaux multidimensionnels, vous remarquerez donc qu'il est possible de déclarer son tableau avec plusieurs dimensions très facilement!
Tableau » 1 dimension:
Un tableau à une dimension se déclare de cette manière :
new montableau[5];
Ici mon tableau comporte donc une seule dimension et est indexé à 5, ce qui signifie qu'il peut contenir 5 valeurs. Si je souhaite y stocker des valeurs, je peux le faire de deux façons!
new montableau[5] = {1, 2, 3, 4, 5}; /* ------ OU ------ */ new montableau[5]; main() { montableau[0] = 1; montableau[1] = 2; montableau[2] = 3; montableau[3] = 4; montableau[4] = 5; }
Comme vous pouvez le constater, la première valeur n'est pas stockée à la case 1 mais bien à la case 0 et donc la case maximale sera inférieure de 1, par rapport à son index. Donc si je fais ceci, cela me retournera obligatoirement une erreur :
new montableau[5]; main() { montableau[0] = 1; montableau[1] = 2; montableau[2] = 3; montableau[3] = 4; montableau[4] = 5; montableau[5] = 6; // <------- ERREUR }
Récupérer la valeur d'un tableau est très très facile, si vous savez utiliser printf, vous devriez comprendre!
new montableau[5] = {942, 174, 88, 9933, 154}; main() { printf("%d", montableau[3]); }
Dans cet exemple, si vous pensez que cela affichera 88 vous avez alors mal compris ce que je vous ai expliqué juste avant. Cela affichera évidemment 9933, car nous récupérons la valeur de troisième case en partant de 3, soit la quatrième valeur en réalité de notre tableau.
Pour afficher tout bêtement toutes les valeurs une par une, on va utiliser une boucle !
#include <a_samp> new montableau[5] = {942, 174, 88, 9933, 154}; main() { for(new i = 0; i < 5; i++) { printf("montableau[%d] = %d", i, montableau[i]); } }
Si vous avez testé ce code, vous devriez avoir ceci dans vos logs :
montableau[0] = 942 montableau[1] = 174 montableau[2] = 88 montableau[3] = 9933 montableau[4] = 154
Mais qu'en est-il des valeurs rationnelles (floats) et des chaines de caractères (string) ?
On y vient !
Rapide explication pour les valeurs rationnelles, ça fonctionne comme les entiers, il faut simplement utiliser le label/tag Float:, pour prévenir au compilateur que nous ne stockons que des valeurs rationnelles !
#include <a_samp> new Float:montableau[5] = {942.665455, 174.021014, 88.548852, 9933.684885, 154.369852}; main() { for(new i = 0; i < 5; i++) { printf("%f", montableau[i]); // Ici j'ai utilisé le spécificateur %f car ce sont des floats. } }
NOW, on en vient pour finir avec les chaînes de caractères.
Première chose à savoir : quand vous stockez des caractères dans votre tableau, la case qui vient après le dernier caractère doit contenir un caractère dit "null" ou le nullbyte : \0.
Donc si je veux stocker dans un tableau le mot "Bonjour", sachant qu'il contient 7 caractères, je devrais donc indexer mon tableau à 8 !
new montableau[8]; main() { montableau[0] = 'B'; montableau[1] = 'o'; montableau[2] = 'u'; montableau[3] = 'j'; montableau[4] = 'o'; montableau[5] = 'u'; montableau[6] = 'r'; montableau[7] = '\0'; }
Ca aussi c'est barbare comme méthode, autant procéder de cette façon :
new montableau[8] = "Bonjour"; /* ----- OU ----- */ new montableau[] = "Bonjour";
Vous n'êtes pas obligé d'indexer votre tableau quand vous stockez des valeurs directement à la ligne de sa déclaration.
Je vous ai fait un schéma pour que vous compreniez comment le programme voit votre tableau avec le mot "Bonjour" :
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
B | o | n | j | o | u | r | \0 |
Evidemment, il ne verra pas directement les caractères, mais leur valeur ASCII.
Tableau » 2 dimensions:
Maintenant que j'vous ai expliqué comment fonctionne un tableau à 1 dimension, comprendre la suite ne sera pas bien difficile!
La déclaration d'un tableau à 2 dimensions vous l'aurez deviné, se fait comme ça:
new montableau[3][5];
La méthode la plus simple pour y stocker des valeurs est bien celle-ci:
new montableau[3][5] = { {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11, 12, 13, 14, 15} };
Mais comment récupérer ces valeurs ? Avec une boucle ? Non moi je dirais 2 boucles !
Oui car la première sera appelée 3 fois, dans laquelle nous aurons notre deuxième boucle qui sera appelée 5 fois, qui dans celle-ci nous aurons notre printf pour afficher les valeurs. Au total *calcuuuuuul* 15 printf !
Essayez sans regarder le code, d'afficher ces valeurs !
#include <a_samp> new montableau[3][5] = { {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11, 12, 13, 14, 15} }; main() { for(new i = 0; i < 3; i++) { for(new c = 0; c < 5; c++) { printf("montableau[%d][%d] = %d", i, c, montableau[i][c]); } } }
C'est aussi simple que ça, fallait pas aller chercher bien loin !
Je vous ai évidemment fait un schéma pour cet exemple sur la façon dont sont rangées dans votre programme les valeurs :
2 | 11 | 12 | 13 | 14 | 15 |
1 | 6 | 7 | 8 | 9 | 10 |
0 | 1 | 2 | 3 | 4 | 5 |
0 | 1 | 2 | 3 | 4 |
Pour les chaînes de caractères, c'est pareil simplement vous devez stocker vos mots de cette façon :
new montableau[5][] = { "- Jean-Paul est à la piscine", "- Ah non il est dans la cuisine !", "- Non ça c'est Bryan !", "- Where is Bryan ?", "Bryan is in the kitchen !" };
En n'indexant pas la seconde dimensions, le compilateur le fera à votre place. C'est courant pour ce genre de cas, de ne pas l'indexer, car si vous vous retrouvez avec des phrases immenses, j'pense pas que vous vous amuseriez à compter caractère par caractère.
Pour récupérer le texte, rien de bien compliqué :
#include <a_samp> new montableau[5][] = { "- Jean-Paul est à la piscine", "- Ah non il est dans la cuisine !", "- Non ça c'est Bryan !", "- Where is Bryan ?", "- Bryan is in the kitchen !" }; main() { for(new i = 0; i < 5; i++) { printf("%s", montableau[i]); } }
Cela vous affichera les 5 phrases à la suite!
Tableau » 3 dimensions:
Le dernier mais aussi le plus dur. Non sérieusement, il n'y a rien de bien compliqué si vous avez compris les deux autres parties.
J'hésite à vous montrer sa déclaration... bon d'accord!
new montableau[3][3][3];
La méthode pour y stocker des valeurs peut-être encombrante si vous n'indentez pas correctement votre code.
new montableau[2][2][2] = { { {1, 2}, {3, 4} }, { {5, 6}, {7, 8} } };
Pour afficher les valeurs, vous devrez utiliser cette fois-ci 3 boucles !
#include <a_samp> new montableau[2][2][2] = { { {1, 2}, {3, 4} }, { {5, 6}, {7, 8} } }; main() { for(new i = 0; i < 2; i++) { for(new c = 0; c < 2; c++) { for(new t = 0; t < 2; t++) { printf("montableau[%d][%d][%d] = %d", i, c, t, montableau[i][c][t]); } } } }
Pour les chaînes de caractères, rien d'bien compliqué à stocker :
new montableau[2][2][] = { { "- Where is Bryan ?", "- Bryan is in the kitchen !" }, { "- Où est Bryan ?", "- Bryan est dans la cuisine !" } };
Et pour les afficher :
#include <a_samp> new montableau[2][2][] = { { "- Where is Bryan ?", "- Bryan is in the kitchen !" }, { "- Où est Bryan ?", "- Bryan est dans la cuisine !" } }; main() { for(new i = 0; i < 2; i++) { for(new c = 0; c < 2; c++) { printf("%s", montableau[i][c]); } } }
Je n'ai pas de schéma à vous montrer réellement, mais imaginez-vous un Rubik's Cube, ben vos valeurs seraient stockés de cette manière : en trois dimensions !
Maitenant il y a un point interessant avec les tableaux à 3 dimensions, vous pouvez stocker autant de types de données pour chaque ligne.
#include <a_samp> new montableau[][][] = { {float:123.456789, "Ballas", 100}, {float:987.654321, "Groove", 200}, {float:159.234678, "MS13", 300} }; main() { for(new i = 0; i < sizeof(montableau); i++) { printf("montableau[%d] : %f - %s - %i", i, montableau[i][0], montableau[i][1], montableau[i][2]); } }
Ce code affichera :
montableau[0] = 123.456789 - Ballas - 100 montableau[1] = 987.654321 - Groove - 200 montableau[2] = 159.234678 - MS13 - 300
Dernier point:
Le nombre de dimensions varient en fonction de vos besoins, réfléchissez bien avant de les utiliser.
Voici un code qui affichera le même résultat pour les 3 types de dimensions:
#include <a_samp> new array1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 8, 7, 6, 5, 4, 3, 2, 1}; new array2[][] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {9, 8, 7}, {6, 5, 4}, {3, 2, 1} }; new array3[][][] = { { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }, { {9, 8, 7}, {6, 5, 4}, {3, 2, 1} } }; main() { for(new i; i < sizeof(array1); i++) printf("array1[%d] = %d", i, array1[i]); print("----------------------"); for(new i; i < sizeof(array2); i++) for(new c; c < sizeof(array2[]); c++) printf("array2[%d][%d] = %d", i, c, array2[i][c]); print("----------------------"); for(new i; i < sizeof(array3); i++) for(new c; c < sizeof(array3[]); c++) for(new t; t < sizeof(array3[][]); t++) printf("array3[%d][%d][%d] = %d", i, c, t, array3[i][c][t]); }
Une fonction ça fonctionne ?
Une fonction en général est une entité informatique contenant des instructions effectuant un traitement spécifique bien identifié (envoi de messages, calculs, etc...). En Pawn comme en C les fonctions ont le choix de pouvoir retourner une valeur, avec une légère différence de structure. (en C on utilise "void" pour dire qu'on ne retournera pas de valeur, "int" pour dire qu'on retournera un entier de 32 bits etc...). Les fonctions peuvent avoir ce que l'on appelle des paramètres, nous verrons ça dans la suite de ce tutoriel.
Fonction basique:
De nombreuses personnes pensent qu'une fonction se déclare à l'aide du mot-clé stock, si toi, lecteur de ce tutoriel, tu fais partie de ces gens, je te propose de rayer cette idée de ta tête. Comme je l'ai dit juste au-dessus, en C, nous avons besoin de mot-clé pour indiquer si nous retournons quelque chose et bien cette fameuse différence de structure dont je vous parlais c'est qu'en Pawn on utilise aucun mot-clé !
Voici un exemple de fonction :
mafonction() { }
Dans l'exemple, nous venons de créer une fonction portant le nom "mafonction". Mais bien évidemment elle ne possède aucune instruction, donc aucun code ne sera exécuté. "Instruction" ? Ah oui j'en avais parlé au début mais sans trop vous avoir expliqué. Les instructions sont les fameuses lignes de code que l'on retrouve entre les accolades.
Par exemple :
#include <a_samp> main() { mafonction(); } mafonction() { print("Bonjour !"); }
Ici mafonction appellera la fonction print qui enverra "Bonjour !" dans les logs. Mais pourtant cette fonction ne retourne rien. Ce qu'il faut savoir c'est qu'une fonction basique peut retourner toutes sortes de valeurs, que ça soit un entier, un nombre rationnel (Float), un booléen ou bien une chaîne de caractères.
Pour ça nous avons besoin du mot-clé return.
#include <a_samp> main() { printf("%d", mafonction()); } mafonction() { print("Bonjour !"); return 1; }
mafonction retourne la valeur 1.
Si vous testez ce code, cela vous affichera "Bonjour !" puis "1" car dans le printf de main nous appelons mafonction et rappelez-vous elle retourne la valeur 1.
J'aurais mis "return 64", cela afficherait 64.
Fonction publique:
A la différence d'une fonction basique, les fonctions publiques sont enregistrées dans la mémoire et possèdent chacune une ID qui leur est propre (on en reparlera après).
Pour être enregistrée dans la mémoire, le nom de la fonction doit être envoyé à l'aide du mot clé forward.
On retrouve aussi un second mot-clé public pour indiquer que le code qui sera à l'intérieur des accolades sera l'instruction de la fonction. Voici un exemple de fonction publique :
#include <a_samp> forward mafonction(); public mafonction() { }
Une autre différence, les fonctions publiques ne peuvent retourner que des entiers ou des booléens. Par exemple :
#include <a_samp> main() { printf("%d", mafonction()); } forward mafonction(); public mafonction() { return 5; }
Ceci affichera bien "5".
Encore une autre différence, les fonctions publiques sont les seules pouvant être appelées à l'aide des fonctions CallRemoteFunction et CallLocalFunction.
Bien, je vous parlais tout à l'heure d'ID, car oui chaque fonction publique possède une ID et c'est à l'aide de la fonction funcidx. Pour savoir si une fonction existe par exemple :
if(funcidx("NomDeLaFonction") != -1)
Car oui, funcidx retourne -1 si la fonction n'existe pas.
Paramètres d'une fonction:
Jusqu'ici je n'ai pas utilisé de paramètre dans les fonctions de mes exemples. Un paramètre est une donnée manipulée par l'instruction de la fonction. Les paramètres sont situés entre les parenthèses après le nom de la fonction. Par exemple, je souhaiterais faire une fonction qui "addition", j'utiliserai les paramètres :
#include <a_samp> main() { new a = 5, b = 3; printf("%d", addition(a, b)); } addition(valeur1, valeur2) { return valeur1 + valeur2; }
Ceci afficherait "8".
Ce qu'il faut savoir à propos des paramètres, c'est qu'ils peuvent être "infinis" en utilisant en paramètre "..." :
mafonction(...) { }
Mais les paramètres seraient réservés à des variables sans tag. Pour prendre en compte les paramètres avec des tags/labels, il faudra obligatoirement "{Float,_}:" par exemple pour les nombres rationnels juste avant les "..." :
mafonction({Float,_}:...) { }
Pour gérer ces paramètres, je vous laisse réfléchir en utilisant ces fonctions : numargs, getarg et setarg.
Un avant-dernier point pour cette partie, vous devrez toujours indiquer quel type de variable sera utilisé dans votre fonction.
Pour les entiers/décimaux : Rien à indiquer.
Pour les nombres rationnels : Le mot clé "Float:".
Pour les booléens : Le mot clé "bool:".
Pour les chaînes de caractères : Des crochets juste après le nombre de la variable.
mafonction(valeur, Float:ratio, bool:ok, string[])
Appel par référence:
Imaginez que vous souhaitiez par exemple coder une fonction swap (échanger la valeur de deux variables), avec ce que je vous ai appris, vous ne pouvez pas encore le faire. C'est pourquoi dans cette dernière partie, nous allons parler des appels par références. Si vous avez déjà codé en C, c'est similaire aux pointeurs, mais c'est encore plus facile.
Les appels par référence aussi appelés appels par adresse permettent d'obtenir la valeur ainsi que l'adresse d'une variable et donc de pouvoir modifier ou utiliser sa valeur. Pour ça nous devons utiliser le symbole "&", que nous placerons devant chaque paramètre pour indiquer que nous souhaitons accéder à l'adresse de la variable qui sera transmise.
Pour cet exemple nous allons coder notre fonction swap, commençons donc par le début :
swap(&a, &b) { }
Pour la suite c'est tout bête, nous allons créer une variable appelée c dans laquelle nous stockerons la valeur de a, nous stockerons ensuite la valeur de b dans la variable a et la valeur de c dans la variable b. Voici donc :
swap(&a, &b) { new c = a; a = b; b = c; }
Il n'y a plus qu'à tester ce code et vous verrez qu'il est fonctionnel :
#include <a_samp> main() { new a = 5, b = 3; printf("a = %d - b = %d", a, b); swap(a, b); printf("a = %d - b = %d", a, b); } swap(&a, &b) { new c = a; a = b; b = c; }
Il y a une autre méthode pour coder la fonction swap sans utiliser une troisième variable mais c'est en assembleur et donc demande un certain niveau.
Si vous avez essayé de placer un "&" pour un tableau en paramètre, sachez que vous avez bien fait car ça prouve votre curiosité, mais ça ne fonctionnera pas. Comment faire ? Et bien il n'y a rien à indiquer, lorsque vous modifier le contenu d'un tableau dans votre fonction, son contenu sera aussi modifié là où la fonction est appelée.
Exemple :
#include <a_samp> main() { new string[] = "Dutheil"; print(string); mafonction(string); print(string); } mafonction(string[]) { string[0] = 'M'; }
Ici le premier print affichera "Dutheil", mais le second "Mutheil".
Paramètre par défaut:
Il est possible, d'utiliser des paramètres par défaut. C'est-à-dire que lorsque vous utiliserez votre fonction, vous ne serez pas obligé d'y placer des paramètres. Cela fonctionne tout bêtement grâce au symbole "=", que vous placez juste après votre paramètre et en y insérant ensuite une valeur par défaut. Par exemple :
mafonction(valeur=2) { return valeur + 5; }
Ici, si vous appelez mafonction sans indiquer de paramètre, cela vous retournera 2 + 5, soit 7. Vous pouvez donc indiquer n'importe quoi, que ça soit un nombre rationnel, un booléen ou une chaîne de caractères et bien évidemment autant que vous voulez.
mafonction(valeur=2, Float:ratio=12.5, bool:ok=true, string[]="Dutheil")
Les directives de pré-processeur
Une directive de pré-processeur a pour but d'être exécutée avant la compilation. Elles sont facilement reconnaissables car elles commencent toutes par un '#'. Je vous rassure, si vous avez mes autres tutoriels, celui-ci sera du gâteau!
Directive » #error:
Je vais aborder celle-ci en première car je l'a ré-utiliserai dans des exemples pour la suite. Cette directive permet de générer tout bêtement une fatal error lors de votre compilation, avec comme seul argument la phrase que vous souhaitez.
#error Voici une erreur main() { }
Si vous avez essayé, vous remarquerez que votre compilateur vous a sorti un jolie "fatal error 111: user error: Voici une erreur", c'est normal!
Directive » #define:
Cette directive vous permet de créer des macros. Cela peut être utile dans tous les cas, au lieu d'écrire un nombre, une chaîne de caractères etc...dans votre votre script, vous le/la définissez avec #define via un symbole/mot que vous pouvez ré-utiliser partout dans votre script, à condition qu'il soit défini défini avant son utilisation.
#define A 155 main() { printf("A = %d", A); }
Dans vos logs vous aurez "A = 155".
"A" s'appelle un identifiant, 155 est le contenu de remplacement.
Je vais pousser un peu plus loin cette directive tout de même, étant donné que vous pouvez l'utiliser avec des paramètres qui sont définis avec le symbole %. Vous pouvez en utiliser 10 : %0 - %9. Par exemple, je souhaite créer une fonction qui me permet de générer un nombre aléatoire entre 10 et 35.
Je ferais comme ça :
minrand(var1, var2) { return (random(var2-var1)+var1); }
Mais je pourrais aussi créer une macro pour faire ça!
#define minrand(%0,%1) (random(%1-%0)+%0)
Un détail très important : vous ne devez pas mettre d'espace(s) dans votre identifiant, sinon le compilateur comprendra que ce qui s'y trouve après sera le contenu de remplacement, donc votre identifiant sera incomplet.
#define DU THEIL 14
Ici, "DU" sera remplacé par "THEIL 14", donc faite bien attention à vos macros!
Directive » #undef:
Maintenant que vous savez définir ce que vous souhaitez avec #define, #undef vous permet de faire le contraire, c'est-à-dire d'escamoter quelque chose qui a été définie avant.
Par exemple, cela peut être utile pour optimiser largement vos scripts et je le conseille à tous d'utiliser cette méthode toute bête : sachant que MAX_PLAYERS est défini dans a_samp.inc à 500 par défaut, vous devez certainement l'utiliser dans vos boucles, vos tableaux globaux, mais vous n'avez que 20 slots sur votre serveur ! Pourquoi pomper le processeur et allouer de la mémoire inutilement ? Voici donc un exemple d'utilisation de #undef :
#undef MAX_PLAYERS #define MAX_PLAYERS (20)
MAX_PLAYERS devra évidemment être défini avant qu'on l'escamote, pour ça il faudra placer ce code après avoir inclus a_samp.
Directives » #if & #endif:
Si vous connaissez le mot-clé if, la directive #if fonctionne de la même manière, mais n'agit pas directement avec le programme puisque comme je vous l'ai dit, elle est exécutée avant la compilation. Elle permet donc de comparer en utilisant les opérateurs de comparaisons (<, >, <=, >=, ==, !=) ou en utilisant un autre opérateur qui lui est lié defined (!defined fonctionnera aussi).
La structure est un peu différente de if, puisque qu'on utilise pas des accolades pour encadrer les instructions à exécuter dans notre condition, mais on utilise la directive #endif pour avertir que notre condition est terminée.
Donc une condition basique avec if donne ça :
#include <a_samp> main() { new A = 6; if(A < 7) { print("A est inférieur à 7"); } }
Ceci compilerait correctement et nous afficherait "a est inférieur à 7".
Et une condition avec #if donnerait ça :
#define A 6 #if A < 7 #error A est inferieur a 7 #endif
Cela ne compilerait pas, et afficherait "fatal error 111: user error: A est inferieur a 7" (les accents ne sont pas pris en compte par la compilateur pour les messages d'erreurs.)
Vous pouvez évidemment remplacer #error par print, cela compilerait et afficherait le message dans vos logs.
#include <a_samp> #define A 6 main() { #if A < 7 print("A est inferieur a 7"); #endif }
Directives » #elseif & #else:
J'vais faire très court! Ces directives fonctionnent exactement comme else if & else, mais on la même structure que #if.
#define A 7 #if A < 7 #error A est inferieur a 7 #else #error A est superieur a 7 #endif
Le compilateur vous afficherait "fatal error 111: user error: A est superieur a 7".
#define A 7 #if A < 7 #error A est inferieur a 7 #elseif A == 7 #error A est egal a 7 #else #error A est superieur a 7 #endif
Et ici "fatal error 111: user error: A est egal a 7".
Directives » #endinput & #endscript:
Ces directives exécutent la même action mais ont des noms différents. (allez savoir pourquoi ptdr). Elles permettent seulement de pouvoir arrêter l'inclusion d'un fichier.
Je vais reprendre une parti du code de zcmd pour vous l'expliquer
#if defined _zcmd_included // Si "_zcmd_included" est défini #endinput // On arrête l'inclusion du fichier #endif // Fin de la condition #define _zcmd_included // On défini "_zcmd_included" puisque qu'il n'existe pas, sinon on aurait arrêté d'inclure la suite
Directive » #assert:
Cette directive a pour but de générer une erreur d'assertion. Elle regroupe PRESQUE à elle toute seule #if et #error. Je dis bien "presque", car si on y place une condition, si celle-ci est fausse, le compilateur nous affichera seulement la condition.
#define A 8 #assert A < 7
Affichera "fatal error 110: assertion failed: 8 < 7".
Directives » #include & #tryinclude:
Beaucoup ici le savent, une include est un fichier que l'on peut intégrer à notre GameMode ou FilterScript. Et c'est grâce à la directive #include que par exemple, le compilateur ne vous retourne pas d'erreurs quand vous utilisez les fonctions propres à SAMP en incluant a_samp. Elle s'utilise tout bêtement de cette manière :
#include <a_samp>
Si vous tentez d'inclure un fichier inexistant avec #include, le compilateur vous retournera un jolie "fatal error 100: cannot read from file".
Tandis que #tryinclude réalise la même action que #include, sauf que si le fichier indiqué est inexistant, vous n'aurez pas d'erreur et votre script continuera de compiler.
#tryinclude <asamp>
Directive » #pragma:
A venir.
Directive » #emit:
Je ne vais vous expliquer #emit car il y a tellement de choses à savoir sur son utilisation, que je n'en connais qu'une petite partie et qu'il serait évidemment trop long pour moi de tout vous apprendre dans un seul tutoriel.
Je vous ai tout de même codé un exemple sur son utilisation :
#include <a_samp> main() { new a = 3, b = 2, c; printf("1 : %d", c); // Donnera 0 #emit LOAD.S.pri a // On charge "a" dans le registre primaire #emit LOAD.S.alt b // On chagre "b" dans le registre alterné/alternate #emit ADD // On additionne #emit STOR.S.pri c // On y place le résultat dans "c" printf("2 : %d", c); // Donnera a+b = 3+2 = 5 #emit LOAD.S.pri c // On charge "c" dans le registre primaire #emit CONST.alt 5 // On charge la valeur 5 dans le registre alterné/alternate #emit ADD // On additionne #emit STOR.S.pri c // On y place le résultat dans "c" printf("3 : %d", c); // Donnera c+5 = 5+5 = 10 #emit LOAD.S.pri c // On charge "c" dans le registre primaire #emit NEG /* La valeur de "c" est inversée, si celle-ci est positive alors elle est négative, sinon si la valeur est négative alors elle est positive. */ #emit STOR.S.pri c // On y place le résultat dans "c" printf("4 : %d", c); // Donnera l'inverse de c(10) = -10 }
Les initialiseurs
Les initialiseurs sont utilisés pour déclarer/initialiser vos fonctions, vos variables ou même vos tableaux.
Vous pouvez, les assembler entre eux, du moment que vous savez ce que vous faites avec.
Initialiseur » new:
Vous le connaissez bien celui-ci, c'est l'un des plus importants. Il n'a pas de caractéristique précise, on l'utilise seulement pour déclarer une variable ou un tableau.
new a, b[10];
Initialiseur » stock:
Celui-ci n'a qu'une seule caractéristique, il permet dans le cas où vous déclarez une variable, un tableau ou une fonction avec, de ne pas avoir de warning quand vous compilez votre script si vous n'utilisez pas l'un de ces éléments.
Cela peut être par exemple de déclarer une variable pour l'utiliser plus tard, sans devoir utiliser #pragma unused.
Il n'est donc pas réservé qu'aux fonctions comme beaucoup le pense.
Le dernier point à savoir sur son utilisation; sa déclaration doit être globale, vous ne pouvez pas déclarer une variable locale avec.
stock a, b[10]; stock fonction() { }
Initialiseur » static:
Il y a deux types de static :
- statique globale - statique locale
statique globale:
Les statiques globales permettent d'être utilisées seulement dans le fichier où elles sont déclarées.
Imaginons que nous avons notre gamemode et une include dans laquelle j'ai déclaré une variable statique :
// dans mon include static a; // dans mon gamemode main() { printf("%d", a); // ne fonctionnera pas }
Celle-ci ne pourra pas être ré-utilisée dans mon gamemode, le compilateur m'enverrait une erreur.
static fonction1(); // on remplace le forward public fonction1() { } static function() { }
Vous pouvez bien évidemment déclarer vos fonctions en statique.
statique locale:
Dans une fonction, déclarer une variable statique locale permet de garder en mémoire sa valeur et donc ne pas ré-allouer de la mémoire à chaque appel de la fonction.
Pour comprendre mieux je vais reprendre l'exemple du wiki anglais :
fonction() { new i; printf("%d", i); i++; printf("%d", i); }
Dans ce genre de cas, à chaque appel de la fonction, la variable "i" est remisé à zéro et donc donnerait ça dans des logs :
0 1 0 1 0 1 0 1
Mais dans le cas où nous déclarerions notre variable en statique :
fonction() { static i; printf("%d", i); i++; printf("%d", i); }
A chaque appel de la fonction, on ré-attribue la valeur à "i", ce qui donnerait :
0 1 1 2 2 3 3 4
Initialiseur » const:
Une variable constante, aussi appelée une macro si elle ne comporte qu'une valeur, est une variable dont la valeur ne peut pas être changée après la déclaration.
main() { const a = 2; a = 3; // ne fonctionnera pas }
Mais cela peut-être utile pour des tableaux, ce qui n'est pas possible en utilisant #define.
const a[5] = {78, 13, 55, 99, 2};
Dernier point:
Vous pouvez utiliser plusieurs mots clés pour déclarer une variable, par exemple :
static stock const Float:a[3] = {309.9570, 45.7836, 2.8272};
Dans cet exemple, les valeurs rationelles de notre tableaux ne peuvent être changées, le tableau ne peut être utilisé que dans le fichier où il est déclaré et si il n'est pas utilisé, le compilateur n'enverra pas de "warning 203 : symbol is never used".