NWScript - pour niveau 4

Vous savez désormais tenir une discussion avec NWScript ?

Voici à présent le moyen d'inclure vos propres mots dans le langage complexe de NWScript. Vous verrez comment définir vos propres constantes, donner vos ordres avec vos propres fonctions, et créer vos propres dictionnaires grace aux bibliothèques. Vous pouvez à présent fermer le manuel de l'apprenti sorcier, et ouvrir le grand livre de l'archimage.

Nettoyez un brin le verre de votre monocle, et entrez dans le monde de l'abstrait.



NWScript - Page générale

NWScript - pour niveau 1
- Bases et notions de programmation -

NWScript - pour niveau 2
- Conditions if/else et opérateurs ET / OU -

NWScript - pour niveau 3
- Boucles while/for et sélection switch/case -

NWScript - pour niveau 4
- Constantes, fonctions, et bibliothèques -

NWScript - pour niveau 5
- Variables persistantes et exécution de scripts -

Constantes

Qu'est-ce qu'une constante ?

Il s'agit d'une sorte de variable. Mais celle-ci a un nom défini, et on peut y faire référence afin d'obtenir une valeur précise.

Dans NWScript, la valeur qu'une constante détient nous permet principalement de remplir un paramètre d'une fonction. Bien qu'il soit possible d'utiliser directement sa valeur, son nom nous sert essentiellement de repère. Car il est bien plus aisé de marquer le nom d'une constante plutôt que retenir par coeur les valeurs de toutes les constantes utilisées par NWScript. Pour résumer, les constantes sont là pour nous simplifier la vie.

A titre d'exemple, examinons les constantes suivantes :

- ABILITY_STRENGTH : sa valeur est de 0*.
- ABILITY_DEXTERITY : sa valeur est de 1.
- ABILITY_CONSTITUTION : sa valeur est de 2.
- ABILITY_INTELLIGENCE : sa valeur est de 3.
- ABILITY_WISDOM : sa valeur de est 4.
- ABILITY_CHARISMA : sa valeur de est 5.

* Attention, 0 est aussi une valeur !

Une fonction telle que "GetAbilityScore(...)" interprétera les constantes ci-dessus pour pouvoir désigner une caractéristique d'un personnage. Je peux donc récupérer la valeur de la caractéristique "force" d'un personnage de la manière suivante :

// Je désigne la caractéristique
// avec la constante :
int nForce = GetAbilityScore(oPersonnage, ABILITY_STRENGTH);

... mais comme c'est la valeur de la constante qui est importante, je peux tout à fait l'écrire de cette manière :

// Je désigne la caractéristique
// avec la valeur de la constante :
int nForce = GetAbilityScore(oPersonnage, 0);

Notez bien que les constantes "ABILITY_" ne font pas elles-même référence aux caractéristiques d'un personnage. C'est à la fonction "GetAbilityScore(...)" d'interpréter la valeur de la constante, et de pointer sur la caractéristique choisie. Ainsi, il est possible d'utiliser directement la valeur de la constante, ou même une constante "hors-sujet" ayant une valeur utilisable par la fonction. Mais pour ne pas s'emmêler les pinceaux, mieux vaut utiliser une constante dont le nom est explicite, ou utiliser sa valeur.


Il est possible de créer nos propres constantes. Attention cependant à ne pas utiliser un nom déjà pris par l'ensemble des constantes de NWScript. Pour pouvoir créer une constante, il faudra l'annoncer hors du champ défini par "void main()". On utilisera la commande "const", suivie du type de la valeur, suivi du nom de la constante. Enfin, pour lui donner une valeur, on procèdera de la même manière que pour une variable :

const float MA_CONSTANTE_FLOAT = 1.0;

const int MA_CONSTANTE_INT = 1;

const string MA_CONSTANTE_STRING = "un";

Au même titre que les variables, le choix du nom d'une constante est totalement arbitraire. Il peut comporter des lettres, des chiffres, mais pas d'espace. Certains caractères spéciaux seront interdits pour nommer une constante.

D'où son nom, une fois qu'une constante est définie, sa valeur ne change plus. Mais son existence est également limitée au script dans lequel elle sera créée. On ne pourra créer que des constantes de type "float", "int", ou "string".

