Que serait un bon jeu de rôle (papier ou vidéoludique) sans système d’inventaire, où stocker les babioles que votre personnage ramasse à droite et à gauche ? Sans boutique, où vous pouvez dépenser votre fortune durement accumulée en échange d’objets divers ?

Certains l’auront remarqué : Twine ne propose pas d’inventaire « prêt à utiliser ». Heureusement, il fournit toutes les macros nécessaires pour vous permettre d’en créer un ! Dans ce tutoriel, nous vous guiderons pas à pas pour ajouter à vos aventures un système d’inventaire simple, ainsi qu’une boutique basique, au format Twine 2 Harlowe. Si vous maîtrisez les fonctions de base de Twine (liens pour aller d’un passage à un autre, variables simples) vous pouvez poursuivre sans souci la lecture de cet article, sinon d’autres articles sur ce site abordent ces aspects : rendez-vous donc ici pour l’installation de Twine et les premières explications ou là pour en apprendre plus sur les variables.

Créons notre inventaire

Un inventaire, c’est tout simplement une liste des possessions de notre personnage. Par chance, l’une des structures de données de Twine, l’array (ou tableau), fonctionne exactement comme une liste ordonnée de valeurs : on peut la rallonger en y ajoutant des valeurs, la raccourcir, chercher si un élément se trouve à l’intérieur, afficher tout ce qu’elle contient, ou bien seulement la troisième ligne, etc.

Créons donc un array vide, nommé inventaire, au tout début de notre jeu, dans un passage portant le tag « startup », qui sera donc lu à chaque fois qu’on lance une nouvelle partie. Comme nous prévoyons de créer une boutique à l’avenir, il nous faudra aussi garder en mémoire la fortune du joueur : une simple variable numérique devrait suffire.

{
(set: $inventaire to (a: ))
(set: $euros to 0)
}

Le joueur commencera donc avec un inventaire vide. Il ne nous reste plus qu’à lui donner des occasions de le remplir. Par exemple, imaginons qu’il trouve à ses pieds une bourse bien pleine, nous voulons donc qu’il puisse cliquer sur un lien « Ramasser la bourse » pour l’ajouter à son inventaire. Il nous faudra aussi augmenter notre variable $euros du contenu de la bourse.

(link: "Ramasser la bourse")[
Vous ramassez la bourse. Elle contient 100 €.
{ (set: $euros to it + 100)
(set: $inventaire to it + (a: "Bourse : $euros €")) }
]

Nous venons donc de créer un lien, avec la macro (link: ), qui disparaîtra une fois cliqué, pour éviter que le joueur ne devienne riche en ramassant une infinité de bourses. Lorsqu’on clique dessus, ce qui est contenu entre les crochets apparaît. D’abord, on affiche un petit texte pour informer le joueur que son action a réussi puis, entre les accolades (qui indiquent à Twine de ne pas afficher les sauts de lignes et autres espaces blancs qui s’y trouvent), on ajoute d’abord 100 à la variable $euros, puis on allonge la liste $inventaire, en y ajoutant un array d’une seule ligne, qui contient une chaîne de caractères correspondant à l’objet qu’on vient de ramasser.

Imaginons que, plus loin dans l’histoire, on souhaite donner l’option au joueur de se débarrasser de la bourse. Tout d’abord, il faut s’assurer que le joueur la possède bien dans son inventaire, à l’aide d’une macro (if: ), et, si c’est le cas, ajouter un lien permettant de réaliser l’action.

(if: $inventaire contains "Bourse : $euros €")[ { 
  (link: "Jeter la bourse")[
  Vous vous débarassez de la bourse et de son précieux contenu.
  (set: $inventaire to it - (a: "Bourse : $euros €"))
  (set: $euros to 0)
  ]
}]

Ce petit extrait de code montre d’abord comment vérifier si un objet est présent dans l’inventaire du joueur et ajouter une option en conséquence, puis comment enlever des éléments de l’inventaire du joueur. On remarque que le procédé est très similaire à l’ajout d’objets dans l’inventaire : il suffit de remplacer le + par un - !

