5. Les structures de contrôle
Voir les opérateurs, c'est bien, mais ce n'est pas ça qui va nous permettre par exemple de définir si un script doit forcer le personnage 'x' à aller dans une telle zone ! Pour cela, nous devons utiliser des structures de contrôle, qui vont nous permettre de « conditionner » nos scripts, qui pourront aussi les alléger, etc. !
a. Les conditions (if, else)
Les instructions if et else sont les plus importantes instructions de tout langage de programmation : elles vont vous permettre de décider si une partie de votre code devra être exécuté ou non. Basiquement, le code est exécuté si la condition est vérifiée :
if ( condition )
instruction;
Mettons que la condition soit : a == b et que a = b (a, b deux entiers). Dans ce cas, la condition renvoie VRAI (true) et l'instruction sera exécutée.
Dans ce cas-ci, notre « instruction » tiens sur une ligne : vous pouvez omettre les accolades. Si votre code fait plusieurs lignes, vous devez utiliser les accolades :
if ( condition )
{
instruction1;
instruction2;
}
Maintenant, nous avons notre condition : néanmoins, il ne se passe rien si celle-ci est fausse. Pour cela, on va utiliser la structure else (sinon) :
if ( condition )
instruction1;
else
instruction2;
(On suppose que « instruction1 » et « instruction2 » sont sur une ligne)
Ainsi, si on reprend notre exemple : on teste a == b, et dans ce cas a > b. Alors l' « instruction2 » sera exécutée au lieu de l'«instruction1».
Vous pouvez bien sur combiner les else et les if : reprenons notre exemple :
if ( a == b )
a = b;
else if ( a > b )
a = b + 1;
else if ( a < b )
a = b - 1;
else
a = a;
Nous vérifions donc successivement :
si a == b
alors a = b
sinon si a > b
alors a = b + 1
sinon a < b
alors a = b - 1
sinon a = a
Nous avons indenté notre exemple pour bien vous montrer une chose importante à savoir (ça peut être la cause d'erreurs pour des conditions plus compliquées) : dans un premier temps, on vérifie si a = b, et dans ce cas on va effectuer l'instruction a = b, si a != b alors on va vérifier en premier lieu si a > b, puis en second lieu si ce n'est pas le cas si a < b et enfin si aucune des expressions n'est vérifiée on exécute l'instruction 'a=a'.
Si vous n'avez pas compris, focalisez vous sur les mots 'premier' et 'seconds' : l'ordre des conditions importe, c'est-à-dire que vous ne pourrez pas avoir de condition 'a < b' sans avoir de conditions 'a == b' et 'a > b'.
Au passage, vous noterez l'inutilité totale du else : nous avons vu chacun des cas possibles.
Voici des exemples de conditions plus compliquées (elles proviennent d'un script en PHP) : vous noterez l'utilisation des parenthèses pour bien séparer les différentes parties de la condition.
if ( $iMode != -1 && $this->_s != -1 && ( ( $this->bUserIsModerator == true && ( ( $MB->aUserModerator['SujetOpen'] == 1 && $iMode == 0 ) || ( $MB->aUserModerator['SujetChRestriction'] == 1 && $iMode != 0 ) ) ) || $this->bUserIsAdmin == true ) )
instruction;
if ( $iType != -1 && $this->_s != -1 && ( ( $this->bUserIsModerator == true && $MB->aUserModerator['SujetChType'] == 1 ) || $this->bUserIsAdmin == true ) )
instruction;
Il s'agit sûrement d'horreurs pour quelqu'un qui débute, mais ces cas là ne sont pas inexistants. Vous aurez vous aussi peut-être besoin de faire d'aussi longues conditions. Pour éviter les erreurs, vous avez plusieurs méthodes :
Vous pouvez décomposer la condition :
(Note : en PHP les variables ne sont pas typées par défaut. Dans le cas des NWScripts vous devrez déclarer des entiers)
$condition1 = $iMode != -1;
$condition2 = $this->_s != -1;
$condition3 = $MB->aUserModerator['SujetOpen'] == 1 && $iMode == 0;
$condition4 = $MB->aUserModerator['SujetChRestriction'] == 1 && $iMode != 0;
$condition5 = $this->bUserIsModerator == true;
$condition6 = $this->bUserIsAdmin == true;
Ce qui nous donne :
if ( $condition1 && $condition2 && ( ( $condition5 && ( $condition3 || $condition4 ) ) || $condition6 ) )
Ce qui, vous l'avouerez est plus lisible, ou plus compréhensible. Néanmoins, vous stockez des variables supplémentaires pour RIEN. C'est tout l'avantage de la deuxième solution :
if ( $iMode != -1 &&
$this->_s != -1 &&
(
( $this->bUserIsModerator == true &&
(
( $MB->aUserModerator['SujetOpen'] == 1 &&
$iMode == 0 ) ||
( $MB->aUserModerator['SujetChRestriction'] == 1 &&
$iMode != 0 )
)
) ||
$this->bUserIsAdmin == true
)
)
Tout de suite, cela prend bien plus de place mais cette méthode est plus utile : premièrement, on n'utilise pas de sous variables, deuxièmement en général le compilateur accepte cette forme, et troisièmement on peut plus facilement savoir ce qui se passe exactement à l'intérieur de notre condition : c'est le but de l'indentation.
Enfin, nous vous avions parlé de l'opérateur ternaire ( « ? : ») : celui-ci permet de faire une condition « if ( condition ) { instruction } else {} » sur une ligne : il a cependant de grandes différences avec le couple if/ else : il ne permet pas d'effectuer toutes les instructions voulues, ça n'est pas son but. Son but ? C'est d'affecter à une variable une valeur en fonction d'une condition.
Voilà la syntaxe générale :
a = (condition) ? instruction_si_vrai : instruction_si_faux;
(On a mis « = » mais ça peut être += , etc.)
Par exemple :
a = ( a < b ) ? b + 1: b - 1;
Cet opérateur ternaire est très pratique, mais évitez d'en abuser. Par exemple évitez ceci :
a = ( a < b ) ? b + 1: b - 1;
b = ( a < b ) ? a + 1: a - 1;
La condition étant la même dans les deux cas, préférez plutôt ceci :
if ( a < b )
{
a = b + 1;
b = a + 1;
}
else
{
a = b - 1;
b = a - 1;
}
b. Les boucles (while, for, do ... while)
Un autre aspect pratique de la programmation est la boucle. Si on reprend notre exemple sur les variables globales :
CreateItemOnObject( "item", oPlayer );
CreateItemOnObject( "item", oPlayer );
Ici, on a écrit 100 fois CreateItemOnObject( "item", oPlayer ); ! Maintenant, avec les boucles, on va pouvoir réduire ceci et corriger cette erreur. L'avantage étant que vous réduirez ainsi la taille de vos scripts (sachant qu'ils sont limités à 512 Ko avec le patch 1.26).
Pour effectuez 100 fois l'instruction CreateItemOnObject( "item", oPlayer ); nous avons trois structures de contrôle qui vont nous permettre de réduire notre code.
La première, c'est l'instruction while, la seconde, l'instruction for et enfin la troisième do ... while.
Voici la syntaxe de while :
while ( condition )
{
instruction;
}
Ici tant que la condition sera VRAIE alors l'instruction sera effectuée. L'instruction est supposée permettre à la condition d'être fausse à un moment : si tel n'est pas le cas, alors vous créerez une boucle infinie ce qui représente bien entendu un danger.
Voici un exemple de boucle simple :
int i = 0;
while ( i < 10 )
{
i++;
}
Et un exemple de boucle infinie :
int i = 0;
while ( i == i )
{
i++;
}
Dans d'autres cas, on peut avoir :
int i = 0;
while ( i != 10 )
{
i++;
if ( i == 9 )
i = 11;
}
Il s'agit d'un autre type de boucle infinie : ici, on teste la condition i différent de 10 et on empêche que i soit égal à 10.
Voici notre exemple réécrit avec la boucle while :
int i = 0;
while ( i < 100 )
{
CreateItemOnObject( "String", oPlayer );
}
Vous voyez ? Notre exemple est tout de suite plus propre. Mais ... nous avons oublié de mettre i++ pour que la boucle puisse se fermer...
int i = 0;
while ( i < 100 )
{
CreateItemOnObject( "item", oPlayer );
i++;
}
(Note : on peut mettre i++ avant CreateItemOnObject)
L'instruction while permet de créer des boucles simplement, sans trop s'embêter. Néanmoins, il faudra penser à chaque fois à mettre une instruction permettant d'arrêter la boucle.
L'autre façon de créer des boucles, c'est avec l'instruction for : celle-ci est plus « sécuritaire » puisque dans sa déclaration vous devez indiquez l'état de départ, la condition à vérifier et une instruction qui sera évaluée à chaque itération (une itération = un passage dans la boucle).
Voici la syntaxe :
for ( expression_depart; condition; instruction1 )
{
instruction2;
}
expression_depart : correspond à l'état initial : cette expression est exécutée lors de la première itération. Le plus souvent, il s'agit de définir la valeur initiale d'un entier : i = 0.
condition : tout comme l'instruction while il vous faut une condition pour que la boucle puisse tourner. Le plus souvent on teste la valeur d'un entier (si celle-ci est inférieure à x par exemple) : i < x;
instruction1 : il s'agit d'une instruction qui est exécutée quoiqu'il arrive. Cette instruction doit permettre de fermer la boucle : par exemple, i++.
instruction2 : instruction à exécuter.
Voici notre exemple réécrit avec la boucle for :
int i;
for ( i = 1; i <= 100; i++ )
{
CreateItemOnObject( "item", oPlayer );
}
Note : on commence à 1 et on va jusqu'à 100 inclus, ce qui nous fait bien 100 objets de créés.
C'est à peu près tout ce qu'il y a à savoir sur les fonctions. Nous verrons plus loin dans cette section deux autres instructions qui sont très utiles avec les boucles.
Dans notre exemple avec la boucle For nous ne sommes pas forcés de mettre les accolades car notre instruction ne tient que sur une seule ligne.
int i;
for ( i = 1; i <= 100; i++ )
CreateItemOnObject( "item", oPlayer );
De même, avec la boucle while :
int i = 0;
while ( i++ < 100 )
CreateItemOnObject( "item", oPlayer );
Ce qui peut s'écrire :
int i = 0;
while ( ++i <= 100 )
CreateItemOnObject( "item", oPlayer );
Ici on pré incrémente i : ce qui veut dire que lors de la première itération de la boucle, i vaut 1 et pas 0 : c'est pour cela que l'on doit utiliser l'opérateur « inférieur ou égal à ».
Il existe aussi un troisième type de boucle : do ... while. Voici sa syntaxe :
do instructions while ( condition );
Dans ce type de boucle, le code est d'abord exécuté (même si la condition est fausse) et la condition est ensuite vérifiée.
Voici notre exemple :
int i = 0;
do CreateItemOnObject( "item", oPlayer );
while (i++ < 100 );
ou
int i = 0;
do
{
CreateItemOnObject( "item", oPlayer );
}
while ( i++ < 100 );
c. l'instruction switch.
L'instruction switch est en fait une sorte de if mais imbriqué : vous allez tester la valeur d'un entier (généralement) et en fonction des différents cas, vous utiliserez une instruction switch.
Voici un exemple :
int i = Rand( 3 );
if ( i == 0 )
instruction1;
else if ( i == 1 )
instruction2;
else
instruction3;
Avec switch ceci donnerait :
int i = Rand( 3 );
switch ( i )
{
case 0 :
instruction1;
break;
case 1 :
instruction1;
break;
default :
instruction3;
break;
}
Rien de bien compliqué dans cet exemple : cependant, vous remarquerez l'instruction break, dont nous reparlerons plus tard : celle-ci permet de sortir du bloc switch.
Voici un exemple sans ce break :
int i = Rand( 3 );
int j = 0 :
switch ( i )
{
case 0 :
j++;
case 1 :
j++;
default :
j++;
}
Au sortir du bloc switch j aura la valeur suivante : 1 si i = 2, 2 si i = 1, et 3 si i = 0.
d. les instructions break et continue.
Les instructions break (que nous avons entrevue au paragraphe précédent) et continue sont surtout utilisées pour les boucles (sauf pour break, qui est aussi utilisée dans switch) : break va comme son nom le suppose arrêter l'exécution de la boucle (ou de l'instruction switch le cas échéant) tandis que continue va elle passer à l'itération suivante en ignorant le code située après continue.
Reprenons notre exemple de boucle infinie : même si c'est déconseillé, une boucle infinie est parfois souhaitable, il faut juste prévoir une condition inévitable afin que la boucle puisse être interrompue.
int i = 0;
while ( i != 10 )
{
i++;
if ( i == 9 )
i = 11;
}
Dans cet exemple, on ne passait jamais par 10 causant ainsi une boucle infinie. On pourrait bien sûr utiliser la forme suivante :
int i = 0;
while ( i != 10 )
{
i++;
}
Mais pour bien montrer l'utilisation de break, nous ne ferons que corriger notre exemple :
int i = 0;
while ( i != 10 )
{
i++;
if ( i <= 9 )
i = 11;
else
break;
}
Ici, nous avons l'assurance que la boucle ne sera pas infinie : quand i sera supérieur à 9, alors la boucle sera interrompue ainsi que le code qui suit l'instruction break. Par exemple :
int i = 0;
while ( i != 10 )
{
i++;
if ( i <= 9 )
i = 11;
else
{
break;
i = 100;
}
}
Lorsqu'on testera la valeur de i, i vaudra 10 mais pas 100 comme le laisse supposer l'exemple. Si vous voulez que i prenne la valeur de 100 : vous devrez placer l'instruction i = 100; avant le break.
L'utilisation de break est strictement la même avec les boucles for, et do while (ainsi que le bloc switch, bien que dans le bloc switch il y ait largement moins de chances d'obtenir une boucle infinie... et pour cause !)
La seconde instruction, continue, va vous permettre de passer à l'itération suivante en ignorant le code suivant « continue ».
Par exemple : vous voulez construire un script qui fait varier la variable i de 1 à 25. Et tout les 5, vous voulez ajouter '+1' à la variable j.
int i, j = 0;
for (i = 1; i <= 25; i++ )
{
if ( (i % 5) != 0 )
continue;
j++;
}
L'exemple est très simple : si le reste de la division entière de i par 5 ne vaut pas 0, on continue la boucle jusqu'à atteindre une valeur de i telle que i % 5 = 0 (i Modulo 5). Quand on atteint cette valeur de i alors on incrémente j.
Ceci vous montre aussi qu'avec l'instruction continue on « part » directement à l'itération suivante sans se soucier du reste.
L'instruction continue fonctionne aussi avec les boucles while et do ... while.
C'est à peu près tout ce qu'il y a savoir pour les structures de contrôle. Sachez juste vous en servir avec parcimonie et préférez la boucle for lorsque vous désirez faire varier un entier d'une valeur de départ x vers une valeur de fin y. Il existe un autre moyen plus rudimentaire de faire des boucles qui consiste à se servir de fonctions : c'est justement ce que nous verrons dans la section suivante.