Fonctions personnalisées

Tout comme les constantes, des fonctions peuvent être créées selon le besoin. Créer une fonction nous sert particulièrement lorsqu'une portion de code doit être répétée à plusieurs endroits d'un même script.

Toujours similaire à la création d'une constante, il faudra déclarer l'existence de la fonction hors du champ défini par "void main()". Il faudra en premier lieu annoncer le type de la fonction, suivi de son nom. Ensuite entre parenthèses, seront définis les paramètres à utiliser. Dans le cas d'une fonction ne nécessitant aucun paramètre, il faudra tout de même laisser les parenthèses. Le nom d'une fonction est soumis à la même règle qu'une variable ou qu'une constante. Les fonctions peuvent être de n'importe-quel type.

Pour définir les paramètres d'une fonction, on emploiera la même méthode que la déclaration d'une variable. Toutefois, ces paramètres seront internes à la fonction, et ne seront pas reconnus au dela de celle-ci. Ils peuvent être de n'importe-quel type, et une valeur par défaut peut leur être attribuée selon le type choisi. Si plusieurs paramètres sont déclarés, il faudra les séparer par une virgule et mettre en dernier ceux qui ont une valeur par défaut.

Enfin, il faudra mettre entre accolades la portion de code que la fonction devra exécuter.

Voici un exemple de création de fonction :

int MaFonctionInt(object oObjet, effect eEffet, int nInt=123)
{
    // Je mettrai ici la portion de code
    // que la fonction devra exécuter
}

Créer une fonction peut représenter une entreprise délicate. Etant hors du champ "void main()", on ne peut pas désigner quelque chose de concret. Ceci dit, un script connait son propriétaire, et "OBJECT_SELF" pourra être utilisé.

Une fonction est en quelque sorte indépendante du script, dans la mesure où le code qu'elle contient lui est propre. Et écrire une fonction ressemble à écrire un script, mis à part le fait que les choses doivent être pensées de manière plus "abstraite". Une fonction aura également la charge de renvoyer une valeur correspondant à son type, excepté pour les fonctions de type "void".

A la question "Ma fonction a-t-elle besoin de paramètre ?", la réponse dépendra de ce dont la fonction a besoin pour avoir des repères. Un paramètre est un lien entre la fonction personnalisée et le script, un moyen de transformer l'abstrait en concret. Si vous n'avez pas besoin de repères pour faire le lien, n'en mettez pas.

Voyons en premier lieu comment faire une fonction de type "void" sans paramètre. Disons que cette fonction nous permettra de faire la liste des joueurs connectés sur le module, et d'envoyer le nombre de joueur sous la forme d'un message sur le canal "crier". Comme d'habitude, je mettrai le script qui suit dans l'événement "déclenché par l'utilisation" d'un plaçable utilisable :

void MaisQuiDoncEstSurLeModule()
{
    // Comme je vais faire une boucle,
    // je vais définir une variable à incrémenter
    // qui au final me donnera le nombre de
    // joueurs connectés au module.
    int nNombre = 0;

    // Je récupère le premier objet de la liste
    // des joueurs connectés au module.
    object oJoueur = GetFirstPC();

    // et tant que oJoueur est un objet "valide"
    while ( GetIsObjectValid(oJoueur) == TRUE )
    {
        // J'incrémente nNombre...
        nNombre++;
        // ... et je passe au prochain objet de la
        // liste des joueurs connectés au module.
        oJoueur = GetNextPC();
    }

    // Une fois le listing terminé, le module va
    // annoncer le nombre de joueurs connectés.
    string sMessage = "Joueur(s) connecté(s) : "+IntToString(nNombre);
    AssignCommand(GetModule(), SpeakString(sMessage, TALKVOLUME_SHOUT));
}

// Maintenant que ma fonction est créée,
// Je peux exécuter mon script.
void main()
{
    // Et je peux utiliser la fonction que
    // j'ai créé avant la partie "void main()".
    MaisQuiDoncEstSurLeModule();
}

C'est le genre de fonction qui n'a pas besoin qu'on lui donne de repères, et qui n'a pas besoin de retourner de valeur.