Si vous avez testé le code présenté dans une histoire Twine au fur et à mesure (c’est très conseillé), vous vous êtes sans doute rendu compte d’un petit problème. En effet, si le jeu semble retenir ce qu’on a ajouté ou supprimé de l’inventaire, le contenu de celui-ci n’est affiché nulle part, le joueur doit donc pour l’instant tout retenir par cœur, ou bien prendre des notes, ce qui est loin d’être idéal ! Heureusement, il est possible de remédier à ce problème.

Afficher notre inventaire

Une solution naïve serait d’utiliser tout simplement un (print: $inventaire). Cela fonctionne, mais le résultat n’est pas très élégant : tous les éléments de la liste sont affichés sur la même ligne, séparés par une simple virgule. On préfèrerait les présenter en colonne, comme une liste à puce. Pour ce faire, créons un nouveau passage (que je nommerais sobrement « Inventaire »), et utilisons le code suivant :

#Inventaire
(for: each _item, ...$inventaire)[* _item ]

La première ligne affiche juste « Inventaire », dans le style d’un titre de premier niveau. C’est l’équivalent de <h1>Inventaire</h1> en HTML. Ajouter plus de # permet de créer des titres de niveau inférieur (## pour <h2>, ### pour <h3>, etc.). Ne reste plus qu’à en modifier le style à votre guise !

La seconde ligne est celle qui nous intéresse tout particulièrement. Elle utilise la macro (for: ) , qui permet comme son nom l’indique d’exécuter une boucle for, bien connue des adeptes d’informatique. Pour les autres, pas de panique, son fonctionnement n’est pas très compliqué ! Cette boucle va permettre de répéter les instructions entre crochets un certain nombre de fois. Ici, la boucle commence par créer une variable temporaire (ce que la documentation de Harlowe appelle un lambda, qui correspond peu ou prou à une variable locale) _item, puis elle donne à _item la valeur du premier élément de notre inventaire, crée une ligne de liste à puce, écrit le contenu de _item (le premier élément de l’inventaire, donc), puis elle passe à la ligne suivante, et recommence, jusqu’à ce qu’elle arrive au bout de notre array.

Pour que ce passage apparaisse en jeu, on peut insérer un lien vers le passage « Inventaire », bien sûr, mais on peut faire mieux : l’afficher dans une colonne à droite des passages de notre aventure. On vous a déjà expliqué sur ce site comment utiliser les fonctionnalités HTML/CSS permettant de réaliser toutes sortes de mises en pages compliquées. Si cet outil est très puissant, il est aussi un peu lourd à utiliser, et un peu complexe pour un novice en HTML. Or ici, on souhaite faire quelque chose de très simple : juste une présentation en deux colonnes. Ça tombe bien : Harlowe donne la possibilité de créer jusqu’à 3 colonnes directement dans Twine. Dans notre cas, on va utiliser la structure suivante :

|||==
Texte de notre passage.
Description, description.
[[Choix 1]]
[[Choix 2]]
=|
(display: «Inventaire»)

La première ligne crée la colonne de gauche. Elle a une largeur de 3 unités et une marge à droite large comme deux caractères. On saute ensuite une ligne et on écrit tout le texte qu’on veut afficher dans notre colonne de gauche, à savoir, le contenu de nos passages, avec ses descriptions et ses liens. La cinquième ligne de cet exemple crée une colonne de droite, d’une unité de largeur, avec une marge à gauche du texte d’un caractère. C’est là que nous souhaitons afficher notre inventaire, on utilise la macro (display: ) qui va tout simplement coller ici le contenu du passage intitulé « Inventaire » que nous venons de créer. Ça nous évitera de le copier-coller nous-mêmes à chaque passage.

