NWScript - pour niveau 6

Désormais, c'est à Neverwinter Nights 2 que nous allons nous adresser à travers les événements. Il s'agira ici de créer le cerveau principal du module, en lui attribuant des scripts.

Lors de ce niveau 6, nous allons découvrir les différents événements qu'un module supporte. Les scripts que le module exécutera seront la souche de votre serveur. Vous découvrirez notamment comment un script peut en lancer un autre, et comment faire naviguer des informations entre plusieurs scripts.



NWScript - Page générale

NWScript - pour niveau 6
- Evénements du module -

NWScript - pour niveau 7
- Evénements d'un plaçable, d'une porte -

NWScript - pour niveau 8
- Evénements d'une zone, d'un déclencheur, d'une rencontre -

NWScript - pour niveau 9
- Evénements d'une conversation, d'une échoppe -

NWScript - pour niveau 10
- Evénements d'une créature -

Evénements du module...

Il est bon de noter que le module est considéré comme un objet. La fonction de type "object" qui illustre parfaitement cet état de fait est "GetModule()".

Le module est donc à ce titre, le support de 18 événements que nous allons découvrir dans l'ordre suivant :
- lors du chargement
- lors du démarrage
- lors de la connexion d'un joueur
- lors de la fin du chargement d'un joueur
- lors de la déconnexion d'un joueur
- lors du repos d'un joueur
- lors de l'agonie du joueur
- lors de la mort d'un joueur
- lors du respawn d'un joueur
- lors du passage au niveau supérieur d'un joueur
- lors de l'acquisition d'un item par un joueur
- lorsqu'un joueur équipe un item
- lorsqu'un joueur retire un item équipé
- lors de l'activation d'un item par un joueur
- lors de la perte d'un objet par un joueur
- lors de l'arrêt d'une cinématique par un joueur
- événement récurent
- événement défini par l'utilisateur

Etant le support de chacun de ces événements, le module pourra être désigné par la commande "OBJECT_SELF".

... Lors du chargement
     (OnModuleLoad)

Il s'agit de la phase de démarrage du module. Le script correspondant à cet événement n'est exécuté qu'une seule fois. Par ailleurs, une très ancienne malédiction veut qu'un maximum de joueurs se connectent lors de cette phase, pouvant mettre à mal la stabilité du module.

Aucun objet autre que le module lui-même n'est susceptible de déclencher cet événement.

Sur un module persistant, par exemple, on peut charger une date préalablement sauvegardée grâce au script suivant :

void main()
{
    // En admettant que j'ai sauvegardé la date
    // dans les variables "de campagne" suivantes :
    int nAn = GetCampaignInt("date_module","DATE_ANNEE");
    int nMois = GetCampaignInt("date_module","DATE_MOIS");
    int nJour = GetCampaignInt("date_module","DATE_JOUR");

    // Je mets à jour la date :
    SetCalendar(nAn, nMois, nJour);
}

... Lors du lancement
     (OnModuleStart)

Hérité de NWN 1, cet événement n'est pas fonctionnel. Il laisse désormais la place à l'événement OnModuleLoad.

... Lors de la connexion d'un joueur
     (OnClientEnter)

Cet événement est déclenché à chaque fois qu'un joueur se connecte. A ce moment est donc exécuté le premier script pouvant affecter un personnage joueur. Ceci dit, pendant cettte phase, le joueur ne voit que l'écran de chargement et toutes les informations relatives à son personnage ne sont pas totalement chargées.

La fonction permettant de retrouver l'objet ayant déclenché cet événement est "GetEnteringObject()". Le terme de "joueur" comprend également les MDs (Maîtres de Donjon).

... Lors de la fin du chargement d'un joueur
     (OnClientLoaded)

Cet événement complète l'événement déclenché "lors de la connexion d'un joueur", en se déclenchant lorsqu'un personnage joueur a fini sa phase de chargement. Celui-ci est pleinement fonctionnel, et peut être affecté de toutes les manières possibles et inimaginables.

