Fiction-interactive.fr

Tutoriels Inform 6

Liste des articles :

Écrire une fiction interactive avec Inform 6, partie 1 : démarrer – Hugo Labrande

Écrire une fiction interactive avec Inform 6, partie 2 : un jeu complet – Hugo Labrande

Manipulations de variables en Inform 6 – Hugo Labrande

Tutoriel Inform 6 : variables d’avancement – Hugo Labrande

Déclarer des nouveaux verbes en Inform 6 – Hugo Labrande

Élargir la grammaire de verbes déjà définis en Inform 6 – Hugo Labrande

Trizbort, plus qu’un simple outil de cartographes – Hugo Labrande

Faites votre propre invite de commandes dans Inform 6 – Hugo Labrande

PhraseNames, mon extension I6 pour varier les tournures – Hugo Labrande

Des trophées pour le joueur, en Inform 6 – Hugo Labrande

Cet ebook compile des tutoriels parus sur le site http://www.fiction-interactive.fr, centrés sur le logiciel Inform 6.

On parle uniquement de technique ici ; nous vous conseillons de l’accompagner de notre ebook sur la conception de jeux ! Et les articles sur Vorple, un outil qui étend les possibilités d’Inform 6, sont dans leur propre ebook.

Nous publions un article sur la fiction interactive, et plus largement les narrations non-linéaires, tous les vendredis de septembre à juin.

Si ces articles vous ont plu, n’hésitez pas à :

parler de http://www.fiction-interactive.fr autour de vous ;

nous rejoindre sur notre serveur Discord ;

faire un don à notre compte paypal (paypal@fiction-interactive.fr), qui servira à couvrir nos frais d’hébergement et à proposer des prix pour notre concours annuel.

Écrire une fiction interactive avec Inform 6, partie 1 : démarrer

Hugo Labrande

Cette semaine, c’est un petit tutoriel pour commencer à programmer en Inform 6 ! Nous allons commencer par mettre en place le code et quelques objets et, demain, dans une deuxième partie, nous mettrons en place des réactions particulières et quelque chose qui ressemblera plus à un jeu. Les tutos pour Inform 6 en français ne sont pas légion, mais j’espère pouvoir faire quelques articles sur différents aspects et fonctionnalités de ce langage — n’hésitez pas si vous avez des questions ou suggestions pour la suite !

Inform 6 est un langage créé au milieu des années 1990 ; sa syntaxe et son mode de fonctionnement sont très stables, mais son développement se poursuit, notamment pour la correction d’éventuels bugs. Il s’agit d’un langage orienté objet, tout comme d’autres langages comme C++ et Java ; on retrouve des mots-clés et des concepts très similaires à d’autres langages de programmation. Il sert de plus de base à Inform 7, qui traduit tout son code en Inform 6 avant compilation. Les bibliothèques et le compilateur I6 sont sous licence libre et open source, et nous travaillons à l’inclusion d’I6 en français dans Linux.

Tout d’abord, allez télécharger le kit pour débuter, que je vous ai préparé avec amour. Je vais ici partir du principe que vous savez comment jouer à un jeu Inform (et en particulier que vous savez ce qu’est un interpréteur) ; si non, rendez-vous sur cette page !

Comment utiliser ce kit ?

Vous trouverez dans ce kit un script pour compiler votre jeu, c’est-à-dire transformer votre code en un jeu jouable et distribuable. Il y a deux options : « publier » (votre jeu tout propre et optimisé, qui peut être distribué directement) et « tester » (votre jeu, avec un « mode développeur » encore activé, c’est-à-dire avec certaines commandes « cheat codes » qui peuvent être utiles pour le debug, mais qui rendront votre jeu trop facile !) ; je vous recommande d’utiliser « publier » quasiment tout le temps, sauf problème coriace ! Par la suite, nous diront « la compilation » pour l’étape qui consiste à lancer ce script pour obtenir votre jeu sous forme jouable.

Le code distribué dans le kit est celui d’un jeu très simple : une pièce totalement blanche (classique) avec un seul objet. Mieux vaut démarrer simple ! Ouvrez le code, et ouvrez le jeu dans une autre fenêtre – et expérimentez ! Vous arriverez sans doute à voir quel bout de code correspond à quelle partie du jeu et vice-versa. Et n’hésitez pas, changez quelques valeurs, quelques chaînes de caractère, et voyez ce que ça donne !

Des milliers de jeux ont été écrits en Inform 6, et vous trouverez sur Internet des dizaines de code sources de jeux, notamment sur l’IFArchive. En français, vous pouvez notamment télécharger les codes source de tous les jeux de FibreTigre, ainsi que les miens ; utilisez les étiquettes dans la section « jeux » pour trouver des jeux avec un code source I6 disponible. Lire le code, même en diagonale, après avoir joué au jeu pourrait vous donner des idées et vous faire progresser !

Nous allons partir de rien, avec pour objectif de reproduire le code donné dans le kit ; nous allons mettre en place les objets aujourd’hui, et les choses plus compliquées (comme le score et les conditions de victoire) demain.

Le code source de base pour un jeu Inform 6 ressemble à ceci :

Constant Story "Mon histoire";

Constant Headline "Mon sous-titre";

Include "Parser";

Include "Verblib";

Object piece;

[ Initialise ;

 location = piece;

 print "texte d'introduction";

 return true;

];

Include "FrenchG.h";

Copiez-collez ce code ! Chez moi, c’est dans un fichier nommé « blanc.inf », parce que c’est un jeu vide, blanc. À mesure que vous créez des jeux, vous pourrez customiser ce fichier pour y inclure des réglages que vous aimez avoir dans vos jeux.

Voyons tout ce que tout ça veut dire

Les deux premières lignes définissent deux constantes. La première est Story, le titre de votre jeu ou histoire ; la deuxième est Headline, le chapeau (ou sous-titre) du jeu. Lorsque le jeu va démarrer, il va afficher le titre du jeu en gras, puis le chapeau en-dessous, puis une ligne pour indiquer avec quelle version d’Inform le jeu a été créé. Actuellement, si nous jouons au jeu, nous verrons :

Mon histoireMon sous-titreRelease 1 / …

Le chapeau peut contenir, par exemple, un sous-titre pour votre jeu, votre nom, et éventuellement d’autres choses comme une indication au joueur. Si nous voulons afficher le chapeau sous le titre, il faut ajouter un retour à la ligne : en Inform 6, on signale ce retour à la ligne par un symbole ^. Nous allons ainsi écrire

Constant Story "Mon premier programme Inform 6";

Constant Headline "^Par un nouveau programmeur^";

Ce qui donne

Mon premier programme Inform 6

Par un nouveau programmeur

Release 1 / …

Remarquez que chaque ligne du programme — en fait, chaque instruction que l’on écrit — doit se terminer par un point-virgule ; c’est une convention présente dans de nombreux langages de programmation pour pouvoir séparer les instructions, et Inform ne déroge pas à la règle.

Les lignes suivantes sont des commandes Include : on les utilise pour ajouter des fichiers de code à votre code, et ainsi pouvoir utiliser leurs fonctionnalités. Ici, les deux fichiers à inclure absolument sont Parser, qui est le fichier de la bibliothèque Inform 6 qui fait l’analyse syntaxique de la commande du joueur, et VerbLib, qui contient les actions définies par défaut, comme « Take » ou « Push ». Tant que nous y sommes, allons voir à la fin du code source : un autre fichier, FrenchG, est inclus ; il s’agit du fichier contenant la définition de la grammaire française, qui spécifie notamment que « prendre » se réfère à l’action « Take », et que « pousser » se réfère à l’action « Push ». Rien qu’avec l’inclusion de ces trois fichiers, vous voilà avec un jeu qui peut comprendre des commandes écrites en français, avec une centaine de verbes (dont de nombreux synonymes) et de constructions reconnus !

L’ordre dans lequel vous incluez ces fichiers a une importance : toujours dans cet ordre (mais si vous sauvegardez ce code vide, vous n’aurez pas de problème). Par contre, on peut mettre FrenchG juste après Verblib si l’on veut ; c’est juste que j’aime bien visuellement séparer la grammaire du reste du jeu.

Initialise

Le dernier point est la ligne qui commence par un crochet ouvrant. Ceci signifie que nous sommes en train de définir une fonction, ou routine, c’est-à-dire une suite d’instructions que l’on exécute les unes à la suite des autres à chaque fois que l’on se réfère au nom de la fonction. Ici, la fonction s’appelle Initialise ; il s’agit d’une fonction qu’il faut obligatoirement définir, car Inform 6 s’attend à ce qu’elle soit définie, et y fait appel dès que le jeu démarre.

S’il ne la trouve pas, il écrira sans doute Error : No such constant as Initialise, pour dire « j’ai écrit quelque part qu’il fallait que je lance la fonction Initalise, mais elle n’est pas définie ! ».

À l’intérieur d’Initialise, il faut absolument donner le nom du lieu de départ du jeu ; sinon, vous aurez une erreur, non pas durant la compilation, mais en lançant le jeu. Le lieu dans lequel se trouve le joueur est contenu dans une variable nommée location : dès que le joueur bouge dans un lieu différent, Inform change la valeur de location. Pour donner une valeur de départ à location, c’est-à-dire définir un lieu de départ pour le joueur, nous écrivons :

location = piece;

Qu’est-ce que piece ? C’est justement la dernière ligne qu’il nous reste à expliquer : piece est le nom d’un objet défini dans le code. Car oui, en Inform 6, un lieu est un objet, tout comme le reste en fait.

L’instruction location = piece veut dire « le lieu actuel est maintenant défini comme étant l’objet ‘piece’ » ; le égal, comme dans beaucoup de langages de programmation, se lit comme un « : » ou une flèche ←.

