explication: Les Patterns

Répondre
Partager Rechercher
Pattern, veut dire motif en anglais. Leur utilisation est liée à la capacité de crier/parler/murmurer des joueurs et PNJ, il s'agit de motifs de phrase (des expressions régulières super light) que l’on peut mettre en place sur un PNJ. A chaque fois qu'il entendra une phrase correspondant à ce motif, son OnConversation sera lancé, et l’on pourra récupérer l’identifiant du motif entendu (fixé par le script).

Les deux fonctions suivantes seront généralement appelées dans l’OnSpawn du PNJ sensé écouter, quoiqu’elles puissent être utilisées partout ailleurs:

1)
SetListening( oObject, TRUE): pour activer l'écoute par le PNJ (ne fait rien par soi-même: il faut ensuite définir ce qu'il doit écouter)

SetListening(oObject, FALSE) annulera l’appel ci-dessus.

2)
SetListenPattern(oPNJ, sPattern , nIdentifiantDuMotif ): pour que le PNJ recherche un motif particulier(sPattern). Il faut appeler cette fonction pour chaque motif que l'on veut établir.

Les possibilités (symboles servant de briques élémentaires) pour construire la chaîne de caractère qui sert de pattern sont les suivantes :



De Noël (Bioware):

**
s’unifiera avec zéro, un, ou plusieurs caractères (wildcard).

*w
un ou plusieurs espaces

*n
un ou plusieurs chiffres

*p
un ou plusieurs signes de ponctuations.
Note : d’après mes tests il faut entendre par là les points d'intérogation,d'exclamation, et le point final, la virgule ou le point virgule ou l’apostrophe par exemple ne sont pas reconnus. Cela peut donc servir de marqueur de fin de phrase.


*a
une ou plusieurs lettres alphabétiques.

|
est ou

( et )
permettent de construire des bloques.


Exemples :
"**" :
toute phrase prononcée sera reconnue. La chaîne vide ,"", est un cas particulier que bioware s’est réservé.

"a*a"
reconnaîtra tout mot commençant par a, à l’exception de "a" lui-même. Il doit en effet y avoir au moins deux lettres : le a initial et une autre pour *a, qui ne peut pas s’unifier avec la chaîne vide. s’il y a un chiffre ou un espace l’expression ne sera pas reconnue.

"pattern 24"
reconnaîtra "pattern 24" et c’est tout. Attention aux espaces ils comptent! "pattern 24 " ne sera pas reconnu.

"bien(tôt|venue)"
reconnaîtra "bientôt" et "bienvenue"

"**(bonjour|pas)***n"
reconnaîtra entre autre : "bonjour, diablo21", "non! pas diablo 2" mais pas "non! pas diablo" (il faut au moins un chiffre à la fin du fait de " *n ").




3)
GetListenPatternNumber() Cette fonction (ainsi que les deux autre qui suivent) s'utilise dans le OnConversation:

C’est a mon avis le seul slot fiable pour cela, en effet c’est le seul qui garantisse de tout capturer. Sur le OnHeartbeat par exemple, ce que dirait le joueur pourrait être perdu dans le bruit de fond (les phrases prononcées par les PNJs).

Cet événement est aussi lancé en cas de clic sur le PNJ par un joueur (si le curseur est l'icone de conversation). Dans ce cas, GetListenPatternNumber() renvoie -1. Ceci est, à mon avis, lié au cas particulier de "".

GetListenPatternNumber(): Renvoie le numéro du pattern (fixé par l'appel correspondant de SetListenPattern) auquel la phrase entendue s'est unifiée.

Exemple :

Dans le OnSpawn :
SetListenPattern(OBJECT SELF,"bonjour monde",11);

Dans le OnConversation :
int nMatch = GetListenPatternNumber();

Si "bonjour monde" est prononcé à côté du PNJ concerné son OnConversation sera lancé. Et dans le script nMatch prendra la valeur 11.


Une remarque, il est tout à fait possible qu'une phrase puisse s'unifier avec plusieurs patterns, dans ce cas, c'est la pattern qui a été crée en premier qui sera prise en compte, les autres seront simplement ignorées et le OnConversation ne sera lancé qu'une seule fois.

ex:
SetListenPattern(OBJECT SELF,"foo",10);
SetListenPattern(OBJECT SELF,"foo**",11);

Le PNJ entend "foo".
Le OnConversation est lancé une seule fois et GetListenPatternNumber() renverra 10.



Un peu plus compliqué: quand des wildcards (**), ou des *a,*n,*p, ou *w, sont présents dans un pattern, la chaîne de caractère que le PNJ reconnaîtra va être "découpée en morceaux":

4)GetMatchedSubstringCount() renvoie le nombre de morceaux.
5)GetMatchedSubstring(i) renvoie les morceaux en questions (indicés de 0 à GetMatchedSubstringCount() - 1)