Au même titre que lors d'une connexion, la fonction permettant de retrouver l'objet ayant déclenché cet événement est "GetEnteringObject()". Le terme de "joueur" comprend également les MDs.

Par exemple, un script téléportant le joueur à un endroit du jeu peut être utilisé lors de cet événement :

void main()
{
    // En admettant qu'il existe, dans le module, un
    // point de passage portant le tag "WP_CONNEXION" :
    object oWaypoint = GetObjectByTag("WP_CONNEXION");

    // Je transporte le joueur à cet endroit :
    AssignCommand(GetEnteringObject(),ActionJumpToObject(oWaypoint));
}

... Lors de la déconnexion d'un joueur
     (OnClientLeave)

Cet événement est déclenché à chaque fois qu'un joueur se déconnecte.

La fonction permettant de retrouver l'objet ayant déclenché cet événement est "GetExitingObject()". Quoi qu'il advienne, il s'agira d'un "joueur" (MD inclus). Notez que l'on ne pourra pas vraiment interagir avec lui, si ce n'est récupérer certaines informations.

... Lors du repos d'un joueur
     (OnPlayerRest)

Cet événement est déclenché de trois manières différentes : Lorsque le joueur se repose, interrompt, ou achève sa phase de repos.

Trois constantes permettent de différencier ces différentes étapes :
- REST_EVENTTYPE_REST_STARTED : Sa valeur de 0 désigne le début d'un repos.
- REST_EVENTTYPE_REST_CANCELLED : Sa valeur de 1 désigne l'annulation d'un repos.
- REST_EVENTTYPE_REST_FINISHED : Sa valeur de 2 désigne la fin d'un repos.

Pour savoir quelle est la phase de repos exacte, la fonction "GetLastRestEventType()" renvoie une valeur que l'on peut ainsi comparer aux précédentes constantes

La fonction permettant de retrouver le joueur ayant déclenché cet événement est "GetLastPCRested()". Quoi qu'il advienne, il s'agira d'un "joueur" (MD inclus). Notez que l'on ne pourra pas vraiment interagir avec lui, si ce n'est récupérer certaines informations.

Une base de script peut donc être écrite de cette manière :

void main()
{
    // Ma variable "oJoueur désignera le joueur
    // ayant déclenché la phase de repos
    object oJoueur = GetLastPCRested();

    // Ma variable "nPhase" désignera la phase
    // de repos concernée
    int nPhase = GetLastRestEventType();

    // Il ne me reste plus qu'à faire plusieurs
    // portions de code selon la phase de repos :

    // Si le joueur commence à se reposer :
    if( nPhase == REST_EVENTTYPE_REST_STARTED )
    {
        // (portion de code)
    }

    // ou s'il a annulé sa phase de repos :
    else if( nPhase == REST_EVENTTYPE_REST_CANCELLED )
    {
        // (portion de code)
    }

    // ou s'il a terminé sa phase de repos :
    else if( nPhase == REST_EVENTTYPE_REST_FINISHED )
    {
        // (portion de code)
    }
}

... Lors de l'agonie du joueur
     (OnPlayerDying)

Cet événement est déclenché lorsque les points de vie d'un personnage joueur tombent entre 0 et -9 inclus.

La fonction permettant de retrouver le joueur ayant déclenché cet événement est "GetLastPlayerDying()".

Par défaut il n'existe pas de script prenant en charge l'agonie d'un joueur, à moins de le faire soi-même. Par conséquent, il vaut mieux "tuer" le joueur afin de déclencher le script correspondant à l'événement "OnPlayerDeath". Un script comme celui-ci peut faire l'affaire :

void main()
{
    // J'applique un effet de mort au joueur en train
    // d'agoniser (l'effet "surnaturel" est juste une sécurité) :
    effect eDeath = EffectSupernaturalEffect(EffectDeath());

    ApplyEffectToObject(DURATION_TYPE_INSTANT,eDeath,GetLastPlayerDying());
}

... Lors de la mort d'un joueur
     (OnPlayerDeath)