On se demandera alors où pourra intervenir un éventuel paramètre. Prenons le cas suivant : Je désire que le nombre de joueurs ne soit communiqué qu'à la personne qui utilise le plaçable. Dans ce genre de cas, ma fonction créée est incapable de determiner qui a provoqué l'execution du script. Je devrai passer par le biais d'un paramètre.

Il faut bien réfléchir aux paramètres indispensables pour votre fonction personnalisée. Ici, seul le joueur ayant utilisé le plaçable est indispensable. J'aurai donc besoin d'un paramètre de type "object" pour faire le lien :

void MaisQuiDoncEstSurLeModule(object oObjet)
{
    int nNombre = 0;
    object oJoueur = GetFirstPC();
    while ( GetIsObjectValid(oJoueur) == TRUE )
    {
        nNombre++;
        oJoueur = GetNextPC();
    }

    // Une fois le listing terminé, le module va
    // annoncer le nombre de joueurs au
    // paramètre de type "objet" faisant
    // le lien entre le script et la fonction.
    string sMessage = "Joueur(s) connecté(s) : "+IntToString(nNombre);
    SendMessageToPC(oObjet, sMessage);
}

void main()
{
    // Je récupère l'objet qui a provoqué
    // l'exécution du script.
    object oUtilisateur = GetLastUsedBy();

    // Et je l'utilise comme paramètre dans
    // ma fonction personnalisée.
    MaisQuiDoncEstSurLeModule(oUtilisateur);
}

D'une certaine manière, on pourrait donc dire que ma fonction a copié la valeur de oUtilisateur dans oObjet, pour pouvoir l'utiliser dans sa propre portion de code. Le paramètre a donc servi de lien entre la partie du script à exécuter, et la fonction créée.


Voyons à présent comment créer une fonction devant renvoyer une valeur. Je vais juste reprendre notre premiere fonction "void" sans paramètre, et en faire une fonction de type "int". La valeur qu'elle devra renvoyer devra donc être de type "int". Je n'ai pas choisi ce type par hasard car je désire que ma fonction renvoie le nombre de joueurs détectés :

int MaisCombienSontIlsDonc()
{
    int nNombre = 0;
    object oJoueur = GetFirstPC();
    while ( GetIsObjectValid(oJoueur) == TRUE )
    {
        nNombre++;
        oJoueur = GetNextPC();
    }

    // Une fois le listing terminé, j'utilise la
    // commande "return" pour renvoyer une valeur.
    return nNombre;
}

void main()
{
    // Je récupère l'objet qui a provoqué
    // l'exécution du script.
    object oUtilisateur = GetLastUsedBy();

    // Je récupère le nombre de joueurs grace
    // à ma fonction personnalisée.
    int nJoueurs = MaisCombienSontIlsDonc();

    string sMessage = "Joueur(s) connecté(s) : "+IntToString(nNombre);
    SendMessageToPC(oUtilisateur, sMessage);
}

Notez bien que la commande "return" ne met pas fin au script, tant qu'elle n'est pas utilisée dans la section "void main()". Elle met simplement fin à la fonction. Faites attention toutefois à ce qu'une fonction devant renvoyer une valeur le fasse quoi qu'il arrive. Si ce n'était pas le cas, elle ne serait pas valide. Voici un exemple :

int SuisJeSuperFort(object oObjet)
{
    int nForce = GetAbilityScore(oObjet);
    if ( nForce > 15 )
    {
        return TRUE;
    }
}

Cette fonction est incorrecte, car il manque une valeur à renvoyer dans le cas où la condition n'est pas vérifiée. Je corrige donc la fonction :

int SuisJeSuperFort(object oObjet)
{
    int nForce = GetAbilityScore(oObjet);
    if ( nForce > 15 )
    {
        return TRUE;
    }

    else
    {
        return FALSE;
    }
}

Il est donc possible de faire tout type de fonction, et à moins d'être du type "void", toute fonction devra renvoyer une valeur.

Bibliothèque de fonctions/constantes

Une bibliothèque peut être considérée comme un script, mais elle ne contient pas d'instruction "void main()". Elle nous sert à recueillir nos fonctions et nos constantes personnalisées. Une fois une bibliothèque créée, il suffit de l'inclure dans un script grace à une fonction spéciale. Tout ce que qu'elle contient deviendra valide pour le script qui l'accueille.