un exemple d’application, cette fonction renvoie l’intégralité de la chaîne entendue, quel que soit le pattern utilisé pour la capturer :

Code:
string GetStringHeard()
{
    int i;
    string sString ="";
    for(i=0; i < GetMatchedSubstringsCount();i++)
    {
        sString += GetMatchedSubstring(i);

    }
    return sString;
}
pattern: "**foo**bar**"
phrase entendue: "le meilleur poste au foot c'est à côté du bar"


Passons sur la phrase en question (je sais pas pourquoi, foo et bar ça réveille mon côté beauf) donc le "but du jeu" c'est de faire correspondre le motif et la phrase entendue, ici, étant donné qu'il y a des ** partout les seules contraintes sont de trouver un "foo" et un "bar" (dans cet ordre de gauche à droite) dans la phrase. On voit que notre phrase est découpée en morceaux:

Code:
pattern                           **            foo        **              bar    **
chaîne de caractère     le meilleur poste au    foo   t c'est à côté du    bar

GetMatchedSubstringsCount() renverrait donc... 5, le fait que la dernière soit "" ne change rien .

GetMatchedSubstring(0) == "le meilleur poste au "
GetMatchedSubstring(1) == "foo"
GetMatchedSubstring(2) == "t c'est à côté du "
GetMatchedSubstring(3) == "bar"
GetMatchedSubstring(4) == ""

L'exemple que j'ai choisi est un peu particulier car l'unification se fait de façon unique, ce n'est généralement pas le cas, hors BIOWARE n'a pas donné, autant que je sache, l'algo d'unification.

D’après mes tests l’unification se fait en maximisant les " morceaux " les plus à droite.
Attention, tout en maximisant, il faut quand même qu’il y ai unification, les mots ou bouts de mots constituent donc des barrières infranchissables.

Pour prendre l’exemple du lexicon un peu modifié pour les besoins de la cause:

SetListenPattern(OBJECT SELF, "**foo**bar****", 500);

Supposons que le PNJ entende "foofoofoobarbarbar":

GetMatchedSubstring(0) renvoie "".
GetMatchedSubstring(1) renvoie "foo".
GetMatchedSubstring(2) renvoie "foofoo".
GetMatchedSubstring(3) renvoie "bar".
GetMatchedSubString(4) renvoie "".
GetMatchedSubstring(5) renvoie "barbar".

Avec:
SetListenPattern(OBJECT SELF, "*afoo*abar*a*a", 500);
On obtient:
GetMatchedSubstring(0) renvoie "foo".
GetMatchedSubstring(1) renvoie "foo".
GetMatchedSubstring(2) renvoie "foo".
GetMatchedSubstring(3) renvoie "bar".
GetMatchedSubString(4) renvoie "b".
GetMatchedSubstring(5) renvoie "arbar".

Tout le "foo" est passé sur le 0 car il n’y a aucun autre moyen d’avoir un foo complet (ce qui est exigé) sur le 1.

5)
TestStringAgainstPattern(sPattern, sChaineATester)

Permet comme son nom l’indique de tester une chaîne de caractères sur un Pattern, renvoyant TRUE si ils peuvent être unifiés, FALSE sinon.

EDIT: bordel j'ai modifié mon message, et au coup d'après j'ai remis la vielle version, je ne vais pas y arriver comme ça
re: la mise en page j'abandonne a
Merci eMRaistlin , j'aurais un ou deux trucs à ajouter (la priorité de "|"...), mais je suis tombé sur un truc étrange, et je n'ai pas le temps (ni le courage) de chercher plus loin ce soir... Donc on verra un peu plus tard (demain quoi ).
J'ai une question a te poser, vu que j'y arrive pas trop, la :

je fais ca dans une de mes conversation:

Code PHP:

if(nMatch == 8000)
    {
    
SetListenPattern(oChatter,"test2",8001);
    }
if(
nMatch == 8001)
    {
    
SetListenPattern(oChatter,"test3",8002);
    }