La ligne suivante est une commande print : là encore, comme dans beaucoup d’autres langages de programmation, cette commande veut dire « afficher quelque chose à l’écran ». Puisqu’on est encore à l’étape d’initialisation, l’« écran-titre » (titre, chapeau, etc.) ne s’est pas encore affiché ; ainsi, si on utilise print, on affichera un texte avant l’écran-titre, c’est-à-dire un texte introductif. Ici nous allons juste afficher une ou deux phrases pour justifier le lieu dans lequel le joueur se trouve :

print "Vous vous réveillez en sursaut. Autour de vous, du blanc à l'infini. Une pièce complètement blanche...^";

Notez que nous terminons par un ^ ; c’est pour décoller le texte de l’écran-titre, pour les mettre en valeur.

Enfin, la dernière ligne est return true;. return est une commande permettant de renvoyer une valeur : la fonction a fini d’être exécutée, et on peut renvoyer un résultat, une valeur, un état… Ici, nous renvoyons true (vrai), pour dire que tout s’est bien passé et que le jeu devrait afficher l’écran-titre maintenant.

Notre premier objet

L’objet piece va être notre premier exemple d’objet. J’ai écrit plus haut Object piece; juste pour le définir ; mettons un peu plus d’information ici. Dans un jeu en Inform, on utilise une convention qui remonte à très loin (sans doute à Adventure) : le système affiche le nom du lieu en gras, et la description du lieu en-dessous. Pour spécifier le nom qui doit être affiché, on l’écrit après le nom de l’objet, comme ceci :

Object piece "Une pièce blanche";

Il y a donc deux types de nom en Inform. Le premier, piece, est le nom interne, utilisé par Inform et par le programmeur pour se référer à l’objet ; ce nom doit être un seul mot, composé de lettres (sans accents) et de chiffres. Le nom affiché au joueur peut être n’importe quel texte, et peut contenir ce qu’on veut.

La description est une propriété de base de l’objet — en fait, de n’importe quel objet d’Inform : tout objet a une propriété description. Pour la spécifier, il faut indiquer with (pour dire qu’on commence à lister les propriétés) ; ça donne :

Object piece "Une pièce blanche"

 with description "Vous ignorez où vous vous trouvez, et comment vous êtes arrivé là. Ce qui est sûr, c'est qu'il n'y a pas beaucoup de choses à voir ici...";

Lancez le jeu et…

L’obscurité

Il fait noir, et vous ne pouvez rien voir.

Mmh, ça n’est pas exactement ce à quoi nous nous attendions. C’est parce qu’il manque une dernière information : que cette pièce est éclairée. Nous le faisons en spécifiant l’attribut light, comme ceci :

Object piece "Une pièce blanche"

 with description "Vous ignorez où vous vous trouvez, et comment vous êtes arrivé là. Ce qui est sûr, c'est qu'il n'y a pas beaucoup de choses à voir ici...",

 has light;

Et en lançant, nous obtenons enfin :

Vous vous réveillez en sursaut. Autour de vous, du blanc à l'infini. Une pièce complètement blanche…

Mon premier programme Inform 6

Par un nouveau programmeur

Release 1 / Serial number 170709 / Inform v6.33 Library 6/11

Une pièce blanche

Vous ignorez où vous vous trouvez, et comment vous êtes arrivé là. Ce qui est sûr, c'est qu'il n'y a pas beaucoup de choses à voir ici…

>

Et voilà, vous avez une ébauche de jeu qui se profile ! Demain, nous remplirons le jeu un peu plus, pour qu’il ait une fin, une condition de victoire, etc. — bref, des éléments qui le rapprochent plus d’un jeu traditionnel. À demain !

Écrire une fiction interactive avec Inform 6, partie 2 : un jeu complet

Hugo Labrande

Hier, nous avons commencé à écrire notre première fiction interactive en Inform 6 ; nous avons réussi à créer la première (et unique) pièce du jeu, ainsi qu’un texte introductif. Aujourd’hui, nous allons rajouter quelques petites choses pour en faire un jeu court.

Objets

Comme je l’ai dit hier, la structure de base d’Inform 6 est l’objet. Le premier objet que nous avons créé est une pièce blanche, notre lieu ; nous avons indiqué le nom qui devrait s’afficher, sa description, et le fait que cette pièce soit illuminée. Nous allons créer un objet de plus : ça sera un miroir. Comme hier, nous commençons par créer le miroir, en indiquant son nom et sa description :

Object miroir "miroir"

 with description "Vous vous regardez dans le miroir. Vous n'avez pas l'air mal en point.";

Il faut également indiquer que le miroir se trouve dans la pièce blanche ; on fait ceci en ajoutant le nom du lieu après le nom complet de l’objet :

Object miroir "miroir" piece

 with description "Vous vous regardez dans le miroir. Vous n'avez pas l'air mal en point.";

Si l’on fait ceci et qu’on lance le jeu, on obtient :

Vous vous réveillez en sursaut. Autour de vous, du blanc à l'infini. Une pièce complètement blanche…

Mon premier programme Inform 6

Par un nouveau programmeur

Release 1 / Serial number 170709 / Inform v6.33 Library 6/11

Une pièce blanche

Vous ignorez où vous vous trouvez, et comment vous êtes arrivé là. Ce qui est sûr, c'est qu'il n'y a pas beaucoup de choses à voir ici…

Vous pouvez voir un miroir.

>examiner miroir

Vous ne voyez rien de tel, à moins que cela ne soit sans grande importance.

>casser miroir

Vous ne voyez rien de tel, à moins que cela ne soit sans grande importance.

Que se passe-t-il ? En fait, quand nous avons défini notre miroir, nous n’avons pas indiqué quels mots se référaient au miroir — et Inform 6 ne sait pas les déduire automatiquement. Il faut donc ajouter les mots qui se réfèrent au nom de l’objet, dans la propriété name :

Object miroir "miroir" piece

 with description "Vous vous regardez dans le miroir. Vous n'avez pas l'air mal en point.",

 name 'grand' 'miroir' 'sur' 'pied';

Avec ceci, nous avons :

>examiner grand miroir

Vous vous regardez dans le miroir. Vous n'avez pas l'air mal en point.

Pour finir cet objet, nous allons modifier notre code de façon à ce qu’il soit mentionné directement dans la description du lieu. Si l’on ne fait pas ça, Inform ajoute automatiquement la liste des objets présents dans la salle, mais c’est vu comme un peu disgracieux ; ça fait un peu « voici la liste des objets avec lesquels vous avez le droit d’interagir et puis c’est tout », et ça casse un peu notre description d’avant. Nous allons donc donner l’attribut concealed (caché) à notre objet : l’objet ne sera pas alors mis en évidence par Inform, mais le joueur n’aura quand même pas de problème pour interagir avec. Mais en fait, on peut aussi rajouter un attribut supplémentaire, scenery (décor) : cet attribut indique que l’objet est non seulement caché, mais qu’il fait également partie des « murs », c’est-à-dire qu’il ne peut pas être pris par le joueur. Ça donne :

Object piece "Une pièce blanche"

 with description "Vous ignorez où vous vous trouvez, et comment vous êtes arrivé là. Ce qui est sûr, c'est qu'il n'y a pas beaucoup de choses à voir ici... Un grand miroir sur pied se tient devant vous.",

 has light;

Object miroir "miroir" piece

 with description "Vous vous regardez dans le miroir. Vous n'avez pas l'air mal en point.",

 name 'grand' 'miroir' 'sur' 'pied',

 has scenery concealed;

Ajoutons des actions spéciales avec « before »

Nous avons désormais un objet, un grand miroir sur pied ; mais pour l’instant, on ne peut pas faire grand-chose avec lui.

>prendre miroir

C’est trop difficile à transporter.

>casser miroir

La violence n’est pas une solution ici.

>entrer dans miroir

Vous ne pouvez pas y entrer.

Par défaut, la bibliothèque standard dit « non » ! Il y a quelques exceptions (prendre un objet qui n’est pas scenery fonctionnera, par exemple), mais elles ne sont pas légion. C’est un des problèmes des jeux avec analyseur syntaxique : si l’auteur n’a pas spécifié le résultat d’une action, le système dira « non » au joueur. C’est frustrant pour le joueur : on lui dit « vas-y, tape n’importe quoi », mais on lui dit également « non, pas ça » des dizaines de fois ! Ainsi, faire un jeu avec analyseur syntaxique demande pas mal de travail à l’auteur, qui doit prévoir tout un tas d’actions, ou regarder ce que ses joueurs ou testeurs essaient.

Ici, nous allons ajouter des réponses pour « casser miroir » (une action logique avec un miroir, sept ans de malheur) et « entrer dans miroir » (une action un peu fantastique, à la Alice au pays des merveilles). Ce qu’il faut faire, en fait, c’est arrêter le système avant qu’il ne fasse ce qu’il fait d’habitude, c’est-à-dire avant qu’il n’affiche la réponse usuelle. Ça se fait en spécifiant une propriété before dans notre miroir, et en écrivant ce qui se passe pour chacun de ces verbes :

Object miroir "miroir" piece

 with description "Vous vous regardez dans le miroir. À part une rougeur aux yeux et quelques plumes en désordre, vous n'avez pas l'air mal en point.",

 name 'grand' 'miroir' 'sur' 'pied',

 before [; Attack: "Vous vous élancez et donnez un coup de tête au miroir. Malheureusement, celui-ci est plus résistant que vous...";

 Enter: "Vous tentez d’entrer dans le miroir, mais il n’a pas l’air d’être magique, et vous vous cognez la tête.";

 ],

 has scenery concealed;

Le système vérifie toujours si before n’indique pas quelque chose pour le verbe actuel avant d’écrire sa réponse par défaut ; ici, nous avons donc ajouté 2 actions où le joueur se cogne la tête.

