voici un copier/coller d´un des tutoriaux qui m´ont servi à début dans le scripting. Ca devrait en aider bien d´autres...
Tutorial sur les Scripts de Bioware
Ecrit par David Gaider
Traduit par Théranthil
Sommaire
I. Stocker des variables. 2
II. Definir un objet 5
III. Creation d’objets 7
IV. A propos des animations. 8
V. Explication des Waypoints et Walkwaypoints. 11
VI. Userdefined Event est votre ami. 12
VII. Un script OnSpawn mieux commente 14
VIII. Recompenser avec de l’or et des XP 15
IX. Faire des jets de compétence. 16
X. Creer des objets uniques 18
XI. S´asseoir sur une chaise et Dormir 19
XII. Comment faire pour qu’un NPC entame un dialogue tout seul ? 20
XIII. Comment faire pour que mon NPC attaque le PC avec lequel il est en train de parler? 22
XIV. Comment puis je ouvrir mon magasin? 23
XV. Utilisation des " Module Events" 24
XVI. Utilisation des " Area Events" 25
XVII. Utilisation des " Creature Events" 26
XVIII. Utilisation des " Placeable Object Events" 29
XIX. Utilisation des " Trigger Events" 31
XX. Ajouter et enlever des effets 32
XXI. Utilisez les dialogue avec les objets placables et les déclencheurs 35
XXII. Factions, cri et attaquer mes ennemis 38
I. Stocker des variables.
Une des choses les plus courantes que vous aurez à faire est de stocker des variables et de les retrouver… C’est la seule manière de déterminer comment la plupart des événements vont prendre place ou bien de changer/garder l’état d’une quête.
La commande que vous utiliserez le plus fréquemment pour stocker des variables est celle ci:
void SetLocalInt(object oObject, string sVarName, int nValue)
Cette commande stocke une ‘local integer’ ( un entier local). Qu’est ce que local veut bien dire ? Cela veut dire que l’entier est référencé, ou ‘stocké’, dans " l’ oObject" que vous avez spécifié. ( Il n’est pas enregistré la physiquement, mais il est plus facile de penser que c’est le cas.)
Il y a trois choses qui sont importantes à retenir ici :
· le " oObject" doit être un objet valide. Vous verrez beaucoup de commandes qui renvoient un objet au début de leur structure ( comme il est marqué " void" au début de la commande " SetLocalInt") Cela signifie que ces commandes retournent comme donnée le type de l’objet.
Dans tous les cas, l’objet que vous spécifié est la où " l’integer" est référencé. Si vous êtes dans un dialogue, utiliser GetPCSpeaker() retournera le PC qui est en train de parler au NPC qui est l’objet. Vous pouvez utiliser GetModule() pour enregistrer " l’integer" dans le module lui-même… Vous pouvez utiliser OBJECT_SELF pour nommer l’objet qui est utilisé dans le script actuel, etc.
C’est important parce que la variable que vous choisissez devient unique pour cet objet. Si vous stockez une variable avec le nom " VARIABLE 1" avec la valeur 1 dans le module, c’est la seule variable qui peut être appelée par ce nom. Vous pouvez avoir une " VARIABLE 1" enregistrée dans un autre objet, comme un PC, et il peut avoir des valeurs complètement différentes.
· La " string sVarName" est le nom que vous donnez à votre variable. Vérifiez bien qu’il soit unique. Comme toutes les strings ( chaînes de caractères), il doit être entouré de guillemets ( comme " VARIABLE 1" ci dessus). Les noms de variables ne différencient pas les majuscules des minuscules.
· La " int nValue" est l’information que vous voulez stocker dans la variable. Tant que c’est un entier nous utiliserons cette commande, mais ça doit être un entier ( sans décimales) ( NdT c’est pas moi qui insiste autant, c’est Bioware) comme 1, 15 ou 320. Si vous voulez entrer différentes informations ici, vous avez besoin d’autres commandes :
o SetLocalFloat = enregistrer un " float number" ( un nombre avec une partie décimale)
o SetLocalString = enregistrer une " string" ( comme un mot, une phrase ou le nom d’un objet… Elle doit juste être écrite entre guillemets, comme le nom de la variable.)
o SetLocalLocation = enregistrer une localisation
o SetLocalObject = enregistrer un objet ( comme un " placeable object", un objet d’inventaire ou une créature.)
NdT : à partir de maintenant, par soucis de clarté, j’utiliserais le terme d’integer ( ou int) et non ça traduction
Comment dois-je utiliser les variables ?
Bien, une fois que vous avez enregistré une variable dans quelque chose, elle reste la jusqu’à ce qu’elle soit remplacée ou détruite. N’importe quand, dans un autre script ( ou dans le même) vous pouvez rappeler cette variable et l’information qu’elle contient.
Si je veux rappeler le nombre que je viens d’enregistrer dans la variable, je dois utiliser cela :
int GetLocalInt(object oObject, string sVarName)
C’est exactement la même commande que celle que vous avez utilisé pour enregistrer la variable… moins la valeur actuelle de la variable ( c’est ce que nous attendons)
Remarquez que ce n’est pas une commande " void"…Elle retourne un entier. Cela veut dire que vous n’utilisez pas seulement toute seule. Utilisez la pour définir tout autre " int" nécessaire dans une autre commande ou variable comme cela :
int nValue = GetLocalInt(OBJECT_SELF, " VARIABLE1")
Cela signifie que vous avez spécifié une nouvelle variable appelée " nValue" qui est égale à l’integer qui était stocké dans l’OBJECT_SELF sous le nom " VARIABLE 1"
C’est encore un peu confus ? voici des exemples.
Un bon exemple pour comprendre comment une variable peut être utilisée est de suivre la progression d’une quête. Je veux avoir une variable qui s’appelle " STORY"… quand le joueur accepte la quête, je veux lui attribuer la valeur 1. Quand il l’a fini, je lui attribue la valeur 2.
Le joueur accepte la quête…
Au moment du dialogue ou le joueur accepte, je vais dans " Actions Taken" et fait un script qui entre 1 dans la variable " STORY" du PC.
void main()
{
SetLocalInt(GetPCSpeaker(), " STORY", 1);
}
Le joueur termine la quête…
Une fois que le PC est revenu et a fait ce que le NPC veut, le NPC le félicite et la valeur 2 est stockée dans la variable " STORY" du PC.
void main()
{
SetLocalInt(GetPCSpeaker(), " STORY", 2);
}
Comment je sais si le joueur a accepté ou terminé la quête ?
Au moment voulu dans le dialogue, vous avez juste besoin de créer un nouveau nœud pour chaque condition. L’ordinateur commencera par le haut et vérifiera tous les scripts dans la section " Text Appears When" et regarde si ils sont vrais ( TRUE) ( si il n’y a pas de scripts, il considérera automatiquement que c’est vrai.)
Dans ce cas, il commence ce nœud. Sinon, il passe au suivant. C’est pourquoi, vous devez lister ces nœuds du dernier au premier.
Maintenant, vous avez besoin d’un script qui renvoie TRUE pour #1 si la variable " STORY" est égale à 2, n’est ce pas ? Et pour #2, le script doit retourner TRUE si la variable est égale à 1. Aucun script n’est nécessaire pour #3 car si #1 et #2 sont faux ( FALSE pour le script), c’est que la quête n’a été ni acceptée, ni terminée.
Est-ce que la quête est terminée ?
int StartingConditional()
{
int nDone = GetLocalInt(GetPCSpeaker(), " STORY") == 2;
return nDone;
}
Est-ce que la quête a été acceptée ?
int StartingConditional()
{
int nAccept = GetLocalInt(GetPCSpeaker(), " STORY") == 1;
return nAccept;
}
Normalement, vous devriez arriver à ceci:
Dans le script ci dessus, je voulais seulement que le NPC donne la quête à 1
Au moment ou il fixe la variable " STORY" à 1, pourquoi est ce qu’il n’a pas crée une variable " PLOT_GIVEN" rattachée à OBJECT_SELF ( c.a.d le NPC) ?
Dans ce cas, lorsqu’un autre PC qui n’aurait pas de variable " STORY" parle au NPC, vous pourriez avoir à la place du texte par défaut ( #3) un dialogue divisé en 2 :
|
|
--> ( script dans " Text Appears When" qui renvoie TRUE si " PLOT_GIVEN" de " OBJECT_SELF" a pour valeur 1)
" Désole, je n’ai pas de travail pour vous aujourd’hui"
|
|
--> ( pas de script) " Est ce que vous êtes intéressé par un travail ? "
Un exemple très simple de dialogue de quête
La chose à se souvenir est que quand vous créez votre premier nœud, juste en dessous de Root, les propositions doivent être dans l’ordre inverse de l’ordre dans lequel elles seront complétées. ( cf. exemple ci dessus)
Admettons maintenant que notre NPC donne 3 boulots. Imaginez la structure suivante sous le " root" :
Il faut se souvenir que lorsqu’on clique sur un NPC, l’ordinateur commence à vérifier le script " Text Appears When" des parties du nœud. Si le script renvoie TRUE ( ou si il n’y a pas de script), ce nœud est initialisé. Si il retourne FALSE, il passe à la suite.
Voici la liste des scripts qui sont attribué à chaque nœud.
#1 - script dans ´Text Appears When´ qui renvoie TRUE si la variable " Job" du PC a pour valeur 4:
int StartingConditional()
{
int nJob = GetLocalInt(GetPCSpeaker(), " Job") == 4;
return nJob;
}
void main()
{
SetLocalInt(GetPCSpeaker(), " Job", 4);
}
Voilà comment stocker et rappeler des variables. Utiliser les " strings", les " object data" et autres données est un peu plus compliqué... mais ça paraîtra plus aisé maintenant que vous êtes habitué à manipuler les " integers". En espérant que ça vous aidera à débuter.
suite:
II. Definir un objet
Maint
Beaucoup d’action requièrent un objet comme cible. La manière la plus facile de définir un objet est de lui avoir donné un tag particulière
ment pratique…c’est génial si l’on sait quel est l’objet qui servira de cible. Mais comment faire si on ne le sait pas ? Comment faire si on veut trouver le PC le plus proche du personnage…où qu’il soit ? Comment faire si on veut cibler la créature la plus proche ? Comment faire si on veut vérifier à quelle distance se trouve quelque chose ?
Il y a de nombreuses manières de vérifier des choses comme ça… Je vais ici me focaliser sir trois :
GetNearestCreature, GetNearestObject et GetDistanceBetween.
GetNearestCreature
Cette commande vous permet de trier des créature selon leur proximité par rapport à un objet et cela en suivant 3 paramètres. Notez que la cible ne doit pas être l’objet de ce type le plus proche…Si vous voulez, par exemple, localiser le second non-PC voleur humain le plus proche vous devez utiliser cette commande.
La syntaxe est la suivante :
object GetNearestCreature ( int nFirstCreatureType, int nFirstParameter, object oTarget=OBJECT_SELF,
int nNth =1, int nSecondCreatureType=-1, int nSecondParameter = -1, int nThirdCreatureType = -1,
int nThirdParameter = -1 )
Ca peut paraître un peu confus, mais nous allons y aller petit à petit.
´nFirstCreatureType´ et ´nFirstParameter´ sont les premières caractéristiques que nous recherchons. Admettons que nous cherchons l’humain le plus proche… " nFirstCreatureType" sera l’attribut CREATURE_TYPE_ RACIAL_TYPE, et ´nFirstParameter´ sera RACIAL_TYPE_HUMAN.
Qu’est ce que ça veut dire ? Vous avez jusqu’à trois qualités que vous pouvez utiliser pour affiner la recherche… en utilisant pour chacun un attribut et un paramètre.
L’objet " oTarget" est bien évidemment l’objet qui sert de centre à la recherche. Ca peut être OBJECT_SELF ( et c’a l’est par défault) si vous voulez cibler la recherche sur l’objet auquel appartient le script. Vous pouvez aussi faire quelque chose d’autre comme par exemple vérifier la créature la plus proche du PC le plus proche…Mais ça vous trouverez vous même comment le faire.
L’integer ´nNth´ est ce que vous utilisez pour déterminer si vous voulez le premier objet le plus proche, le second…Par défaut, le script cherche le premier. Mais n’importe quel nombre que vous mettez ici déterminera combien de créature plus proche ne seront pas retenue par la recherche. Notez que n’importe quelle fonction peut être utilisée à la place du moment qu’elle renvoie un entier… Vous pouvez mettre d4() par exemple, si vous voulez chercher aléatoirement du premier au quatrième objet le plus proche.
Ici, vous avez la liste de arguments que vous pouvez utiliser pour la recherche.
· CREATURE_TYPE_CLASS = classe de personnage ou de monstre
· CREATURE_TYPE_DOES_ NOT_HAVE_SPELL_EFFECT = Si vous recherchez une créature qui n’est pas affectée par un sort ( spécifié par la constante SPELL_*) J’imagine qu’elle ne doit pas servir souvent.
· CREATURE_TYPE_SPELL_EFFECT = Voici l’inverse, si vous recherchez une créature affectée par tels ou tels sorts. Notez que tous les effets de sorts ne viennent pas de sorts.
· CREATURE_TYPE_IS_ALIVE = Ca s’explique tout seul ; si vous voulez ne pas tenir compte des créatures mortes.
· CREATURE_TYPE_PERCEPTION = C’est un cas particulier, car techniquement ce n’es pas un attribut de la créature, il dépend de la nature de la perception de l’objet cible…si il a par exemple été vu ou entendu.
· CREATURE_TYPE_PLAYER_CHAR = Cela détermine si la créature est un PC ou non.. l
· CREATURE_TYPE_RACIAL_TYPE = race de la créature.
· CREATURE_TYPE_REPUTATION = classe d’après sa réputation envers la cible.
Je ne citerais pas toutes les classes particulières, les races et les effets qui peuvent être utilisé parce qu’ils sont assez évidents. Quelque autres le sont moins, les voici :
CREATURE_TYPE_IS_ALIVE: TRUE ou FALSE ( facile)
CREATURE_TYPE_PERCEPTION: PERCEPTION_HEARD
PERCEPTION_HEARD_ AND_NOT_SEEN
PERCEPTION_NOT_HEARD
PERCEPTION_NOT_SEEN
PERCEPTION_NOT_SEEN_ AND_NOT_HEARD
PERCEPTION_SEEN
PERCEPTION_SEEN_AND_HEARD
PERCEPTION_SEEN_ AND_NOT_HEARD
CREATURE_TYPE_PLAYER_CHAR: PLAYER_CHAR_IS_PC
PLAYER_CHAR_NOT_PC
CREATURE_TYPE_REPUTATION: REPUTATION_TYPE_ENEMY
REPUTATION_TYPE_FRIEND
REPUTATION_TYPE_NEUTRAL
( je pense que là, même sans être une bilingue en anglais vous comprenez.)
Bon, et maintenant, si je veux trouver le PC le plus proche de moi ?
GetNearestCreature ( CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC);
Le mage ennemi le plus proche de moi ?
GetNearestCreature ( CREATURE_TYPE_ REPUTATION, REPUTATION_ENEMY, OBJECT_SELF,1, CREATURE_TYPE_CLASS, CLASS_TYPE_WIZARD);
L’ennemi vivant le plus proche du plus proche PC allié ?
GetNearestCreature ( CREATURE_TYPE_REPUTATION, REPUTATION_ENEMY,GetNearestCreature
(CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_IS_PC,OBJEC
T_SELF,1,CREATURE_TYPE_REPUTATION, REPUTATION_FRIENDLY) , 1, CREATURE_TYPE_IS_ALIVE, TRUE);
Le non-PC que je puisse voir le plus proche ?
GetNearestCreature ( CREATURE_TYPE_PLAYER_CHAR,
PLAYER_CHAR_NOT_PC,OBJECT_SELF,1,CREATURE_TYPE_PER
CEPTION, PERCEPTION_TYPE_SEEN);
GetNearestObject
C’est beaucoup moins compliqué parce que vous pouvez seulement trier les objets par leur type…vous n’avez pas les multiples paramètres qu’on les créatures. Mais ça marche de la même manière :
object GetNearestObject ( int nObjectType=OBJECT_TYPE_ALL, object oTarget=OBJECT_SELF, intnNth=1)
´nObjectType´ est évidemment le type d’objet que vous recherchez. Si vous ne marquez rien, ce sera l’objet le plus proche, quel qu’il soit. L’objet " oTarget" correspond toujours au centre des recherches. Par défault, c’est " OBJECT_SELF".L’integer " nNth" n’a pas changé non plus, il correspond au nième objet par ordre de proximité. ( le troisième si nNth=3)
Les types d’objet sont donc :
· OBJECT_TYPE_ALL
· OBJECT_TYPE_AREA_OF_EFFECT
· OBJECT_TYPE_CREATURE
· OBJECT_TYPE_DOOR
· OBJECT_TYPE_INVALID
· OBJECT_TYPE_ITEM
· OBJECT_TYPE_PLACEABLE
· OBJECT_TYPE_STORE
· OBJECT_TYPE_TRIGGER
· OBJECT_TYPE_WAYPOINT
La non plus, je pense que la traduction n’est pas nécessaire.
GetDistanceBetween
Ca donne la distance en mètre entre 2 objets valides. Il est important que les 2 objet soient valides, sinon la distance reportée est de 0.
float GetDistanceBetween(object oObjectA, object oObjectB)
Qu’est ce que float ? Un " float" comme un integer est juste un nombre, mais il possède toujours une partie décimale. 50 est un integer, 50.0 est un " float". Si vous avez besoin, vous pouvez utiliser la commande
int FloatToInt(float fFloat) pour convertir un float en integer.
Si vous voulez simplement connaître la distance entre un objet et celui à qui appartient le script, il est actuellement plus facile d’utiliser cette commande :
float GetDistanceToObject(object oObject)
De cette manière, vous n’êtes pas constamment obligé de spécifier OBJECT_SELF dans la commande GetDistanceBetween.
III. Creation d’objets
object CreateItemOnObject(string sItemTemplate, object oTarget = OBJECT_SELF, int nStackSize = 1)
Cette commande crée un objet ( avec le tag " sItemTemplate") dans l’inventaire de l’objet " oTarget". On l’utilise souvent quand des objets sont " donnez" à un PC…ou si un NPC est supposé avoir un objet seulement pendant un certains temps. Si je suis dans un dialogue et que le NPC récompense le PC avec qui il parle avec une épée longue non magique, je mettrais un script avec la commande ci dessous dans " Action Taken" :
CreateItemOnObject ( "NW_WSWLS001", GetPCSpeaker());
Si je veux mettre 12 flèches normales dans un coffre qui a le tag " CHEST05":
CreateItemOnObject ( "NW_WAMAR001", GetObjectByTag ( "CHEST05"), 12);
Note: Si vous utilisez ActionGivenItem pour pour qu’un NPC donne à un PC un objet dans son inventaire, cet objet doit déjà exister dans l’inventaire du NPC pour que l’action soit complétée. ActionGivenItem transfert l’objet de l’inventaire du NPC à celui du PC…CreateItemOnObject crée un nouvel objet dans celui ci.
Note2: Je rajoute que le " sItemTemplate" utilisé dans cette commande est le tag de l’objet…C’est vrai UNIQUEMENT avec les objet standard inclus dans le jeu. Désolé, c’est tout ce que j’ai l’habitude d’utiliser avec cette commande. Si vous essayez d’utiliser cette commande avec des objets personnalisés ( custom items), vous devez utiliser le blueprint resref de l’objet… et non le tag.)
La commande " CreateObject" est un petit peu différente. Elle crée un objet sur la map. Elle est souvent référencée comme " spawning". L’ " objet" peut être une créature ou un objet ( inerte). Sa structure est la suivante :
object CreateObject ( int nObjectType, string sTemplate, location lLoc, int bUseAppearAnimation = FALSE)
´nObjectType´ est une constante qui défini quel type d’objet va être crée. Par exemple, OBJECT_TYPE_CREATURE ou OBJECT_TYPE_ITEM. Elle viens de la liste des constantes " Constants" du script editor.
La string ´sTemplate´ est son ´blueprint resref´. Oui, je sais…quasiment toutes les autres commandes utilisent le tag de l’objet. Celle ci se réfère au resref, trouvé habituellement sous l’onglet " Advance" de l’objet.
Note: cette particularité est très souvent source d’erreur ! Si vous utilisez le tag à la place du resref, ça ne marchera pas !
Un emplacement ( location) consiste en une zone ( area), et des coordonnées xyz sur la carte ( qui sont chacune connue en tant que vecteur) et d’une orientation. Il est plus facile d’utiliser la commande GetLocation(object oObject) pour obtenir toutes ces informations à partir d’un waypoint ou d’un autre objet ( si vous utilisez un objet comme une créature, l’apparition se fera à l’endroit " valide" le plus proche.) Si nécessaire, vous pouvez aussi construire un emplacement en utilisant
location Location(object oArea, vector vPosition, float fOrientation)…
Par défault, l’animation standard de l’apparition n’est pas utilisée… si vous voulez qu’elle le soit, spécifiez TRUE après " bUbeAppearAnimation"
Donc…pour créer un hobgobelin sur un waypoint déjà placé et qui a le tag " WAYPOINT1"…
location spawn1 = GetLocation ( GetWaypointByTag ( "WAYPOINT1"));
CreateObject ( OBJECT_TYPE_CREATURE, " NW_BUGBEARA", spawn1);
Pour faire apparaître un magicien ( avec le tag " wizard01") avec l’effet activé, à côté du PC le plus proche…
void main()
{
object oWay = GetWaypointByTag("WAYPOINT1");
vector vPos = GetPosition(oWay);
vector vPC =
GetNearestCreatureToLocation(CREATURE_TYPE_PLAYER_
CHAR,PLAYER_CHAR_IS_PC, vPos);
location lPos = Location(GetArea(oWay), vPos, SetFacingPoint(vPC));
CreateObject(OBJECT_TYPE_CREATURE, " wizard01", lPos, TRUE);
}
Une dernière chose à propos des tags: Rappelez vous qu’ils font la différence entre les majuscules et les minuscules ! Si le tag de votre créature est " wizard01", ne l’appelez pas dans votre script " WIZARD01" ou même " Wizard01"…Vous verrez alors apparaître à la place un blaireau ( badger). Pourquoi un blaireau ? Par ce que c’est simplement la créature convoquée par défault et donc utilisée quand l’objet qui doit apparaître est invalide.
suite:
IV. A propos des animations.
Vous avez fondamentalement deux choix quand vous ajoutez des animations aux créatures dans le jeu. Les ajouter directement ou utiliser des fonctions automatiques dans l’IA générique.
Ajouter des animations directement.
La première chose à comprendre quand vous avez affaire à des scripts d’animation est que toutes les créatures n’ont pas toutes les animations. En règle générale, si la créature est une race jouable ( humain, elfe, nain, demi-orc, gnome ou halfelin), elle aura toutes les animations. Si la créature est un monstre humanoïde ( orc, gobelin…) elle a la plupart des animations, ci ce n’est toutes. Les races non-humanoïdes et spécialement les oiseaux, seront très limités dans leurs animations.
La file des actions ( Action Queue)
La seconde chose à savoir quand vous scriptez des animations est comment utiliser la file des actions . Il y a certaines commandes de scripts qui commence par le mot " Action"…quand une créature appelle ces commandes sur elle même, elle place l’action dans une file. Le script ne passera pas à la ligne suivante avant que l’action soit terminée…jusqu’à ce qu’il n’y ait plus d’action ou la commande " ClearAllActions()".
La raison pour laquelle c’est important est qu’il y a 2 commandes principales qui sont utilisées avec les animations : ActionPlayAnimation et juste PlayAnimation. ActionPlayAnimation place la commande de l’animation dans la file…PlayAnimation demande à la créature de jouer l’animation immédiatement dès que c’est inscrit dans le script, en étant " prioritaire" sur tout autre commande placée dans la file.
Si je veux dans un script dire à quelqu’un de bouger jusqu’à un waypoint particulier puis de méditer 6 secondes, il ressemblera à ça :
void main()
{
object oTarget = GetNearestObjectByTag("WAYPOINT1");
ActionMoveToObject(oTarget);
ActionPlayAnimation(ANIMATION_LOOPING_MEDITATE, 1.0, 6.0);
}
La créature se déplacera jusqu’au waypoint et attendra d’être arriver avant de commencer à jouer l’animation " meditate".
Si je veux fixer une variable quand il aura terminé tout cela, je devrais l’ajouter aussi dans la file. Vous pouvez faire cela avec la commande ActionDoCommand(). Cela place une commande " non-Action" ( sans " Action" au début) dans la file.
void main()
{
object oTarget = GetNearestObjectByTag("WAYPOINT1");
ActionMoveToObject(oTarget);
ActionPlayAnimation(ANIMATION_LOOPING_MEDITATE, 1.0, 6.0);
ActionDoCommand(SetLocalInt OBJECT_SELF, " Done_Meditation", 1);
}
Si j’avais fait la commande SetLocalInt sans la mettre dans la file, il se serait déclenché dès que ce point aurait été atteint dans le script…probablement avant que la créature soit arrivée au waypoint.
Les deux commandes pour les animations sont les suivantes :
void ActionPlayAnimation ( int nAnimation, float fSpeed=1.0, float fSeconds=0.0)
´nAnimation´ est la constante pour que l’animation soit jouée ( c’est l’animation que vous voulez jouer)
´fSpeed´ est la vitesse à laquelle l’animation est jouée…la créature peut, par exemple, tourner la tête très rapidement ou très lentement…1.0 est la vitesse normale.
´fSeconds´ est uniquement utilisée pour les animations en boucle ( comme la méditation)…elle détermine combien de temps vous souhaitez que l’information soit jouée. Si elle est laissée nulle dans une animation en boucle, celle ci sera jouée jusqu’à ce qu’on demande de faire autre chose.
void PlayAnimation ( int nAnimation, float fSpeed=1.0, float fSeconds=0.0)
Comme expliqué ci dessus, c’est la même commande que ActionPlayedAnimation, execpté que l’animation n’est pas placée à la suite…elle est jouée immédiatement.
Constantes des Animation ( Animation Constants)
Vous pouvez trouver une liste de toute les animations ( utilisée dans la partie " nAnimation" de la commande) en sélectionnant le bouton " Constants" dans le script editor…toutes les constantes commencent par ANIMATION_*
Il y a deux types d’animations : " fire-and-forget" ou FNF ( tire et oublie), qui sont jouées une fois et aucune durée n’est nécessaire.
Encore un rappel : TOUS LES MODELES N’ONT PAS TOUTES LES ANIMATIONS.Juste pour mentionner aussi que les animations listées dans " Constants" ne sont pas toutes les animations que le modèle est capable de jouer ( il y a une animation de mort, après tout, aussi bien qu’une animation de combat)…C’est juste la liste de toutes celles qui peuvent être jouées par le script.
Utiliser les Animation de l’IA générique.
Pour une solution rapide et facile pour rajouter un peu de vie dans vos créatures placées, l’IA générique a 2 fonctions que vous pouvez utiliser.
Dans le script générique OnSpawn ( "nw_c2_default9"), il y a une liste complète des commandes qui sont toutes commentées ( les commentaires sont précédés par un " //" double qui les colorie en vert et évite qu’ils soient compilés). Pour utiliser l’animation pré construite, vous n’avez qu’à enlever le " //" devant une des commandes suivantes :
SetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS);
SetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANIMA
TIONS);
Ne vous tracassez pas à propos des commentaires qui sont sur la même ligne avec ces commandes…ils disent juste ce qu’ils font. Effacez simplement le double slash au début de la ligne. Ensuite vous re-compilez le script et vous le sauvegardez dans un autre fichier. Et…c’est tout ce que vous avez à faire.
Qu’est ce que cela va faire ?
Fondamentalement, elles sont appellé par le OnHeartbeat ( ce qui veut dire que le script est activé toutes les 6 secondes). Le script vérifie que la créature n’est pas en train de dormir, de combattre, de parler et que aucun ennemi n’est dans la ligne de vue…Si tout est okay, il joue l’animation.
´Ambient animations´ signifit que la créature se déplacera aléatoirement, s’arretant de temps en temps pour se tourner vers les amis proches ( créatures avec une réputation amicale) et jouer les animations " sociales" qu’il a ( et, oui, ca marche avec tous les types de créature…et qu’il fera ce qu’il peut pour les créatures qui n’ont pas toutes les animations.)
´Immobile ambient animation´ fait la même chose… sans le mouvement aléatoire. La créature reste sur place.
Vous pouvez donc placer plusieur crétures de ce type, par exemple, et elle se tourneront l’une vers l’autre à un interval aléatoire et sembleront parler, discuter, débattre…et même mélangeront, avec les animation ambiantes ( ambient animations)
Est ce que ca marche de la même manière avec les objets placable ( placeable objects)?
Oui. Vous pouvez demander à un coffre de s’ouvrir en utilisant ANIMATION_PLACEABLE_OPEN, ou éteindre une lampe en utilisant ANIMATION_PLACEABLE_DESACTIVATE. Vous devez juste garder quelques conseils à l’esprit :
· Pour les objets placable qui sont source de lumière ( comme les lampes), ce n’est suffisant de juste utiliser ANIMATION_PLACEABLE_DEACTIVATE ou ANIMATION_PLACEABLE_ACTIVATE. Cela affecte juste l’apparence de l’objet. Vous devez aussi utiliser la commande SetPlaceableIllumination fixée à TRUE et préciser la zone concernée par RecomputeStaticLighting. Voici un exemple d’utilisation d’une lumière plaçable:
/ / allume ou éteit les objets lumineux quand sélectionné
/ / à placer dans OnUsed
void main()
{
if ( GetLocalInt ( OBJECT_SELF,"NW_L_AMION") == 0)
{
SetLocalInt ( OBJECT_SELF,"NW_L_AMION",1);
PlayAnimation ( ANIMATION_PLACEABLE_ACTIVATE);
SetPlaceableIllumination ( OBJECT_SELF, TRUE);
RecomputeStaticLighting ( GetArea(OBJECT_SELF));
}
else
{
SetLocalInt ( OBJECT_SELF,"NW_L_AMION",0);
PlayAnimation ( ANIMATION_PLACEABLE_DEACTIVATE);
SetPlaceableIllumination ( OBJECT_SELF, FALSE);
RecomputeStaticLighting ( GetArea(OBJECT_SELF));
}
}
· Les portes ne sont pas des objets placables. La première chose que vous devez savoir à propos de ces dernières est que si une porte est déverrouillée, une créature qui doit aller à un point de l’autre côté de celle ci l’ouvrira automatiquement. A part ça, les commandes pour les portes sont les suivantes :
o ActionOpenDoor: Si utilisée dans le script d’une créature, elle se déplacera jusqu’à la porte et l’ouvrira ( si elle est ouverte). Si utilisée dans le script d’une porte ( ou si la commande est envoyée à la porte via AssignCommand), la porte s’ouvrira toute seule.
o ActionCloseDoor: Comme ci dessus, seulement la porte est fermée.
o ActionLockObject: Si utilié dans le script d’une créature, elle se déplacera jusqu’à l’objet ( ca peut être une porte ou un objet placable.) et tentera d’utiliser sa compétence de crochetage pour l’ouvrir. A utiliser UNIQUEMENT dans le script d’une créature !
o ActionUnlockObject: Comme au dessus, sauf que l’objet est déverrouillé.
o SetLocked: C’est la commande à utiliser si vous voulez verrouiller ou déverrouiller une porte ou un objet sans l’aide d’une créature ou d’une compétence. Si ´bLocked´ est fixé sur TRUE, l’objet sera fermé... Si il est fixé sur FALSE, il sera déverrouillé. ( exemple: Utilisé dans le propre script d’une porte, SetLocked ( OBJECT_SELF, TRUE) la fermera.)
/ / A mettre dans le OnHeartbeat d’une porte, il la fermera au crépuscule
/ / et l’ouvrira à l’aube.
void main()
{
if ( GetIsDusk() && GetIsOpen ( OBJECT_SELF))
{
ActionCloseDoor ( OBJECT_SELF)
/ / SetLocked est mise dans une ActionDoCommand parce que nous
/ / voulons qu’elle soit dans la file de la porte... Nous voulons
/ / que ActionCloseDoor soit fini avant de fermer la porte.
ActionDoCommand ( SetLocked ( OBJECT_SELF, TRUE))
}
else if ( GetIsDawn() && GetLocked ( OBJECT_SELF))
{
SetLocked ( OBJECT_SELF, FALSE);
}
}
suite:
V. Explication des Waypoints et Walkwaypoints.
Un waypoint est un objet invisible qui est placé dans une zone ( area) pour marquer un emplacement spécifique. Il a son propre tag ( que vous pouvez choisir quand vous le créez) et peut optionellement être fait dans une " map note" ( Il peut par exemple être nommé " General Store" et dans la mini map une icône apparaîtra à son emplacement…si le joueur déplace son curseur sur cette icône, ce label s’affichera).
L’IA générique qui vient avec NWN est telle que vous pouvez attribuer un circuit à une créature pour qu’elle marche en suivant ces waypoints automatiquement.
Comment cela se passe t’il ?
Tout ce fait avec le tag du waypoint. Prenez le tag des ou de la créature(s) qui doivent utiliser les waypoints…ajoutez le préfixe " WP_" et le suffixe " _0X"…et la créature commencera automatiquement à les suivre dans l’ordre.
Exemple :
Si j’ai un NPC guarde à qui j’ai donné le tag " GUARD". J’ai posé 4 waypoints qui forment son circuit de patrouille.Je leur ai donné les tags " WP_GUARD_01", " WP_GUARD_02", " WP_GUARD_03" et
" WP_GUARD_04".
D’après l’IA par défaut, quand le jeu commence, il se rendra au waypoint le plus proche ( que ce soit " WP_GUARD_01" ou non) puis il se rend au waypoint suivant dans l’ordre numérique. Il continue en suivant l’ordre et en faisant une pause de une seconde à chaque waypoint. Une fois arrivé au waypoint " WP_GUARD_04" il rebroussera chemin jusqu’au " WP_GUARD_01". Si il voit quelque chose d’hostile, il se déplacera pour combattre…Après avoir terminé le combat, il reviendra à sa patrouille.
L’exemple ci dessus montre évidemment un usage simple des waypoints, bien évidemment…il y a beaucoup de manières de l’améliorer. Voici néanmoins quelques questions fréquemment posées qui pourrait vous aider :
Si j’utilise beaucoup de waypoint pour mes NPC, il y aura BEAUCOUP de waypoints à l’écran !
Un waypoint ne peut pas uniquement être utilisé par une seule créature… mais un tag de créatures. Plusieurs créatures peuvent avoir le même tag, si vous le voulez. Il y a aussi quatre couleurs différentes que vous pouvez utiliser pour différencier les waypoints. Cependant, il peut arriver que vous ayez un certain nombre dans une zone donnée, selon ce que vous faites. Heureusement, les waypoint n’utilisent pas de scripts et n’utilise pas la puissance graphique. Si ils gênent l’affichage dans le toolset, il y a un bouton qui permet de les masquer ( il y a aussi un bouton pour masquer d’autres chose, comme les créatures, les objet…).
Cela doit faire beaucoup de travail de renommer tous cess waypoints.
Vous pouvez éditer chaque waypoint et changer son tag si vous voulez. Heureusement, vous pouvez maintenir la touche SHIFT et selectionner les waypoints dans l’ordre dans lequel vous voulez…puis faite un click droit et sélectionnez " CreateSet". Si vous entrez le nom du tag ( ´WP_GUARD´ dans l’exemple ci dessus), le Toolset changera automatiquement leur tag et rajoutera le suffixe numéroté pour vous.
Et si je veux faire un circuit traversant une zone de transition…comme entrer dans une maison ?
Ca marche très bien. Je suposse que vous avez une zone de transition normale avec le wizard ( avec un suspect you would have to have a regular area transition created by the wizard, ou n’importe comment …du moment que vous ne l’avez pas scriptée vous même.
J’ai récemment crée un paysan qui avait quatre waypoints, dont deux dans une autre zone ( dans une maison). J’ai posé la maison dans la zone principale et connecté les deux avec une porte. Il laissa la porte ouverte après l’avoir utilisé la première fois, mais autrement ça marche très bien.
Utilisations avancées
Posts: Au lieu de lui attribuer un waypoint, vous pouvez attribuer un " post" à une créature. Créez un seul waypoint avec le préffixe " POST_" suivi du nom de la créature.
La créature restera à son poste. Si elle engage un combat, elle y reviendra dès la fin de celui ci.
Activitées de jour et de nuit:
Dans le script OnSpawn par défaut, vous pouvez " décommenter" ( enlever le " //")une fonction qui s’appelle ´SetSpawnInCondition ( NW_FLAG_DAY_NIGHT_POSTING)´. Cela dit à la créature de différentier le jour et la nuit.
Comment ça affecte les WP?
Et bien, vous pouvez attribuer à la créature différents set de waypoints ( ou un post différent) pour le jour et la nuit en changeant leur préfixe:
waypoin de jourt: préfixe " WP_"
waypoint de nuit: préfixe " WN_"
post de jour: préfixe " POST_"
post de nuit: préfixe " NIGHT_"
Par exemple, vous pouvez dire au garde de suivre ses waypoints le jour. Alors, si le drapeau est décommenté ( faudrait quand même que je trouve une autre traduction.) vous pouvez créer un waypoint " NIGHT_GUARD" dans la caserne pour qu’une fois la nuit venue, il rentre dedans.
Une dernière chose à se souvenir : quand vous faites un changement dans le script OnSpawn par défaut, vérifiez bien que vous le sauvegardez sous un nom différent…sinon ça affectera toutes les créatures de votre module.
VI. Userdefined Event est votre ami.
Sérieusement. Il n’y a pas du tout de blague la dedans. Il y aura naturellement des utilisateurs qui préfèreront aller plus en profondeur et personnaliser différents scripts pour remplacer l’IA générique…Certain voudront aller plus vite que ça monter leur propre version des fichier de l’IA qui sont inclus. C’est génial, et il n’y a rien de faux à faire ça.
Une des meilleur chose est : vous n’êtes pas obligé de.98% des scripts des créatures de la campagne officielle ont été fait en utilisant UserDefinedEvent. Je me suis assis avec Preston ( un de nos scripteurs et un de nos principaux gars derrière l’IA) et j’ai posé une rapide série de choses que je voulais scripter…à propos de ce que chacun était capable de faire en utilisant UserDefinedEvent. Et c’est facile à utiliser.
Faisons une pause une seconde ici et discutons de ce que sont les événements. Fondamentalement une créature a des scripts numérotés ( des ensembles d’instructions)…et chaque script est lié à des évènements. Quand un événement a lieu, il dit à la créature de lancer le bon script. Pas d’événement=le script ne marche pas.
Il y a un nombre d’évènements qui sont très souvent mentionnés ici. L’évènement " OnHeartbeat" a lieu toutes les 6 secondes…donc le script lié à cet événement se lance aussi souvent. L’évènement " OnPerceived" a lieu à chaque fois que le NPC perçoit quelque chose…le script correspondant se lance.
Ces scripts peuvent aussi entraîner d’autre événement qui lanceront eux même d’autres scripts…et c’est ici que l’on place le UserDefinedEvent.
Fondamentalement, UserDefinedEvent permet d’accéder aux évènements normaux qu’une créature utilise ( OnPerception,OnDeath,etc.) sans déranger l’IA normale ou modifier les scripts déjà existant.
Utiliser UserDefinedEvent
Il y a un script sur chaque créature qui a déjà été souvent mentionné, et c’est un de ceux que vous devriez bien connaître. C’est le script OnSpawn. C’est un événement qui marche seulement quand la créature apparaît la première fois…imaginez que c’est un script d’initialisation.
Le script OnSpawn générique a une série de commandes qui sont inactivées ( précédées par " //")…mais en réalité, sans que l’utilisateur change quelque chose, tout ce long script n’exécute en fait que trois commandes : il dit à la créature de suivre les waypoint qui se réfère spécifiquement à son tag, lui donne son comportement ( il répondra aux attaque des autres NPC) et génère un faible montant de trésor générique dans l’inventaire ( de l’or…)
Il est écrit pour permettre à l’utilisateur de définir les condition pour accéder à l’IA générique comme il le veut. Tout ce qu’il y a à faire est de repérer le " state flag" que vous voulez utiliser et d’enlever le " //" au début de la commande pour l’activer Puis sauvez le script OnSpawn sous un nom différent et utilisez le à la place de script normal…voilà, le comportement générique a été modifié.
Les " state flags" auquels nous avons besoin de faire attention sont ceux la :
SetSpawnInCondition ( NW_FLAG_PERCIEVE_EVENT);//OPTIONAL BEHAVIOR - Fire User Defined Event 1002
SetSpawnInCondition ( NW_FLAG_ATTACK_EVENT);//OPTIONAL BEHAVIOR - Fire User Defined Event 1005
SetSpawnInCondition ( NW_FLAG_DAMAGED_EVENT);//OPTIONAL BEHAVIOR - Fire User Defined Event 1006
SetSpawnInCondition ( NW_FLAG_DISTURBED_EVENT);//OPTIONAL BEHAVIOR - Fire User Defined Event 1008
SetSpawnInCondition ( NW_FLAG_END_COMBAT_ROUND_EVENT);//OPTIONAL BEHAVIOR - Fire User Defined Event 1003
SetSpawnInCondition ( NW_FLAG_ON_DIALOGUE_EVENT);//OPTIONAL BEHAVIOR - Fire User Defined Event 1004
SetSpawnInCondition ( NW_FLAG_DEATH_EVENT);//OPTIONAL BEHAVIOR - Fire User Defined Event 1007
Et après?
Okay... Vous avez activé les " flags" que vous voulez scriptez et re sauvé la script OnSpawn. Ca vous a pris 2 minutes. Maintenant, comment l’utiliser ?
Chaque créature a une section OnUserDefined dans laquelle vous pouvez mettre un script. Pour toutes les créatures que vous avez faites, elle est complètement vide…c’est la ou nous allons faire notre script. Tout script que vous mettez ici sera lancé à chaque fois qu’un événement que vous avez " activé" ( en enlevant le " //" dans le OnSpawn) se produira. Donc, si vous avez activé ´SetSpawnInCondition ( NW_FLAG_DISTURBED_EVENT)´, alors a chaque fois que l’inventaire de la créature est " perturbé" ( quand quelqu’un vole un objet) le script OnUserDefined sera lancé. Le script OnUserDefined basique ressemblerait à ça :
void main()
{
int nUser = GetUserDefinedEventNumber();
if ( nUser == le nombre dans le OnSpawn, de 1001 à 1007)
{
( faire qqch)
}
}
Admettons que je veuille qu’une créature fasse signe au premier PC qu’elle voit. Je vais dans OnSpawn, " j’active" ´SetSpawnInCondition ( NW_FLAG_PERCIEVE_EVENT)´ et je le re sauvegarde comme un nouveau script.
Puis je crée un script dans OnUserDefined:
/ / Ce script fera que le NPC salut le PC en l’apercevant
/ / souvenez vous que si le PC commence le module dans la zone de perception du
/ / NPC, le script ne se lancera pas ( la perception n’a pas changée)
void main()
{
int nUser = GetUserDefinedEventNumber();
if ( nUser == 1002) / / OnPerception event
{
object oCreature = GetLastPerceived();
int nWave = GetLocalInt(OBJECT_SELF, " Wave_Once");
/ /vérifie que ce soit bien un PC vu la première fois
if ( GetLastPerceptionSeen() && GetIsPC(oCreature) && ( nWave == 0))
{
/ / salut qu’une fois
SetLocalInt(OBJECT_SELF, " Wave_Once", 1);
/ / fixe un temps pour que le NPC puisse re saluer 15 sec plus tard
DelayCommand(15.0, SetLocalInt(OBJECT_SELF, " Wave_Once", 0));
/ / se tourne face au PC
ActionDoCommand(SetFacingPoint(GetPosition(oCreatu
re)));
/ / salut
ActionPlayAnimation(ANIMATION_FIREFORGET_GREETING)
;
SpeakString("Hello!");
}
}
}
Et c’est tout. Je peux ajouter d’autres condition comme une distance minimale, voir si le PC est en face de moi…ou lui donner un comportement aussi complexe que je le souhaite…et je n’ai pas à me préoccuper du script qui est déjà dans la case OnPerceive.
suite :
VII. Un script OnSpawn mieux commente
Toutes les lignes du script onSpawn ( "nw_c2_default9") n´espliquent pas comment leurs fonctions sont utilisées en réalité. Ceci est une version mise à jour que vous pouvez utiliser si vous le souhaitez.
//:://////////////////////////////////////////////
/
/ /:: Custom On Spawn In
/ /:: File Name
/ /:: Copyright ( c) 2002 Bioware Corp.
//:://////////////////////////////////////////////
/ *
//:://////////////////////////////////////////////
/ /:: Created By:
/ /:: Created On:
//:://////////////////////////////////////////////
void main()
{
/ / OPTIONAL BEHAVIORS ( Comment In or Out to Activate )
/*************************************************
//SetSpawnInCondition(NW_FLAG_SPECIAL_CONVERSATION
);
/ / Cela demande à un NPC de dire une ligne de son dialogue quand il percoit
/ / un joueur. Vérifiez que la ligne que vous voulez qu’il dise est tout en haut
/ / du dialogue du NPC qui démarre les nœuds et que vous avez placé
/ / le script " nw_d2_gen_check" dans la case ´Text Appears When´ de ce texte.
/ / N’utilisez pas ce " flag" pour des créatures hostiles.
//SetSpawnInCondition(NW_FLAG_SPECIAL_COMBAT_CONVE
RSATION);
/ / Ce " flag" est similaire à celui ci dessus... ci ce n’est qu’il permet à un NPC
/ / hostile de faire apparaître une ligne avant d’attaquer. Mettez la phrase tout en haut
/ / du dialogue du NPC, mais placez le script " nw_d2_gen_combat" dans ´Text Appears When´
//SetSpawnInCondition(NW_FLAG_SHOUT_ATTACK_MY_TARG
ET);
/ / Cela dira au NPC que lorsqu’un NPC appartenant à une faction amie est attaqué ou
/ / attaque un ennemi ( et utilise l’IA générique), ce NPC attaquera à son tour.
/ /SetSpawnInCondition(NW_FLAG_STEALTH);
/ / Si le NPC possède l’aptitude se cacher dans l’ombre ( hide skill), elle sera utilisée
/ / automatiquement, mais seulement quand la commande WalkWayPoints est appelée ( ci dessous)
Suite du script page suivante…
/ /SetSpawnInCondition(NW_FLAG_SEARCH);
/ / Si le NPC possède l’aptitude fouille ( search), elle sera utilisée automatiquement, mais seulement
/ / quand la commande WalkWayPoints est appelée ( ci dessous)
/ /SetSpawnInCondition(NW_FLAG_SET_WARNINGS);
/ / Le NPC donnera un avertissement aux non ennelis avant d’attaquer.
//SetSpawnInCondition(NW_FLAG_DAY_NIGHT_POSTING);
/ / Cela sépare les waypoint du NPC entre le jour et la nuit. Normalement, un waypoint avec le préfixe
/ / " WP" ou " POST" sera utilisé en permanance. Si ce " flag" est activé ces préfixes seront utilisés de
/ / jour et les préfixes " WN" ou " NIGHT" le seront la nuit.
//SetSpawnInCondition(NW_FLAG_APPEAR_SPAWN_IN_ANIM
ATION);
/ / Quand la créature est invoquée, il utilise EffectAppear() au lieu de faire un effet de fondue, mais
/ / seulement si SetListeningPatterns est activé ( ci dessous)
//SetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANI
MATIONS);
/ / Le NPC utilisera les animations communes qu’il a, et il apparaîtra plus " social" si il est placé
/ / près d’un NPC ami.
//SetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS);
/ /Pareil qu’au dessus, mais la créature bouge aussi aléatoirement.
/ /NOTE ces animations sont automatiquement jouées pour les créatures des " Encounter".
/ / NOTE: VOUS NE POUVEZ ACTIVER QU’UNE DES DEUX COMMANDES CI DESSUS A LA FOIS.
/ /SetSpawnInCondition(NW_FLAG_ESCAPE_RETURN);
/ / OPTIONAL BEHAVIOR ( Fuis jusqu’à un waypoint et reviens peu de temps après.)
/ /SetSpawnInCondition(NW_FLAG_ESCAPE_LEAVE);
/ / OPTIONAL BEHAVIOR ( Fuis jusqu’à un waypoint mais ne reviens pas.)
/ /SetSpawnInCondition(NW_FLAG_TELEPORT_LEAVE);
/ / OPTIONAL BEHAVIOR ( se téléporte en suretée mais ne reviens pas.)
/ /SetSpawnInCondition(NW_FLAG_TELEPORT_RETURN);
/ / OPTIONAL BEHAVIOR ( idem, mais reviens peu de temps après.)
/ / Pour utiliser ´escape´, vous avez besoin d’un waypoint vers lequel le NPC fuira avec comme tag
/ / " EXIT_"+le tag du NPC
/ / Cette commande peut être utilisée dans un script avec la commande ActivateFleeToExit(),
/ / " NW_IO_GENERIC" doit être inclus
/ / Pour les commandes " escape" qui ont " return", le NPC re spawnera à son lieu de départ.
/ / CUSTOM USER DEFINED EVENTS
/ *
Les paramètres suivant permettent à l’utilisateur d’activer un événement dans le NW_D2_DefaultD. Comme le script OnSpawn, ce script est fait pour être customisé pour permettre des comportements uniques. L’utilisateur défini les événement de 1000 à 1010
/ /SetSpawnInCondition(NW_FLAG_HEARTBEAT_EVENT); / /OPTIONAL BEHAVIOR - FireUser Defined Event 1001
/ /SetSpawnInCondition(NW_FLAG_PERCIEVE_EVENT); / /OPTIONAL BEHAVIOR - FireUser Defined Event 1002
/ /SetSpawnInCondition(NW_FLAG_ATTACK_EVENT); / /OPTIONAL BEHAVIOR - FireUser Defined Event 1005
/ /SetSpawnInCondition(NW_FLAG_DAMAGED_EVENT); / /OPTIONAL BEHAVIOR - FireUser Defined Event 1006
/ /SetSpawnInCondition(NW_FLAG_DISTURBED_EVENT); / /OPTIONAL BEHAVIOR - FireUser Defined Event 1008
//SetSpawnInCondition(NW_FLAG_END_COMBAT_ROUND_EVE
NT); / /OPTIONAL BEHAVIOR - FireUser Defined Event 1003
/ /SetSpawnInCondition(NW_FLAG_ON_DIALOGUE_EVENT); / /OPTIONAL BEHAVIOR - FireUser Defined Event 1004
/ /SetSpawnInCondition(NW_FLAG_DEATH_EVENT); / /OPTIONAL BEHAVIOR - FireUser Defined Event 1007
/ / DEFAULT GENERIC BEHAVIOR ( NE PAS MODIFIER)
/*************************************************
SetListeningPatterns(); / / détermine quels cris le NPC entendra.
WalkWayPoints(); / / Optional Parameter: void WalkWayPoints(int nRun =FALSE, float fPause = 1.0)
/ / 1. Regarde si un waypoint possède le tag " WP_" + TAG DU NPC + " _0X",
/ / si c’est le cas, marche jusqu’à eux.
/ / 2. Si le tag du waypoint est " POST_" + NPC TAG la créature y retournera après un combat.
/ / Pour qu’un NPC recommence à suivre les waypoint après une conversation, vous devez
/ / aller dans son fichier dialogue, dans " Other Files", appellez aussi cette commande dans les sections
/ / " End" et " Aborted" du script
GenerateNPCTreasure(); / /* C’est pour créer un trésor de faible montant sur la créature
}
VIII. Recompenser avec de l’or et des XP
Les deux principales commandes que vous avez à utiliser .
void GiveGoldToCreature(object oCreature, int nGP)
void GiveXPToCreature( object oCreature, int nXpAmount)
Il y a aucun problème pour la provenance de l’XP et de l’or. Seule la destination peut changer…c’est pourquoi on peut utiliser ces commandes de n’importe où.
Pour donner 100 po au PC avec lequel je parle ( placé dans un script à l’intérieur de " Action Taken") :
GiveGoldToCreature ( GetPCSpeaker(), 100);
Pour donner 100 XP à chaque membre de la partie, en commençant par le plus proche :
object oFirstMember =
GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR,PLAYE
R_CHAR_IS_PC);
object oPartyMember = GetFirstFactionMember(oFirstMember, TRUE);
while ( GetIsObjectValid(oPartyMember) == TRUE)
{
GiveXPToCreature(oPartyMember,100);
oPartyMember = GetNextFactionMember(oFirstMember, TRUE);
}
( Personellement, je voulais mettre cela dans une fonction pour ne pas avoir besoin de tout recopier à chaque fois.)
Quelques autres conseil qu’il est pratique de connaître en rapport avec l’expérience et l’or :
· Quand vous écrivez le journal dans le " journal editor", vous pouvez assigner une " XP Value" à chaque entrée. Ce nombre peut être rappelé avec la fonction suivante :
int GetJournalQuestExperience(string szPlotID)
Dans la campagne, vous avez un PlotID pour chaque quête ( qui peut consister en plusieurs entrées de journal jusqu’à ´completion´) et chaque PlotID a une valeur d’XP. Et nous donnons l’XP en pourcentage de quête réalisé. 50% du total pour ceci, 25% pour cela,etc.
· Combien d’or quelqu’un a?
int GetGold( object oTarget = OBJECT_SELF)
Pour trouver combien d’or quelqu’un a, lancez le script en lancant GetGold()…mais il ne peut littéralement cibler personne.
· Si vous voulez enlever de l’or à quelqu’un d’autre :
void TakeGoldFromCreature( int nAmount, object oCreatureToTakeFrom, int bDestroy = FALSE)
Si vous mettez TRUE après ´bDestroy´, l’or reprise sera enlevée du module et plus personne ne pourra l’utiliser.)
Suite :
IX. Faire des jets de compétence.
Tout d’abord, si vous ne savez pas comment un jet de compétence est fait dans D& 3, voilà une rapide explication :
A chaque fois que vous essayez d’accomplir une action en utilisant vos compétences, vous lancez un d20 ( un dé à 20 faces numérotées de 1 à 20) et vous rajoutez à ce résultat votre rang dans cette compétence et votre bonus dans la caractéristique liée à cette compétence. Plus le résultat est élevé mieux c’est, et vous essayez d’égaler ou de dépasser un DC " difficulty class" ( en français DD " degré de difficulté") un nombre qui a été décidé par le DM.
Admettons qu’un joueur veut utiliser sa compétence de désamorçage ( Disable Traps skill) sur un piège qu’il a découvert. Le DM fixe le DC à 25 ( une tache moyennement difficile). Le joueur a un rang de 8 dans cette compétence et un modificateur de +3 en dextérité ( la caractéristique lié à la compétence de désamorçage). Il lance un d20 et ajoute +11 à ce jet, il a besoin de faire 14 ou plus pour réussir cette tache.
Pour des choses tels que le désamorçage et beaucoup d’autres compétences, le jet est pris en main par le moteur du jeu et souvent, la seule chose demandée au DM est le DC.
Cependant, Il n’y a pas dans les scripts de NWN une commande automatique qui fait ces jets pour vous si vous utilisez une compétence dans un script ( que ce soit pour vérifier le succès d’une persuasion dans un dialogue ou tout autre utilisation de compétence qui n’est pas pris en charge automatiquement). Si vous voulez les utiliser, vous devez soit avoir un script qui le fait, soit utiliser la fonction que nous avons faite dans la campagne officielle.
Les fonctions de la campagne officielle
La chose que certain peuvent ne pas aimer à propos de la commande AutoDC est que le DC est généré automatiquement…il n’est pas spécifié à chaque fois. Quand le scripteur utilise cette commande, il spécifie DC_EASY, DC_MEDIUM ou DC_HARD et le DC est généré en fonction du niveau du PC.
La syntaxe est la suivante:
int AutoDC(int DC, int nSkill, object oTarget)
Donc si je veux faire une persuasion difficile avec le NPC avec qui le PC parle, je peux utiliser ceci:
AutoDC(DC_HARD, SKILL_PERSUADE, GetPCSpeaker());
Un commentaire ici, le jet de compétence renvoie un integer ( the int) parce que la commande doit renvoyer soit TRUE soit FALSE. False ( ou un " 0" integer) signifie que le jet est un échec. True ( ou un ´1´ integer) signifie qu’il est réussi.
Si vous voulez utiliser un jet de compétence dans le " dialogue editor", vous devez " séparer" la conversation…ca veut dire que pour une même réponse du PC, il doit y avoir plus d’un dialogue du NPC. Quand le dialoque est présenté comme ceci, il regarde le script dans " Text Appears when", en commençant par le nœud du haut et descendant jusqu’en bas. Le premier script qui renvoie TRUE ( ou qui n’a pas de script), il l’utilise.
Un exemple de script avec un jet de persuasion facile :
int StartingConditional()
{
int iCheck = AutoDC(DC_HARD, SKILL_PERSUADE, GetPCSpeaker()) == TRUE;
return iCheck;
}
Ainsi, vous pourriez avoir quelquechose de ce style:
" Oh,Je ne peux certainement pas vous le dire, c’est confidentiel."
A. < Persuade> " Oh, Je suis sur qu’il n’y a aucun mal à me le dire."
B. < Threaten> " Dite le moi ou vous en surpoterez les conséquences, jeune fille."
C. " Laissez moi vous dire quelque chose d’autre, alors."
Si je sélectionne l’option B, le dialoque se sépare en 2:
|
|
--> #1. ( avec le script de persuasion dans ´Text Appears When´) " Je suppose que vous avez raison. Ok, c’est comme ça..."
|
|
--> #2. ( sans script) " Oubliez ça, je ne vous le dirais pas."
Ecrire votre propre fonction
Pour écrire votre propre jet de compétence, vous devez suivre la même logique que si vous vouliez faire le jet un JdR sur table. Vous avez besoin du bonus du PC pour le jet de dé, du dé et du DC ( choisi par le DM)
Quelques commandes que vous pouvez utiliser :
Renvoie le rang + le bonus de caractéristique de la cible:
int GetSkillRank(int nSkill, object oTarget=OBJECT_SELF)
Renvoie le niveau de la créature dans une classe particulière ( comme par exemple CLASS_TYPE_FIGHTER):
int GetLevelByClass(int nClassType, object oCreature = OBJECT_SELF)
Celle-ci est pratique si vous voulez le niveau du PC sans tenir compte des classes:
int GetHitDice(object oCreature)
Donne le modificateur d’une caractéristique particulière, pratique si vous voulez faire un jet de force uniquement, ou quelquechose de similaire..
int GetAbilityModifier(int nAbility, object oCreature=OBJECT_SELF)
De toute manière, voici un exemple de jet de premier secour ( Heal skill) fait à la main ( à placer à l’endroit voulu dans le dialogue):
int StartingConditional()
{
int iDC = 20; / / ou ce que le DM veut mettre
int iBonus = GetSkillRank(SKILL_HEAL, GetPCSpeaker());
if ( (d20() + iBonus) > = iDC)
{
return TRUE;
}
return FALSE;
}
Bien évidemment, ce n’est qu’un exemple…il y a une méthode pré construite pour utiliser les soins normalement ( avec la commande ActionUseSkill)…ce type de script est fait pour les jets de dé personnalisés, que, je sais, les gens veulent faire.
X. Creer des objets uniques
NdT: Il est ici difficile de différencier les termes " object" ( qui désigne quasiment tous modèles 3D, des chaises aux personnages et aux pièges) et " item" ( qui désigne ce qu’on peut avoir dans son inventaire ( épée, armure…). Pour les différencier, quand j’utiliserais " objet" ( entre guillemets) ça désigne le terme " object", et sans guillemets, il désigne les " items".
Comment un script crée ces propres objets qui vont au dela des objets standarts…Comment par exemple faire un effet conditionnel ? Comment puis je scripter une baguette de souhait ou une épée parlante ?
Bien, la première a réaliser est que, par opposition aux " objets" ( chaise, lampe…) et aux créatures, ils n’ont pas de script propre. Vous ne pouvez pas aller dans les propriétés des objets et spécifier quels scripts utiliser…il ne peut en utiliser aucun.
La seule manière par laquelle vous pouvez affecter des scripts à des objets, c’est dans le " module level". Dans les propriétées du module, vous avez 3 évènements qui affectent les objets. Deux de ces énènements sont OnAquireItem et OnUnAcquireItem. Cela veut dire qu’à chaque fois qu’un objet est pris ou laissé tomber, un événement est lancer qui démarre le script approprié. Si il n’y a pas de script, rien ne se passe.
Donc, si je veux fixer une variable quand une épée particuliaire ( qui a le tag " SWORD01") est prise, je peux mettre ce script dans le section OnAcquireItem du module :
void main ( )
{
object oItem = GetObjectByTag("SWORD01");
int iVar = GetLocalInt(GetItemPossessor(oItem), " VARIABLENAME");
/ / ci dessous est spécifié : " Si le déclenchement est bien celui voulu, et que aucune variable n’a été
/ / spécifiée, fixez la variable sur le possesseur de l’objet.
if ( (GetModuleItemAcquired() == oItem) && ( iVar == 0))
{
SetLocalInt(GetItemPossessor(oItem), " VARIABLENAME", 1);
}
}
Cependant, ce n’est pas très utile pour scripter vos propres objets, n’est ce pas ? Por cela, vous devez tirer avantage du troisième événement du module : OnActivateItem().
Activer un objet requière un événement spécifique. Quand vous créez un objet, vous pouvez lui assigner une propriétée " Cast Spell" ( lancer un sort)…et deux des choix peuvent être Unique Power et Unique Power :Self Only. La différence entre eux est que le second est simplement un déclencheur de l’événement " OnActivateItem"…le premier vous demande d’abord la cible du pouvoir unique.
Ce " Unique Power" apparaît dans le menu radial de l’objet comme n’importe quel autre caractéristique. Si vous voulez spécifier à l’utilisateur ce qu’est cette caractéristique, vous devez l’inclure dans la description de l’objet ( il y a une description pour l’objet " identifié" et " non identifié", vous pouvez aussi utiliser les 2).
Quand vous écrivez le script à placer dans OnActivateItem(), il y a juste quelques références que vous devez garder à l’esprit :
Renvoie l’objet qui a déclenché l’événement OnActivateItem().
object GetItemActivated()
Renvoie la cible du pouvoir unique, si il est ciblé comme un objet spécifique.
object GetItemActivatedTarget()
Renvoie la cible du pouvoir unique, si il est ciblé comme un emplacement plutôt qu’un objet spécifique.
location GetItemActivatedTargetLocation()
Renvoie la créature qui a activée l’objet... un peu plus pratique que d’utiliser GetItemPossessedBy.
object GetItemActivator()
Et c’est tout ce dont vous avez besoin. Avec ça, vous pouvez scriptez autant de propriétées pour autant d’objets que vous le souhaitez. Gardez quand même à l’esprit que ces scripts sont spécifiques au module. Si vous prenez l’objet d’un autre module qui n’aura pas ce script in le OnActivateItem(), alors rien ne se passera.
Note: Il y a actuellement un bug avec OnActivateItem en ce qui concerne les objet à usage unique ( et qui arrive aussi aux objet qui n’ont plus qu’une charge). Quand un objet est usé, il disparaît…l’événement se lance, mais toutes les commandes comme GetItemActivate renvoie un objet invalide ( car l’objet n’existe plus.)
XI. S´asseoir sur une chaise et Dormir
Comment puis je dire à mon PC de s’asseoir sur une chaise ?
Il n’y a actuellement pas d’ " emote" pour faire asseoir un PC sur une chaise. Jusqu’à ce qu’un soit ajouté pour les PC, le script pour faire asseoir un PC sur une chaise doit être placé dans l’objet lui même. Si vous mettez un objet placable utilisable " chaise" ( la chaise doit avoir la case " Useable" cochée, et vérifiez que le PC à la place de s’asseoir), mettez le script dans la partie OnUsed de l’onglet des scripts :
void main()
{
object oChair = OBJECT_SELF;
if(!GetIsObjectValid(GetSittingCreature(oChair)))
{
AssignCommand(GetLastUsedBy(), ActionSit(oChair));
}
}
Quand je place un NPC dans le toolset, il reste debout. Il n’y a aucun moyen de la mettre assise ?
Pas dans le toolset,non…la créature doit être debout. Elle doit être scriptée pour que quand le jeu démarre, elle trouve une chaise et s’asseye dessus grâce à la commande ActionSit.
Créez un objet placable " chaise" ( chair) et donnez lui le tag " CHAIR". Placez le NPC près de la chaise. Copiez et sauvez son script sous un autre nom en rajoutant cette ligne en haut du script :
ActionSit ( GetNearestObjectByTag ( "CHAIR", OBJECT_SELF));
Notez que quand un PC parle au NPC, il se lèvent. Pour qu’il se rasseye, vous devez aller dans l’onglet " Other Files" dans leur dialogue. Vous devez voir des emplacements pour deux scripts ici, qui sont activés quand le dialogue est fini ou interrompu. Faites un script qui appelle la ligne ci dessus et il se rasseira quand le dialogue se terminera.
C’est bien tout ça, mais je veux déjà asseoir le NPC dans le tileset ?
Quelques utilisateurs ont remporté du succès un suivant la même processus en l’asseyant sur une chaise…mais en trouvant un objet placable " invisible object" , placez le à la place de la chaise. Donnez lui le tag " CHAIR" et utilisez la même ligne qu’au dessus et il s’asseira la pour vous.
Et si je veux que mon PC dorme su un lit ?
Il n’y a actuellement aucun moyen de dire à une créature de l’allonger sur un lit ou un " tileset" lit. Un objet placable peut être placé sur un lit, mais pas une créature.
Une créature peut cependant dormir sur un objet placable " bedroll" ( paillasse) ou sur le sol. Si vous voulez qu’une créature commence le jeu en dormant, vous devez faire une copie du script OnSpawn de la créature et le sauvegarder sous un autre nom, et ensuite rajouter en haut du script les lignes suivantes :
effect eLieDown = EffectSleep();
effect eSnore = EffectVisualEffect ( VFX_IMP_SLEEP);
effect eSleep = EffectLinkEffects ( eLieDown, eSnore);
ApplyEffectToObject ( DURATION_TYPE_PERMANENT, eSleep, OBJECT_SELF);
Réveiller votre NPC
Si vous voulez qu’un NPC se réveille à un certain moment, vous devez utiliser la commande RemoveEffect. Un effet, une fois appliqué, ne peut pas avoir une variable définie comme EffectSleep, et le désactiver par cette commande. Le " pointeur" doit être sur cet effet particulier. Si l’effet est la même variable dans le même script que quand il est utilisé, vous pouvez simplement utiliser ceci :
RemoveEffect ( OBJECT_SELF, eSleep);
Cependant, vous devez scanner les effets d’un objet et trouver celui que vous cherchez parmi tous les autres. Le script ci dessous est un script simple pour enlever l’effet sleep à un objet. ( Il est dans le OnHeartbeat de la créature et la réveille si un hostile approche à moins de 5m.):
/ / script de réveil
void main()
{
/ / si je dors
if ( GetHasEffect(EFFECT_TYPE_SLEEP))
{
/ / donne l’ennemi le plus proche de moi
object oTarget =
GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTA
TION_TYPE_ENEMY);
/ / si il y en a un et qu’il est à moins de 5 mètres
if ( (GetDistanceToObject(oTarget) < 5.0) && ( GetIsObjectValid(oTarget)))
{
effect eSleep = GetFirstEffect(OBJECT_SELF);
/ / regarde les effets actuels
while ( GetIsEffectValid(eSleep))
{
/ / et si l’un d’eux est sommeil, mais ne viens pas d’un sort de sommeil
if ( (GetEffectType(eSleep) == EFFECT_TYPE_SLEEP) && ( GetEffectSpellId(eSleep) ! = SPELL_SLEEP))
{
/ / enlevez le
RemoveEffect(OBJECT_SELF, eSleep);
}
eSleep = GetNextEffect(OBJECT_SELF);
}
}
}
}
Vu que c long a faire et gros si vous voulez la suite demandez-la moi et j´la poste, mais j´veux pas trop emcombrer le topic.
C´est bien compliqué pour ma petite tête mais je vais etudier ça en détails, merci !
Et je veux bien la suite moi ![]()
ok donc suite:
XII. Comment faire pour qu’un NPC entame un dialogue tout seul ?
OK, avant tout : avant de savoir EXACTEMENT ce que vous allez faire, ne touchez PAS au script qui est normalement dans OnPerception ( "nw_c2_default2"). Laissez le tranquille. Si quelqu’un vous demande de changer quelque chose dans les scripts OnSpawn et OnUserDefined de votre NPC et que vous n’êtes pas sur de ce que vous allez faire, ne le faite pas.
Comme mentionné plus haut, l’IA générique est programmée pour permettre grâce à UserDefinedEvent d’utiliser les évènements sans modifier les script par défaut.
Utiliser la méthode " OnPerceive" pour commencer une conversation.
· Allez dans votre script OnSpawn. Sauvez le sous un nouveau nom. Puis, allez jusqu’à la ligne où il y a marqué ´SetSpawnInCondition ( NW_FLAG_PERCIEVE_EVENT);´ et activez la ( enlevez le ´//´ double slashes au début de la ligne). Sauvegardez ce nouveau script.
· Dans le fichier dialogue du NPC, allez à la toute première ligne, la où commence le dialogue avec le PC. Si vous voulez juste qu’il commence à parler avec un PC donné, ajoutez le script suivant dans " Action Taken" :
void main()
{
SetLocalInt(GetPCSpeaker(), " Dlg_Init_" + GetTag(OBJECT_SELF), TRUE);
}
Cela fixe la variable qui est utilisé dans le script que nous allons utiliser, pour qu’il ne lance pas le dialogue plus d’une fois.
· Maintenant, créez un nouveau script dans OnUserDefined event de votre NPC. Vous pouvez y mettre le script suivant:
void main()
{
int nEvent = GetUserDefinedEventNumber();
if ( nEvent == 1002) / / OnPerceive event
{
object oPC = GetLastPerceived();
if(GetIsPC(oPC) && GetLocalInt(oPC, " Dlg_Init_" + GetTag(OBJECT_SELF)) == FALSE
&& ! IsInConversation(OBJECT_SELF))
{
ClearAllActions();
AssignCommand(oPC, ClearAllActions());
ActionMoveToObject(oPC);
ActionStartConversation(oPC);
}
}
}
Une erreur possible: Maintenant... une raison pourquoi ca ne marche pas ? Si votre PC commence le jeu dans la ligne de perception du NPC, il n’y aura pas d’événement OnUserDefined. L’événement a lieu uniquement si le PC ‘était pas perçu et arrive à portée.
Utiliser la méthode des déclencheurs pour démarrer une conversation.
C’est celle que nous avons utilisé dans la campagne officielle. Elle vous permet de déterminer à partir de quel point le NPC va courir vers le PC, et aussi déterminer sur quelle distance le NPC va courir après le PC avant d’abandonner. Pour l’utiliser, voici ce que vous devez faire :
· Comment au paragraphe 2 ci dessus, dans le fichier du dialogue du NPC, allez à la toute première ligne la où le dialogue commence avec le PC. Si vous voulez seulement démarrer ce dialogue avec n’importe quel PC donné, ajoutez le script du paragraphe 2 ci dessus dans " Action Taken" :
void main()
{
SetLocalInt(GetPCSpeaker(), " Dlg_Init_" + GetTag(OBJECT_SELF), TRUE);
}
Allez dans votre toolset. Créez un waypoint à l’endroit ou vous voulez que le NPC revienne. Mettez lui le tag " WP_RETURN_"+ le tag du NPC ( donc si le NPC s’appelle Fred, le tag sera " WP_RETURN_Fred". Souvenez vous que le tag différencie les majuscules des minuscules !
· Pendant ce temps, créez un nouveau déclencheur ( trigger) générique et dessinez un polygone autour du NPC. Quand le PC entrera dans cette zone, le NPC courra jusqu’à lui pour lui parler. Si le NPC quitte cette zone, il arretera de lui courir après et retournera au waypoint.
· Allez dans l’onglet " Script" du déclencheur et ajoutez le script suivant dans OnEnter :
/ / C’est le script OnEnter du trigger si vous voulez
/ / que le NPC déclenche le dialogue.
/ / remplacez bien " NPC_TALKER" par le script du NPC
void main()
{
object oNPC = GetObjectByTag("NPC_TALKER");
object oPC = GetEnteringObject();
if(GetIsPC(oPC) && GetLocalInt(oPC,"Dlg_Init_" + GetTag(oNPC)) == FALSE && ! IsInConversation(oNPC))
{
AssignCommand(oPC,ClearAllActions());
AssignCommand(oNPC,ClearAllActions());
AssignCommand(oNPC,ActionMoveToObject(oPC));
AssignCommand(oNPC,ActionStartConversation(oPC));
}
}
· Maintenant, nous voulons éviter que le NPC poursuive le PC sans arrêt. Entrez le script suivant dans l’emplacement OnExit du déclencheur ( trigger):
/ / Cela va demander au NPC d’arrêter de poursuivre le PC
/ / si il quitte la zone du déclencheir ( trigger)
/ / emplacez bien " NPC_TALKER" par le script du NPC.
/ / Vous devez aussi avoir un waypoint avec le tag " WP_RETURN_" + tag du NPC.
/ / Il doit être placé à l’endroit de départ du NPC.
void main()
{
string sTag = " NPC_TALKER";
object oExit = GetExitingObject();
if(GetTag(oExit) == sTag)
{
AssignCommand(oExit,ClearAllActions());
AssignCommand(oExit,ActionMoveToObject(GetNearestO
bjectByTag("WP_RETURN_" + sTag)));
}
}
Cette méthode ne tiens pas en compte si le PC est visible ou pas…traverser la zone du déclencheur activera le NPC. Si vous voulez en tenir compte, ajoutez la ligne suivante juste après " if(GetIsPC(oPC) &&" dans le script OnEnter:
GetObjectSeen(oPC, oNPC) &&
Mais, en multijoueur, le NPC essayera de parler avec chaque PC. Si vous voulez qui courre vers seulement un PC, faites ce qui suit :
· Dans le script du dialogue, remplacez ´GetPCSpeaker()´ par ´OBJECT_SELF´.
· Dans le script OnUserDefined ( pour la première méthode) ou le OnEnter ( pour la seconde), remplacez " oPC" dans la commande GetLocalInt par " oNPC".
XIII. Comment faire pour que mon NPC attaque le PC avec lequel il est en train de parler?
Je voudrais ajouter un script qui fait que le NPC devienne hostile. Placez le script dans la case " Action Taken" en bas à droite de l’éditeur de dialogue…vérifiez que vous avez bien sélectionné le nœud où vous voulez que la battaille commence. Si vous voulez que la faction du NPC devienne hostile contre la cible, utilisez le script générique " nw_d1_attonend":
//:://////////////////////////////////////////////
/
/ /:: Attaque à la fin de la conversation.
/ /:: NW_D1_AttOnEnd
/ /:: Copyright ( c) 2001 Bioware Corp.
//:://////////////////////////////////////////////
/ *
Le script dit au NPC d’attaque le PC avec lequel il est en train de parler.
//:://////////////////////////////////////////////
/ /:: Created By: Preston Watamaniuk
/ /:: Created On: Nov 7, 2001
//:://////////////////////////////////////////////
void main()
{
AdjustReputation(GetPCSpeaker(), OBJECT_SELF, -100);
DetermineCombatRound();
}
Si vous voulez juste que seul le NPC devienne hostile et attaque, utilisez le script générique " nw_d1_attonend02":
//:://////////////////////////////////////////////
/
/ /:: Attaque à la fin de la conversation.
/ /:: NW_D1_AttOnEnd02
/ /:: Copyright ( c) 2001 Bioware Corp.
//:://////////////////////////////////////////////
/ *
Le script dit au NPC d’attaque le PC avec lequel il est en train de parler.Il ne change que l’attitude du NPC, pas de la faction toute entière.
//:://////////////////////////////////////////////
/ /:: Created By: Preston Watamaniuk
/ /:: Created On: Nov 7, 2001
//:://////////////////////////////////////////////
void main()
{
SetIsTemporaryEnemy(GetPCSpeaker());
DetermineCombatRound();
}
Une chose dont il faut se méfier
Si vous appliquez ce script au nœud ou le NPC est en train de parler, il deviendra hostile tout de suite et le nœud ne sera pas affiché. Comme si le NPC n’avait pas le temps de le lire entièrement, si c’est une longue partie du dialogue.
Si vous êtes d’accord avec ça, tant mieux. Sinon, utilisez le bouton Add pour rajouter un nœud après le dernier nœud du NPC. Effacez le texte de la réponse du PC et appuyez sur Enter…Cela affichera un nœud [End Dialogue]. Ajoutez le script dans la zone " Action Taken" de la fin du dialogue à la place de l’autre emplacement. Comme cela, le Pc peut lire ce que le NPC lui dit…et quand il appuie sur " Enter" pour continuer, le NPC attaque.
Problèmes fréquents:
Verifiez que la créature qui doit entammer le combat n’a AUCUN niveau en " Commoner". Les roturiers sont définis par l’IA pour prendre la fuite face à quelqu’un hostile, et de former un groupe uniquement si il y a des roturiers de niveau 10 ou plus à proximitée.
Vérifiez aussi que si vous utilisez les " Special Behaviors" ( comportements spéciaux) dans le script OnSpawn, vous n’utilisez pas le comportement " Herbivore" ( on ne sait jamais ; )). Les herbivores n’engagent jamais le combat et prennent toujours la fuite.
XIV. Comment puis je ouvrir mon magasin?
Créer des magasin basiques est relativement simple…tout ce que vous avez à faire est d’utiliser le " store wizard" et ça vous crée un " objet" magasin basique avec un inventaire, un pourcentage d’augmentation ou du réduction du prix, un nom et un tag. Mais qu’est ce que vous allez faire avec ?
Etape 1: Placez l’ " objet" magasin dans votre module. Un magasin est juste un blueprint non placé auquel on ne peut accéder par le module. Vous devez maintenant le placer quelque part. Vous noterez que quand vous le placez, il ressemble juste à un waypoint…c’est bien. Les joueurs ne le verront pas et ne pourront pas interagir avec…il a juste besoin d’exister, de préférence près de l’objet qui va l’appeler ( le marchant par exemple).
Etape 2: Créez l’objet y accèdent. Habituellement, c’est un NPC marchant... mais ce n’est pas obligé.
Tout objet qui peut lancer un script peut ouvrir un magasin. Cependant, avoir un marchant PNC est le moyen le plus facile. Créez simplement votre marchant et allez créer un dialogue pour lui. La méthode la plus simple est de lui faire dire " Bienvenue ! Est ce que vous voulez jeter un œil à mes marchandises ? " Puis d’ajouter deux réponses : " Oui" et " Non"
Etape 3: Ajouter le script qui ouvrira le magasin. Dans le dialogue ci dessus, vous devez entrer ce script dans " Action Taken" pour la réponse " oui" du joueur…donc quand le joueur sélectionnera cette réponse, le magasin est ouvert. Vous n’avez rien besoin d’ajouter dans " No"…cela terminera simplement la conversation.
Le script basique pour ouvrir un magasin à la fin du dialogue est celui ci :
void main()
{
object oStore = GetObjectByTag("STORE_TAG_HERE");
OpenStore(oStore, GetPCSpeaker());
}
. ..et c’est tout. Naturellement, vous devez remplacer " STORE_TAG_HERE" par le tag de votre magasin..
Si vous regardez la structure de la commande OpenStore, vous verrez que vous pouvez rajouter marque d’une réduction et une augmentation du prix…c’est juste au cas ou vous voulez en faire une utilisation conditionnelle. Prenez le script qui suit par exemple…le vendeur double les prix pour les elfes ( il ne les aime pas) :
void main()
{
object oStore = GetObjectByTag("STORE_TAG_HERE");
if ( GetRacialType(GetPCSpeaker()) == RACIAL_TYPE_ELF)
{
OpenStore(oStore, GetPCSpeaker(), 100);
}
else OpenStore(oStore, GetPCSpeaker());
}
Un dernier commentaire sur les magasins
Les magasins ne peuvent pour l’instantoffrir que des objets génériques, c’est un peu limité. Peut être que ça changera dans le futur. Sachez que vous ne pouvez pas ajuster le prix des objets individuellement…un marchant applique les même majorations ou réductions à tous les objets…de même vous ne pouvez pas préciser quels types d’objets le marchant pourra acheter ou non. C’est tout ou rien. Une dernière chose, dans tous les scripts, les commande en rapport avec les objets comme CreateItemOnObject, GetFirstItemInInventory et GetNextItemInInventory doit avoir pour cible le magasin lui même. Souvenez vous : c’est lui qui a l’inventaire, pas le marchant.
XV. Utilisation des " Module Events"
Quand des évènements sont déclenchés ( dans n’importe quel module, que ce soit objet valable, ou quelque chose d’autre.), la part la plus importante de l’utilisation des évènements via les scripts est d’être capable d’identifier qui ou qu’est ce qui a déclenché les évènements. Gardez à l’esprit que c’est séparé du UserDefinedEvent…ce sont des événement qui sont " hard codés" dans chaque objet individuel. Vous assignez un script à cet événement et, quand il est déclenché, ce script se sancera.
Liste des " Module Event"
Evenement ( event): OnAcquireItem
déclencheur: à chaque fois qu’un objet est ajouté à l’inventaire de quelqu’un
Qu’est ce qui l’a déclenché ? Utilisez GetModuleItemAcquired().
Aussi utilisé dans: GetModuleItemAcquiredFrom() pour retourner l’ " objet" qui a donné l’objet
GetItemPossessor(object oItem) pour retourner celui qui a " gagné" l’objet.
Evenement ( event): OnActivateItem
déclencheur: à chaque fois qu’un objet utilise une capacité spétiale de l’objet à fin d’incantation, qu’il soit ciblé
( Unique Power) ou non ( Unique Power-Self Only)
Qu’est ce qui l’a déclenché ? Utilisez GetItemActivated().
Aussi utilisé dans: GetItemActivatedTarget() pour déterminer la cible du sort si il est ciblé,
GetItemActivatedTargetLocation() pour déterminer l’emplacement du sort, si la cible est un point,
GetItemActivator() pour déterminer le possesseur de l’objet au moment ou il a été activé.
Evenement ( event): OnClientEnter
déclencheur: à chaque fois qu’un jour arrive dans le module
Qu’est ce qui l’a déclenché? Utilisez GetEnteringObject() pour renvoyer l’"objet" joueur ( savoir quel est le joueur qui
est rentré.)
Evenement ( event): OnClientLeave
déclencheur: à chaque fois qu’un joueur quitte le module
Qu’est ce qui l’a déclenché? Utilisez GetExitingObject() renvoie l’"objet" joueur ( savoir quel est le joueur qui est sorti).
Evenement ( event): OnModuleLoad
déclencheur: quand le module démarre la première fois
Qu’est ce qui l’a déclenché ? Le module lui même... Il n’y a pas d’objet spécifique qui rentre dans cette case. C’est le script OnSpawn du module, ill est lancé une fois, quand le module est chargé.
Evenement ( event): OnPlayerDeath
déclencheur: un PC meurt
Qu’est ce qui l’a déclenché? Utilisez GetLastPlayerDied() pour renvoyer le PC qui vient de mourir.
Utilisation par défaut: le script " nw_o0_death" est généralement utilisé ici, qui annule toute relations avec la faction standard du joueur et détermine quand le panneau " death GUI" qui permet au joueur de quitter ou de respawn.
Evenement ( event): OnPlayerDying
déclencheur: un joueur a été réduit en dessous de 0 point de vie
Qu’est ce qui l’a déclenché? Utilisez GetLastPlayerDying().
La différence entre celui la et OnPlayerDeath est que la lee PC n’a plus de points de vie, mais n’est pas encore officiellement " mort". Utilisation par défaut: Le script placé ici par défaut est " nw_o0_dying" qui applique les effets de la mort au joueur ( EffectDeath) et le tue officiellement pour déclencher l’événement " OnPlayerDeath". Si le script est enlevé, le joueur saignera jusqu’à sa mort à –10 points de vie.
Evenement ( event): OnPlayerLevelUp
déclencheur: un joueur monte de niveau
Qu’est ce qui l’a déclenché? Utilisez l’objet GetPCLevellingUp(). Cela renvoie le joueur qui a monté de niveau... mais il n’y a aucune manière d’empêcher le joueur de terminer le processus, comme de le faire instantanément ainsi que de faire une action comme se reposer.
Suite:
XVI. Utilisation des " Area Events"
Gardez à l’esprit que quand vous créez des scripts pour les évènements de la zone ( Area events) ( ou pour les évènements du modules ( Module events)) " l’éxécutant" par défaut de n’importe quelle " commande action" ( toutes les commandes commençants par le mot Action) est la zone ( area) elle même.
Si vous référez à OBJECT_SELF, vous vous référez à la zone.. Si vous utilisez une commande commençant par Action comme ActionMoveToObjet sans utiliser AssignCommand pour envoyer cette commande à une créature, vous demandez à la zone de bouger ( qui, évidemment, ne le peut pas). Gardez ça simplement à l’esprit quand vous codez, comme ça, vous pouvez faire la différence entre dire à la zone de faire quelque chose et dire à quelque chose de faire quelque chose ( pas très précis hein ? ).
Liste des évènements de la zone " Area Events"
Evènement: OnEnter
déclenché par: quelque chose ( pas forcement un PC) qui est entré dans la zone
Qu’est ce qui l’a déclenché? Utilisez GetEnteringObject() pour renvoyer l’objet dans la zone.
Un exemple: Disons que vous voulez fixer une variable " PC_Entered" qui était dans la zone à chaque fois qu’un PC entre dans celle ci ( avec l’objet qui est égale à " PC")…et cela envoie un événement UserDefined à une créature particulière dans la zone avec le tag " HUNTER"(Je pouvais avoir un événement OnUserDefined pour cette créature qui l’envoie chercher le PC). Vous devez pour cela mettre le script suivant dans le OnEnter de la zone :
void main()
{
object oEntering = GetEnteringObject();
object oMonster = GetObjectByTag("HUNTER");
/ / si l’ojet entrant est un PC
if ( GetIsPC(oEntering))
{
/ / fixer la variable à la valeur du PC
SetLocalObject(OBJECT_SELF, " PC_Entered", oEntering);
/ / envoyer un UserDefinedEvent numéro 100 au monstre
SignalEvent(oMonster, EventUserDefined(100));
}
}
Evenèment: OnExit
déclenché par: quelqu’un qui a quitté la zone actuelle
Qu’est ce qui l’a déclenché? Utilisez GetExitingObject().
Un exemple: Reprennons le script ci dessus et fixons la variable " PC_Entered" à OBJECT_INVALID si le PC qui la zone. Vérifions aussi si l’objet sortant est le monstre ci dessus ( chassant le PC, sans doute)…et si c’est le cas, demandons lui de revenir dans la grotte ( où il y a un waypoint avec le tag " HUNTER_CAVE")
void main()
{
object oExiting = GetExitingObject();
object oMonster = GetObjectByTag("HUNTER");
object oCave = GetObjectByTag("HUNTER_CAVE");
/ / si l’objet sortant est un PC
if ( GetIsPC(oExiting))
{
/ / fixer la variable de la zone à OBJECT_INVALID
SetLocalObject(OBJECT_SELF, " PC_Entered", OBJECT_INVALID);
}
/ / Si l’objet sortant est le monstre
if ( oExiting == oMonster)
{
/ /demander lui d’arrêter ce qu’il est en train de faire.
AssignCommand(oMonster, ClearAllActions());
/ / demandez lui de retourner dans sa caverne.
AssignCommand(oMonster, ActionMoveToObject(oCave));
}
}
Evènement: OnHeartbeat
déclenché par: Rien n’est requit pour déclencher cet évènement... tout script qui est placé ici se déclenchera toutes les 6 secondes. Comme avec les évènements du module, faite attention en plaçant ici un script qui se lancera constamment.
Evènement: OnUserDefined
déclenché par: Il peut seulement être déclenché par quelqu’un utilisant la commande SendSignal pour envoyer un UserDefined event à l’objet " zone" ( Habituellement via la commande GetArea ou GetObjectByTag en utilisant les tags de la zone).
XVII. Utilisation des " Creature Events"
Comme mentionné précédemment dans le post " UserDefinedEvent est votre ami", tant que vous ne savez pas exactement ce que vous faites, ne modifiez pas l’IA par défaut qui est utilisé pour les créatures ( excepté le script OnSpawn qui est fait pour ça). Il est parfaitement possible d’activer tous les " flag" du script OnSpawn et d’écrire un script OnUserDefined associé à chaque numéro d’événement ( Tous les exemples ci dessous sont des script OnUserDefined qui le font).
Liste des évènements des créatures. ( Creature event)
Evènement: OnPerception
déclenché par: quelque chose qui entre dans la zone de perception de la créature.
Ca ne veut pas dire que la créature entend ou voit quelque chose…mais juste quand c’est possible pour lui de la percevoir. Qu’est ce qui l’a déclenché? Utilisez GetLastPerceived() pour renvoyer la dernière créature percus. ( qu’elle soit encore en vue ou non).
Aussi utilisé dans: GetLastPerceptionHeard() renvoie TRUE ou FALSE selon que le dernier objet perçu ai été
entendu ou non,
GetLastPerceptionInaudible() renvoie TRUE ou FALSE selon que le dernier objet perçu soit devenu inaudible ou non, ,
GetLastPerceptionSeen() idem que GetLastPerceptionSeen() pour la vue, GetLastPerceptionVanished() renvoie TRUE ou FALSE selon que l’objet soit encore visible ou non. Une chose dont il faut se servir :
Toutes les commandes ci dessus sont en rapport uniquement avec le OnPerception event. Ne les utilisez pas n’importe où.
Qu’est ce que fait le script d’IA par défaut ? L’IA générique de OnPerception dit en priorité à la créature de se mettre en mode recherche si un ennemi disparaît subitement, puis de l’attaquer dès qu’elle réapparaît.
Exemple : Je veux que mon humain se tourne vers tous ceux qu’il voit, mais si c’est une femme, il s’incline devant elle :
void main()
{
int nEvent = GetUserDefinedEventNumber();
/ / si l’événement OnPerception est lancé
if ( nEvent == 1004) / / OnPerception event
{
object oTarget = GetLastPerceived();
/ / si je peux voir la personne dans la limite de perception
if ( GetLastPerceptionSeen())
{
/ / mettre la commande pour se tourner dans la file
ActionDoCommand(SetFacingPoint(GetPosition(oTarget
)));
}
/ / si il s’agit d’une femme
if ( GetGender(oTarget) == GENDER_FEMALE)
{
/ / pause de 0,5 seconde
ActionWait(0.5);
/ / puis salue
ActionPlayAnimation(ANIMATION_FIREFORGET_BOW);
}
}
}
Evènement: OnSpellCastAt
déclenché par: un sort a été lancé sur la créature... notez que ce n’est pas le sort lui même qui cause cet événement. Vous verrez dans votre liste de commande " EventSpelllCastAt. Le script du sort utilise cette commande sur la cible pour lui spécifié que le sort a été lancé…et la commande lui demande alors si le sort est hostile ou non.
Qu’est ce qui l’a déclenché? Utilisez GetLastSpell() renvoie la constante du sort utilisé. ( SPELL_*).
Aussi utilisé dans : GetLastSpellCaster() pour retourner " l’objet" qui a lancé le sort,
GetLastSpellHarmful() renvoie TRUE ou FALSE selon que l’événement soit ou non marqué hostile.
Que fait le script d’IA par défaut ? Si un sort " nuisible" a été lancé sur la créature et qu’elle n’était pas en combat, elle devient hostile.
Evènement: OnPhysicalAttacked
déclenché par: the creature has been attacked in melee,
Qu’est ce qui l’a déclenché?Utilisez GetLastAttacker(). Aussi utilisé dans: GetLastDamager()
Qu’est ce que fait le script d’IA par défaut? Si la créature est est attaquée et qu’elle n’est pas encore en combat, elle devient hostile, et émet le cri apprprié pour alerter les alliés à proximité.
Evènement: OnDamaged
déclenché par: la créature pert des points de vie
Qu’est ce qui l’a déclenché? Utilisez GetLastDamager().
Aussi utilisé dans: GetTotalDamageDealt(), GetDamageDealtByType(int nDamageType),
GetCurrentHitPoints(object oObject = OBJECT_SELF), GetMaxHitPoints(object oObject = OBJECT_SELF)
Que fait le script d’IA par défaut? Si la créature est attaquée par quelqu’un qu’elle ne peut voir, elle essaye de le trouver. Dans les autres cas, elle trouve une cible appropriée et devient hostile.
Exemple : avec cette créature particulière, si elle pert plus de la moitié de ces points de vie, elle crie quelque chose, courre au waypoint appellé " CAVE_EXIT" et disparaît.
void main()
{
int nEvent = GetUserDefinedEventNumber();
if ( nEvent == 1006) / / OnDamaged event
{
int nMaxHP = GetMaxHitPoints();
int nCurHP = GetCurrentHitPoints();
/ / si elle a moins de la moitié de ses points de vie.
if ( nCurHP < ( nMaxHP / 2))
{
/ / arrête ce que je suis en train de faire
ClearAllActions();
/ / crie
ActionSpeakString("Ahhh! Cours!!");
/ / court vers la sortie
ActionMoveToObject(GetObjectByTag("CAVE_EXIT"), TRUE);
/ / et s’autodétruit
ActionDoCommand(DestroyObject(OBJECT_SELF));
/ / n’accepte plus de commande de l’IA à partir de maintenant
SetCommandable(FALSE);
}
}
}
Evènement: OnDisturbed
déclenché par: quelque chose a été rajouté ou enlevé de l’inventaire de la créature
Qu’est ce qui l’a déclenché? Utilisez GetLastDisturbed() to return the creature object who disturbed the inventory.
Aussi utilisé dans: GetInventoryDisturbType() retournera soit INVENTORY_DISTURB_TYPE_ADDED, soit INVENTORY_DISTURB_TYPE_REMOVED, soit INVENTORY_DISTURB_TYPE_STOLEN
GetInventoryDisturbItem() donne l’objet qui a été ajouté, enlevé ou volé de l’inventaire.
Que fait le script de l’IA par défaut? Puisqu’une créature ne peut avoir un objet ajouté ou enlevé de son inventaire ( ce n’est pas un contenaire), si son inventaire est modifié, il a du être volé…elle deviens hostile si il y a une cible valide.
Evènement: OnCombatRoundEnd
déclenché par: A la fin d’un round de combat, l’événement est déclenché automatiquement..
Que fait le script de l’IA par défaut? Si il n’y a pas de comportement spécial spécifié ou que le " flag" du script OnSpawn n’a été modifié, un nouveau round de combat commence.
Evènement: OnConversation
déclenché par: On a cliqué sur la créature pour une conversation OU quelqu’un a parlé audiblement
Qu’est ce qui l’a déclenché? Utilisez GetLastSpeaker() pour renvoie la créature qui a cliqué dessus ou celle qui vient de crier.
Aussi utilisé dans: GetListenPatternNumber() renvoie un numéro de " pattern" si il y en a un qui a été entré.
Que fait le script de l’IA par défaut? Si quelqu’un a cliqué sur la créature, elle arrête ce qu’elle faisait et commence le dialogue ( si elle le peut). Sinon le script vérifie si la phrase prononcée est reconnaissable, et si la créature a été paramêtrée pour reconnaître un cri de combat.
Evènement: OnRested
déclenché par: la créature se repose via ActionRest(). Les créature du jeu ne se reposent pas, c’est pourquoi il n’y a pas de script d’IA.
Evènement: OnDeath
déclenché par: la créature a été tuée
Qu’est ce qui l’a déclenché? Utilisez GetLastKiller() pour renvoyer " l’objet" ( au sens large) qui a tué la créature. Notez que tout script lancé par cet événement doit être immédiat…utiliser DelayCommand ne marchera pas car la créature ne peut pas accomplir une commande une fois morte.
Que fait le script de l’IA par défaut? Il vérifie si la créature était non mauvaise et avais des niveaux de Commoner ( roturier)…et si c’est le cas change le niveau du tueur un peu plus vers mauvais. Il émet aussi un cri " standard" pour demander de l’aide à toutes les créatures à l’intérieur de la zone de perception auditive ( toutes les créatures qui peuvent l’entendre)
Evènement : OnBlocked
déclenché par: une porte bloque le mouvement de la créature
Qu’est ce qui l’a déclenché? Utilisez GetBlockingDoor() renvoie la porte qui gène..
Notez que seule les portes déclenchent l’évènement OnBlocked event... il ne peut pas être utilisé pour détecter une collision ( du moins pas encore)
Que fait le script de l’IA par défaut? Si la créature est assez astucieuse pour utiliser la porte ( Intelligence de 5 ou plus) elle essayera d’abord de l’ouvrir, et si ce n’est pas possible dela casser.
Evènement: OnHeartbeat
déclenché par: Il ne nécessite pas de déclencheurs... n’importe quel script placé ici se déclenchera toutes les 6 secondes. Il est important que vous n’utilisiez pas des script qui marchent en permanence dans le OnHeartBeat, l’ordinateur serait très vite dépassé ( surtout si vous avez un grand nombre de créatures dans la zone.
Que fait le script de l’IA par défaut? Il vérifie quelques conditions qui sont précisées dans le OnSpawn et les éxécute si elles sont applicables…comme les poste de jour et de nuit ( post) pour la fonction WalkWayPoints et les animation ambientes utilisées par les créatures quand elles restent debout.
encore:
XVIII. Utilisation des " Placeable Object Events"
Comme avec les zones, la chose dont il faut se souvenir est que quand vous écrivez des scripts pour les objets placables et que vous utilisez des commandes commançant par " Action" l’éxécutant par défaut est l’objet lui même. Si vous voulez que le script demande à l’objet d’accomplir une autre commande, vous devez utiliser AssignCommand. Donc gardez à l’esprit qui si les objet placables sont capable d’accomplir des actions ( comme ActionPlayAnimation avec la référence ANIMATION_PLACEABLE_*…comme Open/close ou activate/desactivate quoique tous les placables ne le peuvent pas), leur demander de faire des choses qui leur sont impossible peut causer des problèmes ( ActionLockObject, ActionUnlockObjet, ActionEquipItem, ActionPickUpItem sont des exemple que vous devez pas utiliser pour des objets placables.).
Liste des évènements des objets placables ( placeable object)
Evènement: OnClose
déclencheur: l’objet a été fermé ( notez que seuls les objet qui peuvent être fermé ou ouvert peuvent lancer cet événement…comme un coffre ou une armoire.)
qu’est ce qui l’a déclenché? Utilisez GetLastClosedBy() renvoie " l’objet" qui a fermé l’objet placable.
Exemple: Voici un script OnClose qui permet de vérifier si l’inventaire est vide après qu’il soit fermé…et dans ce cas, il crée 100 po à l’intérieur.
void main()
{
/ / donner le premier objet dans l’inventaire
object oInside = GetFirstItemInInventory();
/ / et si il n’y en a pas
if ( !GetIsObjectValid(oInside))
{
/ / créer des pièces d’or au nombre de 100
CreateItemOnObject("NW_IT_GOLD001", OBJECT_SELF, 100);
}
}
Evènement: OnDamaged
déclencheur: l’objet placable a pris des dégats
Qu’est ce qui l’a déclenché? Utilisez GetLastDamager() pour renvoyer " l’objet" qui lui a infligé des dommages.
Aussi utilisé dans: GetTotalDamageDealt(),
GetDamageDealtByType ( int nDamageType),
GetCurrentHitPoints(), GetMaxHitPoints().
Evènement: OnDeath
déclencheur: l’objet a été détruit ( jouer l’animation " destruction" est automatique)
Qu’est ce qui l’a déclenché? GetLastKiller() renvoie " l’objet" qui l’a détruit.
Evènement: OnHeartbeat
déclencheur: Il ne nécessite pas de déclencheurs... n’importe quel script placé ici se déclenchera toutes les 6 secondes. Il est important que vous n’utilisiez pas des script qui marchent en permanence dans le OnHeartBeat, l’ordinateur serait très vite dépassé.
Evènement: OnDisturbed
déclencheur: Un objet a été ajouté ou enlevé de l’inventaire de l’objet placable
qu’est ce qui l’a déclenché? Utilisez GetInventoryDisturbType().
Le résultat est une de ces constantes: INVENTORY_DISTURB_TYPE_ADDED,
INVENTORY_DISTURB_TYPE_REMOVED
INVENTORY_DISTURB_TYPE_STOLEN.
Aussi utilisé dans: GetLastDisturbed() renvoie " l’bjet" quia enlevé ou rajouté un objet,
GetInventoryDisturbItem() renvoie l’objet qui a été ajouté/enlevé.
Exemple: Voici un script OnDisturbed qui envoie un évènement UserDefinedEvent #500 qui signale a un magicien ( avec le tag " WIZARD1") si quelqu’un enlève une potion particulière ( avec le tag " PRECIOUS_POTION") de l’objet placable.
void main()
{
object oWizard = GetObjectByTag("WIZARD1");
object oPC = GetLastDisturbed();
object oPotion = GetObjectByTag("PRECIOUS_POTION");
/ / si l’objet enlevé est valable et a le tag " PRECIOUS_POTION"
if ( GetIsObjectValid(oPC) && ( GetInventoryDisturbType() == INVENTORY_DISTURB_TYPE_REMOVED)
&& ( GetInventoryDisturbItem() == oPotion))
{
/ / envoie le signal au mage
SignalEvent(oWizard, EventUserDefined(500));
}
}
Evènement: OnLock
déclencheur: L’objet a été fermé ( ou refermé)
Qu’est ce qui l’a déclenché? Utilisez GetLastLocked() pour renvoyer " l’objet" qui l’a fermé.
Aussi utilisé dans: GetLockLockDC() renvoie le DC pour fermer l’objet placable.
Note: J’ai eu quelques messages selon lesquel cet événement ne marcherait pas.
Evènement: OnPhysicalAttacked
déclencheur: l’objet a été attaqué physiquement ( mais n’a pas forcement reçu de dommage)
Qu’est ce qui l’a déclenché? Utilisez GetLastAttacker().
Evènement: OnOpen
déclencheur: l’objet a été ouvert ( pour les placables qui peuvent être ouvert et ont la case " inventory" cochée)
Qu’est ce qui l’a déclenché? Utilisez GetLastOpenedBy().
Evènement: OnSpellCastAt
déclencheur: un sort a été lancé sur l’objet placable
Qu’est ce qui l’a déclenché? Utilisez GetLastSpellCaster().
Aussi utilisé dans: GetLastSpell() retournera la constante du sort lancé ( SPELL_*),
GetLastSpellHarmful() renverra TRUE ou FALSE selon que le dernier sort lancé était hostile.
Evènement: OnUnlock
déclencheur: l’objet placable a été déverouillé
Qu’est ce qui l’a déclenché? Utilisez GetLastUnlocked() pour renvoyer " l’objet" qui l’a dévérouillé.
Note: J’ai eu quelques messages selon lesquel cet événement ne marcherait pas.
Evènement: OnUsed
déclencheur: si un objet placable a la case " Useable", alors cliquer sur cette objet lancera cet événement
Qu’est ce qui l’a déclenché? Utilisez GetLastUsedBy().
Exemple : un script pour un objet qui soigne quiconque l’utilise.
void main()
{
object oUser = GetLastUsedBy();
effect eHeal = EffectHeal(GetMaxHitPoints(oUser));
effect eVis = EffectVisualEffect(VFX_IMP_PULSE_HOLY);
/ / si l’utilisateur de l’objet est valide
if ( GetIsObjectValid(oUser))
{
/ / applique l’effet usuel
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oUser);
/ / et une seconde plus tard, soigne l’utilisateur
DelayCommand(1.0, ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oUser));
}
}
Evènement: OnUserDefined
déclencheur: Cet événement se lancera uniquement si un usage particuliera été défini qui enverra un " UserDefinedEvent" à l’objet placable.
XIX. Utilisation des " Trigger Events"
Encore une fois, soyez sur que toutes les actions que vous souhaitez que le déclencheur ( trigger) mette en place sont envoyées à la cible spécifique via AssignCommand…car sinon vous demandez au déclencheur de faire quelque chose par lui même, et il ne peut pas faire grand chose.
Liste des événement des déclencheurs ( Trigger Event)
Evènement: OnClick
déclencheur: Si vous voulez que le déclencheur cause en effet quand le joueur clique dessus, un script doit être entré dans la zone. Beaucoup de sorte de transitions de zones ( area transition) utilisent cet événement ( bien qu’un déclencheur qui est paramêtré comme une zone de transition peut utiliser le wizard pour faire cela.
Qu’est ce qui l’a déclenché? Utilisez GetClickingObject() pour renvoyer " l’objet" joueur qui a cliqué dessus.
Evènement: OnEnter
déclencheur: tout objet traversant la limite de la zone du déclencheur
Qu’est ce qui l’a déclenché? Utilisez GetEnteringObject().
Exemple: Le script OnEnter suivant ferra " sauter" tous les membres de la partie qui sont dans la même zone jusqu’au waypoint avec le tag " JUMP_HERE".
void main()
{
object oWP = GetObjectByTag("JUMP_HERE");
object oParty = GetFirstFactionMember(GetEnteringObject(), TRUE);
/ / cicle qui passe en revue tous les membres de la partie.
while ( GetIsObjectValid(oParty))
{
/ / si ils sont dans la même zone
if ( GetArea(oParty) == GetArea(GetEnteringObject()))
{
/ / jump the party member to the waypoint
AssignCommand(oParty, JumpToObject(oWP));
}
/ / va jusqu’au prochain membre de la party
oParty = GetNextFactionMember(GetEnteringObject(), TRUE);
}
oParty = GetFirstFactionMember(GetEnteringObject(), FALSE);
/ / maintenant, passe en revu tous les membres non-joueurs de la partie(comme les familiers)
while ( GetIsObjectValid(oParty))
{
/ / et si ils sont dans la même zone
if ( GetArea(oParty) == GetArea(GetEnteringObject()))
{
/ / faites " sauter" les membres de la partie jusqu’au waypoint
AssignCommand(oParty, JumpToObject(oWP));
}
/ / aller au prochain membre de la partie
oParty = GetNextFactionMember(GetEnteringObject(), FALSE);
}
/ / maintenant, fait sauter le PC entrant jusqu’au Waypoint
/ / cette ligne n’est pas nécessaire…l’inclure
/ / permet juste d’être certain de déplacer aussi le PC qui a activé le déclencheur
AssignCommand(GetEnteringObject(), JumpToObject(oWP));
}
Evènement: OnExit
déclencheur: à chaque fois qu’un ennemi traverse la limite de la zone du déclencheur pour la quitter
Qu’est ce qui l’a déclenché? Utilisez GetExitingObject() pour renvoyer l’objet sortant.
Exemple: Ce script OnExit pour le déclencheur envoie une commande à n’importe quel géant sortant pour retourner à un waypoint.
void main()
{
object oWP = GetObjectByTag("RETURN_HERE");
object oCreature = GetExitingObject();
/ / si la créature sortante est un géant
if ( GetRacialType(oCreature) == RACIAL_TYPE_GIANT)
{
/ / lui demande d’arrêter ce qu’il fait
AssignCommand(oCreature, ClearAllActions());
/ / et de bouger au waypoint
AssignCommand(oCreature, ActionMoveToObject(oWP));
}
}
Evènement: OnHeartbeat
déclencheur: comme les autres heartbeat, Le Heartbeat du déclencheur est activé toutes les 6 secondes. Un déclencheur n’est cependant pas comme tous les autres objets…il est seulement activé quand quelque chose est à l’intérieur de ces limites ou qu’on clique dessus. Vous ne pouvez placer un déclencheur et lui donner un script pour constamment commander un ordre…si vous voulez faire ça, utilisez à la place un objet invisible.
Evènement: OnUserDefined
déclencheur: encore, cet événement se lancera uniquement si un UserDefinedEvent a été spécifiquement envoyé au déclencheur par la commande SignalEvent.
XX. Ajouter et enlever des effets
Si vous faîtes défiler la liste des commandes dans votre " script editor", vous pouvez voir une longue liste de commandes qui commencent par le mot " Effect". Ceux sont les commandes pour faire tous les sorts, les capacilés spétiales et les effets visuels dans le jeu. Il y a deux commandes qui permettent de les utiliser : ApplyEffectToObject et ApplyEffectAtLocation. Toutes les deux sont très similaires dans leur structure, à l’exception que la cible de la première est un objet spécifique ( créature, objets placables, etc) et la cible de la deuxième est un emplacement spécifique ( un point dans l’espace)
void ApplyEffectToObject ( int nDurationType, effect eEffect, object oTarget, float fDuration=0.0f)
Duration Type: " nDurationType" demande une constante commencant par DURATION_TYPE_*, il n’y en a en fait que trois possibles. DURATION_TYPE_INSTANT s’applique à tous les effets qui sont instantannés, permanents et ne peuvent être annulés. EffectDamage serait un exemple, ainsi que tous les objets " fire-and-forget" ( tire et oublie). Si elle n’est pas " instant", la constante doit être soit DURATION_TYPE_PERMANENT…l’effet reste jusqu’à se qu’il soit enlevé ou dissipé. Une durée temporaire signifie que l’effet doit avoir une durée spécifique dans la commande ( et c’est le seul moment où c’est le cas)
Effect: C’est l’effet spécifique que vous voulez utiliser Il est courant de définir d’abord une variable qui contient toutes les informations pertinentes, et ensuite de l’utiliser cette variable quand vous le voulez…mais ce n’est pas nécessaire. Chaque effet varie dans ces conditions. Quelques uns sont simplement appellés EffectSleep ou EffectCharmed et n’ont besoin de rien d’autre. D’autres ont besoin de paramêtres additionnels, comme EffectDamage ou EffectPolymorph. Je ne détaillerais pas tous les effets ici, mais il est important que vous fournissiez tous les paramêtres requis par un effet ou il sera non valide.
Target: Cela doit être soit un objet ( dans ApplyEffectToObject) ou un emplacement ( dans ApplyEffectAtLocation). Il peut être déclaré séparement dans une variable ou défini dans la commande.
Duration: Une durée ( en seconds) est uniquement requise pour les effets avec DURATION_TYPE_TEMPORARY. Quand elle est spécifié, ce doit être un " float" ( et non un integer). Donc mettez " 3.0" pour 3 secondes, ou " 10.5" pour 10 secondes et demi.
Quelques exemples d’effets
Dans le OnUsed d’un objet, il transforme l’utilisateur en pixie :
void main()
{
effect ePixie = EffectPolymorph(POLYMORPH_TYPE_PIXIE);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, ePixie, GetLastUsedBy());
}
Un objet avec en capacité un sort unique ( avec le tag " WONDERFUL_WAND" qui soigne le " hâte" la cible pour 30 secondes:
void main()
{
object oWand = GetItemActivated();
if ( oWand == GetObjectByTag("WONDERFUL_WAND"))
{
object oTarget = GetItemActivatedTarget();
int nMaxHP = GetMaxHitPoints(oTarget);
int nCurHP = GetCurrentHitPoints(oTarget);
effect eHeal = EffectHeal(nMaxHP - nCurHP);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oTarget);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectHaste(), oTarget, 30.0);
}
}
Dans le OnUserDefined d’une créature, quand un événement #100 est envoyé, un blaireau générique est convoqué au waypoint avec le tag " SUMMON_POINT":
void main()
{
int nUser = GetUserDefinedEventNumber();
if(nUser == 100)
{
effect eSummon = EffectSummonCreature("bugbeara001");
location lWP = GetLocation(GetObjectByTag("SUMMON_POINT"));
ApplyEffectAtLocation(DURATION_TYPE_PERMANENT, eSummon, lWP);
}
}
et hop:
Effets visuels
Les effets visuels sont très similaires aux effets normaux, vous les appliquez de la même manière. Vous utilisez EffectVisualEffect et fournissez lui fournissez le paramêtre d’une des constantes des effets visuels…elles commencent toutes par VFX_*. Cependant, il y en a un certain nombre, et elles sont différenciées par des mentions différentes.
VFX_BEAM_* = il y a différents types de beam ( poutres) visuels, et bien qu’elles soient groupées avec les autres effets visuels, ces constantes sont utilisées avec EffectBeam et non avec EffectVisualEffect.
VFX_COM_* = ce sont les effets visuels de combat, presque tous sont utilisés avec une durée instantanée(INSTANT).
VFX_DUR_* = beaucoup sont des effets utilisés pour les sorts. Ils doivent tous être applqiés avec une durée soit permanente, soit temporaire ( PERMANENT OU TEMPORARY)
VFX_FNF_* = Ils sont appellés les effets " fire-and-forget" ( tire et oublie) dans le sens ou ils ne sont joués qu’une fois. Ils doivent avoir une durée instantannée ( INSTANT)
VFX_IMP_* = Ce sont les effets " impact", ils sont habituellement utilisés pour des effets de courte durée quand un sort frappe une cible. Ins ont une durée instantannée ( INSTANT).
Voici un exemple d’un puit de sorcière magique, quand il est utilisé, il crée un effet sur lui même et augmente la force avec un effet sur la cible ( mais il ne peut seulement être utilisé une fois…si il est utilisé une seconde fois, il attaque avec un éclair de 8d6 de dégats) :
void main()
{
effect eFirst;
effect eSecond;
effect eStrength = EffectAbilityIncrease(ABILITY_STRENGTH, ( d4() + 1));
object oTarget = GetLastUsedBy();
int nOnce = GetLocalInt(oTarget, " Use_Pool_Once");
/ / si le PC n’a pas utilisé la fontaine auparavant
if ( nOnce == FALSE)
{
eFirst = EffectVisualEffect(VFX_IMP_GOOD_HELP);
eSecond =
EffectVisualEffect(VFX_IMP_IMPROVE_ABILITY_SCORE);
/ / crée l’effet sur la fontaine
ApplyEffectToObject(DURATION_TYPE_INSTANT, eFirst, OBJECT_SELF);
/ / applique l’augmentation de la force avec un effet 3 secondes plus tard
DelayCommand(3.0, ApplyEffectToObject(DURATION_TYPE_INSTANT, eSecond, oTarget));
DelayCommand(3.0, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eStrength, oTarget, 300.0));
/ / se fixe la variable pour que le PC ne puisse pas l’utiliser une seconde fois
SetLocalInt(oTarget, " Use_Pool_Once", TRUE);
}
/ / Si il a utilisé la fontaine
else
{
eFirst = EffectVisualEffect(VFX_IMP_LIGHTNING_M);
/ / permet un jet de sauvegarde pour éviter l’éclair
int nDamage = GetReflexAdjustedDamage(d6(8), oTarget, 25, SAVING_THROW_TYPE_ELECTRICITY);
eSecond = EffectDamage(nDamage, DAMAGE_TYPE_ELECTRICAL);
/ / Puis appliquer l´effet visuel de l´éclair et les dégâts
ApplyEffectToObject(DURATION_TYPE_INSTANT, eFirst, oTarget);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eSecond, oTarget);
}
}
Enlever des effets
La chose pour enlever des effets est qu’un effet a un " pointeur" unique qui a été établie quand il a été appliqué. Si vous définissez un nouvel effet de variable plus tard et que vous le définissez comme un effet du même type, RemoveEffect ne le ciblera pas.
La voie la plus facile pour enlever un effet est de parcourir les effets actifs qu’ils sont appliqués à la cible en utilisant GetFirstEffect et GetNextEffect... et ensuite de filtrer l’effet spécifique que vous recherchez. Une fois que vous l’avez trouvé ( ou les avez trouvés si vous voulez en enlever plus d’un) le pointeur est fixé sur le bon effet et vous pouvez l’enlever.
Les commandes suivantes sont utile pour filtrer :
GetEffectCreator = Si vous voulez spécifier seulement les effets qui ont été appliqués par une créture ou un objet spécifique
GetEffectDurationType = Cela retournera la constant du type de durée d’un effet ( soit " instant", " permanent", " temporary"). Très générales, mais utile dans certaines circonstances.
GetEffectSpellId = Cel retournera la constante du sort qui a appliqué l’effet ( SPELL_*). Très utile si vous cherchez tous les effets d’un sort particulier ou si vous voulez les exclure.
GetEffectSubType = Cela renverra la constante SUBTYPE_MAGICAL
SUBTYPE_SUPERNATURAL
SUBTPE_EXTRAORDINARY
Tous les effets par défaut d’un sous type sans les spécifier exactement.
GetEffectType = C’est le filtre le plus utilisé quand vous cherchez un objet spécifique. Il renvoie une constante du type EFFECT_TYPE_*, qui est presque la même que la liste de commande des effets. Seul les effets visuels n’ont pas un type d’effet ( effect type).
Le script basique pour enlever un effet ressemble à ceci:
effect eEffect = GetFirstEffect(oTarget);
while ( GetIsValidEffect(eEffect))
{
if ( eEffect == filter used)
{
RemoveEffect(oTarget, eEffect);
}
eEffect = GetNextEffect(oTarget);
}
Exemple
pour enlever une malédiction ( curse) lancée par une sorcière ( avec le tag " EVIL_WITCH") à la fin d’un dialogue:
void main()
{
effect eEffect = GetFirstEffect(GetPCSpeaker());
while ( GetIsEffectValid(eEffect))
{
if ( (GetEffectType(eEffect) == EFFECT_TYPE_CURSE)
&& ( GetEffectCreator(eEffect) == GetObjectByTag("EVIL_WITCH")))
{
RemoveEffect(GetPCSpeaker(), eEffect);
}
eEffect = GetNextEffect(GetPCSpeaker());
}
}
Quelques choses à retenir
· EffectKnockdown cause un crasch quand il est appliqué par autrechose que par une créature ( ce sera fixé par le prochain patch) NdT ça devrais être fait.
· EffectHitPointChangeWhenDying ne marche pas ( ce sera fixé par le prochain patch) NdT idem
· Tous les effets ne sont pas permanent... Ils ne sont pas sauvé quand le personnage retourne au menu, mais il persiste tant qu’il est sur le module.
· Le plus long type d’effet est l’effet extroardinaire, qui résiste à la dissipation de la magie et au repos. Les ExtraordinaryEffect et les SupernaturalEffect sont appliqué quand le commande suivante est dite:
· effect eEffect = ExtraordinaryEffect ( EffectSilence());
et encore...:
XXI. Utilisez les dialogue avec les objets placables et les déclencheurs
Il y a beaucoup d’effets qu’un MD intelligent peut simuler juste en utilisant un petit peu les dialogue en même temps que les objets placables et les déclencheurs.
Vous pouvez avoir une description ambiente d’une zone ou d’un objet, commencer un " dialogue" avec un objet étrange qui permet au joueur d’interagir avec lui, avoir quelqu’un de l’autre côté d’une porte qui parle à travers elle, informer les joueur des événements qui peuvent arriver…il y a beaucoup de possibilitées.
La première chose que vous devez savoir est quelles options vous avez. Ci dessous, vous avez des commandes qui peuvent être utilisées pour initier un dialogue ou afficher un texte :
ActionSpeakString et SpeakString
La seule différence entre ces deux actions est que ActionSpeakString pousse la commande dans la file des actions ( ça veut dire qu’elle ne sera pas effectuée avant que les actions placées au dessus dans la liste, comme ActionWait ou ActionMoveToObject, n’aient été effectuées) et que SpeakString est effectuée immédiatement.
Qu’est ce que ca fait: une ligne de texte est affichée au dessus de celui qui a effectué cette commande. Cela est affiché pour le joueur comme un dialogue normal, le texte apparaît derrière le nom du personnage et son portrait ( si c’est possible)
Advantages: Un volume peut être appliqué au texte en placant la constante TALKVOLUME_* appliquée dans la commande. TALKVOLUME_TALK est utilisé par défaut et est entendu par tous avec une portée normale. TALKVOLUME_SHOUT est entendu par tout le monde dans le module. TALKVOLUME_WHISPER est entendu par tout la monde dans une zone réduite. TALKVOLUME_SILENT_TALK et TALKVOLUME_SILENT_SHOUT sont des variante du " talk" et du " shout" normal... mais il sera affiché et entendu uniquement par les NPC.
La particularité d’avoir un module est que le texte est aussi " entendu" par les objets du jeu. Les créatures sans portée auditive tourneront la tête, et si vous faite un script qui reconnais le texte via SetListenPattern et GetListenPatternNumber ( comme les " cris" de combat), SpeakString peut être utilisé pour cela.
ActionStartConversation
Qu’est ce que ça fait : Ca demande à l’éxécuteur de la commande d’initier le dialogue avec la cible. Ca doit faire partie de l’événement OnDialogue de la cible ( si elle en a un, et que le PC n’en pas). Si aucun fichier de dialogue resref est spécifié, celui attaché à la créature ou à l’objet sera utilisé ( celui que vous avez spécifié dans le toolset). L’interlocuteur dans la conversation doit toujours être l’objet cible.
Advantages: Vous pouvez démarrer un conversation entière de cette manière, avec des choix de réponse et de multiples conditions de départ et de script. Vous pouvez aussi spécifier dans la commande principale si c’est ou non une conversation privée…Si c’est le cas, seule la cible verra le texte apparaître sur l’écran ( c’est bien pour interagir avec les objets sans que les autres joueurs entendent cette conversation).
BeginConversation
Qu’est ce que ça fait: Cette commande est normallement seulement utilisée dans le " OnDialogue event" de la créature. C’est la commande qui lui demande de débuter la conversation avec le joueur qui vient juste de lui cliquer dessus. L’utiliser sur un objet ne lancera pas son " OnDialgue event". Si la cible de l’action n’est pas spécifiée, l’objet qui a initié le OnDialgue event sera utilisé.
Advantages: Peut être utilisé pour une conversation de NPC à NPC... autrement ActionStartConversation doit toujours être utilisé hors du OnDialogue event.
FloatingTextStringOnCreature
Qu’est ce que ça fait: Quand vous accomplissez de nombreuses actions dans le jeu, vous avez remarqué le texte qui apparaît au dessus de la tête du joueur flotte puis disparaît. Cette commande faît la même chose. Le texte est aussi montré dans la fenêtre du chat du joueur…Mais il n’est pas montré comme originaire d’une créature et ne montrera aucun portrait. Le texte est simplement marqué en jaune, comme la plupart des texts ambiants.
Advantages: C’est bien pour un texte d’ambiance ou simuler un message décrivant une action, puisqu’il n’ajoute pas un préfixe au début du texte et ne ressemble pas au texte des dialogues.
Une chose dont il faut être conscient: Un texte flottant, par défault, ne peut être vu que par la créature au dessus de laquelle il flotte. Si vous sélectionnez la commande " display to faction", alors les membres de la même faction verront aussi le texte s’afficher. Cela veut dire que le texte flottant est utile uniquement pour les PC, puisque aucune créature non PC ne peut être attribuée à la faction PC.
SpeakOneLinerConversation
Qu’est ce que je fais: Cela marche presque comme ActionStartConversation, mais son usage est plus spécifique. Il est utilisé pour accèder au fichier dialogue, mais seulement pour une ligne de dialogue unique sans réponses ni liens.
Advantages: Vous pouvez encore scripter les conditions de lancement du dialogue , vous pouvez avoir une longue sélection de ligne simple. Le locuteur ne se tourne vers aucune cible avant de parler…et l’objet identifié dans la commande ( si il y en a un) devient la référence pour chaque marque utilisée dans le dialogue ( seulement si elles ne sont pas présentes).
Faire dire un texte à un objet placable.
Pour l’exemple que je vais utiliser ici, nous allons faire qu’un objet placable " signpost" affiche un texte quand on clique dessus.
Important! Si vous voulez avoir un objet placable qui interagit avec les autres de cette manière, vérifiez que la case " Statique" est décochée ! Si un objet placable est statique, il fera partit de l’arrière plan et ne sera pas sélectionnable. La plupart des objets sont marqués statique par défaut !
Donc sélectionnez un " signpost" ( une pancarte) dans la liste des objets placables et placez le ou vous voulez. Ensuite allez dans ces propriétés et soyez sur que la case statique est décochée. Comme je veux que le joueur soit capable de cliquer dessus, la case " useable" ( elle doit être cochée). Allez dans la section script et mettes le script suivant dans le OnUsed event :
void main()
{
SpeakString("North: Waterdeep, 20 miles");
}
Maintenant, quand vous êtes dans le jeu et que vous cliquez sur la pancarte, elle affichera le texte précédé de son portrait ( qui est par défault une pancarte) et de son nom. Naturellement vous pouvez toujourzs changer le portrait comme vous voulez, ainsi que le nom, et l’affichage sera différent.
Faire apparaître un texte flottant à un déclencheur ( trigger)
Dans cet exemple nous voulons que quand un joueur traverse la zone du déclencheur, un texte ambient apparaît au dessus de sa tête.
Allez dans le menu déclencheur ( trigger menu) et sélectionnez " generic trigger". Ca vous permettra de dessiner les limites du trigger. Si je veux afficher une description de quelque chose que le joueur voit dans la zone, par exemple, je peux le mettre juste derrière la porte.
Allez dans la section script du trigger. Comme nous voulons que le texte apparaisse quand le joueur entre dans le déclencheur, nous mettrons le script dans la section OnEnter. Quelques choses à prendre en compte : est ce que nous voulons qu’il l’affiche seulement pour les PC ou pour toutes les créatures entrantes ? Est ce que nous voulons qu’il s’affiche à chaque fois que le PC entre, ou seulement une fois ? Cette fois, nous voulons que le texte apparaisse à tout le monde…mais une fois seulement.
void main()
{
object oPC = GetEnteringObject();
if ( GetLocalInt(oPC, " Saw_Trigger_Text") == FALSE)
{
FloatingTextStringOnCreature("La pièce est sombre et angoissante.", oPC);
SetLocalInt(oPC, " Saw_Trigger_Text", TRUE);
}
}
Quand le joueur entre dans le trigger pendant le jeu, le texte apparaît au dessus de sa tête et flotte, puis disparaît. Cela l’affichera aussi dans la fenêtre de chat en jaune. Notez que le déclencheur ne peut pas utiliser SpeakString oou ActionSpeakString. Il ne peut tout simplement pas parler. Mais, il peut cependant demander à un autre objet de parler…et cet objet peut aussi être invisible ou non. Si j’ai à proximité un objet placable avec le tag " DISPLAY_HERE" ( qui souvenez vous a la case " Static" décochée) et que je veux que le déclencheur le fasse afficher un texte, j’utiliserais le script suivant:
void main()
{
object oPC = GetEnteringObject();
object oDisplay = GetObjectByTag("DISPLAY_HERE");
if ( GetLocalInt(oPC, " Saw_Trigger_Text") == FALSE)
{
AssignCommand(oDisplay, SpeakString("La pièce est sombre et angoissante."));
SetLocalInt(oPC, " Saw_Trigger_Text", TRUE);
}
}
Avoir un objet qui affiche un texte quand un personnage vient à proximité.
Dans cet exemple, je vais poser un objet invisible ( que le personnage de peut ni voir ni toucher) qui va afficher un texte quand le joueur approche à moins de 10 mêtres.
Premièrement, sélectionnez l’objet invisible dans le menu des objets placables et placez le. Allez dans ces propriétées et vérifiez que " Static" est décoché. Je ne veux pas que le joueur puisse cliquer dessus, donc laissez " Useable" décoché. Changez le nom de l’objet en " Unknow Person" et laissez le portrait blanc.
Allez dans la section des scripts. Mettez le script suivant dans le OnHearbeat event:
void main()
{
object oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC);
if ( GetIsObjectValid(oPC) && ( GetDistanceToObject(oPC) < 10.0))
{
SpeakString("Psst! Viens par ici!");
}
}
Dans le jeu, quand le player viens à moins de 10 mètres de l’objet, le texte apparaîtra à l’écran…et dans la fenêtre de chat et le PC pourra lire " Unknown Person : Psst ! Viens par ici ! ". Je pourrais dévelloper un petit peu plus le script…par exemple, vérifier si le personnage s’approche à moins de 3 mètres la personne inconnu apparaîtra au dessus de l’objet placable.
Une chose que vous devez savoir est que l’objet placable est un petit peu petit… et l’affichage du texte est un peu bas sur le sol ( ca dépend du zoom de la caméra). Il y a un truc pour l’afficher un peu plus haut : avant de placer cet objet invisible, sélectionnez un autre objet placable qui est aussi haut que vous le souhaitez…Placez le en premier. Puis sélectionnez l’objet invisible dans le menu, placez le curseur directement au dessus du premier objet avant de cliquer pour le poser. Il apparaîtra au sommet du premier objet. Maintenant, effacez le premier objet et l’objet invisible flottera au milieu de la pièce ( et y restera).
Avoir un objet placable qui lance un dialogue.
Admettons que je veuille un objet aue le joueur puisse sélectionner et avoir un dialogue interactif…ou peut être avoir un objet qui parle. Premièrement, allez dans la liste des objets placable et sélectionnez un objet. Par exemple un piédestal et placez le dans le module. Allez dans ces propriétés et vérifiez que " Static" est décoché et " Usable" est cochée.
Allez dans l’onglet advance. Ecrivez dans un fichier dialogue ce que nous allons créer et appuyez sur Edit. Vous pouvez commencer par une ligne comme " Il y a quelque chose décrit sur le piédestal dans un language étrange. Est ce que vous essyez de le déchiffrer ? " Vous pouvez ensuite continuer à donner des réponses et faire un dialogue aussi long que vous le voulez, comme un dialogue normal. Allez dans l’onglet script et placez le script suivant dans OnUsed:
void main()
{
object oPC = GetLastUsedBy();
ActionStartConversation(oPC, " ", TRUE);
}
Dans le jeu, quand le joueur clique sur le piédestal, il s’approche de lui et le dialogue commence normalement. Puisque j’ai spécifié TRUE dans le paramêtre bPrivateConversation de la commande, lui seul verra la commande…un autre joueur étant a proximité ne verra et n’entendera rien. Si je ne me souciais pas du texte affiché, j’aurais pu simplement avoir écrit ActionStartConversation(oPC), puisque les " " signifient que le fichier dialogue attaché est utilisé.
Commencer une conversation avec une porte
Admettons que je veuille simuler ça : le joueur essaie d’ouvrir une porte mais la trouve fermée. Une voie parle de l’autre côté : " Quel est le mot de passe ? "
Premièrement, placez la porte normalement. Allez dans ses propriétées et changez le nom. Une chose pratique dans le nom de la porte : c’est qu’il n’est montré à aucun endroit ( même si on l’examine) sauf dans le dialogue…donc changez le en " Le garde de l’autre côté", puisque c’est ce que le joueur verra.
Marquez la porte comme " plot" pour qu’elle ne puisse être cassée ( mais vous pouvez aussi la laisser comme tels, et écrire un script avec les évènements OnDamaged ou OnPhysicalAttacked). Allez dans l’onglet lock et cochez la case " Locked". Si vous ne voulez pas qu’elle soit crochetable, cochez " Key required to lock or unlock" ( encore une fois, vous pouvez faire comme vous voulez). Allez ensuite dans l’onglet advanced et mettez un nom dans conversation puis appyuez sur Edit. Nous pouvons créer la première ligne " Quel est le mot de passe ? " et continuez comme vous le souhaitez…quand vous avez fini et que vous avez crée une réponse ou le garde accepte d’ouvrir la porte, placez le script suivant dans la section " Action Taken" du nœud ou ce dernier accepte d’ouvrir.
void main()
{
ActionDoCommand(SetLocked(OBJECT_SELF, FALSE));
ActionOpenDoor(OBJECT_SELF);
}
Maintenant, nous devons placer le script qui démarre la conversation. Placez le script suivant dans le OnFailToOpen event:
void main()
{
object oPC = GetClickingObject();
if ( !IsInConversation(OBJECT_SELF))
{
ActionStartConversation(oPC);
}
}
Dans le jeu, lorsque le joueur cliquera sur la porte et s’en approchera, il entendra le son habituel quand une porte refuse de s’ouvrir, et soudain le dialogue apparaît. Le joueur verra le portrait de la porte et " Le garde de l’autre côté" s’afficher au dessus avec le texte " quel est le mot de passe ? " affiché. Quand le moment arrive dans le dialogu ou le guarde doit ouvrir la porte, elle est déverouillée puis s’ouvre d’elle même…le joueur peut maintenant entrer.
suite:
XXII. Factions, cri et attaquer mes ennemis
NdT : important différenciez bien " party" qui est un groupe de joueurs de " partie" qui a le sens habituel
Qu’est ce qu’est au juste une faction?
Une faction est une de ces trois choses : une party de PC & leur associés ( mercenaires, familiers…), un joueur seul sans membre dans la partie & ses associés ou un groupe de NPC ou un NPC seul a qui ont été assignés des réactions par défault envers les autres factions. Cela ne doit pas être confondu avec une " organisation" comme une guilde de voleurs ou la garnison d’un ville. Les joueurs de peuvent pas " rejoindre" une factions , et chaque organisation peut contenir plusieurs factions.
Que font donc les factions ?
Juste ce qu’elles sont supposé faire : fixer le comportement par défaut d’un NPC face aux autres factions. L’IA générique donne une position comme amical ( Friendly) ou hostile ( Hostile) et le NPC réagit conformement au script. Un NPC maintiendra cette position par défaut jusqu’à et à moins qu’il lui soit demandé de faire autre chose.
Ce qui est probablement le plus importa nt à comprendre à propos des factions est qu’il y a cependant une différence entre ce que la faction " ressent" et ce que le NPC seul " ressent". Si un NPC individuel sans faction est attaqué, il deviendra hostile et se défencra tout seul. Il restera aussi hostile jusqu’à ce qu’on lui demande autrechose. Cependant, les autres membres de la faction non prészent ne seront pas nécessairement hostile comme lui. La différence de réaction est du à la " réputation", qui est différent de la réputation de quelqu’un avec la faction.
La commande NWscript se réfère, malheureusement, aux deux types de réputation de la même manière, donc faire la distinction entre les deux peut être difficile.
Pour mieux expliquer la différence, considerez ceci :
Une party de PC sont dans une taverne. Le voleur de la party décide d’attaquer un roturier à proximité ( qui est membre de la faction Commoner). Puisqu’il est attaqué, le roturier devient hostile envers le voleur…ce qui entrainera aussi une attaque de la part de tous les NPC à proximité qui sont amicaux avec cette faction et qui deviendront aussi hostile envers le voleur.Le reste de la party n’a encore rien fait donc personne n’est hostile envers elle. Si le reste de la party est engagée dans le combat, les NPC déjà hostile avec le voleur le seront avec toute la partie. Admettons maintenant que la party entière quitte la taverne tout de suite et échappe à ces poursuivants…ces individus resteront hostile avec le voleur ( et les autres membres de la party ayant réagit) jusqu’à ce qu’on leur dise de réagir autrement. Les autres membres de la faction Commoner réagiront normallement.
La seule raison que ce ne soit pas le cas est si la case " Global Effect" est cochée ( Elle peut être trouvé dans le Faction Editor). Les individus écartés ( dans l’espace) dans une faction maintiendront des relations différentes…mais, si vous en attaqué un et qu’il devient hostile, tous les membres de la faction seront hostiles. Tous les changement dans les relations d’un membre seront appliqués à la faction entière. ( Il y a certains moment où c’est pratique dans un module…mais soyez conscient que toute nouvelle faction à cette option activée).
Que fait l’IA standard avec les factions?
Pour mieux comprendre pourquoi quelque chose se passe ou ne se passe pas, voici un résumé détaillé de comment les factions affectent l’IA standart :
· Dans l’événement OnPerception ( qui se lance rappelez vous uniquement quand la cible entre dans la limite de perception, ou si le type de percepption change),si un NPC perçoit un hostile et qu’il n’est pas encore en combat le lançe le cri " NW_I_WAS_ATTACKED" ( regardez ci dessous) et attaque.
· Si la créature est attaquée mais pas encore en combat, elle lance les cris " NW_I_WAS_ATTACKED" et " NW_ATTACK_MY_TARGET" puis attaque son attaquant.
· Si la créature est tuée ça lance automatiquement les cris " NW_ATTACK_MY_TARGET" et " NW_I_AM_DEAD".
Et que font ces cris ?
D’une part, il y a les mots simplements parlés qu’on avait demandé à la créature de dire, les même que ceux que peut taper le PC dans son système de Chat. Ces cris cependant sont silencieux…les joueurs ne les entendent ou ne les voient pas flotter, mais les PC oui, et le script OnSpawn leur demande de les écouter et de réagir. Les cris ne sont pas entendus à travers les murs ou les portes, et ils ont une portée limitée.
· Le cri " NW_ATTACK_ MY_TARGET" a seulement un effet sur les NPC qui ont activé ( enlevé les " //") de la ligne " SetSpawnInCondition ( NW_FLAG_SHOUT_ ATTACK_MY_TARGET) ; " dans leur script OnSpawn. Si il a été entendu par un allié ( un membre d’un faction amicale envers celui qui a poussé le cri), il fixe les relations de sa faction en hostile avec celui qui a attaqué le crieur ( si ce n’était pas déjà le cas,) arrête ce qu’il fait et attaque tout ennemi à portée.
· Le cri " NW_I_WAS_ATTACKED" peut être entendu par n’importe qui. Si celui qui l’entend est un allié du crieur et n’est ni en combat ni a moins de 10 niveaux de roturier, il attaquera le crieur.
· Le cri " NW_I_AM_DEAD" peut aussi être entendu par n’importe qui. Son effet est le même que le cri " I was attacked" ci dessuss.
Comment puis-je faire attaquer mes NPC ?
Si est créature démarre hostile et détecte un PC, son événement OnPerception se lancera et elle attaquera immédiatement. Pas de porblème dans ce cas. Cependant, la situation devient souvent plus compliquée si vous avez un NPC qui était neutre face au PC quand l’événement OnPerception se lance et elle deviendra hostile lors du dialogue ou du script.
Pour qu’un NPC devienne hostile face à un PC et l’attaque, vous devez d’abord identifier la cible. Dans le dialoque c’est facile :
object oTarget = GetPCSpeaker();
En dehors d’un dialogue, dans un script normal, ça peut varrier :
/ / la cible est le PC le plus proche de celuui à qui appartient le script.
object oTarget = GetNearestCreature ( CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC);
/ / C’est le dernier objet perçu, doit être seulement utilisé dans le OnPerception.
object oTarget = GetLastPerceived();
Ci dessous, vous avez deux possibilitées.Sans problèmes, une fois que vous avez identifié votre cible, vous pouvez appeler le reste de la commande normallement. Pour le script suivant, nous nous assurons que nous allons commencer le combat à la fin du dialogue ( en plaçant ce script dans la section " Action Taken" de la ligne de dialogue ou nous voulons que le combat commence).
Premièrement, est ce que je veux que l’attaquant change la faction entière en hostile ? Si c’est le cas, j’utilise ça :
void main()
{
/ / Baisse la relation de ma faction avec le PC de 100 ( jusqu’à hostile)
AdjustReputation ( GetPCSpeaker(), OBJECT_SELF, -100);
/ / Démarre le combat ( Cela nécessite le fichier générique nw_i0_generic ci dessus)
DetermineCombatRound();
}
Et c’est tout ( c’est aussi le script générique " nw_dl_attoned"). Maintenant, je veux juste que l’attaqué soit hostile ( et lui seul) :
void main()
{
/ / me fixe comme hostile face au PC
/ / Notez que le " temporary" ( temporaire) dure 3 minutes sans une valeur de durée précisée dans la
/ / commande.
SetIsTemporaryEnemy ( GetPCSpeaker());
/ / démarre le combat ( ça nécessite le fichier générique nw_iO_generic, ci dessus)
DetermineCombatRound();
}
Mais je supose qui j’ai d’autres amis à côté que je voudrais voir prendre part au combat. Si j’utilise AdjustReputation pour rendre leur faction hostile, il n’attaqueront toujours pas. Pourquoi ? Regardez dans le paragraphe à propos de l’IA générique…je n’ai pas attaqué le premier et je suis déjà en combat, c’est pourquoi je ne pousserais pas de cri. Nous avons tous déjà perçut le PC et il n’était pas hostile. Et je ne suis pas mort…donc mes amis resteront la jusqu’à ce qu’ils soient attaqué ou que je meurs. J’ai deux choix.
Soit leur envoyer la commande via AssignCommande comme ceci :
void main()
{
/ / Ce script marche bien si vous savez exactement qui enroler dans le combat
/ / et qu’ils ont des resrefs uniques
object oGoblin2 = GetObjectByTag ( "GOBLIN2");
object oGoblin3 = GetObjectByTag ( "GOBLIN3");
/ / Baisse ma valeur de réaction avec le PC de 100 ( jusqu’à hostile)
AdjustReputation ( GetPCSpeaker(), OBJECT_SELF, -100);
/ / demande à mes amis de débuter le combat
AssignCommand ( oGoblin2, DetermineCombatRound());
AssignCommand ( oGoblin3, DetermineCombatRound());
/ / démarre le combat ( ça nécessite le fichier générique nw_iO_generic, ci dessus)
DetermineCombatRound();
}
Ou comme cela :
void main()
{
/ / Ce script fait que tous les allié dans la zone actuelle attaquent
location oHere = GetLocation ( OBJECT_SELF);
/ / Baisse ma valeur de réaction avec le PC de 100 ( jusqu’à hostile)
AdjustReputation ( GetPCSpeaker(), OBJECT_SELF, -100);
/ / maintenant, cicle tous les objets dans la zone
object oFriend = GetFirstObjectInArea ( GetArea(OBJECT_SELF));
while ( GetIsObjectValid(oFriend))
{
/ / Si c’est objet fait parti de ma faction et que c’est une créature
if ( GetFactionEqual(oFriend) && ( GetObjectType(oFriend) == OBJECT_TYPE_CREATURE))
{
/ / et qu’ils peuvent voi le PC
if ( GetObjectSeen ( GetPCSpeaker(), oFriend))
{
/ / leur demande de commancer le combat
AssignCommand ( oFriend, DetermineCombatRound());
}
Suite du script page suivante…
…Suite du script de la page précédente
/ / si il ne peut pas le voir
else
{
/ / lui demande d’arrêter ce qu’il fait
AssignCommand ( oFriend, ClearAllActions());
/ / and come to my location
AssignCommand ( oFriend, ActionMoveToLocation(oHere, TRUE));
}
}
oFriend = GetNextObjectInArea ( GetArea(OBJECT_SELF));
}
/ / maintenant, je démarre le combat moi même
DetermineCombatRound();
}
Il y a aussi des variation à faire…ce ne sont que des exemples. Une autre manière de faire commencer un combat à un groupe est de vérifier que tous ont la ligne " SetSpawnInCondition ( NW_FLAG_SHOUT_ ATTACK_MY_TARGET);" activée dans leur script OnSpawn. Puis, quand vous commencez à attaquer, vous pouvez juste fare cela:
void main()
{
/ / Baisse ma valeur de réaction avec le PC de 100 ( jusqu’à hostile)
AdjustReputation ( GetPCSpeaker(), OBJECT_SELF, -100);
/ / démarre le combat ( ça nécessite le fichier générique nw_iO_generic, ci dessus)
DetermineCombatRound();
/ / et cri à tous mes alliés dans le voisinage de se joindre à moi
SpeakString ( "NW_ATTACK_MY_TARGET", TALKVOLUME_SILENT_TALK);
}
Vous ne verrez pas la phrase, mais tous les alliés avec une portée auditive qui peuvent voir la cible coureront à l’attaque.
Comment je fais revenir mes NPC à une attitude neutre?
Si vous voulez rendre un NPC non hostile une fois qu’il est en combat avec les PC ( si le PC est mort ou pour d’autres raisons), vous devez être sur que deux choses se sont passées : un, que la faction ait une attitude non hostile face au joueur ( soit neutre soit amicale). Deux, si la faction n’a pas " Global Effect" cochée, vous devez changer aussi tous les sentiments personnels des NPC face au PC.
Les factions " standard" dans le jeu ( Commoner, Defender, etc) sont les seules pour lequels vous pouvez faire les deux dans une seule commande. Cela est fait à travers la commande SetStandardFaction, et un exemple de celle ci est dans le script par défault de mort ( "nw_o0_death"), qui rement toutes les factions standarts à la neutralité à la mort du joueur.
Note: de nombreuses personnes ont rapportées que la faction defender n’est pas remise à " zéro" avec cette commande, mais vous pouvez le faire en la traitant comme une faction personnalisée. Cf. ci dessous
Pour les factions personnalisées ( Custom factions), vous aveez besoin de deux commandes : AdjustReputation pour changer les relation de la faction avec la cible et ClearPersonnalReputation pour changer tous les sentiments du NPC. Dans les deux cas, vous devez isoler la cible qui esi un membre d’un faction ( ce qui peut être difficile si vous avez beaucoup de membres avec des tags différents ou qui peuvent être mort)…les factions personnalisées ne sont pas identifiées par un nom, vous pouvez seulement dire " la faction a qui appartient un tel et un tel". et si vous voulez effacer les réputations personnelles du PC avec une faction personnalisée entière, vous devez créer un cycle.
Voici un exemple de script qui ajustera la faction à neutre et fera un cycle à l’intérieur de celle ci pour restaurer pour tous les membres une réaction normale avec le PC. Cette faction n’a pas " Global Effect" cochée, et a des membres avec le même tag " GOBLIN01". Le PC est mort et ce script est dans son OnDeath event:
/ / Je crée ici une commande " ClearAll FactionMembers’" personnalisée
/ / premièrement déclarer les paramêtres de la nouvelle commande, puis ce qu’elle sera
/ / j’aurais pu raisonnablement les membres dans un autre script et utiliser
/ / #include pour le mettre dans ce script ( ou dans un autre)
void ClearAllFactionMembers ( object oMember, object oPlayer)
{
object oClear = GetFirstFactionMember ( oMember, FALSE);
while ( GetIsObjectValid(oClear) == TRUE)
{
ClearPersonalReputation ( oPlayer, oClear);
oClear = GetNextFactionMember ( oMember, FALSE);
}
}
/ / Voici le corp principal de mon script
void main()
{
/ / identifie le joueur
object oPlayer = GetLastPlayerDied();
/ / identifie une membre de la faction, je m’assure que ces membres sont vivants.
/ / Ceêndant, je pourrais essyer plusieurs choses pour modifier un membre qui est
/ / valid puis d’utiliser la commande if
object oGoblin = GetObjectByTag("GOBLIN1");
if ( GetIsObjectValid(oGoblin))
{
/ / augmente la relation de la faction de 100
AdjustReputation ( oPlayer, oGoblin, 100);
/ / utilise ma commande personnalisée dans la faction
ClearAllFactionMembers ( oGoblin, oPlayer);
}
}
Et pour finir:
Ce qu’il y a de bien avec les cris
Quelques trucs à essayer ou à utiliser en rapport avec les cris.
Le premier: J’ai toujours détesté le fait que un NPC qui entend le cri " NW_ATTACK_ MY_TARGET" mais ne voit pas le crieur ne fait rien. Il devient hostile mais n’attaque pas tant que l’ennemi ne vient pas dans sa ligne de vue. Il yt a un moyen que j’ai utilisé pour qu’il vienne vers l’ondroit d’où provient le cri
.
Etape 1 – La première chose qui doit être faite est qu tous les membres de la factions qui repondront au cri doivent avoir leur script OnSpawn paramêtré de la même manière. Allez dans leur script OnSpawn et activez les lignes suivantes ( enlevez les " //"):
SetSpawnInCondition ( NW_FLAG_SHOUT_ ATTACK_MY_TARGET);
SetSpawnInCondition ( NW_FLAG_ ON_DIALOGUE_EVENT);
Puis re sauvez le script sous un nom différent.
Etape 2 – Souvenez vous qu’une créature qu’une créature ne criera " attack myt target" uniquement si elle est attaquée en première ou si elle est tuée. Si vous voulez qu’elle crie, elle même, vous devez mettre ceci dans un script. Il y en a un fournit dans l’exemple ci dessous dans lequel la créature devient hostile et pousse le cri à la fin du dialogue.
Etape 3 – Mettez le script suivant dans le OnUserDefined event de chaque créature:
/ / Ce script fait que le récepteur d’un cri"NW_ATTACK_MY_TARGET" vienne
/ / et regarde si il ne voit pas un ennemi. Une fois qu’il s’est approché du crieur,
/ / si il voit un ennemi, son script OnPerception sera activé et il attaquera.
/ / Une modification interessante de ce script serait d’avoir que les récepteur du cri
/ / situé hors de vue pousse un deuxième cri qui porte plus loin que le cri original…
/ / cela nécessiterait cependant un nouveau cri et une nouvelle définition du
/ / comportement
void main()
{
int nEvent = GetUserDefinedEventNumber();
if ( nEvent == 1004) / / OnDialog event
{
/ / Cela regarde si le cri correspont au comportement fixé par le
/ / ´SetListeningPatterns´ dans OnSpawn
int nMatch = GetListenPatternNumber();
object oShouter = GetLastSpeaker();
object oIntruder;
/ / si je reconnais ce cri et que le crieur est valide et amical avec le NPC
if(nMatch ! = -1 && GetIsObjectValid ( oShouter) && ! GetIsPC(oShouter) && GetIsFriend ( oShouter))
{
/ / et que le cri est " NW_ATTACK_MY_TARGET"
Suite du script page suivante…
if ( nMatch == 5)
{
/ / essaie de trouver l’ennemi
oIntruder = GetLastHostileActor ( oShouter);
if(!GetIsObjectValid ( oIntruder))
{
oIntruder = GetAttemptedAttackTarget();
if(!GetIsObjectValid ( oIntruder))
{
oIntruder = GetAttemptedSpellTarget();
if(!GetIsObjectValid ( oIntruder))
{
oIntruder = OBJECT_INVALID;
}
}
}
/ / si je ne peux trouver ni le crieur ni son ennemi
if ( GetIsObjectValid ( oShouter) && ! GetObjectSeen ( oIntruder) && ! GetObjectSeen ( oShouter))
{
/ / défini l’emeplacement du crieur
location lShouter = GetLocation(oShouter);
/ / arrête ce que je suis en train de faire
ClearAllActions();
/ / et bouge jusqu’à cette emplacement
ActionMoveToLocation ( lShouter, TRUE);
}
/ / ou si je peux voir le crieur mais pas l’ennemi
else if ( GetIsObjectValid ( oShouter) && ! GetObjectSeen ( oIntruder) && GetObjectSeen ( oShouter))
{
/ / arrête ce que je fais
ClearAllActions();
/ / et bouge plus près du crieur
ActionMoveToObject ( oShouter, TRUE);
}
}
}
}
}
La seconde: Voici un exercice iteressant qui démontre comment faire votre propre cri. Dans cet exemple, je veux avoir un dialogue qui lance un cri et entraine que tous ceux qui l’entendent joue l’animation " worship" ( ils pensent que le PC est un devin.)
Etape 1 – Nous allons faire le dialogue à la fin duquel le NPC poussera le cri. Faisons le très simple…une ligne seulement " C’e’st un devin ! " et puis le script suivant est attaché à la zone " Action Taken" de cette ligne:
void main()
{
/ / entre une variable sur moi même éguale au PC qui est en train de me parler
SetLocalObject ( OBJECT_SELF, " worship", GetPCSpeaker());
/ / lance le cri " BOWDOWN"
SpeakString ( "BOWDOWN", TALKVOLUME_SILENT_TALK);
/ / faite l’animation worship pendant 30 secondes
ActionPlayAnimation ( ANIMATION_LOOPING_WORSHIP, 0.5, 30.0);
}
Etape 2 – Maintenant, nous devons paramétrer les autre NPC pour qu’ils entendent le cri. Pour cela, nous devons activer la ligne " SetSpawnInCondition ( NW_FLAG_ ON_DIALOGUE_EVENT);" dans leur scriptOnSpawn et ajouter la ligne suivant n’importe où :
SetListenPattern ( OBJECT_SELF, " BOWDOWN", 100);
Cela rend la chaine de caractère " BOWDOWN" égale à 100 ( pris au hasard) et reconnaissable par les NPC qui doivent l’entendre ( en utilisant la commande SetListening ( OBJECT_SELF, TRUE)... qui est lancée par la commande du OnSpawn SetListeningPatterns).
Etape 3 – Maintenat, nous devons juste demander au NPC que faire quand ils entendent cette commande. Toute chaine de caractères dite fait partie de leur OnDialogue event... donc le script suivant peut être placé dans le OnUserDefined event:
void main()
{
int nEvent = GetUserDefinedEventNumber();
/ / si je recois OnDialogue event
if ( nEvent == 1004)
{
/ / regarde ce que j’ai entendu
int nMatch = GetListenPatternNumber();
/ / identifie le locuteur
object oShouter = GetLastSpeaker();
/ / et attrappe la variable du NPC qui s’incline
object oWorship = GetLocalObject(oShouter, " worship");
/ / Si la chaine de caractèreest une que je reconnais et que l’émetteur est un NPC valid et amical
if(nMatch ! = -1 && GetIsObjectValid ( oShouter) && ! GetIsPC ( oShouter) && GetIsFriend ( oShouter))
{
/ / et que la chaine de caractère " BOWDOWN" est égale à 100
if(nMatch == 100)
{
/ / se tourner face au PC avec lequel le NPC parle
ActionDoCommand ( SetFacingPoint ( GetPosition(oWorship)));
/ / puis joue l’animation worship pendant 30 secondes
ActionPlayAnimation ( ANIMATION_LOOPING_WORSHIP, 0.5, 30.0);
}
}
}
}
Ce n’est évidemmement pas tout ce qu’il faut savoir à propos des factions et des cris, mais ça devrais, je l’espère être utile à un grand nombre de personnes.
Théranthil, Mage Elfe prêtre de Mystra Membre des GdE
Parti Elmotiste pour un parler jowilien: Ministre supérieur de la corruption
" Les langues vont toujours bon train. Les pieds ont du mal à suivre."
( les bourses aussi NdT )
- Mespert de la porte de Baldur
Bon ben voila j´ai tout mis, j´espere que cela vous sera utile,ce n´est pas toujours chose aisée d´utiliser l´aurora toolset, il y a encore bien d´autres choses à apprendre mais vous avez déja de quoi faire ![]()
arg!
jeu veuuuu
si tu etait saint pas tu pouraai remettre tout ca par ecris sur doc et le poster a . .. dison gobelalune@yahoo.fr . .. par exemple! ![]()
Merci beaucoup pour tout ça, je vais l´étudier, mais j´en ai déjà mal à la tête
)
Voila une adresse bien utile de lien vers des tutoriaux de aurora toolset:
http://nwn.chez.tiscali.fr/tutoriaux.htm