Vous pouvez ajuster la largeur de vos colonnes en modifiant le nombre de | : chaque symbole ajoute une unité de largeur. Il en est de même pour la largeur de la marge, en ajoutant ou en supprimant des =, à raison d’un caractère par =. Si vous souhaitez un jour créer une colonne du milieu, il vous faudra une ligne du type =|=, avec le nombre de = et de | que vous préférez. Si vous vous demandez à quoi correspond une unité de largeur, c’est bien simple : lorsque la page est créée, Twine va compter le nombre d’unités de largeur total de toutes les colonnes, diviser la largeur disponible par ce nombre (dans notre cas, 4) et attribuer comme il se doit la place à chacune de nos colonnes (notre colonne de gauche occupera les trois-quarts de la largeur de la zone de texte, et celle de droite le quart restant).

Voilà ! Je pense qu’avec tout ça, vous êtes capables de créer un inventaire qui fonctionne et de l’afficher à droite de votre texte. Il ne reste plus qu’à le remplir à présent ! Pour ce faire, créons une boutique toute simple, ou notre héros pourra acheter ou revendre toutes sortes d’objets.

Faisons l’inventaire de notre boutique

Une boutique, c’est tout un tas de choses : un lieu (un ou plusieurs passages, dans le cadre de Twine), dans lequel on peut se rendre (via des liens), un vendeur avec qui ont peut discuter (d’autres passages, reliés par des liens) — je laisse tout cela à vos bons soins d’auteur — mais aussi une liste d’objets à vendre, ayant chacun un nom, un prix, éventuellement une description… et c’est ce qui va nous intéresser ici.

Notre boutique, donc, dispose elle aussi d’un inventaire, dans le principe pas si différent de celui du joueur. Cependant, utiliser une simple liste pour gérer tout le catalogue, ça paraît un peu léger : savoir quels objets sont disponibles à l’achat ne suffit pas, on a aussi besoin de garder en mémoire certaines de leurs caractéristiques. Pour cela, un array ne convient pas, il va nous falloir une autre structure de données : le (la ?) datamap.

Un datamap, c’est un « tableau » à deux colonnes, qui associe à chaque élément de la colonne de gauche l’élément qui lui fait face dans la colonne de droite. Malheureusement, il n’est pas possible de créer de tableaux à plus de deux colonnes, aussi, pour gérer des objets à plus d’une caractéristique, il va falloir créer plusieurs datamaps, tous avec la même colonne de gauche. Commençons par en créer un qui associe à chaque objet de la boutique son prix, et un autre qui lui associe une description, dans un passage possédant le tag « startup ».

(set: $prix to (dm:
  "Toupie",3,
  "Voiture radiocommandée",17,
  "Set de petit-chimiste",22,
  "Poupée à coiffer",18
)
)
(set: $desc to (dm:
  "Toupie","Une jolie toupie de bois aux couleurs vives",
  "Voiture radiocommandée","Un modèle réduit de voiture de rallye et sa manette. Piles AA non incluses",
  "Set de petit-chimiste","Un lot contenant béchers en plastiques et boites aux substances multicolores. Dès 8 ans.",
  "Poupée à coiffer","Une poupée habillée à la mode à la chevelure soyeuse et étincelante. Accompagnée d'une brosse miniature et de pinces et élastiques."
)
)

Sous la description de notre boutique, nous pouvons maintenant afficher la liste de tous les objets à vendre, accompagnés de leur prix et de leur description. Le processus sera très similaire à ce qu’on a fait pour afficher l’inventaire de notre joueur :

(for: each _item, ...(datanames:$desc))[* _item ( (print: _item of $prix) €) : (print: _item of $desc)]

La boucle est un poil plus compliquée que la précédente, mais c’est tout à fait normal : il y a plus d’informations à afficher après tout.

La première chose qu’on remarque, c’est qu’on n’a pas pu mettre directement …$desc ou …$prix dans notre macro (for: ). En effet, elle n’accepte pas les datamaps : elle ne saurait pas quoi faire avec ces deux colonnes. On lui précise donc qu’on souhaite effectuer notre boucle sur la première colonne en utilisant (datanames: $desc) (ou (datanames: $prix), au choix, puisque la première colonne est la même pour ces deux tableaux), qui crée un array identique à la colonne de gauche de notre datamap.

