J'espère faire ici un petit regroupement des bonnes pratiques de codage à employer sous NWN. Cela s'adresse principalement à des gens qui savent coder (ou qui apprennent), afin de leur donner une méthodologie de travail pour leur module.
J'étofferais sûrement ce post à l'avenir.
Sommaire- Bien nommer ses scripts
- De l'importance des noms de variables
- Créez vos constantes
- Factoriser : Les bibliothèques de fonctions
- Utiliser les variables
1. Bien nommer ses scripts
C'est important, et pourtant peu nombreux sont ceux qui y pensent vraiment. Les noms des scripts sont très importants ; ils doivent indiquer leur rôle, et l'élément sur lequel ils s'appliquent, le cas échéant.
C'est une méthodologie à prendre en compte dès la création d'un module. Cela permet d'éviter les soucis du style :
- Ne pas retrouver un script parmi la centaine que vous avez déjà fait et appelés "truk_bidule" et "machin"
- Refaire des scripts en double pour cette même raison précédente
- Etc...
Adonc, adoptez tout de suite une bonne sémantique des noms de scripts. Pour exemple :
ressource_événement_nom
Le problème, c'est que NWN ne permet qu'un nombre de caractères limités. Utilisez-en un maximum !
Voici des exemples de noms de scripts intelligents :
- Script de porte à mettre sur l'événement OnOpen des portes : door_onopen
- Script de PNJ à mettre sur l'événement OnSpawn des gardes : npc_spawn_gardes
- Script de connexion d'un joueur à mettre sur l'événement OnClientEnter du module : mod_cliententer
- Script de l'item "objet mj" qui se déclenche à l'utilisation : itm_objetmj
- Script de plaçable quelconque : plc_onused
Cela peut sembler inutile, mais c'est une très bonne habitude à prendre, ne serait-ce que pour vos propres nerfs par la suite. Quand vous recherchez un script, vous n'avez plus besoin de vous souvenir comment il s'appelle : il porte un nom logique. Pour vos portes, vous cherchez "
door_", pour votre module vous cherchez "
mod_", etc.
2. De l'importance des noms de variables
Là encore, cela peut vous sembler anodin, mais il n'en est rien. Le soucis de NWN, c'est de ne pas gérer proprement les accents, aussi si vous maîtrisez l'anglais, je vous suggère de faire des noms de variables en anglais.
L'autre "soucis", si c'en est un, est que les scripts par défaut de NWN utilisent des variables créées avec ce qu'on appelle la
notation hongroise, c'est à dire l'ajout d'un caractère devant le nom de variable qui définit son type. Bien que logique en soit, c'est un concept plutôt dépassé en programmations. Du reste, les variables de NWN sont signées, c'est à dire que contrairement au PHP par exemple, elles ont un type propre (
int,
float,
string,
object,
effect...), donc le risque de se tromper est minime, sachant que le compilateur vous signalera que le type de variable n'est pas le bon si vous vous trompez.
Que vous conserviez ou non cette notation, surtout donnez des noms explicites à vos variables ! N'hésitez pas à prendre de la place, la taille des variables n'a pas d'importance.
Songez que vos scripts feront parfois plusieurs dizaines, voire centaines de lignes ! Vous ne vous y retrouverez pas avec des variables nommées "
iXP1", "
sBienv", "
fDistAr". Clairement pas. Alors que l'emploi de noms clairs et explicites, même si longs, vous évitera de chercher à quoi correspond telle ou telle variable : "
xpActuelDuJoueur", "
messageDeBienvenueNouveauJoueur", "
distanceDeTirDesArchers".
C'est forcément plus simple de s'y retrouver, non ?
3. Créez vos constantes
Encore quelque chose qui peut s'avérer utile : la création d'une liste de constantes, au sein d'un script à inclure au besoin. Les constantes sont des sortes de variables, qui restent les mêmes pour tout le monde, et ne peuvent être modifiées une fois créées.
Les constantes s'écrivent toujours en majuscules. Encore une fois, utilisez des noms explicites ! On déclare des constantes comme une variable normale, si ce n'est qu'on ajoute le tag
const devant.
const string NOM_MODULE = "Mon super module";
const int TEMPS_DE_REPOS = 2;
C'est simple à faire, ça coûte rien, et
ça fait ben d'bien au chien !
L'intérêt ? Imaginez que vous gériez une base de données (c'est courant pour un module persistant). Vous allez devoir faire plusieurs scripts, plusieurs fonctions qui appellent ces bases de données, pour les charger ou écrire dedans. Maintenant que vous avez plus d'une trentaine de scripts qui appellent la BDD, horreur, vous devez changer le nom d'une des tables de la base ! Il va falloir modifier tous ces scripts, ça va faire des bugs à cause d'oublis, et un travail conséquent.
Sauf si vous définissez des constantes ! Exemple :
On définit une constante pour l'utilisation de la table "
GestionBanque", qui recevra les sommes détenues par chaque personnage.
const string BDD_BANQUE = "GestionBanque";
Pour chaque fonction et script, plutôt que de définir le nom de la table "
GestionBanque", vous allez faire un appel à la constante "
BDD_BANQUE". Ainsi, il suffira de modifier cette constante une seule fois pour que la modification soit prise en compte par tous les scripts qui font référence à cette constante.
4. Factoriser : Les bibliothèques de fonctions
Créez vos propres fonctions ! Plutôt que d'utiliser le même bout de code plusieurs fois, il suffit simplement de créer une fonction, et de l'appeler ! C'est simple et efficace. Pour modifier la fonction sur l'ensemble du module, il n'y a qu'un seul endroit où le gérer ! C'est ça, factoriser.
Exemple : Un script simple qui donne une certaine quantité d'xp en fonction de la race du personnage :
void main()
{
object oPC = /*à définir*/;
int xpFinalRecu;
switch(GetRacialType(oPC))
{
case RACIAL_TYPE_DWARF : iXPfinal = 500; break;
case RACIAL_TYPE_ELF : iXPfinal = 250; break;
case RACIAL_TYPE_GNOME : iXPfinal = 350; break;
case RACIAL_TYPE_HALFELF : iXPfinal = 350; break;
case RACIAL_TYPE_HALFLING : iXPfinal = 400; break;
case RACIAL_TYPE_HALFORC : iXPfinal = 100; break;
case RACIAL_TYPE_HUMAN : iXPfinal = 600; break;
default: iXPfinal = 400; break;
}
GiveXPToCreature(oPC, xpFinalRecu);
}
Vous allez donc écrire ce script autant de fois que nécessaire, entièrement. Ça peut vouloir dire 40 scripts identiques, juste pour ça. La solution ? Faire une fonction !
int XPRecompenseParRace(object oPC, int iXP)
{
int iXPfinal;
switch(GetRacialType(oPC))
{
case RACIAL_TYPE_DWARF : iXPfinal = iXP*50/100; break;
case RACIAL_TYPE_ELF : iXPfinal = iXP*50/100; break;
case RACIAL_TYPE_GNOME : iXPfinal = iXP*75/100; break;
case RACIAL_TYPE_HALFELF : iXPfinal = iXP; break;
case RACIAL_TYPE_HALFLING : iXPfinal = iXP*75/100; break;
case RACIAL_TYPE_HALFORC : iXPfinal = iXP; break;
case RACIAL_TYPE_HUMAN : iXPfinal = iXP; break;
default: iXPfinal = iXP; break;
}
return iXPfinal;
}
Ainsi vous avez une seule fonction qui renvoie une valeur de type int, l'xp calculé que vous voulez donner à vos joueurs. Dans vos scripts, il suffira alors d'appeler la fonction, de cette manière :
GiveXpToCreature(oPC, XPRecompenseParRace(oPC, 1000));
Bien entendu, vous écrivez vos fonctions dans un script que vous incluez par la suite lorsque vous en avez besoin.
5. Utiliser les variables
NWN permet d'ajouter des variables automatiques aux plaçables et aux pnj, ainsi que sur le module et les zones. Ces variables se trouvent dans les propriétés des objets, généralement dans l'onglet "avancés".
vous avez à disposition 3 types de variables :
int,
float,
string.
À partir de là, vous pouvez donc ajouter des variables sur chaque objet de votre choix, par exemple une porte, pour définir si elle doit se fermer la nuit par exemple, ou au bout de combien de temps elle doit se refermer.
Vous pouvez donc allègrement créer des scripts génériques, qui lancent des fonctions si une variable et présente, ou exécutent telle ou telle action en fonction de ces mêmes variables.
Un exemple simple d'utilisation : Certaines zones autorisent ou non le port d'arme dans une main il suffit d'ajouter dans les variables de la zone un int "
armeInterdite", et de mettre la valeur à
1. Sur l'événement OnEnter des zones, un seul script pour toutes les zones, vous vérifiez par une condition si la zone possède la variable "
armeInterdite", et si c'est le cas, vous exécutez un bout de code pour déséquiper automatiquement une arme.
Ainsi vous pouvez avoir un seul script pour toutes vos portes, toutes vos zones, etc. !
Conclusion
Il y a sûrement des choses à ajouter. Je mettrais peut-être d'avantages d'exemples plus concrêts. Le but ici est surtout de sensibiliser à l'utilisation intelligente des ressources offertes par Aurora pour avoir un module le plus optimisé possible, car c'est ce qui permet à ce dernier d'être stable et évolutif.
En espérant que ce post vous ait été utile.