On peut mettre n’importe quel code dans un before ! Pour les cas simples comme celui-ci, on indique juste la nouvelle réponse ; c’est équivalent à écrire
Attack: print "Vous vous élancez et donnez un coup de tête au miroir. Malheureusement, celui-ci est plus résistant que vous..."; return true;
Ce bout de code veut dire « afficher le texte, puis c’est bon, on a fini, pas besoin d’afficher autre chose » ; ainsi, le jeu affiche votre réponse et n’affiche pas la réponse par défaut, puisque vous lui avez dit que c’était tout. Si vous voulez écrire quelque chose de plus complexe, qui change l’état du jeu ou autre, il faut finir par return true; si vous voulez court-circuiter la réponse usuelle.

Dans before, il faut indiquer l’action (en anglais) correspondant au verbe français. En général, c’est simple : Take, Turn, Push, Pull, Enter, Attack, Eat, Touch, etc. Si vous avez du mal à savoir quelle action correspond à quel verbe, il suffit d’aller dans FrenchG.h et de trouver le verbe en français correspondant. S’il n’y est pas, c’est à vous de le définir ! (Voir ci-dessous pour la méthode.)

C’est une bonne gymnastique cérébrale de se demander ce qu’un joueur peut avoir envie de faire avec un objet, pour pouvoir le prendre en compte. Ici, on peut vouloir regarder derrière le miroir, le toucher, le tourner, voire le lécher ! Entraînez-vous et ajoutez des réponses spéciales pour ces verbes !

Victoire, défaite

Le jeu que nous sommes en train d’écrire est un jeu court, très court même : on va dire qu’il va suffire d’une action pour gagner ou perdre. Vu les actions que nous avons prises en compte, disons qu’essayer de casser le miroir fait perdre le jeu, et essayer d’y entrer fait gagner le jeu.

La variable deadflag est la variable d’Inform qui détermine si l’on est toujours en jeu (dans ce cas, deadflag contient 0) ou si le joueur a gagné (dans ce cas deadflag vaut 1) ou perdu (deadflag vaut 2). Il y a moyen de spécifier plus de fins que cela, mais ça sera pour un autre article !

L’effet est instantané : dès que vous changez la valeur de deadflag en 1 ou 2, le jeu s’arrête et le message de fin est affiché. Donc ici, à l’intérieur des before, il faut changer la variable après avoir affiché le texte, mais avant le « return » qui signale la fin de la fonction.

Ça donne :

Attack: print "Vous vous élancez et donnez un coup de tête au miroir. Malheureusement, celui-ci est plus résistant que vous..."; deadflag = 2; return true;

Enter: print "Mû par une intuition soudaine, vous regardez le miroir de plus près... Et en effet, il s'ouvre, et vous entrez de l'autre côté du miroir..."; deadflag = 1; return true;

Et notre jeu est enfin finissable :

>entrer dans miroir

Mû par une intuition soudaine, vous regardez le miroir de plus près... Et en effet, il s'ouvre, et vous entrez de l'autre côté du miroir...

 *** Vous avez gagné ***

Dans cette partie vous avez obtenu 0 sur un score possible de 0, en 1 tour.

Voulez-vous RECOMMENCER, CHARGER une partie sauvegardée ou QUITTER ?

>

Score

Vous avez sans doute remarqué que le jeu dit à la fin « Vous avez obtenu un score de 0 sur 0 ». C’est assez disgracieux, et nous allons essayer de changer cela.

Par défaut, un jeu en Inform a un score, contenu dans la variable score. Cependant, au fil des années, de plus en plus de jeux sans score sont apparus, si bien qu’il paraît maintenant optionnel (comparé aux années 80, où c’était un passage obligé). Si vous ne voulez pas de score dans votre jeu, écrivez

Constant NO_SCORE;

avant Include "parser";.

Si vous en voulez un, vous n’avez rien à changer ; il suffit de changer la variable score directement pour attribuer des points. Par exemple, nous pouvons donner 5 points pour la fin victorieuse :

Enter: print "Mû par une intuition soudaine, vous regardez le miroir de plus près... Et en effet, il s'ouvre, et vous entrez de l'autre côté du miroir..."; score = 5; deadflag = 1; return true;

Ce qui donne :

>entrer dans miroir

Mû par une intuition soudaine, vous regardez le miroir de plus près... Et en effet, il s'ouvre, et vous entrez de l'autre côté du miroir...

    *** Vous avez gagné ***

 Dans cette partie vous avez obtenu 5 sur un score possible de 0, en 1 tour.

Voulez-vous RECOMMENCER, CHARGER une partie sauvegardée ou QUITTER ?

>

Il y a une erreur avec le « 5 sur 0 » ; en fait, il faut spécifier le score maximal du jeu, en déclarant

Constant MAX_SCORE = 5;

avant Include "parser";.

Il faut déclarer le score maximal vous-même ! Inform ne peut pas le calculer pour vous : vous pourriez avoir plusieurs solutions alternatives, des chemins qui ne rapportent pas le maximum de points, etc. Mais faites alors attention à bien calculer le nombre de points : si vous mettez que le score maximal est de 6 alors qu’on ne peut avoir que 5 points, certains joueurs tourneront en rond pendant des heures à la recherche du point manquant…

Définir ses propres verbes

Terminons ce tutoriel en ajoutant une solution supplémentaire au jeu. Cette solution est inspirée d’un moment culte d’un jeu anglophone, dont je tairai le nom pour ne pas trop gâcher la surprise… Le joueur regarde dans le miroir et se voit, pas trop amoché ; nous allons modifier un peu la description :

Object miroir "miroir" piece

 with description "Vous vous regardez dans le miroir. À part une rougeur aux yeux et quelques plumes en désordre, vous n'avez pas l'air mal en point.",

 ...

Grâce à cette description, le joueur doit comprendre qu’il incarne en fait un oiseau ! (Et après tout, il n’y a rien dans Inform qui nous en empêche !) À partir du moment où le joueur est un oiseau, il y a une solution évidente pour s’échapper de la pièce blanche : le joueur peut taper « voler ».

« Voler » n’est pas un verbe standard d’Inform, parce que le joueur en a justement rarement besoin. Il va donc falloir le définir nous-mêmes, en ajoutant un verbe à la grammaire reconnue par le jeu. On fait comme ceci : après l’inclusion de la grammaire française standard, on écrit

Verb 'voler'

*  ->Voler;

Ceci veut dire qu’on définit la commande « voler », suivie de rien du tout, et que si le joueur tape cette commande, il faudra aller voir dans la fonction VolerSub.

Attention au piège : si on écrit quelque chose au bout de la flèche, Inform rajoute « Sub » au nom et cherche cette routine-là !

Il reste à définir VolerSub : on écrit un message, on donne 10 points au joueur, et on dit qu’il a gagné, et c’est fini pour cette fonction :

[ VolerSub ;

 print "Vous agitez les ailes. Elles n'ont pas l'air d'avoir souffert ! Vous prenez un peu d'élan et volez hors de ce lieu étrange.";

 score = 10;

 deadflag = 1;

 return true;

];

N’oubliez pas de changer le MAX_SCORE! Et maintenant, le joueur qui entre dans le miroir sait qu’il a gagné, mais n’a pas le maximum de points : il sait qu’il y a une meilleure fin !

Terminé ?

Non, ça ne fait que commencer !

Vous venez de décortiquer votre premier exemple en Inform 6. N’hésitez pas à le modifier, à l’étendre (essayez de rajouter des objets de décor, un lieu derrière le miroir, etc.), et à expérimenter !

Nous parlerons de beaucoup d’aspects d’Inform 6 sur ce site ; vous trouverez également sur le forum beaucoup de sujets relatifs à du code Inform 6, qui pourraient répondre à des questions que vous vous posez. N’hésitez pas également à regarder du code Inform 6 ; la source de tous mes jeux est disponible sur mon site, et il y a aussi la source de tous les jeux de FibreTigre (en général des jeux longs, qui font des choses très intéressantes).

Pour les anglophones, vous trouverez beaucoup d’informations dans l’Inform Beginner’s Guide et l’Inform Designer’s Manual 4, ainsi que sur Internet, et dans le répertoire des codes sources I6 de l’IFArchive.

Tout ceci représente par contre une somme énorme de choses, et c’est peut-être difficile de savoir par où commencer ! Alors revenez bientôt sur ce site pour plus d’articles et de réponses à vos questions pour progresser en Inform 6 !

Manipulations de variables en Inform 6

Hugo Labrande

Nous continuons notre série de tutoriels Inform 6 ; cette semaine, voici un article sur les variables locales et globales.

Les variables sont des petits bouts de choses qui peuvent stocker une information, une valeur, un état qui est amené à potentiellement évoluer au cours de la partie. Oui oui, une variable est variable, et je viens de faire une grande phrase pour dire ça.

Un exemple de variable est le score. Au début, on démarre à 0, et si le joueur fait quelque chose de chouette, on lui donne un point. Comment donner un point au joueur ? On prend le nombre de points, on ajoute 1, et on dit que c’est le nouveau score. Ça s’exprime comme ça :

score = score + 1;

Le signe égal veut dire que la variable de gauche va maintenant prendre la valeur qui est à droite ; on met donc le nom d’une variable à gauche, et sa nouvelle valeur à droite. C’est une construction standard dans beaucoup de langages de programmation ; dans certains langages, on écrit même ← à la place du égal, ce qui est encore plus explicite (ce qui est à droite est mis dans la variable de gauche).

Un exemple plus complet : si nous avons une variable qui contient le nombre de pièces d’or du joueur, nous pouvons dire :

Global argent = 100;

Et ça veut dire que l’on commence avec 100 pièces. Si le joueur en dépense 10, nous écriront

argent = argent - 10;

Et quand nous voulons l’afficher, nous écrivons tout simplement print argent. Ce n’est pas très difficile et vous prendrez vite l’habitude.

Ce bout de code définit une variable globale, qui est définie une fois pour toute dans le code : il n’y en a qu’une avec ce nom, et c’est celle-là. Maintenant, qu’est-ce qu’une variable locale ? En Inform 6, c’est simplement une variable qui est rattachée à un objet. La façon de la définir est un peu différente. Prenons un exemple, tiré d’Ekphrasis, de FibreTigre :