Par exemple, je vais inclure mes deux fonctions dans un script que je nommerai "biblio". Je ne mettrai pas d'instruction "void main()", et je compilerai ce script. Il deviendra alors une bibliothèque :

void MaisQuiDoncEstSurLeModule(object oObjet)
{
    int nNombre = 0;
    object oJoueur = GetFirstPC();
    while(GetIsObjectValid(oJoueur) == TRUE)
    {
        nNombre++;
        oJoueur = GetNextPC();
    }
    string sMessage = "Joueur(s) connecté(s) : "+IntToString(nNombre);
    SendMessageToPC(oObjet,sMessage);
}

int SuisJeSuperFort(object oObjet)
{
    int nForce = GetAbilityScore(oObjet);
    if(nForce > 15)
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

Il ne me reste plus qu'à inclure cette bibliothèque dans un script pour qu'elle lui communique ses fonctions. Comme pour une fonction personnalisée, j'utiliserai cette commande hors de la section "void main()" :

// La commande ci-dessous me permet
// d'inclure une bibliothèque à un script.
#include "biblio"

void main()
{
    object oJoueur = GetLastUsedBy();

    // Grace à ma bibliothèque, je peux
    // exécuter les fonctions suivantes :

    // Si oJoueur est super fort,
    if(SuisJeSuperFort(oJoueur) == TRUE);
    {
        // Je lui communique le nombre de
        // joueurs connectés sur le module :
        MaisQuiDoncEstSurLeModule(oJoueur);
    }
}

Vous pouvez créer autant de bibliothèques que vous le voulez, et toutes les inclure dans un seul script. Il est également possible d'inclure une bibliothèque dans une autre. Dans ces deux cas, attention à ne pas nommer deux fonctions/constantes de la même manière, car il y aurait un conflit entre les deux bibliothèques. Tant que les bibliothèques ne communiquent pas entre elles, il n'y a pas lieu de s'inquiéter.

Attention, il faut garder en mémoire qu'une bibliothèque ne se compile pas vraiment. Elle se compile dans le script où elle est incluse. Si une bibliothèque est modifiée, il faudra re-compiler tous les scripts comportant celle-ci.


Le petit plus du programmeur, c'est d'afficher l'aide de sa propre fonction, de la même manière que les fonctions de NWScript. Il suffit de l'annoncer et de commenter un texte juste au dessus, de la manière suivante :

// J'annonce ma fonction...

// Voici l'aide de ma fonction :
// Celle-ci sera affichée dans la
// liste des scripts de NWScript.
void MaFonctionVoid();


// ... et je la développe.
void MaFonctionVoid()
{
    // Instructions de ma fonction
}

Et voici ce que nous verrons dans l'éditeur de script :

image

Notez bien qu'annoncer une fonction se ponctue par un point-virgule. Si vous oubliez cette ponctuation, la fonction sera considérée comme devant contenir du code.

Vous avez gagné un niveau !

A présent vous pouvez créer vos propres instructions, et donner vos propres ordres à NWScript. En maniant avec habileté les bibliothèques, vous pourrez économiser beaucoup de temps en évitant d'écrire plusieurs fois les mêmes lignes de code.

Prenez un peu de repos, car prochainement nous verrons comment devenir plus intimes avec Neverwinter Nights 2.



NWScript - Page générale

NWScript - pour niveau 1
- Bases et notions de programmation -

NWScript - pour niveau 2
- Conditions if/else et opérateurs ET / OU -

NWScript - pour niveau 3
- Boucles while/for et sélection switch/case -

NWScript - pour niveau 4
- Constantes, fonctions, et bibliothèques -

NWScript - pour niveau 5
- Variables persistantes et exécution de scripts -

Réactions


Personne n'a encore réagi. Soyez le premier.

Que pensez-vous de Neverwinter Nights 2 ?

127 aiment, 20 pas.
Note moyenne : (155 évaluations | 5 critiques)
8,4 / 10 - Très bien

315 joliens y jouent, 840 y ont joué.