if(
nMatch == 8002)
    {
        
ApplyEffectToObject(DURATION_TYPE_PORTNAWAK,EffectQuiVaBien(),oCible,fDuree);
        
SetListenPattern(oChatter,"test3",7999);
    } 
Mon but : seul l'enchainement des trois phrases va declencher l'effet qui va bien.

Et ca marche : c'est la fête. Mais j'ai un gros probleme :

Le SetListenPattern qui s'effectue dans le cas ou l'on prononce la derniere phrase ("test3") est sensé initialiser la pattern correspondante a 7999, et donc, repeter "test3" une deuxieme fois devrait, selon moi, declencher la pattern 7999 et non la 8002, vu que je la redefinie si ca marche.

Je suis assez perplexe. Il m'applique bien l'effet, mais pas moyen de reinitialiser les patternes une fois qu'elles sont active.

Tu as une idée ?



[EDIT]

Pour info, et pour ceux que ca interessent, voici mon correctif, en attendant une reponse de procto:

Code PHP:

if(nMatch == 8000)
    {
    
SetListenPattern(oChatter,"test2",8001);
    
SetLocalInt(oChatter,"EFX",1);
    }

if(
nMatch == 8001)
    {
        if (
GetLocalInt(oChatter,"EFX") == 1)
            {
            
SetListenPattern(oChatter,"test3",8002);
            
SetLocalInt(oChatter,"EFX",2);
            }
    }

if(
nMatch == 8002)
    {
        if (
GetLocalInt(oChatter,"EFX") == 2)
            {
        
ApplyEffectToObject(DURATION_TYPE_PORTNAWAK,EffectQuiVaBien(),oCible,fDuree);
            
SetLocalInt(oChatter,"EFX",0);
            }
    } 
Je ne sais pas trop quoi te dire... SetListening(oObjet,FALSE) ne réinitialiserait pas les patterns- j'en doute très fortement? Dans ton dernier cas l'OnConversation part 1 fois ou 2 (pour chacune des deux patterns)? 2 fois si je me souviens bien, mais bon ça te fait une belle jambe...
La possibilité de désactiver les patterns individuellement serait un truc à demander à Bioware à mon avis, à vu de nez cela semble très faisable.Mais bon ça ne règle pas ton problème. Je crois que la solution des LocalInt est effectivement la seule.
Citation :
...mais je suis tombé sur un truc étrange, et je n'ai pas le temps (ni le courage) de chercher plus loin ce soir... Donc on verra un peu plus tard (demain quoi ).
Le truc étrange était en fait un gros bug, suite à un pétage de plomb indépendant de ma volonté, je ne suis effectivement pas allé plus loin. Je viens donc de reporter le bug chez Bio. J'aurais du mal à vous le décrire étant donné que je ne le comprend pas vraiment. Il s'agit d'un problème avec le | (ou) quand GetSubstringCount peut prendre plusieurs valeurs pour une même pattern. Les données brutes:

Code:
pattern "*n|foo": ok

              "foo|*n": ok
              
              "*n*a|foo":  ok

              "foo|*n*a": "foo" n'est pas reconnu

              "foo|*a*p": "foo" n'est pas reconnu

              "foo|*n**": ok
//////////////////////////////////////////////////////////
Les patterns qui suivent devraient être équivalentes:    

"(((*n*a*n*a|*n*a*n)|*n*a)|*n)|foo" : ok
  
"(((*n*a*n*a|*n*a*n)|foo)|*n*a)|*n" : ok
     
"(((*n*a*n*a|*n)|*n*a*n)|*n*a)|foo" "1" n'est pas reconnu, le "*n"  de la pattern est ignoré. 
    
"(((*n*a*n|*n*a*n*a)|*n*a)|*n)|foo" :"1a1" n'est pas reconnu, le "*n*a*n" de la pattern est ignoré.
    
"(*n*a*n*a|*n*a)|(foo|(*n*a*n|*n))": "1a" n'est pas reconnu, "*n*a" est ignoré. 
   
"(*n*a*n*a|*n*a)|((foo|*n*a*n)|*n)": "1a" n'est pas reconnu, "*n*a" est ignoré. 
    
"((*n*a*n*a|*n*a)|foo)|(*n*a*n|*n)": "1a" n'est pas reconnu, "*n*a" est ignoré. 