Object simonetta "femme" plage

 with parled 0,

 ...

 life [; Answer,Ask,Tell: if (self.parled==0) {SimonettaTalks();self.parled=1;return 2;} else {print "..."} ], ...

Nous avons ici Simonetta, qui possède une variable locale qui démarre à 0, et si on essaie de lui parler, le jeu regarde la variable locale : si on ne lui a pas déjà parlé, on lance la routine SimonettaTalks (une cinématique ou cutscene en fait) et on met la variable à 1, pour indiquer qu’on vient de lui parler ; si on lui a déjà parlé, on affiche autre chose. Donc on lance la cutscene la première fois qu’on lui parle, et sinon on affiche un autre message. Et oui oui, la variable s’appelle parled, comme il y en a d’autres dans le code nommées tremped.

Et en fait, parled et tremped sont des bons noms ! Vous avez tout à fait le choix dans le nom des variables, à part que vous ne pouvez pas mettre d’accent dans le nom ; mieux vaut choisir un nom explicite pour se souvenir de ce que modélise la variable, comme « combien de fois a-t-on parlé à cette femme » ou « est-ce que le drap est trempé ou non ».

Remarquez bien la façon dont on y fait référence : self.parled, « self » voulant dire « l’objet dans lequel on se trouve au moment du code » (ici, simonetta). Si on avait besoin de tester à un autre moment du code si on a déjà parlé à Simonetta, on ferait if (simonetta.parled == 0) (plutôt que self.parled == 0). La variable locale est rattachée à l’objet : il y a beaucoup d’autres personnages dans Ekphrasis auxquels on peut parler (Pazzi, Saint George, etc.) et qui ont tous une variable locale nommée parled ; pazzi.parled est une variable différente de simonetta.parled, même si dans les deux cas la variable qui nous intéresse s’appelle parled.

Vous allez me dire : « Pourquoi utiliser des variables locales si on a des variables globales ? » On pourrait juste dire que c’est plus propre : mieux vaut garder les propriétés et l’état d’un objet à l’intérieur de l’objet (c’est un grand principe de la programmation orientée objet, dont Inform 6 s’inspire). Mais en fait, la raison la plus intéressante à mon sens est le fait qu’on puisse justement définir des variables avec le même nom, et du coup faire du code un peu plus générique.

Voici maintenant un exemple plus complexe pour illustrer une différence entre variables globales et variables locales (ce qui suit est plus avancé, donc n’hésitez pas à y revenir plus tard !). Il s’agit d’un jeu avec des combats : chaque ennemi sera un objet, et aura des points de vie. Nous pouvons coder ceci avec une variable locale pv (points de vie) pour chacun des ennemis :

Object garde "garde"

 with pv 20,

 description

 ...

Object paladin "paladin"

 with pv 40,

 description

 ...

Object ecuyer "écuyer"

 with pv 12,

 description

 ...

C’est plus logique que d’avoir des variables globales très similaires garde_pv, paladin_pv, etc. : tout le monde a des pv, y’a les pv du garde, ceux de l’écuyer, et ainsi de suite.

On peut ensuite implanter des verbes de combat ; on va dire par exemple qu’on aura le verbe « estocade X » qui fait perdre 7 points de vie à la cible. Si nous avions des variables globales, il faudrait faire un gros test : si c’est le garde qu’on attaque, enlever 7 à garde_pv, si c’est l’écuyer, enlever 7 à ecuyer_pv, etc. Si nous avons 20 ennemis, nous allons avoir un if avec 20 cas et, à chaque nouvel ennemi, il ne faudra pas oublier d’ajouter le test correspondant. Bon courage… Avec des variables locales, c’est tout facile car elles ont toutes le même nom ! Il suffit de faire référence à la variable pv de l’objet concerné, qui sera ici noun (le nom reconnu durant l’analyse de la commande tapée). Nous pouvons même ajouter du code pour faire mourir les ennemis. Cela donne :

[ EstocadeSub ;

 noun.pv = noun.pv - 7;

 if (noun.pv <= 0) { remove noun; print "Le ", noun, " pousse un râle et meurt."; }

 return true;

];

Ce code est court et vous permet d’avoir du code flexible, car vous pouvez ajouter autant d’ennemis que vous voulez sans toucher à ce code. Bref, merci les variables locales !

Tutoriel Inform 6 : variable d’avancement

Hugo Labrande

Un petit article aujourd’hui sur une technique générale pour gérer la progression du joueur dans un jeu linéaire. C’est une technique que j’ai vue la première fois dans le code source de Filaments, de FibreTigre, et je l’ai utilisée dans quasiment tous mes jeux ; mais j’imagine que la technique est classique et a été mise en œuvre dans de nombreux jeux, notamment anglophones.

Pour gérer la progression du joueur, il y a plusieurs approches, et la meilleure dépendra de la structure de votre jeu. Est-ce que votre jeu se passe toujours dans le même environnement ? Est-ce que toutes les salles sont accessibles au début du jeu, façon open world à la Morrowind, ou est-ce que le joueur avance petit à petit et découvre des salles nouvelles, jusqu’à arriver à la dernière, façon Aventure ? Est-ce que votre jeu a une progression plutôt linéaire, une énigme ou une étape après l’autre, ou est-ce que le joueur peut travailler sur plusieurs énigmes en même temps, de façon à ne jamais être bloqué ?

Je vais partir du principe que vous voulez, pour débuter, faire un jeu simple, linéaire : une histoire qui se lit du début à la fin, qui ne change pas beaucoup d’une session à l’autre (un peu comme un jeu d’aventure point-and-click classique), où il faut résoudre une énigme pour avancer, puis on en trouve une autre, etc. Dans ce cas, le plus simple pour gérer la progression est d’utiliser une variable pour l’avancement du joueur. Cette variable, vous pouvez l’appeler avancement, progression, plot (mot anglais pour l’histoire dans une œuvre) ou comme vous voulez ; l’idée est qu’elle démarre à 0 et que, quand il se passe quelque chose dans l’histoire qui change la situation, débloque des choses, ou autre — dès que l’histoire avance, donc — vous augmentez cette variable.

Concrètement, je vous recommande de mettre la signification des différentes valeurs en commentaire – c’est-à-dire sur une ligne qui démarre par un « ! », qui ne sera pas lue par le compilateur (et qui ne compte donc pas comme du vrai code). Ça vous évitera de vous gratter la tête après une pause de quinze jours dans votre travail : « alors, euh, ça veut dire quoi déjà quand l’avancement est à 7 ? je sais plus… »

Ça donnera quelque chose comme ceci, à mettre assez tôt dans votre code :

 ! Variable d’avancement

 Global avancement = 0

 ! 0 = début du jeu, le joueur n’a pas encore touché l’idole maléfique

 ! 1 = l’idole s’est réveillée, le joueur doit parler au chef du village

 ! 2 = le chef est au courant, tout le monde cherche l’entrée de la caverne

 ! 3 = l’entrée est trouvée, tout le monde essaie d’ouvrir la porte de la caverne

 ! 4 = tout le monde est dans la caverne, le jeu est fini si le joueur creuse au bon endroit

Pour augmenter la variable « avancement », il faut se placer aux bons endroits du code et augmenter la variable à ce moment-là. Par exemple, vous pouvez faire comme ceci :

Object idole "idole maléfique" jungle

 with description "Une idole aux grands yeux vous regarde fixement.",

 name 'idole' 'maléfique' 'yeux',

 before [; Touch: avancement = 1; print "Vous touchez l’idole, et elle se réveille ! Une voix caverneuse se fait entendre et vous voilà maudit !" ; return true; ],

 has scenery concealed;

Vous pouvez même faire un peu plus subtil : avoir un message qui s’affiche la première fois que le joueur touche l’idole (et vous mettez la variable avancement à 1 à ce moment-là), et un autre message les autres fois. Ça pourrait donner :

before [; Touch: if (avancement == 0) {

 avancement = 1; print "Vous touchez l’idole, et elle se réveille ! Une voix caverneuse se fait entendre et vous voilà maudit !" ; return true;

 } else {

 print "Vous retouchez l’idole, en espérant que cela enlève le maléfice, mais rien ne se passe."; return true;

 } ],

Ce genre de mécanismes, où l’on donne une réponse la première fois, et une autre les fois suivantes, est crucial et énormément utilisé dans les fictions interactives. Ceci permet de faire varier les textes et d’avoir un monde plus réactif, mais aussi (voire surtout) de rappeler au joueur qu’il a déjà fait l’action ; par exemple, « Vous lui avez déjà demandé, et la réponse est non. Il va falloir trouver un autre moyen », ou « Vous avez déjà perdu deux heures à compter les feuilles, et vous en aviez trouvé 69 105 », ou encore « Le souvenir de cette texture gluante sur le bout de vos doigts vous fait encore frémir ».
À chaque fois, la structure est la même : si une variable a une certaine valeur de base, on affiche la première réponse et on change la variable ; les autres fois, la variable n’aura plus la valeur de base et on pourra afficher quelque chose d’autre. Vous pouvez même faire plusieurs réponses à la suite, comme faire en sorte que le garde craque et donne le mot de passe la septième fois que vous lui demandez, ou juste des réponses de plus en plus loufoques à une action !

Enfin, l’utilisation d’une variable d’avancement vous permet d’implanter une fonctionnalité très appréciée : des indices contextuels, c’est-à-dire adaptés à la situation. Par exemples, vous pouvez programmer le verbe « indice » pour qu’il donne un indice dépendant de la variable « avancement ». Ça donnerait :

