Avant-propos
Lors de mon premier projet de jeu (toujours à l’état d’ébauche) j’ai très vite buté sur un problème. J’étais dans un tramway, il y avait donc :
- le tramway
- le conducteur du tramway
- les vitres (du tramway)…
Je voulais que « regarder le tramway » porte sans ambiguïté sur l’objet tramway, pas qu’on me demande « le tramway ou le conducteur du tramway ? », mais que je puisse aussi désigner le « conducteur du tramway ». Je ne pouvais pas donc donner au conducteur comme propriété name : « conducteur » « du » « tramway » (« regarder du » était équivalent à « regarder conducteur », en plus). J’ai ainsi vite voulu que certains mots permettant de désigner un objet soient optionnels et de faible priorité.
Après avoir essayé l’extension pname.h qui ne répondait que partiellement au problème, j’ai fini par m’en inspirer pour ajouter aux objets qui le nécessitent une propriété « adjective ». Les mots définis dans cette propriété sont acceptés pour désigner un objet, à condition qu’au moins un mot de la propriété « name » soit présent également.
Exemple raccourci de code (initialement) :
Object conducteur_tramway1 "conducteur" tramway1
with name 'conducteur' 'chauffeur' 'homme' 'bonhomme' 'mec' 'type' 'gars' 'traminot' 'etre',
adjective 'de' 'du' 'tramway' 'tram',
description "Malgré son sourire bienveillant, vous voyez bien que le conducteur commence à s'impatienter.",
[...]
Puis lors du projet lieux communs, où cette gestion des adjectifs a été (un peu) utilisée, j’ai trouvé que les petits mots « de », « des », « du », « à », « aux », « en », etc… gagneraient à être tout simplement ignorés quand ils sont entre deux mots désignant le même objet.
La création d’une fonction ParseNoun à cet effet simplifie la déclaration de l’objet puisqu’il n’est plus nécessaire de se préoccuper de ces petits mots de liaison (attention, ils ne sont acceptés que s’ils sont entre deux mots reconnus pour le même objet, sinon ils sont traités comme d’habitude) :
Object conducteur_tramway1 "conducteur" tramway1
with name 'conducteur' 'chauffeur' 'homme' 'bonhomme' 'mec' 'type' 'gars' 'traminot' 'etre',
adjective 'tramway' 'tram',
description "Malgré son sourire bienveillant, vous voyez bien que le conducteur commence à s'impatienter.",
[...]
L’extension « adjectifs.h » (nom temporaire)
Pour pouvoir utiliser la gestion des adjectifs (et compléments du nom), il faut :
- Ajouter la ligne suivante juste avant l’inclusion de Parser :
Replace Identical; ! pour la gestion des adjectifs
Cette ligne dit juste qu’on va re-définir la routine Identical, qui est normalement définie dans les libs inform de base.
- Ajouter le code suivant juste après l’inclusion de Parser
!--------------------------------------------
! Attributs et propriétés
!--------------------------------------------
Property adjective; ! pour la gestion des adjectifs
!--------------------------------------------
! Directives de remplacement
!--------------------------------------------
! (Samuel Verschelde, tiré de pname.h)
! Cette fonction est recopiée de parserm.h, avec un ajout pour tenir
! compte de la propriété "adjective."
[ Identical o1 o2 p1 p2 n1 n2 i j flag;
if (o1 == o2) rtrue; ! This should never happen, but to be on the safe side
if (o1 == 0 || o2 == 0) rfalse; ! Similarly
if (parent(o1) == compass || parent(o2) == compass) rfalse; ! Saves time
! What complicates things is that o1 or o2 might have a parsing routine,
! so the parser can't know from here whether they are or aren't the same.
! If they have different parsing routines, we simply assume they're
! different. If they have the same routine (which they probably got from
! a class definition) then the decision process is as follows:
!
! the routine is called (with self being o1, not that it matters)
! with noun and second being set to o1 and o2, and action being set
! to the fake action TheSame. If it returns -1, they are found
! identical; if -2, different; and if >=0, then the usual method
! is used instead.
if (o1.parse_name ~= 0 || o2.parse_name ~= 0) {
if (o1.parse_name ~= o2.parse_name) rfalse;
parser_action = ##TheSame; parser_one = o1; parser_two = o2;
j = wn; i = RunRoutines(o1,parse_name); wn = j;
if (i == -1) rtrue;
if (i == -2) rfalse;
}
! This is the default algorithm: do they have the same words in their
! "name" (i.e. property no. 1) properties. (Note that the following allows
! for repeated words and words in different orders.)
p1 = o1.&1; n1 = (o1.#1)/WORDSIZE;
p2 = o2.&1; n2 = (o2.#1)/WORDSIZE;
! for (i=0 : i<n1 : i++) { print (address) p1-->i, " "; } new_line;
! for (i=0 : i<n2 : i++) { print (address) p2-->i, " "; } new_line;
for (i=0 : i<n1 : i++) {
flag = 0;
for (j=0 : j<n2 : j++)
if (p1-->i == p2-->j) flag = 1;
if (flag == 0) rfalse;
}
for (j=0 : j<n2 : j++) {
flag = 0;
for (i=0 : i<n1 : i++)
if (p1-->i == p2-->j) flag = 1;
if (flag == 0) rfalse;
}
! DEBUT MODIFICATION POUR LES ADJECTIFS
! test sur la propriété adjective :
! deux objets qui ont le même "name" mais
! pas le même "adjective"
if ((o1 provides adjective) ~= (o2 provides adjective))
rfalse;
if (o1 provides adjective && o2 provides adjective)
{
n1=(o1.#adjective)/WORDSIZE; n2=(o2.#adjective)/WORDSIZE;
! If there are a different number of words in the adjective
! properties, they are distinguishable.
if (n1 ~= n2)
rfalse;
p1=o1.&adjective; p2=o2.&adjective;
for (i=0 : i<n1 : i++)
{
flag = 0;
for (j=0 : j<n2 : j++)
if (p1-->i == p2-->j) flag = 1;
if (flag == 0) rfalse;
}
for (j=0 : j<n2 : j++)
{
flag = 0;
for (i=0 : i<n1 : i++)
if (p1-->i == p2-->j) flag = 1;
if (flag == 0) rfalse;
}
}
! FIN MODIFICATION POUR LES ADJECTIFS
! print "Which are identical!^";
rtrue;
];
Il s’agit de la définition de la propriété « adjective » que l’on pourra ensuite définir pour chaque objet qui le nécessite, et du code de la routine Identical, modifiée pour gestion des adjectifs. Pour information cette routine sert juste à dire si deux objets sont identiques, en comparant en plus de leur propriété « name », la propriété « adjective » si elle est définie.
- Inclure le code suivant après l’inclusion de VerbLib
! (Samuel Verschelde, tiré et amélioré de pname.h) gestion des adjectifs
! et gestion aussi des mots de liaison comme dans "boule DE cristal"
! => de est accepté, ainsi que d'autres mots que l'on peut trouver
! entre deux mots désignant un même objet.
[ ParseNoun obj n m p w dict_flags_of_noun;
dict_flags_of_noun = 0;
w = NextWord();
while((WordInProperty(w,obj,name) == 1) || (WordInProperty(w,obj,adjective) == 1) || (w=='de' or 'du' or 'des' or 'd^' or 'la' or 'le' or 'l^' or 'en' or 'a//' or 'à//' or 'au' or 'aux'))
{
! remarque : si un mot est à la fois dans name et adjective, il ne sera
! compté qu'une fois, pour la propriété name
if (WordInProperty(w,obj,name) == 1)
{
dict_flags_of_noun = ((w->#dict_par1) & $$01110100);
if (dict_flags_of_noun & 4) ! si c'est un pluriel (//p)
{
parser_action = ##PluralFound; ! notifier qu'un pluriel pouvant désigner
! plusieurs objets a été trouvé
}
m++;
p=0;
}
else
{
if (WordInProperty(w,obj,adjective) == 1)
{
n++;
p=0;
}
else
{
! Si le mot est 'de', 'du', 'des', 'la', 'en', 'a', 'au' ,'aux' :
if (w=='de' or 'du' or 'des' or 'd^' or 'la' or 'le' or 'l^' or 'en' or 'a//' or 'à//' or 'au' or 'aux')
{
! ajouter 1 à la variable p, qui contient le score à retirer
! au final (afin de revenir en arrière en cas d'occurrences qui ne
! sont pas comprises entre deux noms reconnus).
n++; ! compter ce mot comme pertinent est discutable,
! mais sans cela cela ne fonctionne pas
p++;
}
}
}
w = NextWord();
}
wn = wn -p -1; ! retour arrière car le dernier mot lu n'a pas été reconnu
if (m == 0) return 0; return (m+n-p);
];
Il s’agit de la routine ParseNoun, tirée de pname.h puis modifiée pour gérer les adjectifs et accepter les petits mots de liaison au milieu d’une expression.
Voilà, vous avez tout ce qu’il faut pour supprimer une sérieuse limitation d’inform 6!
On peut maintenant : définir une « porte en bois » et une « table en bois » à côté d’un « bois de hêtres » sans la moindre difficulté, une « cuillère à pot » à côté d’un « pot de crème » (dans ce dernier cas crème sera mis dans name plutôt que adjective pour permettre de désigner le pot par le mot crème seul).
Dans le cas de la place centrale dans le jeu Ma princesse adorée, cela permet de définir facilement :
- la taverne (name : « taverne »)
- la porte de la taverne (name : « porte », adjective : « taverne »)
- l’école (name : « école »)
- la porte de l’école (name : « porte », adjective : « école »)
- la boutique (name : « boutique »)
- la porte de la boutique (name : « porte », adjective : « boutique »)
Chacun pourra être défini sans ambiguïté, et si un joueur tape « ouvrir la porte », si chaque porte a un nom du type « porte de la taverne », le parser demandera : « Précisez : la porte de la taverne, la porte de l’école ou la porte de la boutique ». Le joueur répondra par exemple « de la taverne » et hop…