"((*n*a*n*a|foo)|*n*a)|(*n*a*n|*n)"  "1a" n'est pas reconnu, "*n*a" est ignoré.
Sur les deux dernières j'ai permuté le "foo" et le "*n*a" dans la pattern et j'obtiens le même bug...
Rug, il me semblait bien que j'obtenais pas toujours les résultats prévus....
Un de ces jours je vais me coller à la création d'une librairie pour la gestion des expressions régulières....ça va juste être chaud mais ça devrait être faisable (et un peu plus souple que cette fonction de m****), et en plus ça devrait permettre de récupérer les morceaux (parce que c'est quand même la principale utilité des regexps !! ).
A propos est-ce que quelqu'un a déjà tenté de voir si GetMatchedSubstring() marchait après un TestStringAgainstPattern() (après tout si c'est le même moteur...).
Citation :
Provient du message de Jedaï
Rug, il me semblait bien que j'obtenais pas toujours les résultats prévus....
Un de ces jours je vais me coller à la création d'une librairie pour la gestion des expressions régulières....ça va juste être chaud mais ça devrait être faisable (et un peu plus souple que cette fonction de m****), et en plus ça devrait permettre de récupérer les morceaux (parce que c'est quand même la principale utilité des regexps !! ).
A propos est-ce que quelqu'un a déjà tenté de voir si GetMatchedSubstring() marchait après un TestStringAgainstPattern() (après tout si c'est le même moteur...).
Une noble cause les expressions régulières.... il faudrait voir si ça ne serait pas un peu lourd (l'interface pour les chaînes de caractères, et l'absence de type char dans nwscript). Mais pour ce qui est des SetListen... GetListen... je crois qu'il faut attendre une amélioration de la part de Bio, c'est trop intimement lié à l'événement OnConversation. On pourrait bien sûr capturer toutes les phrases par un SetListenPattern(...,"**" ,...), et les tester sur les expressions régulières, mais, à mon avis c'est ralentissement assuré.

A mon avis, et sans avoir testé, le GetMatchedSubstring ne marchera pas, je pense que les valeurs retournées sont passées au script par l'OnConversation, en dehors de ce contexte donc, elles seraient n'importe quoi (comme avec un GetLastAttacker() par exemple)

PS: j'ai rajouté -en rouge- une remarque importante à mon message d'origine. Je ne crois pas qu'on l'avait mentionné (mais ça fait longtemps).
le plus simple, si tu veux éviter le bug que je mentionne plus haut (qui se produit dans certaine configuration de pattern quand les | provoquent des "StringCount" variables je crois) :
"amen|amen!|amen |amen !"
si là ça marche pas...évidemment étant donné le genre d'algo qu'il y a derrière ce n'est pas optimisé, si tu veux mon avis ce n'est pas très grave, et au moins c'est lisible.

remarque que ça ne devrait pas poser de problème non plus:
"ame(((n|n!)|n )|n !)"

c'est si tu rajoutes des *w que le problème devrait se poser. Dans ce cas il faut que tu mettes les alternatives de plus forts string count à gauche en ouvrant et fermant les parenthèse tel que je l'ai fait ci-dessus (canonique).

PS: le ou étant binaire il faut mettre les deux arguments a priori, d'où la racine "ame" au lieu de "amen".
Question à 0,2€...

Existe-t-il un moyen d'utiliser GetListenPatternNumber() hors d'un script onConversation ?
En fait, on peut mettre autre chose qu'un OBJECT_SELF dans SetListening et SetListenPattern, et même... autre chose qu'une créature. Ca m'arrangeait bien, je voulais qu'un placeable "écoute" les paroles d'un PJ. Sauf qu'un placeable n'a pas de onConversation, bien sûr. Est-ce donc irrémédiable que les placeables ne tiennent pas compte des paroles prononcées autour d'eux ? Une fois qu'on les a placé en SetListening TRUE et SetListenPattern, on ne peut pas récupérer le PatternNumber quelque part ? (dans le onHeartBeat, le onUserDefined, je sais pas..)

Il reste toujours la possibilité de planquer une créature invisible espionne, mais... beaark...
Vi on peut, mais c'est vraiment pas très propre, j'avais vu un objet invisible qui marchait comme ça, il utilisait le DelayCommand() dans son OnHeartbeat pour vérifier toutes les secondes (grâce à un UDE)... On créait et détruisait l'objet quand c'était nécessaire ou possible, de façon à économiser des ressources mais même comme ça... Enfin si y en a pas quarante dans le module en même temps, y a pas de blème mais bon...

Pourquoi ne pas plutôt utiliser le 'null human' vraiment 'null' que nous a concocté Skanzo ?
Répondre

Connectés sur ce fil

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