[ IndiceSub ;

 if (avancement == 0) { return "Promenez-vous et soyez aventureux !"; }

 if (avancement == 1) { return "Oh oh... On dirait que toucher cette idole n’était pas la meilleure idée qu’il soit. Peut-être pourrez vous demander de l’aide au village voisin..."; }

 if (avanc…

Bon, en fait il y a plus rapide que d’écrire if (avancement == ... 5 fois, c’est d’utiliser switch, qui sert pour distinguer différents cas selon les valeurs que peut prendre une variable. Ça donne :

[ IndiceSub ;

 switch(avancement){

 0: return "Promenez-vous et soyez aventureux !";

 1: return "Oh oh... On dirait que toucher cette idole n’était pas la meilleure idée qu’il soit. Peut-être pourrez vous demander de l’aide au village voisin...";

 2: return "Le chef a dit de trouver l’entrée de la caverne, qui doit être cachée quelque part dans la jungle...";

 3: return "Cette porte doit bien s’ouvrir, peut-être avec un mécanisme caché...";

 4: return "Où cacheriez-vous un trésor pour qu’il soit à l’abri des regards ?";

 }

];

Verb 'indice'

 * ->Indice;

Et voilà, vous avez une histoire linéaire avec des indices pour faire avancer le joueur !

Si vous voulez plus d’exemples, mes jeux Gossip (qui avait une variable plot et un verbe spécifique pour donner des indices) et Life On Mars? utilisent quelque chose comme ça, et je vous encourage à aller consulter leur code source (à part que pour Gossip, je ne sais plus à quoi correspond chaque valeur, donc je suis bien embêté !) ; et comme je le disais en introduction, il y a aussi Filaments (variable plot_depth), dont le code source est disponible librement. Cette technique, bien que basique, vous permet déjà de construire un jeu linéaire suivant une trame narrative définie, qui progresse à mesure que le joueur résout les énigmes !

Déclarer des nouveaux verbes en Inform 6

Hugo Labrande

Voici un article tutoriel sur l’ajout de nouveaux verbes dans votre fiction interactive ; pour un autre article sur un aspect complémentaire, l’extension de verbes existants; c’est ici.

Dans une fiction interactive avec analyseur syntaxique, toute l’expérience du joueur repose sur la qualité de ce dernier. Fort heureusement, vous n’avez pas à en coder un vous-même : Inform inclut des fonctions génériques puissantes, qui permettent de reconnaître des entrées à partir d’une grammaire spécifiée ; et la bibliothèque francophone définit la grammaire d’une centaine de verbes usuels, ce qui vous donne déjà une bonne longueur d’avance. Mais fatalement, il vous faudra mettre les mains dans le cambouis à un moment ou un autre : votre verbe préféré n’est pas dans la bibliothèque de base, une joueuse a eu une difficulté car elle a utilisé un synonyme que vous n’aviez pas prévu, ou vous voulez définir votre propre commande. Voyons comment ajouter des verbes à la grammaire reconnue par le jeu.

Que la lumière soit !

Supposons que je sois en train de développer un jeu qui soit une fan fiction Harry Potter (ou mieux, l’adaptation officielle de Harry Potter en fiction interactive, et si c’est le cas, félicitations, on attend ça avec impatience !). Il vous faut implanter des sorts, comme « lumos », un sort qui vous permet de faire de la lumière. Pour cela, vous allez créer un nouveau verbe, comme ceci :

Verb 'lumos'

*  ->Lumos;

Les plus perspicaces reconnaîtront ce bout de code : en effet, dans notre tout premier tutoriel Inform 6, on avait déjà défini un verbe « voler » selon le même schéma !

La ligne en-dessous du verbe précise ce qui devra suivre le verbe (ici, rien, le joueur tape juste « lumos »), puis on écrit une petite flèche pour indiquer le nom de la routine à exécuter si c’est bon. Ici, on écrit Lumos, et le jeu ira donc chercher (c’est une convention) une routine qui s’appelle LumosSub. La routine pourra être par exemple :

[ LumosSub ;

 if (location has light) { "Rien d'évident ne se produit."; }

 else { give location light; "Votre baguette émet une lumière blanche qui se répand au plafond et illumine la pièce."; }

];

Attention, il faut que cette routine soit définie avant la ligne de grammaire qui y fait référence. Avec cette ligne de grammaire, voilà ce qu’on obtient :

Une pièce sombre

Il fait noir, et vous ne pouvez rien voir.

>lumos partout

Je ne comprends pas cette phrase.

>lumos chandelier

Je ne comprends pas cette phrase.

>lumos

Votre baguette émet une lumière blanche qui se répand au plafond et illumine la pièce.

Le jeu ne comprend pas « lumos partout » ou « lumos chandelier », car la grammaire spécifie « lumos, puis rien du tout ».

Patronus !

Prenons maintenant le sort pour faire apparaître un patronus : ça n’est plus un mot, mais deux. La grammaire correspondante sera :

Verb 'expecto'

*  'patronus' ->ExpectoPatronus;

C’est-à-dire un premier mot (qu’Inform 6 appelle « verbe »), et s’il est suivi de patronus, on exécute ExpectoPatronusSub ; si le joueur tape juste « expecto », ou « expecto mon patronus », le jeu ne le comprendra pas.

Le regard de la Méduse

On peut également créer un verbe s’appliquant à un objet ; il me semble qu’aucun des sorts d’Harry Potter ne comprend le nom de l’objet, mais faisons comme si on jetait un sort de pétrification en faisant « petrificus totalus grenouille », par exemple. Il faut alors écrire :

Verb 'petrificus'

*  'totalus' noun ->PetrificusTotalus;

Le noun veut dire que ça sera bon tant que c’est le nom d’un objet — une suite de mots dans le name, ou quelque chose de plus compliqué (capturé par parse_name). Pour la routine, ça pourra être quelque chose comme ça :

Attribute petrified;

Object neville "Neville Londubas"

 with name 'neville' 'londubas' 'ami',

 description ... ! Je vous laisse finir

[ PetrificusTotalusSub;

 if (noun == neville) { "Ah non, vous n'allez pas encore pétrifier Neville !";} else { give noun petrified; "Vous lancez le sort, et la chose se transforme en pierre."; }

];

Ainsi si le joueur tape « petrificus totalus neville », « petrificus totalus neville londubas », ou « petrificus totalus neville ami », le jeu comprendra qu’on veut lancer PetrificusTotalusSub sur l’objet neville. Et si le joueur tape « petrificus totalus », le jeu lui demandera de préciser le nom concerné par l’action, car la ligne de grammaire dit qu’il faut le nom d’un objet après ces mots. C’est plutôt pratique !

J’ai cependant un léger souci avec le code qui précède : le if (noun == neville) introduit une exception pour neville. Et si on veut aussi une exception pour ginny, pour dumbledore, pour dobby ? Est-ce qu’on va enchaîner les if (noun == ... ? Il vaut mieux mettre dans la routine le comportement normal, par défaut, et intercepter ce comportement dans les objets qui en ont besoin. On fait cela avec before, comme on l’a expliqué dans le premier tutoriel, et on utilise le nom de routine déclaré dans la grammaire ; ça devient

Attribute petrified;

Object neville "Neville Londubas"

 with name 'neville' 'londubas' 'ami',

 before [; PetrificusTotalus: print "Ah non, vous n'allez pas encore pétrifier Neville !"; rtrue;} ],

 description ... ! Je vous laisse finir

[ PetrificusTotalusSub;

 give noun petrified; "Vous lancez le sort, et la chose se transforme en pierre.";

];

Avada…

Je sais que ça risque de nous envoyer à Azkaban, mais faisons le sort de mort. Ce sort ne s’applique que si l’objet est animé : on ne peut pas le lancer sur un caillou. Ça donne :

[ AvadaKedavraSub ;

 "Vous fermez les yeux. Il y a sûrement une façon plus pacifique d'arriver à vos fins.";

];

Verb 'avada'

 * 'kedavra' animate -> AvadaKedavra;

Dans ce genre de déclarations, vous pouvez utiliser n’importe quel attribut, comme edible (pour ne cibler que la nourriture par exemple) ou openable !

Demain, je vous parlerai non pas de création de nouveaux verbes, mais d’extension et d’élargissement de la grammaire de verbes déjà définis. Alors, à demain !

Élargir la grammaire de verbes déjà définis en Inform 6

Hugo Labrande

Hier, nous avons commencé à coder un système de sorts pour notre adaptation de Harry Potter en fiction interactive. Aujourd’hui, apprenons non pas à créer des verbes, mais à en étendre.

Un exemple que l’on peut prendre pour commencer est celui du verbe « lancer » ; dans notre contexte de Harry Potter, on voudrait bien que « lancer sort » soit reconnu. Sauf que « lancer » fait déjà partie des verbes reconnus par Inform 6 en français ! Si on écrit Verb 'lancer', le système va se plaindre que ce verbe est déjà défini. Il ne s’agit pas ainsi de créer un verbe, mais d’en étendre un, ce qu’on fait avec la commande Extend :

[ SortPasPrécisSub ;

 "Veuillez préciser le sort que vous souhaitez lancer ; vous pouvez également juste taper son nom directement.";

];

Extend 'lancer'

* 'sort'  -> SortPasPrécis;

Grâce à ces deux dernières lignes, nous ajoutons une ligne de grammaire supplémentaire pour le verbe « lancer », après celles déjà définies.

Supposons ensuite que vous vouliez empêcher le joueur de taper « lancer sort lumos » ou tout autre sort, pour insister sur le fait qu’il faille taper directement le sort.

Personnellement je trouve que ça n’est pas une bonne idée : je déteste les jeux qui donnent une réponse spécifique pour me dire la façon « correcte » de le formuler. Si le jeu a une réponse spécifique pour cette commande, c’est qu’il la reconnaît, et si il la reconnaît, pourquoi fait-il comme si il ne comprenait pas ?? Grr !

Dans ce cas, il faut une ligne qui oriente le joueur vers la bonne commande. On écrit :

[ SortPasCommeCa ;

 "[Dans ce jeu, vous n'avez pas besoin de taper 'lancer sort' ; tapez le sort directement.]";

];

Verb 'lancer'

* 'sort' topic ->SortPasCommeCa;

Le topic veut dire « n’importe quel texte entré par le joueur » ; le jeu n’essaiera pas de le faire correspondre à un objet.

Pour être plus gentil avec le joueur, nous allons autoriser les formulations « lancer sort lumos », « lancer sort expecto patronus », etc. Pour cela il faut rajouter plusieurs lignes à la grammaire de « lancer », comme ceci :

Extend 'lancer'

 * 'sort'  ->SortPasPrécis

 * 'sort' 'lumos'  ->Lumos

 * 'sort' 'expecto' 'patronus'  ->ExpectoPatronus

 * 'sort' 'petrificus' 'totalus' noun ->PetrificusTotalus

 * 'sort' 'avada' 'kedavra' animate  ->AvadaKedavra;

Vous pouvez également rajouter une ligne de plus, * 'sort' topic ->SortInconnu, si vous voulez pouvoir dire au joueur que ce sort est inconnu.

Attention à bien rajouter cette ligne en dernier ! Il s’agit de la ligne la plus générale ; si vous l’ajoutez avant, le programme trouvera que cette ligne décrit la commande très bien comme ça, et ne regardera pas les lignes d’après, qui seront donc toujours ignorées.

Maintenant, comment faire si vous voulez reconnaître « jeter sort » et consorts en plus de « lancer sort » ? En fait, c’est déjà fait : « jeter » est un synonyme de « lancer » dans la bibliothèque Inform francophone ! Mais supposons que vous vouliez les différencier – par exemple, parce que votre jeu s’appelle Harry Potter et la Pollution de l’environnement naturel et que vous voulez pouvoir jeter des détritus. Vous utiliseriez le mot-clé only, comme ceci :

Extend 'jeter' only

 * 'détritus'  ->PoubelleNormale

 * 'plastique'/'carton'/'emballage'/'papier' ->Recyclage;

Les barres obliques « / » permettent de séparer des options équivalentes — pas besoin d’écrire 4 lignes de grammaire (une pour le plastique, une pour le papier, etc) différentes !

Nous l’avons dit avant, les lignes de grammaire que vous définissez sont mises après les autres, c’est-à-dire qu’Inform commencera toujours l’analyse de la phrase en regardant les lignes de grammaire prédéfinies d’abord, puis les vôtres. Parfois, on a envie de changer ce comportement, par exemple si l’on veut une règle similaire mais avec des caractéristiques plus spécifiques (par exemple, 'jeter' edible est plus précis que 'jeter' noun). On utilise alors le mot-clé first, comme ceci :

Extend 'jeter' first

 * edible ->GaspillageNourriture;

Ceci permet de placer vos règles en premier, avant les autres.

Et si vous voulez en mettre certaines en premier, mais en garder d’autres, mais pas d’autres, et puis… Ouh là… Parfois, il vaut repartir de zéro, et utiliser le mot-clé replace :

Extend 'jeter' replace

 * ... ! À vous de jouer !

Trizbort, plus qu’un simple outil de cartographes

Hugo Labrande

Aujourd’hui, parlons d’un outil relativement récent : Trizbort. Certes, le nom ne donne pas envie (ah, ces anglophones et leurs références obscures à des jeux Infocom), mais il s’agit d’un outil de dessin de cartes, bien fait et avec quelques fonctionnalités très chouettes.

Carte de ma fiction interactive Tipelau, un jeu très cool.

Avant toute chose, il faut que je vous l’avoue, c’est un outil qui ne marche que sous Windows, hé oui… J’ai déjà essayé de le compiler pour Linux avec Mono, mais ça n’a pas marché (une sombre histoire de bibliothèques partagées non portables *grommelle grommelle*). Si jamais il y a un développeur C# dans le coin qui veut regarder, je lui serai très reconnaissant !

Bref, reprenons. Dans la plupart des fictions interactives avec analyseur syntaxique, on se déplace énormément de salle en salle, en parcourant la géographie de part et d’autres, et parfois même en tournant en rond. À mesure que l’on bouge, on essaie de se faire une carte mentale des lieux que l’on rencontre. C’est parfois pas facile — et c’est ce qui est parfois exploité et transformé en énigme, notamment dans les sections « labyrinthe » — mais, parfois, si la carte du jeu est trop grande, le joueur normalement constitué se décourage, submergé par la quantité d’informations à retenir (pour moi, c’est Inside Woman et Worldsmith).

Donc, il est souvent nécessaire de faire une carte ; les joueurs aguerris ont toujours eu un bout de papier à portée de main, et finissent avec un bureau couvert de notes (vous pouvez voir des exemples sur le site de David Welbourn, joueur vorace et très méticuleux qui a scanné toutes ses notes de jeu récemment). C’est le problème que veut résoudre Trizbort : il s’agit à la base d’un logiciel pour tracer des cartes au fur et à mesure que vous explorez le jeu, que vous puissiez vous y retrouver. Il y a même une fonction incroyable, l’auto-mapping : lancez le jeu dans un interpréteur, allumez la transcription (« transcript on », normalement), puis ouvrez Trizbort et dites-lui où est le fichier de transcription : Trizbort s’occupe de dessiner la carte tout seul au fur et à mesure !

Trizbort est très simple d’utilisation. Lancez-le, et vous vous retrouvez face à un canevas vide. Créez une salle en appuyant sur R, puis appuyez sur Entrée : vous pouvez alors changer le nom de la salle, mettre des indications, sa description (qui apparaîtra quand vous passez la souris sur la boîte), etc. Vous pouvez aussi faire une liste des objets dans la salle, ce qui peut vous aider pour les jeux avec beaucoup d’énigmes à la fois ou beaucoup de choses qui pourraient être utiles mais pour lesquelles vous devrez revenir plus tard : la règle, c’est un objet par ligne, et si un objet est dans un autre, on l’écrit dessous et avec une espace en plus au début de la ligne. Vous pouvez aussi, si le cœur vous en dit, changer le style de boîte (bords ronds, en pointillé, etc.) pour distinguer certaines salles visuellement ; ça peut vous servir si vous vous créez un code (« les salles secrètes en ellipse », par exemple), ou si vous voulez faire une jolie carte de votre jeu afin de pouvoir la donner au joueur avec votre jeu (ou la faire payer, si vous êtes comme ça). Bref, beaucoup d’options pour ajouter des informations et des annotations à votre carte.

Ma première pièce avec Trizbort !

Ensuite, vous pouvez en créer une autre, et faire de même. Et pour les relier, vous avez le choix : vous pouvez soit sélectionner les deux salles et appuyer sur J et laisser Trizbort faire le lien, soit dessiner à la main, en cliquant sur un des points d’accroche qui apparaissent quand vous passez la souris sur une salle, et en tirant le lien. Les liens persisteront, même si vous décidez de bouger une salle, de réorganiser toute une section (vous pouvez sélectionnez plusieurs salles et les déplacer, pas de souci), ou agrandir ou diminuer la taille d’une boîte ! L’outil est vraiment flexible et vous permet de faire beaucoup de choses graphiquement avec les boîtes.

Le reste de ma maison est en passe d’être raccordé…

J’ai déjà vu certains articles expliquant que l’on pouvait utiliser Twine pour faire des dessins de boîtes reliées entre elles, pour plusieurs applications (flowchart du jeu, dépendance entre les énigmes, etc.). J’aurais tendance à dire personnellement que Trizbort est plus flexible graphiquement (plein de couleurs, de types de boîtes, de types de liens), et permet de coder plus d’informations différentes, donc c’est vraiment un outil à ne pas négliger ; en revanche, il est peut-être moins portable (Windows seulement, alors que Twine fonctionne même dans un navigateur), et Twine permet de générer une page web avec les liens cliquables, ce qui peut être utile pour simuler l’expérience utilisateur. Bref, à ne pas négliger pour des utilisations qui ne sont pas forcément des cartes.

Une autre carte, celle de Jason Dyer jouant à Adventure 501, créée avec Trizbort.

Enfin, je ne peux pas parler de Trizbort sans évoquer une fonctionnalité extraordinaire, qui a changé la façon dont je travaille : l’export de code. Oui, vous pouvez dessiner la carte d’un jeu, avec les bons noms, les descriptions, et la liste des objets, et ensuite demander à Trizbort d’exporter le code vers un langage de programmation de fiction interactive à analyseur syntaxique ! Beaucoup de langages sont pris en charge : Inform 6 et Inform 7 (code en anglais), TADS, Alan, Hugo et ZIL.

Ceci fait que vous pouvez utiliser Trizbort comme un outil de prototypage : faites une ébauche de carte pour votre jeu, mettez les quelques objets dont vous aurez besoin dans le jeu, et exportez le code, et vous avez maintenant le squelette de votre jeu, à remplir avec toute la logique du jeu et les interactions — ça vous donne une bonne longueur d’avance, sans avoir eu à toucher au code !

Bref, que vous soyez joueur ou auteur, Trizbort est un outil moderne qui gagne à être connu et peut vous être très utile !

Faites votre propre invite de commande dans Inform 6

Hugo Labrande

Le personnage central de la fiction interactive à parser, c’est l’invite de commande, ou pour faire en anglais, le prompt. Ah, le prompt. Si on a l’habitude, c’est un ami que l’on attend impatiemment pour taper notre idée suivante ; pour un débutant, c’est un symbole arbitraire et déconcertant, dont on a du mal à comprendre ce qu’il nous veut. Par sa forme, le plus souvent un chevron (>), il rappelle les invites de commandes classiques que les programmeurs, notamment sous Linux, connaissent très bien. Naturel dans les années 1970, car c’était la seule interface connue, il a été supplanté par des interfaces graphiques plus intuitives, si bien que, pour beaucoup de joueurs modernes, c’est sans doute la première fois qu’ils voient ce genre de choses.

Cet article vous apprendra diverses façons, dans divers contextes, de modifier le prompt ; bien souvent, vous ferez ces modifications dans un souci d’ergonomie, et pour rendre plus intuitive l’interface, même si vous aurez peut-être des idées pour d’autres modifications. Les possibilités sont illimitées ! Voici cependant quelques choses assez classiques.

En Inform 6, les réponses par défaut sont spécifiées dans la fonction LanguageLM, qui regroupe toutes les réponses de la bibliothèque à des actions (et c’est la partie qu’on traduit pour faire des bibliothèques Inform 6 en français, en allemand, en slovène, etc.). On modifie ces réponses par défaut avec l’objet spécial LibraryMessages, plutôt que d’aller trifouiller dans LanguageLM. Pour ce qui est de l’invite de commandes, ça a été implanté comme une fausse action, Prompt ; ce qui fait que, comme toutes les réponses par défaut, le texte de l’invite de commande est stocké dans LanguageLM, et est modifiable par LibraryMessages.

Concrètement : je veux faire un jeu original où l’invite de commande est à l’envers (waouh). Il suffit d’écrire le bout de code suivant, et de le mettre entre l’inclusion de Parser et celle de Verblib :

Object LibraryMessages

 with before [ ; Prompt: "^< "; ];

Si je veux le changer pour qu’il demande de taper une commande (ce qui est quand même plus explicite, tout de même) :

Object LibraryMessages

 with before [ ; Prompt: "^Tapez votre commande ici : "; ];

Le seul avertissement que je donnerais, c’est qu’il y a des cas particuliers qui ne seront pas remplacés par ce message (enfin, vu les bibliothèques à l’heure actuelle) : les prompts de YesOrNo(), de LowKeyMenu() et celui de la fin (« Voulez-vous RECOMMENCER… »), par exemple. Mais ce sont des cas mineurs, qui se règlent en modifiant directement la bibliothèque.

Le plus beau, c’est que vous pouvez mettre n’importe quel bout de code à cet endroit-là ! Par exemple, vous pouvez faire varier le message de prompt pendant la partie. L’exemple donné dans le Designer’s Manual 4 (exercice 59) est celui d’une invite de commande qui demande « Qu’est-ce que vous, le détective, faites maintenant ? » au premier tour, puis « Et ensuite ? » aux tours suivants ; on peut même dire au joueur au bout d’un certain temps que l’on raccourcit le message. Ça donne :

Object LibraryMessages

 with before [; Prompt: switch (turns) {

 1: print "^Qu’est-ce que vous, le détective, faites maintenant ?^>";

 2 to 9: print "^Et ensuite ?^>";

 10: print "^(Ça ne serait pas fatiguant, les @<< Et ensuite ? @>> à chaque fois ? À partir de maintenant le prompt sera plus court.)^^>";

 default: print "^>";

 } rtrue; ];

À vous de voir comment adapter ce bout de code, sachant que vous pouvez utiliser n’importe quelle condition pour faire varier votre message — à vous de jouer !

Bonus pour les fans de Vorple

Vorple ajoute des possibilités intéressantes, notamment dans l’extension « Vorple command prompt control ». Vous pouvez par exemple préremplir le prompt, ce qui peut être utile par exemple pour faire une section « tutoriel » à votre jeu : lorsque vous laissez la main au joueur, il y a déjà écrit « ouvrir la porte » dans l’invite de commande, et le joueur n’a pas à se demander comment taper la commande. Vous pouvez aussi le cacher, en faisant attention : le jeu n’avance que si des commandes sont exécutées, donc il faut avoir des hyperliens prêts, mais ça rend possible de faire une section (ou un jeu) « à la Twine » très facilement ; pour plus de précisions, vous pouvez voir le message de Corax sur le forum.

Vous pouvez aussi ajouter un un infobulle sur l’invite de commande, par exemple pour un message de bienvenue, ou une indication discrète de ce que le joueur devrait faire ; ce sont des outils plutôt chouettes pour cet effet, car ils sont visuellement distincts du monde décrit dans le jeu, s’adressent directement au joueur et disparaissent au bout de quelques secondes. Par exemple, on peut mettre une bulle sur l’invite de commande pendant le premier tour, en écrivant dans Initialise :

VorpleTooltipPrompt("Si vous n’avez jamais joué à une fiction interactive, tapez TUTORIEL.");

Enfin, une dernière possibilité offerte par Vorple est de mettre un hyperlien sur le prompt ; j’ai déjà utilisé ceci dans mes jeux (par exemple dans Tipelau), pour qu’un clic sur l’invite de commande soit équivalent à l’action « regarder ». Le souci dans Vorple est que le prompt doit être une chaîne de caractères, et donc on ne peut pas rajouter d’hyperliens directement (et c’est dommage ; le prompt est quelque chose de spécial, pris en compte séparément par Vorple). Je vous livre alors mon secret : on peut rajouter un peu de code JavaScript au bon endroit dans le jeu pour remplacer le prompt habituel par une version avec un hyperlien :

Object linkOnPrompt "hyperlink on prompt" VorpleInterfaceUpdate

 with description [; VorpleExecuteJavaScriptCommand("$($(haven.prompt.get()).find('#lineinput-prefix').contents()[0]).replaceWith('

<a class="~vorple-link" href="~#~" data-command="~regarder~" data-silent="~false~">&gt;</a>')"); ];

Avec tout cela, plus d’excuses pour se contenter d’une invite de commande classique !

PhraseNames, mon extension I6 pour varier les tournures

Hugo Labrande

La propriété name d’Inform 6 est très facile à utiliser : on met la liste des mots qui ont un rapport avec l’objet, qui pourraient être des synonymes, et on laisse le système analyser l’entrée du joueur, compter le nombre de mots consécutifs qui se rapportent à chaque objet, et déterminer l’objet qui a le meilleur score, et donc le plus de chances de correspondre à ce que la joueuse voulait dire. Mais quand on gratte un peu, on s’aperçoit qu’il y a beaucoup de problèmes potentiels : comment différencier l’échoppe et la porte de l’échoppe ? Comment différencier le bleu de travail du ciel bleu ? Comment différencier la lance en fer et le fer de lance ?

Ce problème se pose moins avec Inform 7, car on peut définir des locutions comme se référant à l’objet :

Understand "ciel bleu" as le ciel.

Understand "bleu" as le bleudetravail.

Cette formulation rend aisée la définition d’une expression, ou juste l’ajout d’adjectifs, comme synonyme du mot ; ça manquait à Inform 6, et ça me manquait, j’avoue que j’étais un peu jaloux.

Pname, de Neil Cerutti

J’ai regardé les extensions Inform 6 qui étaient disponibles, et il n’y en avait pas beaucoup qui me plaisaient ; certaines permettaient de définir certains mots comme étant des adjectifs, ce qui n’était pas exactement ce que je cherchais… Finalement, j’ai trouvé l’extension « pname » de Neil Cerutti (mentionnée dans le chapitre « Too Many Toilets » de l’Inform Beginner’s Guide).

« pname » est une extension très chouette, qui m’a donné beaucoup d’idées pour PhraseNames. En gros, on peut définir une propriété pname, qui contiendra des suites de mots, comme ceci :

 '.x' 'red' '.x' 'toilet' 'door'

 'soda' '.or' 'coke' 'machine'

 '.x' 'old' '.x' 'brass' 'lantern' '.p' '.x' 'shining' 'light'

Dans cette suite de mots, il y a des petits marqueurs, qui indiquent au parser la fonction des mots ; ces marqueurs sont ceux qui commencent par un point.

Le mot '.x' indique que le mot suivant est optionnel : s’il n’est pas dans la commande, ça n’est pas grave (si vous ne mettez pas le '.x' devant red, on ne pourra pas dire door, le joueur devra absolument écrire « red door ».).

Le mot '.or' veut dire que le mot suivant est une alternative au mot qu’on vient de passer : « soda machine » et « coke machine » sont tous les deux acceptés.

Le mot '.p' est là pour séparer deux expressions : la troisième ligne définit « old brass lantern » et « shining light ».

Et enfin, les mots doivent être lus dans la commande dans le même ordre que dans la propriété pname : « machine coke » ne marchera pas.

Avec ceci, on peut faire pas mal de choses : la troisième ligne permet de reconnaître « lantern », « brass lantern », « old brass lantern », « old lantern », « light » et « shining light ».

Il y a en revanche quelques problèmes avec cette extension. Premièrement, certaines choses sont toujours impossibles, ou du moins difficiles à écrire de façon compacte ; par exemple, reconnaître « barre en fer » sans reconnaître « barre en ». Mais surtout, l’extension date de 2001, et fonctionne en remplaçant 4 routines de la bibliothèque Inform 6 version 6.10 — et on en est à la 6.11 ou 6.12, donc le code qui a été copié-collé à l’époque a depuis été modifié, corrigé, etc. Utiliser cette extension, c’est se priver de 15 ans de développement sur la bibliothèque Inform 6 ! Il fallait donc une autre solution.

Étendons cette extension avec PhraseNames

PhraseNames repose sur le même principe, celui d’écrire des motifs avec des mots spéciaux pour préciser ce qui est autorisé ou non pour faire référence à l’objet, et ainsi d’éviter d’écrire des parse_name à foison. Cependant, elle ne touche pas à la bibliothèque Inform 6 (et est donc compatible avec n’importe quelle version de celle-ci), et est plus flexible, grâce à l’ajout de fonctionnalités. PhraseNames, c’est pname, en mieux !

Pour utiliser PhraseNames, il faut inclure l’extension, puis remplacer parse_name par la routine ComplicatedParsing ; on peut ensuite faire ce genre de manipulations. Voici la définition par PhraseNames de la lanterne :

Include "PhraseNames";

Object lantern "old brass lantern"

 with parse_name ComplicatedParsing,

 phrase_name '.opt' 'old' '.opt' 'brass' 'lantern' './' '.opt' 'shining' 'light;

Exactement le même principe que pour pname, sauf qu’on dit '.opt' pour un mot optionnel, et qu’on sépare par des './' les locutions. Le '.or' reste identique et a la même fonction, et on peut les enchaîner aussi ; bref, tout ce qui est faisable avec pname est faisable avec PhraseNames.

J’ai aussi rajouté d’autres fonctionnalités bien utiles. Par exemple, on peut aussi écrire '.name', pour dire « n’importe quel mot que vous avez mis dans la propriété name de l’objet ». Ceci permet d’écrire des choses comme :

name 'depliant' 'papier' 'livre' 'livret' 'fascicule',

phrase_name '.name' '.opt' 'colore';

Ce qui donne « papier coloré », « livre coloré », « dépliant coloré », « fascicule coloré », d’un coup d’un seul !

On peut aussi ajouter non pas un mot optionnel, mais des suites de mots optionnelles, avec le mot-clé '.opt(' ; on ferme la parenthèse avec '.)'. Par exemple :

name 'barre' 'barreau',

phrase_name '.name' '.opt(' 'de' '.or' 'en' 'fer' '.or' 'metal' '.)',

Et voilà pour « barre de fer », « barreau en fer », « barreau en métal », etc. !

La seule contrainte est qu’on ne peut pas mettre de '.opt(' dans un '.opt(' – trop difficile à prendre en charge pour moi !

À cause de cette restriction, j’ai ajouté une fonctionnalité pour compenser : il s’agit de '.rest', qui veut dire « et puis le reste de la phrase, qui est optionnel ». Ceci permet de complexifier les choses, car on peut alors utiliser des '.opt(' en suivant ; c’est un peu comme un '.opt(' dans un '.opt(' en bout de phrase (miam, n’est-ce pas ? N’est-ce pas ??). Cela permet des exemples compliqués, comme celui-ci, tiré de Homeland Security :

name 'depliant' 'papier' 'livre' 'livret',

phrase_name '.name' '.opt' 'colore' '.rest' 'sur' '.opt' 'la' 'securite' '.opt(' 'de' '.or' 'a' 'l^' 'aeroport' '.)' '.opt(' 'd^' 'Aruba' '.)'

Ceci permet de reconnaître « papier coloré », « dépliant sur la sécurité de l’aéroport d’Aruba », « livre sur la sécurité à l’aéroport », et d’autres tournures complexes comme celles-ci ! C’est en fait cet exemple qui m’a motivé à développer l’extension : après quelques heures de réflexion, j’étais arrivé à créer un parse_name compliqué de 22 lignes, alors que ça prend une seule ligne avec PhraseNames ! Un autre exemple, tiré de Tipelau :

name 'generateur' 'generateurs' 'moteur' 'moteurs',

phrase_name '.name' '.opt(' '.opt' 'a//' 'diesel' '.or' 'petrole' '.or' 'essence' '.)' '.rest' '.opt' 'du' 'village';

Avec cette extension, vous pourrez créer très facilement des noms complexes pour vos objets. De plus, avec les différentes possibilités pour les sections de l’expression, on arrive facilement à reconnaître des dizaines, voire des centaines de formulations équivalentes pour le même objet ! Bref, je n’en suis pas peu fier : allez la télécharger sur mon Bitbucket, et à vous les noms longs et flexibles pour tous vos objets en un rien de temps !

Des trophées pour le joueur, en Inform 6

Hugo Labrande

Si vous avez joué à des jeux vidéo récemment, que ce soit sur une console ou sur PC, vous savez sûrement ce qu’est un achievement, un succès, un trophée, une médaille, un haut fait (ma traduction préférée, celle de World of Warcraft). Certaines actions dans un jeu sont récompensées par les développeurs par un badge ; à vous de les trouver, de les collectionner, de consulter les guides en ligne pour trouver comment obtenir ceux qui vous manquent, etc. C’est purement pour le fun (parfois on vous donne des points attachés à votre compte, mais les points ne servent à rien de toute façon), un peu parfois pour le prestige puisque certains sont plus durs que d’autres (et il y a des sites pour se comparer aux autres, bien sûr), et surtout ça vous donne une carotte pour explorer le jeu dans ses moindres recoins ou jouer au jeu d’une façon différente de celle dont vous avez l’habitude.

Pour le développeur, c’est un très bon moyen d’augmenter la durée de vie du jeu pour pas cher, et de valoriser tout le travail qui a été fait sur le jeu (puisque si une joueuse veut tous les succès, elle est un peu obligée d’aller dans des recoins qu’elle n’aurait peut-être pas exploré, ou des modes de jeux qu’elle aurait ignorés sinon, ce qui rentabilise le travail du développeur).

Dans le monde de la FI, une sorte d’équivalent de ceci pourrait être la commande « amusing ». Il s’agit d’une des options données une fois qu’on a fini le jeu avec le score maximum ; c’est une fonctionnalité inventée par Inform. Quand on sélectionnait cette option, on avait « avez-vous essayé de… » et une liste de commandes à essayer dans le jeu ; en fin de compte, une liste d’easter eggs, de réponses drôles cachées dans le jeu, que le joueur est invité à essayer en y rejouant. Le but est donc un peu similaire : pousser le joueur à passer un peu plus de temps sur le jeu et à en explorer les moindres recoins.

Pourquoi n’y en a-t-il pas plus dans les fictions interactives ? La tradition s’est en effet un peu perdue, ou peut-être est-ce la taille des projets qui a diminué – même si, de nos jours, les jeux Choice of Games ont un nombre conséquent de succès à débloquer en explorant le graphe narratif. Une raison est également qu’une grande partie de l’intérêt des achievements est qu’ils sont permanents, reliés à votre compte de joueur, et que vous pouvez ensuite faire la liste de ceux que vous avez obtenus et les comparer à d’autres joueurs. (C’est aussi l’intérêt pour les entreprises comme Steam ou Sony : vous avez passé du temps sur ce compte à accumuler les succès, donc restez fidèles pour ne pas les perdre…). Et comme on n’a pas de Steam pour la FI, ou même peu de FI sur Steam, la question ne s’est pas trop posée, en tout cas pas dans la scène amateur.

Il y a cependant de plus en plus de fictions interactives qui sortent sur des plateformes grand public comme Steam, le Play Store, ou même la Switch. Il est donc bon d’y réfléchir, et d’avoir des outils adaptés. Pour ce qui est des jeux avec analyseur syntaxique, ceux sur lesquels je me concentre dans l’article d’aujourd’hui, il y a quelques outils écrits ces dernières années. Pour Inform 7, il me semble que la plus utilisée (par exemple dans le jeu Scroll Thief) est Achievements de Mikael Segercrantz, mais il en existe d’autres, comme celle-ci de Juhana Leinonen.

Les succès de Kerkerkruip, affichés sous forme de menu — basique, mais fait le boulot.

En Inform 6, c’est l’extension « achievements.h » de Vince Laviano, développée en 2016, qui vient remplir ce rôle — et très franchement, elle est bien faite, et très facile d’utilisation ! Je vais vous la présenter brièvement, pour que vous puissiez voir les principes qui sous-tendent sa conception.

L’extension implémente un trophée comme un objet de la classe Achievement, comme ceci :

Achievement metro "Métro mais pas trop"

 with description "Prenez le métro au moins une fois.";

Une fois déclaré, vous pouvez le décerner au joueur en appelant la routine award, qui est déclarée dans la classe Achievement. En écrivant metro.award();, on donne le trophée au joueur, et celui-ci apparaîtra alors comme débloqué. Le joueur peut regarder la liste des succès, et voir ceux qui ont été débloqués, en tapant la commande « achievements ».

Un autre type de succès est ceux basés sur la répétition d’une action ; on utilisera pour ceux-ci CountingAchievement :

CountingAchievement sousmarins "Terreur des océans"

 with target_count 5,

 description "Détruisez 5 sous-marins.";

Il y a aussi les trophées qui sont « en plusieurs parties », dans ce cas c’est un MultiAchievement :

MultiAchievement pinballwizard "Pinball Wizard"

    with num_tasks 5,

    description "Obtenez le high score sur les 5 flippers.";

La différence est que pour un CountingAchievement on peut faire 5 fois la même action, alors que pour un MultiAchievement il faut faire 5 actions différentes, que vous numérotez dans le code (ici, de 0 à 4 inclus). Quand le joueur a fait le high score sur le flipper numéro 0 on écrit pinballwizard.complete_task(0); ; et pour tester si le joueur a un high score sur le flipper numéro 2 on fait pinballwizard.is_task_completed(2);.

Il y a enfin les MetaAchievements, c’est-à-dire les succès qui sont décernés une fois que d’autres succès ont été décernés :

MetaAchievement tout "100 %"

 with description "Obtenez tous les succès.",

 depends_on metro sousmarins pinballwizard;

Il y a quelques autres fonctionnalités intéressantes : donnez l’attribut concealed à un succès et il sera caché dans la liste ; donnez l’attribut persistent pour qu’il soit inscrit dans un fichier à part et que le jeu puisse se souvenir que ce succès a été obtenu, même si le joueur commence une autre partie. Vous pouvez aussi faire parfait.fail() si vous voulez que l’achievement échoue, c’est-à-dire que le joueur ne pourra plus jamais le débloquer (ici par exemple le joueur a fait une erreur). Enfin, un succès qui est réussi est déplacé dans l’objet EarnedAchievements — ainsi, si vous voulez tester que le succès a été réussi, vous pouvez écrire if parfait in EarnedAchievements.

Le développement de cette extension s’est arrêté ; il y a deux ou trois problèmes mais globalement le code est très complet et plutôt dense. J’ai beaucoup aimé le principe de cette extension quand elle est sortie, et je l’ai reprise récemment pour y ajouter quelques bouts de code spécifiques à Vorple. Vous pouvez voir cette extension en action dans mon jeu Neon Vertex : une notification s’affiche lorsque vous obtenez un succès, et il est possible de déclarer une icône pour chacun des succès et afficher la liste en tant que succession d’icônes, comme sur Steam !

Vous pourrez trouver le code sur le dépôt de code de Vorple sur Github. J’espère qu’elle vous sera utile !