Cet événement fonctionne de pair avec l'événement précédent, et se déclenche lorsque les points de vie d'un personnage joueur tombent en dessous de -9.

La fonction permettant de retrouver le joueur ayant déclenché cet événement est "GetLastPlayerDied()".

Voici un script par défaut, permettant au joueur de voir la fenêtre de mort apparaître :

void main()
{
    // Je fais apparaître la fenêtre de mort
    // au personnage joueur venant de mourir :
    DisplayGuiScreen(GetLastPlayerDied(),"SCREEN_DEATH_DEFAULT",FALSE);
}

... Lors du respawn d'un joueur
     (OnPlayerRespawn) :

Désormais, cet événement n'est plus actif. Il a été remplacé par deux scripts : "gui_death_respawn" et "gui_death_respawn_self". L'un des deux est sensé agir lors d'une partie en mode solo, et l'autre en mode multijoueur. Pour plus de sûreté, je vous conseille de modifier les deux scripts en faisant de l'un la parfaite copie de l'autre.

Le personnage joueur devient alors le support du script en appuyant sur le bouton "Respawn". Il peut donc être retrouvé grace à la commande "OBJECT_SELF". En général, le personnage joueur est soigné puis téléporté vers un point de passage, de cette manière :

void main()
{
    // Le personnage joueur est assimilé à "OBJECT_SELF"
    object oJoueur = OBJECT_SELF;

    // Les quatre fonctions ci-dessous permettent
    // de supprimer toute fenêtre de mort active.
    // ça ferait désordre de les laisser après un respawn...
    CloseGUIScreen(oJoueur,"SCREEN_DEATH_DEFAULT");
    CloseGUIScreen(oJoueur,"SCREEN_PARTY_DEATH");
    CloseGUIScreen(oJoueur,"SCREEN_HIDDEN_DEATH");
    SetLocalInt(oJoueur,"__bKnockOutPopUpDisplayed",FALSE);

    // Comme un personnage "mort" ne peut pas
    // être téléporté, je le ramène à la vie et le soigne
    // AVANT de l'envoyer vers le lieu de respawn :
    effect eResurrect= EffectResurrection();
    ApplyEffectToObject(DURATION_TYPE_INSTANT,eResurrect,oJoueur);
    ForceRest(oJoueur);

    // Maintenant que le personnage est comme neuf,
    // Je le transporte vers mon lieu de respawn, soit
    // un point de passage portant le tag "WP_RESPAWN" :
    object oWaypoint = GetObjectByTag("WP_RESPAWN");
    AssignCommand(oJoueur,ActionJumpToObject(oWaypoint));
}

... Lors du passage au niveau supérieur d'un joueur
     (OnPlayerLevelUp)

Cet événement est déclenché lorsqu'un joueur confirme son passage au niveau supérieur. Si le joueur annule son passage de niveau, l'événement n'est donc pas déclenché.

La fonction permettant de retrouver le joueur ayant déclenché cet événement est "GetPCLevellingUp()".

Un script vicieux est d'annuler la montée de niveau d'un joueur. C'est très mesquin mais ce script peut procurer des sensations étranges chez la personne l'utilisant. Notez la manière dont je retrouve le niveau d'un joueur ayant un facteur de puissance modifié (par exemple un drow ou un aasimar) :

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

    // Je vais récupérer son niveau
    // grâce à la fonction "GetHitDice(...)" :
    int nNiveau = GetHitDice(oJoueur);

    // Il s'agit là de son niveau effectif.
    // Grâce à une fonction précise, je vais pouvoir
    // fouiller dans un fichier 2DA spécifique, afin
    // de récupérer son "Facteur de puissance", et
    // ainsi calculer son niveau théorique :

    // Je récupère en premier lieu sa sous-race :
    int nSubRace = GetSubRace(oJoueur);

    // Puis l'ajustement du Facteur de puissance de
    // cette sous-race, dans le fichier 2DA correspondant.
    // Il s'agit d'une valeur "string" :
    string sAjust = Get2DAString("racialsubtypes","ECL",nSubRace);

    // La valeur obtenue précédemment est un chiffre.
    // Je peux donc la convertir en valeur de type "int",
    // et l'ajouter au niveau du joueur. Si la sous-race
    // n'a pas d'ajustement, pas de soucis. le niveau ne
    // sera simplement pas modifié.
    nNiveau = nNiveau + StringToInt(sAjust);

    // Je calcule la limite d'XP pour son niveau actuel :
    int nXP = nNiveau * (nNiveau - 1) * 500;

    // Puis j'ajuste son XP à la limite moins 1, juste
    // pour de rire. Il perdra alors son niveau...
    SetXP(oJoueur, (nXP-1));

    // Avouez que c'est tordant...
}

