Cours de Scripts pour les Quiches. Leçon Six.

Répondre
Partager Rechercher
Pour une meilleure lisibilité, ce fil de discussion regroupe les traductions des cours de scripts de Celowin par Amaranthe (merci pour ces contributions ) et uniquement les cours.
Merci de poster vos commentaires ou questions dans les sujets relatifs à chaque leçon :

- Leçon n°1,
- Leçon n°2,
- Leçon n°3,
- Leçon n°4,
- Leçon n°5,
- ...

Merci.

A noter que l'ensemble des messages suivants sont des *copies* (les plus fidèles possibles) des messages de Amaranthe, linkés ci-dessus.
Lightbulb
Cours de Scripts pour les Quiches. Leçon Une.
Par Celowin mailto:james.foxglove@verizon.net
traduit par Amaranthe mailto:amaranthe@free.fr

Introduction
Le but de cette série de leçons, est d'apprendre a se servir des scripts a n'importe qui. Cette première leçon est très simple, et quiconque ayant déjà codé devrait pouvoir s'en passer. Maintenant, il est toujours possible d'y apprendre un truc ou deux.

Je ne m'étendrais pas en détail sur le fonctionnement du toolset, ceci est suppose comme déjà connu.

Ouvrez le Toolset et créez un nouveau module, que nous appellerons 'Test Module'. Créez une nouvelle zone, que nous appellerons 'Test Area 001'. Utilisez la taille et le tileset qui vous plait, cela n'a aucune incidence sur les scripts. Préférez cependant une taille pas trop grande, il vous sera plus facile de vous y retrouver et cela prendra moins de temps à sauvegarder/charger pour les innombrables tests que vous devrez forcément faire.

Placez un npc sur votre carte. Donnez-lui l'apparence que vous voulez.
Puis :
- Clic droit sur le npc.
- Choisissez l'option de menu 'Propriétés' ( properties )
- Changez le tag du NPC en CHANTEUR
- Ouvrez l'onglet des scripts.
- Il y a des scripts par défaut a chaque option. Cliquez sur chacun et supprimez-les.
- Cliquez sur le bouton 'Edit' a coté du script sur le 'On Heartbeat'.
- Tapez le script suivant en respectant les minuscules et les majuscules.

Code PHP:

void main()
{
    
ClearAllActions();
    
ActionSpeakString("Ils sont dans les vignes les moineaux...");
    
ActionWait(1.5);
    
ActionSpeakString("Ils ont manges les raisins...");
    
ActionWait(1.5);
    
ActionSpeakString("Ils ont craches les pepins...");
    
ActionWait(1.5);

- Je vous conseille de tapez le texte plutôt que de le copier/coller. Vous apprendrez mieux ainsi. Et si vous faites une erreur, vous n'en apprendrez que plus.
- Cliquez sur 'Save As' et appelez le tm_chanteur_hb
- Vérifiez que le résultat ressemble à : "0 Errors. ‘tm_singer_hb’ Compiled successfully." Sinon c'est que vous avez du faire une erreur quelque part... revérifiez !
- Fermez l'éditeur de script.
- Cliquez sur "OK" dans la fenêtre du NPC.
- Sauvegardez votre module.
- Relancez le jeu et choisissez 'Other Modules' pour vérifier ce que vous avez fait.
- Votre npc devrait chanter cette ritournelle... encore et encore...


Analyse.
Tentons maintenant d'explique ce que nous venons de faire. Ceci sera fait par le biais de questions / Réponses, en tentant d'imaginer les questions que vous pourriez vous poser.

Pourquoi l'appeler 'Test Module' ?
Cela n'a en fait aucune importance. Le seul intérêt est de le distinguer facilement des autres dans la liste des modules proposés. Pour cela, il est préférable que tous vos modules ne commencent pas par 'Module'...

Pourquoi avoir appelé la zone 'Test Area 001' ? Est-ce que le nom par défaut n'était pas suffisant ?
Pour ce petit module de test, cela ne devrait effectivement pas prêter à conséquences. Mais c'est une bonne habitude à prendre que de renommer les choses que l'on customise, afin que chacune aie un nom unique et non équivoque. Si tout le monde utilise 'Area 001', alors le transfert de zones d'un module a l'autre risque d'être problématique.

Pourquoi avoir donné au npc le tag CHANTEUR ? Pourquoi l'avoir mis tout en majuscules ?
Il est préférable pour des raisons internes au programme, de mettre les tag en majuscules. Vous pourriez très bien ne jamais avoir de problèmes, mais c'est quand même une bonne habitude à prendre.
Je recommande de la même manière de donner à chaque créature ou npc un tag clair et non équivoque. CHANTEUR décrit parfaitement le npc. Il est maintenant plus simple de s'y référer dans un script plus compliqué.
Les tags doivent également rester court. Evitez les tags de plus de 8 caractères.

Pourquoi avoir supprimer tous les scripts par défaut ? A quoi servent-ils si on doit les détruire ?
Les scripts par défaut définissent tout un tas de comportements dont nous n'avons que faire pour le moment. Nous les avons supprimés afin qu'ils n'interfèrent dans notre test. Nous souhaitions que notre npc chante, et rien d'autre.
Nous verrons dans de prochaines leçons comment se servir des scripts par défaut.

Pourquoi avoir placé notre script sur le 'On Heartbeat' ? Pourquoi est ce qu'il y a tant de choix ?
Chacun de ces choix, définit un moment ou le script est appelé. Le 'On Heartbeat' appelle le script a chaque battement de cœur, soit toutes les 6 secondes. C'est pourquoi le chanteur ne s'arrête jamais. Toutes les six secondes, il recommence sa chanson.
Chacun des autres choix a son utilisation, en fait, le "On Heartbeat' est celui qui devrait être le moins utilisé. Trop de scripts lancés toutes les six secondes devraient rapidement mettre votre ordinateur sur les rotules.

Qu'est ce que ce "void main()" ?
Ce petit bout de code n'a l'air de rien, mais essayez donc de l'oublier et vous verrez son importance. Je vais essayer de vous l'expliquer, même si cela ne sera sans doutes clair qu'après les autres chapitres.
Les scripts sont codés en utilisant des 'fonctions', qui disent au programme ce qu'il doit faire. Certaines fonctions sont déjà écrites, et nous ne faisons que les utiliser. Ecrire un script revient en fait à créer une nouvelle fonction. Cette ligne est en quelque sorte une description de cette fonction.
Premièrement le mot "void". Qui peut signifier néant. Cela indique au script la "réponse" qui va revenir de la fonction. Notre chanteur fait des choses, mais il ne calcule rien. Ce "néant" indique que la fonction ne ramènera aucune réponse.
Le mot "main" qui signifie principal indique que cette fonction est la principale. Nous pouvons en écrire d'autres, et nous le ferons, mais ce mot indique l'endroit ou le script commencera.
Entre les parenthèses se trouvent les arguments, ou paramètres, ou encore informations qui sont donnés et nécessaires à la fonction pour qu'elle s'exécute. Dans ce cas, notre script se suffit à lui-même et n'a besoin d'aucunes informations.

Qu'est ce que les { et } ?
Ils sont utilisés pour délimiter les sections du script et les fonctions. Cela signifie que tout ce qui est entre nos deux { } fait partie de notre fonction "main".

Pourquoi est ce que chaque ligne se termine par un ; ?
Principalement pour indiquer au script que la ligne se termine. Certaines instructions sont parfois trop complexes pour tenir sur une seule ligne. Il est ainsi possible de les écrire sur autant de ligne que l'on veut. Le programme les considérera comme une seule et même ligne tant qu'il ne tombera pas sur un ;

A quoi sert le ClearAllActions() ?
En fait dans ce cas, a rien
C’est une garantie.
Chacune des autres lignes du script est une commande. Le npc va les exécuter une par une, en prenant bien soin de finit la première avant de passer à la suivante.
Mais, notre fonction est placée dans le "On Heartbeat" et va donc être appelée toutes les 6 secondes. Mais nous avons 7 actions. Que se passe t'il s’il a seulement le temps d'en faire 3 en 6 secondes ? Il reste encore 4 actions à faire et on lui redemande d'en faire 7... ce qui lui en fait 11 maintenant... Cela va aller en augmentant, et au bout d'un moment, cela risque de poser problème.
Il y a de meilleurs moyens de régler ces problèmes. Mais pour l'instant, contentons-nous de dire au npc d'oublier tout ce qu'il lui reste à faire. C'est ce que fait le ClearAllActions().
Si vous voulez expérimenter, éditez le script et changez les valeurs a l'intérieur des commandes ActionWait. Remplacez par exemple le 1.5 par 3.0
Pendant qu'on y est. Le ClearAllActions est une fonction, comme notre main. Une fois de plus, le fait qu'il n'y ait rien entre les parenthèses signifie qu'on ne passe aucune information a cette fonction. Par contre, la fonction ActionWait, elle a besoin d'informations, en l'occurrence le temps qu'il va falloir attendre. D'où la présence d'un nombre a l'intérieur des parenthèses...

Et pour les autres lignes du main ?
Ce sont toutes les instructions expliquant au npc ce qu'il doit faire. Elles sont assez explicites, mais je vais quand même détailler un peu.
ActionSpeakString fait dire au npc quelque chose. Ce quelque chose étant le texte passé en paramètre a la fonction. Ce texte est une Chaîne de caractères ( String ), et doit être encadré par des ".
ActionWait fait asseoir et attendre le npc, le nombre de secondes précisé. ActionWait(0.5) fait attendre le npc pendant 0.5 secondes entre chaque phrase.

Pourquoi nommer le script tm_chanteur_hb ?
Une fois encore, vous pouvez l'appeler comme bon vous semble, du moment que vous vous en souvenez.
"tm" signifie ici Test Module. Tous nos scripts pour ce module commenceront ainsi.
"chanteur" signifie que ce script s'applique au npc chanteur. Il est préférable de mettre les noms de scripts en minuscules, et je vous recommandais de garder les noms de tags sur 8 caractères au plus, de façon justement a ce qu'on puisse les réutiliser dans les noms de scripts sans que cela soit gênant.
"hb" signifie que le script concerne le "On Heartbeat".

Pourquoi est ce que cette leçon n'est pas plus longue ?
Je trouve pour ma part qu'elle est suffisamment longue. Elle le fut à écrire au moins. De plus je ne souhaite pas vous noyer sous des tonnes d'informations. Laissez-moi quelques jours, et vous aurez votre deuxième leçon.

Amaranthe.
Lightbulb
Cours de Scripts pour les Quiches. Leçon Deux.
Par Celowin mailto:james.foxglove@verizon.net
traduit par Amaranthe mailto:amaranthe@free.fr

Introduction
Le but de cette série de leçons, est d'apprendre a se servir des scripts a n'importe qui. Cette deuxième leçon porte sur les variables. Il est toujours possible de trouver la première leçon <a href = «https://forums.jeuxonline.info/showthread.php?t=101346 »>ici</a>

Cette leçon risque d’être un peu plus longue que la précédente. Les concepts abordés sont également un peu plus complexes. Si ma manière de présenter les choses n’est pas assez claire, n’hésitez pas a posez des questions. Partez toujours du principe que pour une personne qui pose la question, il y en a cinq qui attendent la réponse.

Modifions donc notre module de test. Ouvrez le dans l’éditeur.
Cliquez droit sur le npc que nous avions crée. Choisissez propriétés et allez dans l’onglet des scripts.

Nous allons cette fois ci écrire deux petits scripts.

Le premier va dans le « OnSpawn »

Code PHP:

void main()
{
  
SetLocalInt(OBJECT_SELF“CHANTEUR_COUNT”0);

Sauvegardez le en tm_chanteur_os ( « os » pour « OnSpawn » )
Bien, même si ce script ne fait qu’une seule ligne, il est du genre a faire fuir la plupart des gens qui n’ont pas l’expérience de la programmation. Essayons donc d’expliquer un peu avant de s’attaquer au script réel.

Premièrement, remarquez que ce script est placé dans le « OnSpawn ». Ce qui signifie qu’il ne sera exécuté qu’une seule fois, lorsque le npc apparaît pour la première fois dans le jeu. C’est donc l’endroit idéal pour initialiser des choses, et c’est justement ce que nous cherchons a faire.

Cette ligne définit une variable. Souvenez vous de vos cours de math, qu’est ce qu’une variable ? Une lettre qui représente un nombre. C’est a peu près la même chose ici, mis a part qu’au lieu d’utiliser une simple lettre, nous lui donnons un nom.

La commande SetLocalInt est une fonction que sert a définir une variable. Elle nécessite trois paramètres, séparés par des virgules.
- Le premier est ce a quoi la variable va être attachée.
- Le second est le nom que nous voulons donner a la variable.
- Le troisième est la valeur que nous voulons mettre dans la variable.

J’espère que vous avez compris que nous voulions créer une variable, nommée CHANTEUR_COUNT et que nous voulions lui donner la valeur 0. Le premier paramètre nécessite un peu plus d’explications.

Tout ou presque dans un module, est un objet ( object ). Les npcs sont des objets, les Placeables sont des objets. Même les pcs eux mêmes sont des objets. Avec tant et tant d’objets dans un module, il est vital de savoir s’y retrouver et de toujours savoir duquel on parle.

OBJECT_SELF est un des moyens les plus pratique de se référer a un objet. Comme vous pouvez le deviner, il fait référence a l’objet qui l’appelle. Dans notre cas, notre script est attaché a un npc, donc OBJECT_SELF est ce npc. Aussi simple que cela.

Nous avons donc, par le truchement de cette simple ligne, définis une variable nommée CHANTEUR_COUNT, qui a pour valeur 0. Cette variable étant attachée au npc CHANTEUR.

Il est intéressant aussi de noter que le Int de SetLocalInt est pour « Integer » ce qui veut dire Entier. C’est a dire un nombre entier. Nous aurions pu mettre 2, 15 et même –7 mais pas 3,8 par exemple.

Le deuxième script maintenant. Nous allons écraser le « OnHeartbeat » de notre première leçon.

Code PHP:

int nCount=GetLocalInt(OBJECT_SELF"CHANTEUR_COUNT");
void main()
{
    
nCount nCount+1;
    
ActionSpeakString("J’ai parle "+IntToString(nCount)+" fois.");
    
SetLocalInt(OBJECT_SELF"CHANTEUR_COUNT"nCount);

Sauvegardez le, gardez le nom tm_singer_hb.

Avant que j’explique, regardez donc ce que fait ce script. Fermez la fenêtre de script. Cliquez sur Ok pour le npc, et sauvegardez le module. Lancez le module de test et regardez comment se comporte votre chanteur.

C’est votre premier script avec quelque chose d’écrit avant le void main() et cela mérite donc d’être signalé. Ceci est appelé la phase d’Initialisation du script. Nous parlons ici des choses auxquelles le script va se référer. Contrairement a la variable locale que nous avons défini précédemment, ce nCount n’aura d’existence que le temps que ce script tourne. Des que ce script sera terminé, nCount n’existera plus. C’est pour cette raison que ces variables sont nommées « Temporaires ».

Remarquez combien de fois nous utilisons nCount dans notre fonction, et imaginez combien cela serait long et déroutant si nous devions a chaque fois réécrire a la place quelque chose comme GetLocalInt(OBJECT_SELF, "CHANTEUR_COUNT").

Pour ce qui est du nCount. A chaque fois que vous déclarez une variable temporaire, la première lettre est un tag qui sert a déterminer le type de valeur que cette variable peut prendre. Ce n’est nullement obligatoire, c’est simplement une convention. N est ici mis pour Integer. Quelque soit le nom que vous donnez a votre variable, vous savez qu’elle va contenir un Integer.

Les « int » signifie donc que nous définissons une variable de type integer, le « = » signifie que nous allons lui donner une valeur et le GetLocalInt récupère la valeur de la variable CHANTEUR_COUNT.

Notez au passage la similarité entre les fonctions SetLocalInt et GetLocalInt.

Cette première ligne crée donc une variable temporaire nCount, que notre script va pouvoir utiliser, et lui assigne la valeur de CHANTEUR_COUNT stockée au niveau de notre npc. Donc la première fois que le script est appelé, nCount vaut 0. Puisque c’est ce que nous avons initialisé dans notre script « OnSpawn ».

Je sais que cela fait beaucoup d’explication pour une simple ligne de code. Et je me doute que beaucoup ne comprendrons pas du premier coup. Relisez et si cela n’est toujours pas clair, attendez de l’avoir utilisé deux ou trois fois et vous verrez alors…

Voyons maintenant le corps du script et décortiquons le.

nCount = nCount+1;

Cela ne fait que prendre notre variable temporaire et lui ajouter 1. Il suffit de lire : Affectez a la variable nCount le contenu de la variable nCount + 1.

Si nCount vaut 9, alors après cette ligne, elle vaudra 10.

( Il est également possible d’écrire cela : nCount++ ;
le ++ est l’incrément de 1. C’est parfait une fois que l’on y est habitué, mais au début c’est généralement assez déroutant. )

La deuxième ligne est une commande connue : ActionSpeakString. Par contre le paramètre demande peut être explication. Il est composé de trois parties, séparées par des +. Lorsqu’on l’applique a des chaînes de caractères ( String ) le + sert a la concaténation. Par exemple :
« Ceci est une String » est équivalent à : « Ceci est » + « une String ».

Cela n’a que peu d’intérêt écrit comme cela, mais prends tout son sens quand on regarde la partie du milieu de l’expression : IntToString(nCount).
Ce qui fait exactement ce que ça veut dire. nCount est un integer, et cela transforme sa valeur en une chaîne de caractère de façon a ce qu’elle puisse être concaténée et utilisée par la commande ActionSpeakString.

Vous devriez maintenant facilement comprendre la dernière ligne. Cela ne fait que stocker la nouvelle valeur de nCount dans notre variable locale. Si nous voulons que cette variable soit réutilisée, il faut stocker sa valeur. Rappelez vous que nCount n’existera plus une fois que vous serez sortis de la fonction.

Amaranthe.
Lightbulb
Cours de Scripts pour les Quiches. Leçon Trois.
Cours de Scripts pour les Quiches. Leçon Trois.
Par Celowin mailto:james.foxglove@verizon.net
traduit par Amaranthe mailto:amaranthe@free.fr

Introduction
Le but de cette série de leçons, est d'apprendre a se servir des scripts a n'importe qui.

Commençons.
Cette leçon va tenter de couvrir ce qui est sans doutes l'aspect le plus important de l'art du scripteur. Comment faire en sorte que votre code fasse quelque chose... seulement dans certaines conditions !

Un peu de théorie tout d'abord. Le format de base d'une condition, ou d'un test est :

Code PHP:

if ( condition ) { fait_cecifait_celafait_autre_chose; } 

(Ce n'est pas un vrai script, juste un prototype.)

De façon simple, si la condition est remplie, le script va faire ce qu'il y a entre les {}. Sinon, il ne fera rien.

Il est a noter qu'une erreur classique consiste a mettre un ; après la ligne commençant par un " if ". Or il n'en faut pas.

Voilà, vous savez maintenant comment écrire un test. Il ne reste plus qu'a savoir comment écrire la condition. Il y a énormément de types de condition différents, et beaucoup de petites règles qui en découlent. Je vais seulement vous en expliquer les bases de façon a ce que vous soyez capable de chercher la suite par vous mêmes.

La plupart des conditions se résument a une comparaison. Par exemple :

if (nCount == 1)

Comme nous l'avons vu, un " = " correspond a une assignation de valeur, il en faut deux " == " pour tester cette valeur.

if (nCount == 1)
Correspond a se demander si nCount est égal à 1 ?
Rappelons nous que nCount est une variable, et est en fait équivalent a un nombre. Si nCount contient au moment du test la valeur 1, alors la condition sera réalisée. Si nCount contient 7, alors elle ne le sera pas.

Quand on parle de conditions, il est difficile d'éluder le nom de Boole. Je ne vais pas aller bien loin, qu'il vous suffise de savoir que c'était un mathématicien qui énonça certaines lois, incontournables en informatiques.

Une condition est soit VRAI ( TRUE ) soit FAUSSE ( FALSE ).
FALSE vaut 0. TRUE vaut 1.
Regardez donc un interrupteur, celui de votre ordinateur ou celui de votre cafetière électrique... Vous y verrez sûrement d'un coté un 0, de l'autre un 1. 1 pour allumé, 0 pour éteint. Bienvenus dans un monde binaire...

Tout ce qui n'est pas TRUE est FALSE et inversement.

Mais revenons a nos moutons...

L'autre façon d'énoncer une condition, est au travers d'une fonction prédéfinie. Considérons donc l'exemple suivant.
Script d'exemple.
Ecrivons donc un script avec une condition simple. Il y aura un certain nombre de nouvelles fonctions que nous expliquerons après.

- Ouvrez votre module de Test.
- Créez une nouvelle Area, forest tileset, dimension 4 par 2. Appelée la " Test Area 002 ".
- A une extrémité, placez une " Start location "
- A l'autre extrémité placez un npc. Juste un " Commoner " pour plus de simplicité.
- Changez son tag en " GARDE "
- Allez dans ses scripts. Supprimez les tous.
- Allez dans le " OnPerceive " et entrez ce script.

Code PHP:

object oVu GetLastPerceived(); 
void main() 

   if (
GetIsPC(oVu)) 
   { 
      
ActionSpeakString("Bienvenu mon ami."); 
   } 

- Sauvegardez le avec notre convention de nomage en tm_garde_op ( " op " pour " OnPerceive " )
- Validez tout, sauvegardez et allez tester votre module.

Lorsque vous vous approchez du garde, il parle. Mais si vous restez a coté de lui, il ne dit plus rien. Eloignez vous, revenez...

Reprenons notre jeu des Questions/Réponses pour expliquer ce script.

Encore un autre " handle ", a quoi sert donc ce " OnPerceive " ?
Ce handle, appelle le script associé des que le npc remarque quelque chose. Si ce quelque chose est invisible, ou caché et que le npc ne le remarque pas, alors le script ne sera pas appelé.
Nous voulons que notre garde réagisse en voyant quelqu'un. D'ou l'utilisation du " OnPerceive ".

Ré explique moi donc pourquoi tu met des choses avant le main. Ca ne ressemble pas a ce qu'il y avait dans la leçon deux.
Et pourtant, c'est la même chose... ou presque. Il s'agit juste d'un autre type de variables. Nous avions crée une variable pour stocker un integer, maintenant nous voulons une variable pour y stocker un objet.

Je l'ai déjà dit, mais je me répéterai. Quasiment tout dans le jeu est un Objet. Les npc, les pc, les objets, les waypoints... et quasiment toutes les fonctions sont écrites afin de manipuler des objets.

Nous créons donc ici une variable temporaire, que nous appelons oVu ( commençant par o pour se rappeler qu'il s'agit d'un objet ). Et nous stockons une valeur dedans.

Et cette valeur est le résultat de la fonction GetLastPerceived(). Cette fonction, comme toutes celles qui commencent par le mot Get, renvoie une donnée. Le nom de la fonction suffit généralement pour savoir ce qu'elle fait. Ici, la fonction renvoie le dernier objet vu par le npc.

On a passé du temps a apprendre ce que c'était qu'une comparaison et maintenant il n'y a une condition sans comparaison ?
Souvenez vous, je vous avais expliquer que tout était soit VRAI, soit FAUX. Si c'est VRAI, j'exécute mon script, si c'est FAUX je fait rien.

C'est exactement ce qu'on veut faire ici. Si le résultat de GetIsPC(oVu) est VRAI alors j'exécute mon script. Si c'est FAUX, je ne fait rien.

GetIsPC() est une fonction qui prend un objet en paramètre et renvoie un booléen. Un booléen ( d'après le nom de Boole ) est un type de variable qui ne peux prendre que deux valeurs... vous l'aurez deviné... TRUE ou FALSE. GetIsPC renverra donc TRUE si oVu est un Pc, FALSE dans tout les autres cas.

Ce qui est exactement ce que nous voulons. Si l'objet perçu par le garde est un Pc, alors le script sera exécuté, sinon, il ne se passera rien.

Vous pouvez aussi écrire, si vous préférez.
Code PHP:

if (GetIsPC(oVu)==TRUE
de façon a ce que cela ressemble plus a une comparaison... mais ce n'est pas utile.

A t'on forcement besoin de ce test... n'est on pas sur que ce soit toujours une pc qui soit percu par le garde ?
Pour notre petit module de test, il est clair que ce pauvre garde n'a rien d'autre a percevoir. Mais imaginez que ce garde perçoive un gobelin... vous ne voudriez pas qu'il l'appelle mon ami quand même ?

A quoi cela peut il bien me servir de parler a un NPC s'il n'y a personne pour s'en rendre compte ? De toute façon il sont tous amicaux.

Ah oui ?
Voyons un peu ce que l'on peut faire...

Nous allons modifier notre module de façon a ce que notre garde attaque tout personnage ne portant pas... mettons un anneau spécial. Dans le cas ou cet anneau est porté, alors le personnage a droit a une remarque amicale.

- Ouvrez le Toolset. Chargez votre module et choisissez l’ Area002.
- Commençons par créer l’anneau. Go to « Paint Items », "Miscellaneous", "Jewelry", "Rings", "Copper Ring". Placez le près de l’entrée du module.
- Editez les propriétés de l’anneau, changez son tag en « PASSRING ».
- Si vous vous sentez ambitieux, vous pouvez changer le nom de l’anneau, sa description… mais pour les besoins du script, seul son tag importe.
- Choisissez maintenant le garde, et éditez son script « OnPerceive ».

Code PHP:

// Amis ou Ennemis : tm_guard_op //
// Doit être placé dans le « OnPerceive » du garde. // 
// Le garde va vérifier si le personnage possède l’anneau « PASSRING » et sinon l’attaque. //

object oSeen GetLastPerceived(); 
object oRing GetItemPossessedBy(oSeen"PASSRING"); 
void main() 

// Si ce que voit le garde n’est pas un personnage, il ne fait rien.
   
if (GetIsPC(oSeen)) 
   { 
      if (
oRing == OBJECT_INVALID
      { 
      
// Si le personnage n’a pas l’anneau. Attaque !
         
ActionSpeakString("Meurt donc Etranger!"); 
         
ActionAttack(oSeen); 
      } 
      else 
      { 
// Si le personnage a l’anneau, on le salue.
   
ActionPlayAnimation(ANIMATION_FIREFORGET_GREETING);
         
ActionSpeakString("Bien le bonjour, messire."); 
      } 
   } 

Je commence a enrichir les script, en ajoutant de plus en plus de nouvelles commandes. Cela ne devrait plus trop vous poser de problèmes maintenant.

Si vous avez encore des problèmes, chargez le script et lancez le module pour voir ce qu’il fait. Cela devrait être rapidement plus clair. Regardez comment se comporte le garde, suivant que vous ayez ou non l’anneau sur vous.

Argh ! Tu as ajouté une autre initialisation ! Je hait ça !
Tout ce que je peux dire, c’est que le script serait beaucoup plus compliqué sans cela.
object oRing = GetItemPossessedBy(oSeen, "PASSRING");
Une fois de plus, nous créons une nouvelle variable temporaire, qui contiendra un objet. La commande GetItemPossessedBy prend deux paramètres, le premier est l’objet créature que l’on veut tester, et le second est le tag de l’item que l’on cherche sur cette créature.
On sait déjà que oSeen est la personne qui a déclenché le script en étant « Vue » par le garde. ORing est donc l’objet porté par cette personne qui a le tag « PASSRING ».

Mais que se passe t’il si le personnage n’a pas l’anneau ? que vaut oRing dans ce cas la ?
Excellente question. La commande GetItemPossessedBy marche toujours, bien entendu. Mais comme elle ne peut pas trouver l’objet sur la personne, elle renvoi un objet de type OBJECT_INVALID. Une sorte de moyen pour elle de dire qu’elle n’a rien trouvé de ce qu’on lui demandait.

Qu’est ce que toutes ces lignes commençant par des //. Pour une fois elles sont écrites en français ?
Tout ce qui est écrit après des // est ignoré par le script. Cela permet de mettre des commentaires afin de rendre les scripts plus lisibles, et plus facilement compréhensibles.
Prenez l’habitude d’en mettre le plus possible. Cela peut vous sembler rébarbatif quand vous écrivez un script, mais vous serez heureux de les retrouver un ou deux mois plus tard quand vous voudrez le modifier.
C’est également plus gentil pour tous ceux a qui vous donnez de ce script, plus vous expliquez ce que vous faites, plus votre script sera facilement compréhensible.
Pensez de la même façon a citer vos sources et a garder le nom de l’auteur.

Ca commence a devenir compliqué avec tous ces { et ces } Comment je sais ou je dois les mettre ?
Ca peut effectivement devenir compliqué. Plus le script est compliqué, plus les « imbrications » sont délicates. C’est pour cela qu’il faut utiliser l’indentation… comme je l’ai fait.
On peut aussi mettre des lignes blanches, pour aérer le script. Ou utiliser des commentaires pour séparer le script en blocks.
Comme d’habitude, cela deviendra de plus en plus simple avec l’usage.

Qu’est ce que c’est que ce « Else » ?
Vous vous souvenez du format d’un test ?
Code PHP:

if ( condition ) { fait_cecifait_celafait_autre_chose; } 

Maintenant, un test un peu plus poussé s’écrit comme cela.
Code PHP:

if ( condition )
{
fait_ceci;
et_cela;
}
else
{
fait_ca_a_la_place;
et_encore_ca;

De façon simple, si la condition n’est pas réalisée, le script passe au « else » a la place.

Si nous regardons notre script. On test si la variable temporaire oRing est égale a OBJECT_INVALID. C’est a dire, si le personnage ne possède pas l’objet, alors attaque. Sinon ( « else » ) soit gentil.
Pour beaucoup, lire un test est une chose compliquée. N’hésitez pas a faire un petit dessin, ou un diagramme.

Qu’est ce que c’est que ces nouvelles commandes ?
Je pense qu’elles sont assez simple a comprendre.
ActionAttack fait attaquer l’objet passé en paramètre.
ActionPlayAnimation fait faire au npc une animation, dans notre cas, celle nommée ANIMATION_FIREFORGET_GREETING. C’est juste un moyen barbare de demander au npc de faire un coucou.

Une dernière petite chose.
Cette leçon est déjà assez grosse, mais j’aimerai ajouter une dernière chose. Bon ok, j’aimerai en ajouter… 251… mais je vais tâcher de ne pas vous assommer. )
Comment changer le comportement de notre garde et lui faire faire exactement l’inverse ? C’est a dire, attaquer si le personnage porte l’anneau ? Disons par exemple que cet anneau est un anneau volé.
Nous pourrions très bien déplacer des blocks de script et les intervertir. Au risque de se tromper. Mais il y a beaucoup plus simple. Il est possible d’obtenir ce résultat en changeant simplement un caractère !
La ou on avait
Code PHP:

if (oRing == OBJECT_INVALID
mettez
Code PHP:

if (oRing != OBJECT_INVALID
« != » est une autre sorte de comparaison. Cela signifie « différent de » ou « non égal à ».

Conclusion.
Il y a beaucoup de nouvelles idées dans cette leçon. Mais quand vous l’aurez maîtrisée, vous aurez déverrouillé le vrai pouvoir derrière la programmation. Combiner les tests avec les variables locales, ouvre toutes sortes de possibilités.

Amaranthe.
Lightbulb
Cours de Scripts pour les Quiches. Leçon Quatre.
Cours de Scripts pour les Quiches. Leçon Quatre.
Par Celowin mailto:james.foxglove@verizon.net
traduit par Amaranthe mailto:amaranthe@free.fr

Introduction
Le but de cette série de leçons, est d'apprendre a se servir des scripts a n'importe qui.

Commençons.
Bon, les plus perspicaces ou acharnés d’entre vous aurons remarqué que notre garde de la leçon trois n’a pas exactement le comportement voulu. A chaque fois que le garde est amical, il salue deux fois. Il faut bien faire attention pour s’en apercevoir, mais quand même, c’est… gênant. De plus, cela me donne la chance d’expliquer une autre concept.

Premièrement, tâchons de comprendre le pourquoi de la chose. Il faut pour cela en venir au fonctionnement du script du « OnPerceive ». Il y a quatre chose qui « appellent » ce script.
 Le npc voit quelque chose.
 Le npc entend quelque chose.
 Le npc s’aperçoit de la disparition de quelqu’un.
 Le npc s’aperçoit du silence de quelqu’un.

Ce qui s’est passé dans notre exemple est que notre garde a vu ET entendu le personnage, ce qui fait que le script a été appelé deux fois. Il y a plusieurs moyen de corriger cela, mettons que nous voulons que le garde ne réagisse qu’a la VUE de l’objet. Ce qui correspond plus a ce que nous cherchons a faire. Il est en effet peu probable qu’il entende l’anneau…

On pourrait faire cela en rajoutant une imbrication dans nos tests. Mais ceux ci étaient déjà un peu compliqués. Ce que nous voulons est que notre script ne soit activé que si l’objet perçu est un personnage, et que cet objet ait été perçu par la vue.

Il suffit donc de modifier la condition en ajoutant l’opérateur « && » qu’il suffit de lire comme un « ET ». Notre condition sera vraie, si et seulement si les deux parties sont vraies.
Code PHP:

if (GetIsPC(oSeen)) 

Changée en
Code PHP:

if (GetIsPC(oSeen) && GetLastPerceptionSeen()) 

La commande GetLastPerceptionSeen ramène TRUE si la dernière chose perçue a été vue, et FALSE dans les trois autres cas.

Il y a un autre opérateur qui peut être utilisé pour relier deux conditions, c’est le « || » qui peut être lu comme « OU ». La condition se réalise si l’une ou l’autre de ses deux parties est vraie.

Ces opérateurs nous ramènent a Boole… vous vous souvenez de lui ?
Boole a créé ce qu’on appelle aujourd’hui « l’algèbre de Boole ». Et comme dans toute algèbre, il y a des opérateurs et des opérandes.
Si j’insiste sur ce point, c’est parce que c’est un des sujets les plus difficiles a maîtriser pour un développeur. Et que c’est un des plus vitaux.
On parle ici de logique et de mathématique. Cela n’a que peu de rapport avec le français.
A la question : « Est ce un garçon ou une fille ? » N’importe qui répondra « un garçon » ou « une fille »… Un script répondra « VRAI ».

Si cela vous intéresse je vous ferai un vrai cours de logique booléenne. Mais il y a déjà beaucoup de sites très bien fait…

Confession.
Jusqu'à présent, je vous ai toujours dit que je vous proposais des scripts comme je les aurai écrit. Je doit maintenant vous avouer que c’est faux. Jamais je n’aurai écrit les scripts comme cela.
Le problème vient du fait de tous ces scripts par défaut que l’on a effacés pour mettre les nôtres. Vous vous en souvenez ? Maintenant, notre garde fait ce qu’on lui demande, mais il ne répond plus a aucun autre stimulus. Essayez par exemple de l’attaquer… il ne se défendra même pas ;(
La question qui se pose donc est, comment ajouter un comportement, tout en gardant tous les comportements par défaut. Et la réponse est : En utilisant les « User defined » scripts. Ce qui est l’objet de ce cours. Avouez que cela tombe bien non ?

Retravaillons donc notre garde.
- Ouvrez votre module de Test.
- Supprimez le garde.
- Créez un autre npc a sa place, de façon a récupérer tous les scripts par défaut.
- Changez son tag en « GARDE ».
- Vérifiez dans l’onglet « Advanced » que sa faction soit bien « Commoner ».
- Activez l’onglet « Scripts ».
- Editez le script du « OnSpawn ». Il y a beaucoup de choses ici, nous allons en ignorer la plupart. Mais vers la fin du script, il y a une ligne qui nous intéresse.
Code PHP:

//SetSpawnInCondition(NW_FLAG_PERCIEVE_EVENT); //OPTIONAL BEHAVIOR - Fire User Defined Event 1002 

- Supprimez les // au début de cette ligne.
- Sauvegardez le script en tm_garde_os
- Fermez la fenêtre de scripts et allez sur le « OnUserDefined »
- Choisissez le script tm_garde_op
- Editez le, sauvegardez le en tm_guard_ud
- Changez éventuellement les commentaires pour refléter le fait qu’il aie changé de place.
- Sauvegardez le tout et lancez votre module.

Maintenant le garde réagit beaucoup plus normalement, et en plus, il fait toujours ce que l’on veut.
Bien, qu’avons nous fait ? En enlevant les commentaires « // » dans le script de « OnSpawn », nous avons dit au script que nous voulions que ce bout de code optionnel prévu par Bioware soit exécuté.
De façon schématique nous lui avons demandé. Quand tu fait ton script de « OnPerceive », fait en plus le nôtre.

Bien sur les plus malins d’entre vous demanderons… Et si je veux ajouter plusieurs comportement spéciaux, sur le « OnPerceive » et sur le « OnHeartbeat » par exemple ?
C’est toujours faisable, même si cela demande un tout petit peu plus de travail.
Si par exemple nous voulions que notre garde dise toutes les six secondes « Je m’ennuie » .
- Retournez dans le script de « OnSpawn » de votre garde et enlevez le commentaire devant le « OnHeartbeat ».
- Remarquez que les lignes « OnPerceive » et « OnHeartbeat » ont chacune un numéro d’associé. 1002 pour le « OnPerception » et 1001 pour le « OnHeartbeat ».
- Sauvegardez votre script en tm_ennui_os.
- Modifiez votre script de « OnUserDefined » comme suit et sauvegardez le en tm_ennui_ud.
Code PHP:

// « OnUserDefined » Script : tm_bored_ud
// Appele par les scripts OnHeartbeat et OnPerception
//
// Le npc se plaint de s’ennuyer toutes les six secondes
//  et salue un pc des qu’il le voit.
//
int nAppelePar GetUserDefinedEventNumber();
void main()
{
 switch(
nAppelePar)
 {
   case 
1001:  // Appele par OnHeartbeat
     
ActionSpeakString("Je m’ennui.");
     break;
    
//
   
case 1002:  // Appele par OnPerception
     
object oSeen GetLastPerceived();
     if (
GetIsPC(oSeen) && GetLastPerceptionSeen())
       
ActionPlayAnimation(ANIMATION_FIREFORGET_BOW);
     break;
 }

Questions / Réponses…

Qu’est ce que le GetUserDefinedEventNumber?
C’est exactement le nombre auquel je vous ai demandé de prêter attention un peu plus haut. Bioware a bien fait les choses. Non seulement chaque événement peut appeler le script « UserDefined » mais en plus, chacun lui passe un paramètre différent.

Et la commande « switch » ?
Je pourrai vous en parler pendant des pages et des pages… qu’il suffise de dire que cette commande prend une constante en paramètre et fait « sauter » le script jusqu'à ce nombre. Le script continue alors de s’exécuter jusqu'à ce qu’il rencontre la commande « break ».
Donc si notre nAppelePar vaut 1001, le script va rechercher la ligne « Case 1001: »

Quand j’écris des scripts « UserDefined » je me sert toujours de la commande « switch ». Même si j’en ai qu’un seul. Cela me permet plus tard, d’en rajouter de façon simple et rapide.

Ehhh tu as oublié des { et des } avec ton if ?
Euhh… non
Quand il n’y a qu’une seule commande attachée au « if », les { et les } ne sont pas obligatoires. Dans ce cas, le script est plus propre sans.

Voilà pour aujourd’hui…

Amaranthe.
Lightbulb
Cours de Scripts pour les Quiches. Leçon Cinq.
Cours de Scripts pour les Quiches. Leçon Cinq.
Par Celowin mailto:james.foxglove@verizon.net
traduit par Amaranthe mailto:amaranthe@free.fr

Introduction
Le but de cette série de leçons, est d'apprendre a se servir des scripts a n'importe qui.

Maintenant vous commencez a en connaître un rayon sur les scripts, je pense que tous ceux qui ont suivi mes leçons jusqu’ici et qui arrivent a les mettre en pratique ne sont plus des quiches en programmation
Il y a bien sur encore beaucoup de choses qui peuvent être dites, mais avec ce que vous avez appris jusqu'à présent vous êtes d’hors et déjà capables de faire de chouettes scripts.

Voici un exemple de script un peu plus compliqué. Il requiert un peu de travail et introduit de nouvelles commandes, mais je pense que le jeu en vaut la chandelle.

- Ouvrez le module de Test.
- Reprenez votre première « Area », on a assez travaillé avec ce garde.
- Virez moi ce chanteur.
- Mettez un « start point » a votre module.
- A l’autre bout de la zone, placez un npc, un « commoner » par exemple, en fait cela n’a que peu d’importance, on va changer pas mal de choses.
- Dans l’onglet « Basic » changez son nom en « Cible », son tag en « CIBLE », sa race en « Construct », son apparence en « Archery Target », son genre en « None » et son portrait en po_PLC_F01_ .
- Dans l’onglet « Advanced » cochez la case « Plot ».
- Allez dans l’éditeur de faction. Créez une nouvelle faction « Cible », dont la faction parente est « Hostile ». Mettez les Cible - Commoner et la Commoner - Cible a 50
- Changez la faction de notre cible en « Cible ».
- Dans l’onglet « Scripts », supprimez tous les scripts. Dans notre cas, nous ne voulons pas que notre cible se retourne contre son agresseur et l’attaque
- Mettez le script suivant dans le « OnDamaged ».
Code PHP:

// Script de « OnDamaged » : tm_cible_dm
// Script de Cible.
// Produit un "thunk" lorsqu’elle est touchée //
void main()
{
    if (
GetWeaponRanged(GetLastWeaponUsed(GetLastAttacker())))
    {
    
SpeakString("**Thunk**");
    }

- Vous pouvez d’hors et déjà tester votre cible, mais ce n’est pas le plus intéressant.
- Placez un « Waypoint » a environ un carré de votre cible.
- Donnez lui le tag « CibleWp001 ».
- Placez un autre npc commoner près de votre Waypoint.
- Changez son tag en « Tireur ».
- Laissez le soin au programme de choisir son nom au hasard.
- Dans l’onglet « Feats » ajoutez lui « Weapon Proficiency (simple) ».
- Dans l’onglet « Inventory », choisissez lui des « Darts ».
- Click droit sur les fléchettes que vous venez de lui ajoutez, changez la « Stack Size » a 3.
- Cliquez sur Ok et équipez le npc avec.
- Sortez de l’inventaire en validant.
- Ouvrez l’onglet « Scripts ».
- Editez le script du « OnSpawn » et enlevez le commentaire devant le « OnHeartbeat ».
Il y a une autre ligne que nous allons décommenter, le :
Code PHP:

SetSpawnInCondition (NW_FLAG_SET_WARNINGS); 

- Ajoutez egalement une ligne au début :
Code PHP:

 SetLocalInt(OBJECT_SELF"ETATCIBLE"1); 

- Allez dans le « UserDefined » et ajoutez le script suivant.
Code PHP:

// Script « OnUserDefined »
// tm_flechette_ud
// Jeu de flechette
// Appelé par le OnHeartBeat script.
//
// Le joueur de fléchettes lance les dards de son inventaire ( 3 au départ ) 
// Va jusqu'à la cible. Récupère ses dards, revient et recommence.
// 
void main()
{
    
int nAppelePar GetUserDefinedEventNumber();
    
object oCible GetNearestObjectByTag("CIBLE");
    
int nCiblePrete GetLocalInt(OBJECT_SELF"ETATCIBLE");
//
    
switch(nAppelePar)
    {
     case 
1001:  // Appelé par OnHeartbeat
       //
       // nCiblePrete est a 1 si la cible est prête.
       //
       
if ((GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)))
            && (
nCiblePrete == 1))
          {
             
// Si on a des dards dans la main droite et qu’on est prêt a les lancer, alors faisons le. 
             
ClearAllActions();
             
ActionAttack(oCibleTRUE);
           }
       else
          {
             
// Sinon, soit nous n’avons plus de dards, soit nous sommes déjà en train d’aller les chercher.
if (nCiblePrete == 1)
              {
                
SetLocalInt(OBJECT_SELF"ETATCIBLE"2);
                
ActionMoveToObject(oCible);
                
ActionWait(0.5);
                
ActionPlayAnimation(ANIMATION_LOOPING_GET_MID1.01.0);
                
ActionWait(0.5);
                
ActionPlayAnimation(ANIMATION_LOOPING_GET_MID1.01.0);
                
ActionWait(0.5);
                
ActionPlayAnimation(ANIMATION_LOOPING_GET_MID1.01.0);
                
object oDestination=GetNearestObjectByTag("CIBLEWP001");
                
ActionMoveToObject(oDestination);
                
CreateItemOnObject("nw_wthdt001"OBJECT_SELF3);
                
ActionEquipMostDamagingRanged();
                
ActionDoCommand(SetLocalInt(OBJECT_SELF"ETATCIBLE"1));
              }
            }
       break;
    }

Voilà un script un petit peu plus compliqué…
Je ne vais pas le détailler, vous avez maintenant suffisamment d’expérience pour pouvoir comprendre par vous même, je vais quand même expliquer deux ou trois petites choses.

J’utilise la variable « ETATCIBLE » pour être sur que nous n’attaquons pas avant d’être prêt. Et que nous ne demandons pas plusieurs fois d’aller chercher les dards.

La commande ActionDoCommand est merveilleuse. Elle prend une commande et la place dans la file d’action. Normalement, dès que le script voir une commande SetLocalInt, il initialise la variable, dans ce cas, cela le force a attendre d’avoir fini tout ce qu’on lui a déjà demandé de faire.

La commande CreateItemOnObject est utilisée pour créer de nouvelles fléchettes. "nw_wthdt001" est le blueprint pour un dard, et le 3 est la taille du stack.

Voilà, je pense que vous pouvez comprendre le reste par vous même. Sinon, ne vous génez pas pour poser des questions…

Et… un petit exercice pour vous maintenant…

J’aimerai qu’un troisième npc annonce les scores ;=)

A vous de bosser un peu…

Amaranthe.
Lightbulb
Cours de Scripts pour les Quiches. Leçon Six.
Par Celowin mailto:james.foxglove@verizon.net
traduit par Amaranthe mailto:amaranthe@free.fr

Les Sources.

Je reçois de plus en plus de demandes d'aide ces temps ci. Lorsque j'ai le temps d'aider, je ne demande pas mieux, mais je ne peux pas toujours me permettre le luxe d'expliquer en détail et de commenter chacune de mes réponses comme je le voudrais. Je me propose donc d'investir ici un peu de temps pour vous montrer comment trouver les réponses par vous même. Honnêtement, je n'ai pas la science infuse, et je ne connais pas toutes les fonctions par cœur. De plus, la plupart du temps, les réponses aux questions posées existent déjà ailleurs. Dites vous bien que malgré votre géni, si vous osez poser une question, il y en a douze qui se posent la même et un ou deux qui ont la réponse.

Il y a quatre endroits ou vous devez tout d'abord chercher les réponses, et dans cet ordre.
* Les " sticky topics " de ce forum.
* Les différents posts de ce forum.
* La campagne officielle.
* Le Toolset en lui même.

Je ne parle volontairement pas des ressources dans la langue de Shakespeare

Détaillons un peu.

La FAQ.
Pour rappel, cela veut dire "frequently asked questions" et ce n'est pas pour rien. Les discussions qui ont été compilées regroupent les questions les plus fréquemment posées. Il est généralement utile d'y jeter un coup d'œil. Il n'y a pas forcément la réponse a votre question, mais peut être quelque chose qui s'en rapproche de très près. De plus, même si cela ne répond pas a votre question, cela peut vous donner des idées pour contourner le problème.

Le Forum.
Tout le monde y pose ses questions, et certains se spécialisent dans les réponses. Encore une fois, il est de bon ton de faire une petite recherche avant de reposer sa question pour la dixième fois. Le forum est équipé d'outils de recherche perfectionné, n'hésitez pas a en user et en abuser. Lorsque je ne trouve pas ce que je veux par la recherche, je regarde quand même les derniers entêtes de discussions, des fois que ma question ait été posée récemment.
Si je ne trouve rien se rapportant a mon problème alors seulement je fait un nouveau post pour demander de l'aide. Sachez quand même, que plus vous vous investissez sur votre problème, plus les gens auront envie de vous aider.
Prenons un exemple. ( Toute ressemblance avec un ou plusieurs fils de discussion est voulue ! Par contre, il n'est nullement dans mon intention de tourner les auteurs de ces demandes en ridicule, d'une quelconque manière que ce soit ).
Un nouveau message, et une seule question. " Comment faire un levier qui ferme une porte ? "
Une autre poste le même genre de question mais explique de façon détaillée son script, les tags qu'elle utilise et pourquoi. Elle explique qu'elle a cherché a attacher le script sur le " OnActivate " de levier, explique les variables locales qu'elle utilise...
Je ne sais pas pour vous, mais pour ma part, j'ai beaucoup plus envie de répondre a la seconde personne et de lui montrer la ou elle a pu faire une erreur. Je ne parle même pas ici de ma répulsion vis a vis des orthographes approximatives. Mais on sent au moins que cette personne s'est investie dans son script, peut être qu'elle a déjà passé des dizaines de minutes a essayer de la faire fonctionner. Alors que je ressent la première question comme : " J'ai pas envie de faire des efforts, pouvez vous en faire pour moi ? ".
Et, encore autre chose au sujet de l'aide apportée sur les forums, il est poli, même si cela n'est pas strictement nécessaire, de remercier les gens qui nous ont aidé pour un script. Juste un petit quelque chose comme ça :
// OnUserDefined Script : tm_garde_ud
// Fait en sorte que le garde grogne s'il voit quelqu'un avec une arme sortie.
// Dernière mise a jour le 11/07/02
// Ecris par Gatsby le magnifique.
// avec l'aide de Celowin.
// Traduis par Amaranthe.
Les gens qui joueront votre module ne verrons jamais ces remerciements, mais ceux qui " piquerons " votre code, oui.
Même si vous ne connaissez pas le nom de la personne, son pseudonyme fera l'affaire.

La campagne officielle.
Je vais le dire et le redire, mais cela m'énerve de voir des gens poser cette question : " Comment je fais pour faire ceci ou cela comme dans la campagne a tel endroit ? "
* Dans le répertoire NeverWinter Nights, il y a un sous répertoire nommé " nwn ". Vous trouverez dans ce sous répertoire les modules de la campagne officielle.
* Copiez les fichiers qui vous intéressent dans le répertoire " modules ".
* Changez leur extension, de " .nwn " en " .mod ".
* Click droit, propriétés, enlevez le " Read Only ".
* Maintenant ce fichier peut être ouvert dans le Toolset.
Vous pouvez apprendre énormément, en regardant comme Bioware a codé telle ou telle chose.
Vous cherchez a savoir comment un Npc peut commencer une conversation ? Regardez le script de Pavel dans le prélude !
Vous voulez savoir comment conjurer des créatures qui attaquent les pcs ? Regardez le script de conversation d'Aribeth dans le prélude !
Vous voulez un npc qui vous suive jusqu'à ce qu'il rencontre un autre npc, puis s'en aille ? Regardez donc dans le chapitre 1...
Il y a plein de petits trucs dans la campagne... apprenez a vous en servir.
Bien sur, des fois, le code est un peu ardu a lire, surtout pour les choses compliquées, mais vous commencez a vous débrouiller en script non ? Sinon, c'est peut être que vous vous attaquez a un trop gros morceau.
Ce qui m'amène a un point que je voulais aborder depuis longtemps. Il est normal quand on veut faire un module, d'essayer de suite de scripter le truc particulier qui va faire de son module, celui dont on sera fier. Hors, au début, c'est souvent une mauvaise idée. Commencez a vous faire la main sur de petits scripts, qui prêtent moins a conséquence, et qui vous découragerons moins vite surtout. Vous devez apprendre a marcher avant de courir.

Le Toolset.
Ne soyez pas étonnés, il y a beaucoup a apprendre du Toolset. C'est la dedans que j'ai appris la plupart de ce que je sais. Une fois que vous connaissez les bases de script, et vous les connaissez n'est ce pas ?, il y a beaucoup a apprendre de la documentation du Toolset, aussi minimale soit elle.
Vous avez sans doutes noté que quand vous éditez un script, toutes les fonctions étaient regroupées dans une fenêtre a votre droite. Vous pouvez cliquer sur chacune d'elle et avoir quelques informations dans l'écran du bas.
De plus le nom des fonctions est souvent assez explicite, utilisez donc la recherche et regardez ce que chaque fonction fait. Vous ne devriez pas tarder a trouver celle qu'il vous faut.
Prenons donc un exemple qui concerne le script écrit plus bas. Je souhaites téléporter un pc d'un endroit a un autre. Je ne sais pas comment faire, donc je commence a regarder les fonctions. Il semblerait que la fonction " ActionMoveToLocation " corresponde plutôt bien. Cependant, j'ai déjà utiliser les commandes " Move " et je sais que cela veut dire marcher. Je continue ma rechercher et je vous " ActionJumpToLocation ". Voyons donc ça de plus près. Je clique dessus et cela me dit.
Code PHP:

// The subject will jump to lLocation instantly (even between areas).
// If lLocation is invalid, nothing will happen.
void ActionJumpToLocation(location lLocation
Les deux premières lignes sont des commentaires. La première nous apprends que la fonction correspond bien a ce que l'on veut. La deuxième nous explique comment elle se comporte dans certains cas.
La troisième ligne nous explique comment mettre en œuvre la fonction. Si vous ne comprenez pas, alors relisez donc la leçon 1
Reprenons notre recherche dans la liste...
Cette commande est une commande de type " Action ". Il va donc probablement que nous placions notre téléport dans la file des actions. Si nous voulons qu'il opère immédiatement, il va nous falloir trouver quelque chose... Et si on utilisait juste " JumpToLocation " ?
Le problème suivant est, comment récupérer une " Location " a passer en argument a la fonction ? La plupart des fonctions ramenant une réponse commencent par " Get ", regardons donc celles ci. Il y a bien une fonction " GetLocation ".
Hmmmm... la commande " JumpToLocation " ne prend pas en argument ce que vous voulez bouger. C'est un petit peu plus compliqué a trouver, mais je vous laisse chercher par vous même
Vous voyez qu'avec un peu de persévérance, et un peu d'intuition, on en apprends beaucoup... et vous verrez qu'au fil du temps, cela viendra de plus en plus facilement.
Apprenez a connaître les fonctions, vous en trouverez des usages peut être inventifs.

Continuons.
Bon assez parlé méthode, faisons un peu de script
Tout ce que nous avons appris dans les leçons précédentes concernait les nps, passons a quelque chose de différent.
En règle générale, que l’on scripte un npc, un objet ou un « trigger », la démarche est la même. Il suffit souvent de faire attention a ce a quoi fait référence le OBJECT_SELF, mais la structure et les commandes sont les mêmes.
Dans notre module de test, si vous avez suivi mes instructions, nous avons deux zones qui ne sont en rien connectées entre elles. Nous avons juste placé notre point d’entrée comme nous en avions besoin. Même si c’est utile pour les tests, c’est un peu gênant. On pourrait placer une zone de transition entre les deux mais ce serait… inintéressant. Faisons donc quelque chose de plus sympa.

Voici ce que je vous propose, dans une des zones, nous allons placer deux leviers, tous les deux en position « off » par défaut. Quand les deux leviers sont abaissés, ils provoquent l’apparition d’un portail qui permet la téléportation dans l’autre zone.
Voici comment faire pas a pas.

 Ouvrez le Toolset, choisissez votre module, choisissez une des zones. A priori la plus grande des deux.
 Placez deux leviers. "Placeable Objects", "Containers and Levers" « Floor Lever ».
 Donnez au premier le tag « LEVIER1 » et au deuxième le tag « LEVIER2 ».
 La ou vous voulez faire apparaître le portail de téléportation, placez un « WayPoint », donnez lui le tag « TM_INWP ».
 Dessinez un « trigger » autour de ce « WayPoint ». Donnez lui le tag « PORTTRIG ».
 Mettez ce script dans le « OnUsed » de chacun des leviers. Editez le une fois en tant que tm_levier_ou et mettez le sur les deux leviers.

Code PHP:

// OnUsed script: tm_levier_ou 
// 
// Ce script active les leviers nommés LEVIER1 et LEVIER2.
// Les deux sont levés par défaut. 
// Lorsque les deux sont baissés, un portail est activé au WayPoint « TM_INWP »
// Et le trigger « PORTTRIG » est activé. 
// 
// Ecrit par Celowin 
// Le 12 Juillet 02 
// 
void main() 

   
int nUsed1 GetLocalInt(OBJECT_SELF"ETAT_LEVIER") ; 
   
int nUsed2 
   
// 
   // nUsed1 et nUsed2 renseignent de manière temporaire l’état des leviers.
   // 0 pour « Off », 1 pour « On »
   // Chaque levier a son propre « ETAT_LEVIER ».
   // 
   
if (nUsed1 == 1
   
// Si le levier est a « On » alors le placer a « Off »
      
SetLocalInt(OBJECT_SELF"ETAT_LEVIER"0); 
   else 
   
// Si le levier est a « Off » alors le placer a « On »
      
SetLocalInt(OBJECT_SELF"ETAT_LEVIER"1); 
      
nUsed1 GetLocalInt(GetObjectByTag("LEVIER1"), "ETAT_LEVIER"); 
      
nUsed2 GetLocalInt(GetObjectByTag("LEVIER2"), "ETAT_LEVIER"); 
      if ((
nUsed1==1) && (nUsed2==1)) 
      
// Est ce que les deux leviers sont a « On » ?
      

         
// Alors créer le portail et le trigger.
         
object oPortalSpot=GetWaypointByTag("TM_INWP");
         
CreateObject(OBJECT_TYPE_PLACEABLE,"plc_portal",
            
GetLocation(oPortalSpot), TRUE);
         
SetLocalInt(GetObjectByTag("PORTTRIG"), "READY"1); 
      } 

Il y a quelques petites choses a expliquer dans ce script, mais pour la plupart, j’espère que les commentaires sont suffisants.

 Prenez maintenant le trigger que vous avez dessiné.
 Placez ce script sur le « OnEnter ». Sauvegardez le en « tm_portail_en ».
Code PHP:

// « OnEnter » script: tm_portal_en 
// 
// Si le portail est « On » et qu’un Pc entre dedans, 
// alors téléporter le Pc au WayPoint « TM_OUTWP ».
// 
// Ecrit par Celowin 
// Le 12 Juillet 02.
// 
void main() 

// Initialisation de nos variables
// 
object oPC GetEnteringObject(); 
object oDest GetWaypointByTag("TM_OUTWP"); 
int nReady GetLocalInt(OBJECT_SELF"READY"); 
// 
// Vérifie si c’est un Pc et si le portail est « On » ?
// Dans ce cas, téléporter le Pc.
if ((nReady==1) && (GetIsPC(oPC))) 
AssignCommand(oPCJumpToLocation(GetLocation(oDest))); 

 On a presque terminé, sortez du trigger.
 Prenez votre zone de destination.
 Dessinez un « WayPoint ». Donnez lui le tag « TM_OUTWP ».
 Sauvegardez le tout et sortez de l’éditeur.
 Testez.

Ces scripts sont relativement linéaires. La seule chose qui mérite d’être précisée a mon sens est le « AssignCommand ». C’est une de ces commandes qui nous font nous demander comment on a pu vivre sans. Elle sert simplement a faire effectuer une action a quelqu’un d’autre. Nous voulons que le joueur se téléporte, donc nous lui demandons de le faire.
Un petit mot encore au sujet des variables renseignant l’état des leviers. J’utilise nUsed1 deux fois. Une fois pour chaque levier. En fait, c’est l ‘état du levier sur lequel on est. Hors plus tard, je dit que nUsed1 est l’état du LEVIER1.
Il y aura sans doutes des programmeurs qui ne trouveront pas ça très propre. Alors pourquoi l’ai je fait ? Par soucis de simplicité. Le script est suffisamment court. Il n’y a pas de besoin de rajouter une autre variable. Sur un script plus long ou plus compliqué, je l’aurai sans doutes fait.
Une autre chose a préciser est la commande « CreateObject » et pour ce faire, il faut expliquer la différence entre un tag et une référence de bluePrint.
Les objets qui sont dans le jeu ont des Tags. Il est donc possible de retrouver tout objet, déjà crée par son tag. Mais un objet qui n’a pas encore été crée n’en a pas… encore. A la place, nous utilisons un « BluePrint », une sorte de schémas de l’objet, avec toutes les instructions permettant de le créer. C’est exactement cela, le « plc_portal », la référence du « BluePrint » de l’objet Portail. Notez que pour trouver la référence du « BluePrint », j’ai juste dessiné un portail. En regardant dans l’ongle propriétés, j’ai noté sa référence.

Exercices complémentaires.
Apres le cours, les devoirs a la maison… et j’aimerai beaucoup que vous les fassiez. Je ne peux en aucun cas vous y contraindre, mais je peux vous assurer qu’ils sont nécessaires afin d’assimiler ces leçons.
Essayez donc de faire ceci.
A l’heure actuelle, abaisser les deux leviers active le portail. Mais une fois activé, le portail reste en place quelque soit ce qu’on fait aux leviers. Il faudrait que le portail disparaisse des que l’on relève l’un OU l’autre des leviers. Et bien sur, qu’il réapparaisse des que les deux sont baissés.
Une autre idée…
Essayez de faire un portail qui marche dans les deux sens…
Je pense que le premier exercice est moyennement difficile, le second étant assez facile.
Quoiqu’il en soit, bon courage et a bientôt pour une nouvelle leçon.

Amaranthe.
Heu c'est pi être ca
Bon c'est mon premier post alors soyez indulgents,
Voila la réponse, enfin un bidouillage on vas dire.


void main()
{
int nUsed1 = GetLocalInt(OBJECT_SELF, "ETAT_LEVIER") ;
int nUsed2 ;

if (nUsed1 == 1)

SetLocalInt(OBJECT_SELF, "ETAT_LEVIER", 0);
else

SetLocalInt(OBJECT_SELF, "ETAT_LEVIER", 1);
nUsed1 = GetLocalInt(GetObjectByTag("LEVIER1"), "ETAT_LEVIER");
nUsed2 = GetLocalInt(GetObjectByTag("LEVIER2"), "ETAT_LEVIER");
if ((nUsed1==1) && (nUsed2==1))

{

object oPortalSpot=GetWaypointByTag("TM_INWP");
CreateObject(OBJECT_TYPE_PLACEABLE,"plc_portal",
GetLocation(oPortalSpot), TRUE);
SetLocalInt(GetObjectByTag("PORTTRIG"), "READY", 1);
}
if ((nUsed1==1) != (nUsed2==1))
{
DestroyObject(GetObjectByTag("Portal"));
}

}



Voila chez moi ca marche, mais il faut bien sur qu'il n'ay ai pas d'autres portal dans l'area avec le tag Portal sinon ils l'est éfface tous.
CA marche mais ce n'est certainement pas la bonne réponse.

PS: je suis plus qu'un quiche en script mais je travaille.
ca ne fait que une semaine que je m'y suis mis.
Re: Cours de Scripts pour les Quiches. Leçon Deux. - Erreur
Citation :
Provient du message de Uther
Par Celowin mailto:james.foxglove@verizon.net
traduit par Amaranthe mailto:amaranthe@free.fr

Introduction
Le but de cette série de leçons, est d'apprendre a se servir des scripts a n'importe qui. Cette deuxième leçon porte sur les variables. Il est toujours possible de trouver la première leçon...

...

Le premier va dans le « OnSpawn »

Code PHP:

void main()
{
  
SetLocalInt (OBJECT_SELFCHANTEUR_COUNT0);


Sauvegardez le en tm_chanteur_os ( « os » pour « OnSpawn » )
...
28/10/02 21:57:41 : Erreur. 'tm_chanteur_os n'est pas compilé.
tm_chanteur_os.nss(3): ERREUR : VARIABLE DEFINED WITHOUT TYPE

Voilà le resultat, en faisant un simple copier coller... C'est très frustrant de se voir limité à la leçon UNE sur une serie de 6...

Edit Il est bugge today le forum...
Citation :
Voilà le resultat, en faisant un simple copier coller...
Ton copier coller est facétieux: il manque les guillemets.
Code PHP:

void main()
{
  
SetLocalInt (OBJECT_SELF"CHANTEUR_COUNT"0);

Edit: il est possible que se soit véritablement un pbm de codage de caractère, mais de toute façon évite les copier-coller tu n'apprendra rien comme ça. Retaper c'est parfois fastidieux et cela peut parraître idiot, mais cela favorise la mémorisation.
ok, c'est ma faute, j'ai pas fait gaffe aux guillements a cause des couleurs.
Avant de tester le copier coller j'avais fait ça a la main, mais j'ai été trompe par le bleu au lieux du rouge.

Merci proto

Bon question béte mais , je bloque sinon
C'est quoi un commoner? moi j'ai une version francaise du jeu donc bon certain truc demander j'arrive a trouver mais la je vois pas, pour la lecon 5 et ca me bloque, pareil pour choisir "ArchetType" Ben je bloque.

Sinon felicitation pour ces lecons elles sont super bien et tres utiles

edit : ce qui serais bien c'est ques les mots anglais de la version du jeu soit traduit pour la version francaise, histoire que les quiches en anglais comme moi puisse quand meme evoluer en tant que quiche de scriptage deja
Genre, commoner, feats, weapon, autant de choses demander dont j'ai du mal a savoir de quoi il s'agit

Merci pour la reponse
Code PHP:

// Script « OnUserDefined »
// tm_flechette_ud
// Jeu de flechette
// Appelé par le OnHeartBeat script.
//
// Le joueur de fléchettes lance les dards de son inventaire ( 3 au départ ) 
// Va jusqu'à la cible. Récupère ses dards, revient et recommence.
// 
void main()
{
    
int nAppelePar GetUserDefinedEventNumber();
    
object oCible GetNearestObjectByTag("CIBLE");
    
int nCiblePrete GetLocalInt(OBJECT_SELF"ETATCIBLE");
//
    
switch(nAppelePar)
    {
     case 
1001:  // Appelé par OnHeartbeat
       //
       // nCiblePrete est a 1 si la cible est prête.
       //
       
if ((GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)))
            && (
nCiblePrete == 1))
          {
             
// Si on a des dards dans la main droite et qu’on est prêt a les lancer, alors faisons le. 
             
ClearAllActions();
             
ActionAttack(oCibleTRUE);
           }
       else
          {
             
// Sinon, soit nous n’avons plus de dards, soit nous sommes déjà en train d’aller les chercher.
if (nCiblePrete == 1)
              {
                
SetLocalInt(OBJECT_SELF"ETATCIBLE"2);
                
ActionMoveToObject(oCible);
                
ActionWait(0.5);
                
ActionPlayAnimation(ANIMATION_LOOPING_GET_MID1.01.0);
                
ActionWait(0.5);
                
ActionPlayAnimation(ANIMATION_LOOPING_GET_MID1.01.0);
                
ActionWait(0.5);
                
ActionPlayAnimation(ANIMATION_LOOPING_GET_MID1.01.0);
                
object oDestination=GetNearestObjectByTag("CIBLEWP001");
                
ActionMoveToObject(oDestination);
                
CreateItemOnObject("nw_wthdt001"OBJECT_SELF3);
                
ActionEquipMostDamagingRanged();
                
ActionDoCommand(SetLocalInt(OBJECT_SELF"ETATCIBLE"1));
              }
            }
       break;
    }

C'est curieux, ça ne marche pas. Le tireur lance ses dards, se dirige vers la cible et la dégaine d'autre dards (premier bug, il ne devrait les sortir qu'après les avoir enlevé de la cible), arrive près de la cible, récupère ses trois dards (qu'il a déjà en mains ^^), et ne revient pas, et recommence à tirer juste devant la cible.
Je suis un peu dérouté, j'ai tenté (vainement) quelques variantes, mais j'ai fait pire que mieux.
Quelqu'un saurait d'où viennent ces bugs ?
d'avance !
Amicalement, Grand Khân.
En première lecture, je me suis dit que le "ClearAllActions()" de la section où le PNJ peut attaquer la cible brisait son retour au point de départ. Tu as déjà essayé sans ?

Pour le WayPoint de départ, j'aurai peut-être aussi essayé un tag "POST_tagduPNJ" qui m'aurait assuré que le PNJ retournait bien au point de départ.

Pour l'effet visuel des fléchettes dans la main, tu pourrais ne lui les créer qu'une fois au lieu où il doit les lancer.
Voila ce que j'ai tenté:

Code PHP:

string Flechette() //Ce que peut dire l'elfe joueur.
{
  
string txt;
  switch(
d8())
  {
     case 
1txt="Cette fois ci je l'aurais!";
     case 
2txt="Avec un peu d'chance...";
     case 
3txt="J'suis l'roi d'la flechette!";
     case 
4txt="Plus fort que Guillaume Tell!";
     case 
5txt="J'suis vraiment un bon!";
     case 
6txt="A moi Las Vegas !";
     case 
7txt="J'rate jamais mon coup!";
     case 
8txt="At..At..Atchoum!";
  }
  return 
txt;
}


void main()
{
    
object oCible=GetNearestObjectByTag("CIBLE");
    
int nCible=GetLocalInt(OBJECT_SELF,"pret");
    if (
GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND))&& nCible==1)
    
//On regarde si il a bien des dards dans la main droite et s'il est pret.
    
{
        
ClearAllActions();
        
SpeakString(Flechette(),TALKVOLUME_TALK);//Une phrase bidon au pif.
        
ActionAttack(oCible);//Il balance les dards sur la cible.
        
ActionEquipMostDamagingRanged();//On s'assure qu'il utilise bien toutes ses fleches.
    
}
    if (!(
GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND))) && nCible==1)
   
// Si le joueur n'a rien dans la main droite, et qu'il est pourtant toujours pret a tirer.
    
{
       
SetLocalInt(OBJECT_SELF"pret"2); //Alors l'elfe joueur n'est pas pret a tirer, il va bouger.
       
ActionMoveToObject(oCible); //Il se dirige vers la cible.
       
ActionWait(0.5);
       
AssignCommand(OBJECT_SELF,ActionPlayAnimation(ANIMATION_LOOPING_GET_MID,1.0,1.0));
       
ActionWait(0.5);
       
AssignCommand(OBJECT_SELF,ActionPlayAnimation(ANIMATION_LOOPING_GET_MID,1.0,1.0));
       
ActionWait(0.5);
       
AssignCommand(OBJECT_SELF,ActionPlayAnimation(ANIMATION_LOOPING_GET_MID,1.0,1.0));
       
object oPosition=GetNearestObjectByTag("flechette");
       
CreateItemOnObject("wthdt002"OBJECT_SELF);// On cree une flechette.
       
CreateItemOnObject("wthdt002"OBJECT_SELF);// Une autre.
       
CreateItemOnObject("wthdt002"OBJECT_SELF);// La 3eme...
       
ActionMoveToObject(oPosition); // Il revient a sa position de tir.
       
ActionEquipMostDamagingRanged();//Il prend ses flechette.
       
SetLocalInt(OBJECT_SELF"pret"1);//Il est pret a tirer.
   
}

Alors il y a un gros bug et un moins important.
Le gros problème vient du fait que le tireur ne fait pas les 3 animations voulues.
Le petit est que des fois sur la barre des dialogues on voit qu'il va parler mais en fait le texte est vide, on voit juste son nom s'afficher, pas de texte.
Aucun problèmes provenant des tags, waypoints et autres, j'ai tout vérifié une centaine de millions de fois (j'exagère ok ^^).
En tout cas merci pour votre aide, et j'espère pouvoir un jour finir ce foutu script
Amicalement, Grand Khân.
A vu de nez comme ça je dirai qu'il dit tout le temps atchoum (ok je sors ^^). Il faut des breaks après les 7 premiers case. Ensuite tes anims durent 1 seconde et sont espacées toutes les demi secondes donc ça peut être le problème. ActionWait je sais pas trop, j'utilise DelayCommand en général, mais j'ai des problèmes aussi à l'occasion.
Ah oui aussi:

AssignCommand(OBJECT_SELF,ActionPlayAnimation(ANIMATION_LOOPING_GET_MID,1.0,1.0));


Ca peut s'écrire:

ActionPlayAnimation(ANIMATION_LOOPING_GET_MID,1.0,1.0));

et même:

PlayAnimation(ANIMATION_LOOPING_GET_MID,1.0,1.0));
Olblach a tout dit, et je suis d'accord avec ce qu'il dit sur les ActionWait(). Ils ne temporisent rien du tout et je me suis toujours demandé à quoi elle peut bien servir cette fonction. Peut-être dans l'empilement des actions... Je ne sais pas...
J'utilise moi aussi des DelayCommand().

Code PHP:


switch (X)
{
     case 
Atxt "blabla"; break; // Toujours mettre des break ou il va jusqu'au bout
                                    //=> "atchoum" chez toi d'où la judicieuse plaisanterie d'Olblach
     
case Btxt "blibli"; break;
     case 
Ctxt "bloblo"// ici ça sort de toute façon, c'est la fin

Cet Enchaînement:

Code PHP:


ActionMoveToObject
(oCible); //Il se dirige vers la cible.
ActionWait(0.5);
AssignCommand(OBJECT_SELF,ActionPlayAnimation(ANIMATION_LOOPING_GET_MID,1.0,1.0)); 
personnellement, je ne le sens pas bien. Il marche, attend rien (ça ne l'arrêterait pas ça, déjà ?), et au cas où il aurait envie de continuer à marcher, l'animation lui donne le compte et il s'arrête.
Si je devais tester ça, je le mettrai dans un script séparé que j'appellerai avec un DelayCommand(X.0, ExecuteScript("nomduscriptdesanimations", object_personnage_anime) avec X aussi court que possible pour ne pas déborder des 6 secondes totales du OHB et là, je verrai s'il bouge de place ou non avant de faire ses beaux gestes

Autre suggestion: les 1.0 d'exécution des animations, c'est suffisant pour celles que tu as choisies ?

Et dernière impression mais elle est un peu au pif: Tout ça, ça ne prend pas plus de 6 secondes à faire justement ?
Ce script me plaisait bien et je le trouvais ingénieux. Mais quelle galère !

Il y avait un peu de tout là dedans. Il m'a donc fallu contourner l'interruption du script et son éternel recommencement à cause de sa durée. Certaines conditions ne se remplissaient jamais. J'en ai donc conservé l'esprit initial et j'y ai fait des retouches surtout sur la variable locale renseignée sur le personnage.

D'abord, c'était beaucoup trop long pour les 6 secondes. Le ClearAllAction() nuisait également au bon déroulement, et tel quel, ton script ne pouvait pas tourner. Sans compter les ActionWait()...
Il se déroule maintenant en gros sur 5 OHB. Je t'ai laissé le SpeakString() qui te donne la valeur de la variable locale pour que tu puisses facilement te rendre compte du temps que prend le script à se dérouler.

Voici une version pas vraiment optimisée mais qui tourne:
(normal, il est 5:50 )

Code PHP:


string Flechette
() //Ce que peut dire l'elfe joueur.
{
  
string txt;
  switch(
d8())
  {
     case 
1txt="Cette fois ci je l'aurais!";break;
     case 
2txt="Avec un peu d'chance...";break;
     case 
3txt="J'suis l'roi d'la flechette!";break;
     case 
4txt="Plus fort que Guillaume Tell!";break;
     case 
5txt="J'suis vraiment un bon!";break;
     case 
6txt="A moi Las Vegas !";break;
     case 
7txt="J'rate jamais mon coup!";break;
     case 
8txt="At..At..Atchoum!";
  }
  return 
txt;
}


void main()
{

    
object oCible=GetNearestObjectByTag("CIBLE");
    
//int nCible=GetLocalInt(OBJECT_SELF,"pret");


    
if ((GetLocalInt(OBJECT_SELF,"pret")>= 
&& (
GetLocalInt(OBJECT_SELF,"pret")<=
&& (
GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_RIGHTHANDOBJECT_SELF))))
    
//On regarde si il a bien des dards dans la main droite et s'il est pret. 
// NOTE: oui, mais c'est un peu plus compliqué à cause des 6 secondes
// et on va jouer sur les tests pour pouvoir repasser plusieurs fois 
// dans le OHB sans forcément tout recommencer grâce à la variable locale "pret"

    
{

        
SpeakString(Flechette(),TALKVOLUME_TALK);//Une phrase bidon au pif.
        
ActionAttack(oCible);//Il balance les dards sur la cible.
        
ActionEquipMostDamagingRanged();//On s'assure qu'il utilise bien toutes ses fleches.
        
SpeakStringIntToString(GetLocalInt(OBJECT_SELF"pret")), TALKVOLUME_TALK); // A supprimer. 
 // Il est là pour visualiser la durée du script
        
SetLocalInt(OBJECT_SELF"pret"GetLocalInt(OBJECT_SELF,"pret")+1);

    }

    if( ((
GetLocalInt(OBJECT_SELF,"pret")>= 3
&& (!
GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_RIGHTHANDOBJECT_SELF)))) 
|| (
GetLocalInt(OBJECT_SELF"pret") == 0))
   
// Si le joueur n'a rien dans la main droite, et qu'il est pourtant toujours pret a tirer.

// NOTE:  Idem que ci-dessus, on restreint les passages très précisément.
// Et le "==0" c'est pour le premier passage sinon, ça ne commence jamais
    
{

       
//SetLocalInt(OBJECT_SELF, "pret", 2); //Alors l'elfe joueur n'est pas pret a tirer, il va bouger.
       
ActionMoveToObject(oCibleFALSE1.0f); //Il se dirige vers la cible.
       //ActionWait(0.5);
       
ActionPlayAnimation(ANIMATION_LOOPING_GET_MID,1.0,1.0);
       
//ActionWait(0.5);
       //AssignCommand(OBJECT_SELF,ActionPlayAnimation(ANIMATION_LOOPING_GET_MID,1.0,1.0));
       //ActionWait(0.5);
       //AssignCommand(OBJECT_SELF,ActionPlayAnimation(ANIMATION_LOOPING_GET_MID,1.0,1.0));
       
object oPosition=GetNearestObjectByTag("flechette");
       
CreateItemOnObject("wthdt002"OBJECT_SELF);// On cree une flechette.
       
CreateItemOnObject("wthdt002"OBJECT_SELF);// Une autre.
       
CreateItemOnObject("wthdt002"OBJECT_SELF);// La 3eme...
       
DelayCommand(1.0ActionMoveToObject(oPositionFALSE1.0f)); // Il revient a sa position de tir.
       
ActionEquipMostDamagingRanged();//Il prend ses flechette.
       
SetLocalInt(OBJECT_SELF"pret"1);//Il est pret a tirer.
   
}

Il est malheureusement impossible, sans rentrer dans des complications infernales, de faire se succéder les 3 animations car le gaillard n'en fait qu'à sa tête et retourne au pas-de-tir avant d'avoir terminé de retirer ses fléchettes. De plus, tu remarqueras qu'il court vers la cible, mais ça aussi... Je n'ai pas su le supprimer correctement. C'est le reliquat de son attitude agressive et il s'attaquerait volontiers à la cible à mains nues.

Le waypoint taggé "flechette", je l'ai mis à environ un 3/4 de carré de la cible.

Pfiouuu... Quelle bourrique que cet elfe !

Tiens, pour te donner une idée du rythme, mets ce script dans ton OHB de la zone où il lance ses fléchettes:

Code PHP:

void main()
{
    
object oChanteur GetObjectByTag("CIBLE");
    
AssignCommand(oChanteurPlaySound("as_cv_brickscrp1"));


Et voilà. Bon amusement
A noter que effectivement, ActionWait sert a timer des action dans une pile d'action. Il est important de noter que de tout prevoir en pile d'action est interessant, car cela assure une continuité des actions quelques soit les problemes de lag ou de timegap.

A noter aussi que chaque objet, bien sur, a sa propre pile d'action, qu'il execute independamment de la pile de son voisin.

Un script comme cela, par exemple :
Code:
ActionSpeakString("Bonjour");
AssignCommand(oPNJ2,SpeakString("Hello"));
ActionWait(0.5);
ActionSpeakString("Ca va ?");
AssignCommand(oPNJ2,SpeakString("l'a matelas et toi ?"));
ActionSpeakString("Yeau de poîle !");
ActionWait(0.5);
AssignCommand(oPNJ2,SpeakString("Hahaha"));
ne s'executera pas en ordre, et les actionWait feront tout foirer, par exemple...

Il devient important de charger la pile d'action d'une seule personne :

Code PHP:

ActionSpeakString("Bonjour");
ActionDoCommand(AssignCommand(oPNJ2,SpeakString("Hello")));
ActionWait(0.5);
ActionSpeakString("Ca va ?");
ActionDoCommand(AssignCommand(oPNJ2,SpeakString("l'a matelas et toi ?")));
ActionSpeakString("Yeau de poîle !");
ActionWait(0.5);
ActionDoCommand(AssignCommand(oPNJ2,SpeakString("Hahaha"))); 
et la, de suite, la scene se deroulera bien en ordre, et toujours beaucoups plus precisement qu'avec un DelayCommand.


Arf je suis en retard ^^
Bon, j'ai lu attentivement vos conseils, et j'ai appliquer autant que je pouvais, et rien à redire, ca marche plutôt bien ^^
Maintenant j'ai un joueur de flechettes qui s'il rate, peut dire 1 fois sur 3 une réplique du genre juron, s'il reussit peut dire 2 fois sur 3 une acclamation. De plus, il y a un spectateur, celui ci l'encourage si le lanceur rate deux fois de suite, et l'acclame avec un petite animation si celui ci reussit deux fois de suite.

*content*

Maintenant je vais faire en sorte que les PJs puissent y joueur eux aussi.

Encore merci pour votre aide et d'avoir pris la patience de me répondre.
Amicalement, Grand Khân.
pas tout suivi
voila, je quitte neverwinter sur la version 1.28, je reviens on parle de la 1.31.
Faut pas sortir p....(uriner) car on est vite débordé.

plus sérieusement je me remet a étudier tous les pro du script

hors sujet ? ok je sort !!!!

Répondre

Connectés sur ce fil

 
1 connecté (0 membre et 1 invité) Afficher la liste détaillée des connectés