À l’intérieur des crochets, on commence de la même façon, avec notre puce, le nom de l’objet stocké provisoirement dans _item. Puis, on veut montrer avec un (print: ) le prix correspondant entre parenthèses. Pour le retrouver, on cherche dans le datamap $prix la ligne correspondant à l’objet dans _item. Deux syntaxes équivalentes sont reconnues par Harlowe, $prix's _item ou bien _item of $prix (utilisée dans l’exemple). De la même façon, on retrouve la description correspondante à l’objet  dans $desc.

Les joueurs peuvent maintenant allègrement explorer les rayonnages, observer les objets mis en vente, regarder les étiquettes… Mais pas encore les acheter ! Remédions à ça tout de suite.

Acheter des objets

Pour que le joueur puisse agir, il faut lui créer des liens. Ajoutons donc un lien « Acheter » à chaque ligne de notre liste. Il faut alors vérifier que le joueur possède suffisamment d’argent ; si c’est le cas, on enleve à $euros le prix de l’objet, on ajoute l’objet à son inventaire et comme toujours on l’informe de son achat d’une petite phrase. S’il n’a pas assez d’argent, on lui indique qu’il ne peut pas acheter cet objet.

Ajoutons donc cela à l’intérieur de notre boucle :

(for: each _item, ...(datanames:$desc))[* _item ( (print: _item of $prix) €) : (print: _item of $desc)
{(link-repeat: "Acheter")[
  (if: _item of $prix <= $euros)[
    	(set: $euros to it - _item of $prix)
(set: $inventaire to it + (a: _item))
	Vous achetez _item et il vous reste $euros €.
    ]
  (else:)[Vous n'avez pas assez d'argent pour acheter ça.]
  ]
}]

On utilise cette fois un (link-repeat: ) afin que le lien ne disparaisse pas quand on clique dessus ; le joueur peut alors acheter autant de fois le même produit qu’il le souhaite, dans la mesure de ses moyens. Dans le (if: ), on vérifie que le prix de l’objet choisi (_item of $prix) est bien inférieur ou égal au pactole de notre joueur contenu dans $euros. Si c’est le cas, l’achat a lieu, on modifie $euros pour refléter ses dépenses et on ajoute l’objet en question à l’inventaire du joueur. Sinon, on utilise (else: ) pour informer au joueur que ce n’est pas possible.

Il est maintenant possible d’acheter tout ce qui se trouve dans la boutique ! Pour la vente, c’est presque la même chose, si ce n’est qu’il faut ajouter l’option « vendre » dans une boucle sur l’inventaire du joueur, qu’il n’y a pas besoin de (if: ), qu’on augmente $euros au lieu de réduire sa valeur et qu’on retire l’objet de l’inventaire du joueur au lieu de l’y ajouter. Normalement, vous avez à ce stade toutes les cartes en main pour rédiger vous-même le code correspondant !

Pour aller plus loin

Dans ce tutoriel, vous avez appris à mettre en place un système d’inventaire et une boutique, et découvert deux structures de données, les arrays et les datamaps, ainsi que les principales fonctions qui y sont associées.

Harlowe possède une troisième structure de données, le dataset, qui ressemble beaucoup à l’array. Il permet de créer des listes sans ordre particulier (impossible, donc, d’afficher le 5e élément) et qui ne peuvent pas contenir de doublon (si on tente d’y ajouter un élément qui figure déjà dans le dataset, rien ne se passe). Je n’ai pas trouvé quoi en faire, mais peut-être aurez-vous plus d’imagination !

Si vous le désirez, vous pouvez complexifier votre boutique, par exemple en limitant le nombre d’objets de même type disponible, regrouper les objets du même type possédés par le joueur sur une seule ligne, ou bien adapter la formule des achats pour créer un médicament ou une potion qui restaure les points de vie.