... Lors de l'acquisition d'un item par un joueur
     (OnAcquireItem)

Cet événement est déclenché lorsqu'un joueur acquiert un item (objet pouvant être stocké dans l'inventaire).

La fonction permettant de retrouver le joueur ayant acquis un item est "GetModuleItemAcquiredBy()".

La fonction permettant de retrouver l'item acquis par le joueur est "GetModuleItemAcquired()".

S'il y a lieu, la fonction permettant de retrouver l'objet d'où provient cet item est "GetModuleItemAcquiredFrom()".

S'il y a lieu, la fonction permettant de connaître le nombre d'items empilés est "GetModuleItemAcquiredStackSize()".

Cet événement peut être soumis au système "TagBased" décrit sommairement ICI (français), ou LÀ (anglais). Ce système permet l'exécution d'un script précis, selon le "tag" de l'item.

... Lorsqu'un joueur équipe un item
     (OnPlayerEquipItem)

Cet événement est déclenché lorsqu'un joueur équipe son personnage d'un item.

La fonction permettant de retrouver le joueur s'étant équipé d'un item est "GetPCItemLastEquippedBy()".

La fonction permettant de retrouver l'item dont s'est équipé le joueur est "GetPCItemLastEquipped()".

Cet événement peut être soumis au système "TagBased" décrit sommairement ICI (français), ou LÀ (anglais).

... Lorsqu'un joueur retire un item équipé
     (OnPlayerUnequipItem)

Cet événement est déclenché lorsqu'un joueur retire un item équipé par son personnage.

La fonction permettant de retrouver le joueur ayant retiré un item équipé est "GetPCItemLastUnequippedBy()".

La fonction permettant de retrouver l'item équipé retiré par le joueur est "GetPCItemLastUnequipped()".

Cet événement peut être soumis au système "TagBased" décrit sommairement ICI (français), ou LÀ (anglais).

... Lors de l'activation d'un item par un joueur
     (OnActivateItem)

Cet événement est déclenché lorsqu'un joueur active un item.

Cet item doit bénéficier d'au moins une des propriétés suivantes :
    - Lancer un sort :
        - Activation d'objet (contact)
        - Activer objet
        - Activer objet (longue portée)

La fonction permettant de retrouver le joueur ayant activé un item est "GetItemActivator()".

La fonction permettant de retrouver l'item activé par le joueur est "GetItemActivated()".

S'il y a lieu, la fonction permettant de l'objet ciblé par un item activé est "GetItemActivatedTarget()".

S'il y a lieu, la fonction permettant de la localisation de l'objet ciblé par un item activé est "GetItemActivatedTargetLocation()".

Cet événement peut être soumis au système "TagBased" décrit sommairement ICI (français), ou LÀ (anglais).

... Lors de la perte d'un objet par un joueur
     (OnUnAcquireItem)

Cet événement est déclenché lorsqu'un joueur se sépare d'un item.

La fonction permettant de retrouver le joueur s'étant séparé d'un item est "GetModuleItemLostBy()".

La fonction permettant de retrouver l'item dont s'est séparé le joueur est "GetModuleItemLost()".

Cet événement peut être soumis au système "TagBased" décrit sommairement ICI (français), ou LÀ (anglais).

... Lors de l'arrêt d'une cinématique par un joueur
     (OnCutsceneAbort)

Cet événement est déclenché lorsqu'un joueur quitte une scène cinématique.

La fonction permettant de retrouver le joueur ayant quitté une cinématique est "GetLastPCToCancelCutscene()".

... Evénement récurent
     (OnHeartbeat)

Cet événement est déclenché toutes les 6 secondes par le module. Il n'y a pas d'objet associé au déclenchement de cet événement.

... Evénement défini par l'utilisateur
     (OnUserDefined)

Cet événement est utilisé afin de réagir à d'autres événements, ou de définir des événements personnalisés.

Il est déclenché par une commande spécifique lors de l'exécution d'un autre script, dans lequel on utilisera les commandes suivantes :

1°) "EventUserDefined(...)" qui permet de définir un numéro d'identification à l'événement. Il ne comporte qu'un paramètre, celui-ci devant être le numéro d'ID à signaler. Mieux vaut utiliser un nombre élevé par sécurité (3000 ou plus). Cette fonction est d'un type particulier, il s'agit du type "event".

2°) "SignalEvent(...)" permettant d'envoyer un signal à un objet précis. Ce signal comportera une ID définie grâce à la fonction "EventUserDefined(...)". Cette fonction comporte deux paramètres, permettant :
- de définir l'objet pour lequel l'événement "OnUserDefined" sera déclenché
- de désigner le numéro d'ID de l'événement à signaler

Par exemple, dans le script lié à l'événement "OnDamaged" d'une créature, je vais envoyer un signal au module. Dans le script lié à l'événement "OnDeath" de cette même créature, j'enverrai un autre signal:

// Script "OnDamaged" de la créature :

void main()
{
    // Je crée ma première ID :
    event eEvent = EventUserDefined(3000);

    // puis j'envoie un signal au module, qui déclenchera
    // le script défini dans l'événement "OnUserDefined" :
    SignalEvent(GetModule(), eEvent);
}

// Script "OnDeath" de la créature :

void main()
{
    // Je crée ma deuxième ID :
    event eEvent = EventUserDefined(3001);

    // puis j'envoie un signal au module, qui déclenchera
    // le script défini dans l'événement "OnUserDefined" :
    SignalEvent(GetModule(), eEvent);
}

Enfin, dans le script lié à l'événement "OnUserDefined" du module, la fonction "GetUserDefinedEventNumber()" nous permettra de déterminer quel est l'ID du signal envoyé. Je pourrai ainsi faire la différence entre mes deux signaux :

// Script "OnUserDefined" du module :

void main()
{
    // Je regarde quelle est l'ID reçue :
    int nEvent = GetUserDefinedEventNumber();

    // S'il s'agit de l'événement 3000 :
    if(nEvent == 3000)
    {
        // J'envoie un message idiot sur le canal "crier" :
        string sMessage = "Arrêtez de taper mes créatures !";
        SpeakString(sMessage, TALKVOLUME_SHOUT);
    }

    // S'il s'agit de l'événement 3001 :
    else if(nEvent == 3001)
    {
        // J'envoie un autre message idiot :
        string sMessage = "Arrêtez de tuer mes créatures !";
        SpeakString(sMessage, TALKVOLUME_SHOUT);
    }
}

Vous avez gagné un niveau !

Vous avez désormais les principaux outils pour commencer à faire battre le coeur de votre module. Notez bien les particularités de chaque événement, car chacun d'entre eux recèle de nombreux secrets.

A vos claviers, et à très bientôt.




NWScript - Page générale

NWScript - pour niveau 6
- Evénements du module -

NWScript - pour niveau 7
- Evénements d'un plaçable, d'une porte -

NWScript - pour niveau 8
- Evénements d'une zone, d'un déclencheur, d'une rencontre -

NWScript - pour niveau 9
- Evénements d'une conversation, d'une échoppe -

NWScript - pour niveau 10
- Evénements d'une créature -

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é.