Dendry est un moteur de jeux à choix, basé sur le principe des narramiettes. Il a été développé par Ian Millington entre 2014 et 2015 puis repris par Autumn Chen en 2020, qui l’a utilisé pour créer de nombreux jeux très bien reçus du public et de la critique. Il s’inspire fortement de Varytale un moteur de fiction interactive commercial maintenant disparu.

Ce tutoriel va vous apprendre à créer un jeu très simple basé sur les narramiettes. Histoire de vous donner une idée de là où nous allons, le jeu final est jouable à l’adresse suivante https://smwhr.github.io/dendry-tutorial/

Mise en place du projet (10min)

Si vous êtes un expert de JavaScript et que vous voulez créer un jeu Dendry en local, des instructions existent en anglais : Full project setup

Pour les autres, il existe un “starter pack”, un projet déjà tout fait à dupliquer, que vous pouvez utiliser en ligne, tout ce qu’il vous faut, c’est un compte GitHub. Pour la suite, vous devez être identifié sur GitHub.

La première chose à faire est de dupliquer le projet (repository). Rendez-vous sur

https://github.com/smwhr/dendry-starter-pack/

puis cliquez, en haut à droite, sur

et choisissez “create a new repository”.
Donnez lui un nom (par exemple ile-au-tresor)

Seconde étape, activer la page github qui hébergera le jeu terminé.
Dans les Settings, partie Pages, section Build and deployment choisissez “GitHub Actions”.

(ne choisissez aucun des choix proposés en dessous, le projet contient déjà une Action prédéfinie)

Pour modifier les fichiers, pas besoin de logiciel, GitHub fourni tout ! Depuis l’onglet “Code” appuyez simplement sur la touche . de votre clavier : l’éditeur se lance !

À gauche les fichiers déjà prêts, à droite le code que vous allez modifier !

Le premier fichier à modifier est info.dry: changez le nom du projet et l’auteur par le vôtre

# info.dry

title: Ile au Trésor
author: Super Auteur
ifid: 12345ABC-67DE-F8A9-B01C-234567890CDE

Pour sauvegarder les fichiers modifiés, rendez-vous sur l’icône de gestion des versions

et, après avoir renseigné un message (il est obligatoire et permet d’historiser les changements, mais vous n’êtes pas obligé de vous torturer si vous ne trouvez rien à dire, un bête “ok” suffit), cliquez sur “Commit & Push”

À chaque sauvegarde (commit), le jeu va être automatiquement compilé et republié à l’adresse https://VOTREUSERNAME.github.io/NOM-DU-PROJET

Vous pouvez suivre l’avancement de ceci dans l’onglet Actions sur la page GitHub (pas dans l’éditeur)(cela prend en général un peu moins d’une minute)

Notez que l’adresse du projet apparaît ici

C’est également à cet endroit qu’apparaîtront les rapports d’éventuelles erreurs dans le code que vous avez écrit.

Premier concept : une scène et quelques choix (15min)

Le premier concept important dans un projet Dendry est celui de scene.

Concrètement, une scène, est un fichier nommé nom_de_la_scene.scene.dry. Il faut l’envisager comme une brique de contenu du jeu (on peut facilement l’assimiler à un passage dans twine, ou a un noeud en ink). Le nom du fichier sert d’identifiant à la scène, on évitera donc les espaces, accents et caractères spéciaux.

Dans sa plus simple expression, une scène possède un titre (title) et du contenu.

# root.scene.dry

title: Bienvenue sur l'Île au Trésor
new-page: true

Une île mystérieuse, bordée de falaises abruptes, de plages de 
sable et d'une mer turquoise. Des maisons de bois bordent
le rivage, ornées de drapeaux multicolores. C'est ici que
se retrouvent pirates et chasseurs de trésor pour échanger
leurs histoires et trouvailles à propos d'un fabuleur trésor.

Le fichier root.scene.dry est le point d’entrée du jeu, il est obligatoire. Le mot-clé new-page signifie, lorsqu’il est renseigné à true(vrai), que le contenu précédent sera effacé pour afficher le nouveau passage. Dans le cas contraire, le contenu est simplement rajouté à la suite.

Publiez le jeu, vous verrez le nouveau passage apparaître suivi du message suivant :

Fin du jeu (rechargez la page pour lire à nouveau)

Si vous utilisez les GitHub Pages, vos changements peuvent ne pas apparaître immédiatement, même si l’Action s’est terminée, il est alors nécessaire de recharger la page avec un hard refresh. Comment faire un hard refresh ?

Et pour cause, il n’y a nulle part où aller depuis cette première scène d’intro ! Créons une seconde scène, la Capitainerie.

# capitainerie.scene.dry

title: Capitainerie
new-page: true

La Capitainerie est une petite hutte en bois installée
sur le rivage, là où les bateaux vont et viennent. D'apparence 
simple, son toit est en tôle ondulée. À l'intérieur,
vous trouvez une grande table sur laquelle une carte de l'île
est déroulée.

Il s’agit maintenant de dire à Dendry que la Capitainerie est accessible depuis la scène d’intro.
Pour cela, nous allons rajouter un choix à la suite du contenu de la scène d’intro.

# root.scene.dry

leurs histoires et trouvailles à propos d'un fabuleur trésor.

# les choix
- @capitainerie: Visiter la capitainerie

Les lignes commençant par un # sont des commentaires et seront ignorées au moment de l’affichage du jeu.

Le choix “Continue…” a été automatiquement rajouté par Dendry parce que la scène d’intro est toujours accessible (on verra ce que cela signifie plus tard) et qu’il n’y a aucun autre choix possible. Si vous voulez faire de la Capitainerie une impasse, il suffit d’ajouter la métadonnée game-over: true en haut de la scène de la Capitainerie.

Collecter plusieurs choix : tags et conditions (15min)

Imaginons que nous ayons plusieurs destinations où nous rendre depuis la place principale de l’île. La manière la plus évidente d’exprimer cela pourrait être de simplement ajouter des choix les uns à la suite des autres, manuellement.

- @chantier_naval: Visiter le chantier naval
- @taverne: Visiter la taverne
- @marche: Visiter le marché

Mais plus la liste s’allonge, plus cela devient fastidieux. Pire, si il nous prend de vouloir faire en sorte que tous ces lieux soient accessibles les uns des autres, on se retrouve à copier plusieurs fois cette liste et bonne chance pour ne pas oublier un choix ici ou là !

Heureusement, nous avons pour ça des tags.
Les tags sont une manière de catégoriser les scènes, et Dendry est construit sur un système astucieux qui lui permet de retrouver tous les éléments qui seraient taggés d’une certaine manière.
Ajoutons des tags dans la scène de la Capitainerie.

# capitainerie.scene.dry

title: Capitainerie
new-page: true
tags: port

Informons la scène d’intro qu’elle a besoin de collecter toutes les scènes qui ont un tag port et de les présenter sous forme de choix (vous pouvez supprimer le choix précédent)

# root.scene.dry

leurs histoires et trouvailles à propos d'un fabuleur trésor.

- #port

Profitez en pour rajouter quelques autres destinations !

Publions et voyons ce que ça donne :

Parfait ! Dendry a collecté toutes les scènes possédant le tag port et a créé les choix correspondants. Les intitulés de ces choix sont les title des scènes liées.

J’ai également ajouté une métadonnée subtitle à la scène passage_secret.scene.dry qui permet l’affiche d’une description supplémentaire.

OH ! MAIS ATTENDEZ ! Je ne veux pas afficher le passage secret tant que la joueuse n’a pas examiné la carte dans la Capitainerie !!!
Ajoutons une condition à la scène en question pour cacher ce choix.

# passage_secret.scene.dry

title: Passage secret
subtitle: Un passage secret vers la forteress
tags: port
view-if: connaissance_carte = "vue"

Dominant l'Île, une forteresse de pierre se dresse sur les
hauteurs. D'allure imprenable, décorée de drapeaux flottant 
au vent, on y distingue les gardes de la garnisons qui 
protège l'île des dangers venus de la mer.

Republions le jeu : le passage secret a maintenant disparu de la liste des choix. Tant mieux ! La variable connaissance_carte s’appelle, dans Dendry, une quality (qualité). Les qualités peuvent être des nombres, des textes ou des valeurs booléennes (true, false)

Par défaut une qualité vaut “0”, nous allons donc la passer à “vue” lorsque la joueuse examine la carte dans la Capitainerie :

# capitainerie.scene.dry

title: Capitainerie
new-page: true
tags: port

La Capitainerie est une petite hutte en bois installée
sur le rivage, là où les bateaux vont et viennent. D'apparence 
simple, son toit est en tôle ondulée. À l'intérieur,
vous trouvez une grande table sur laquelle une carte de l'île
est déroulée.

-@examiner_carte: Examiner la carte

@examiner_carte
view-if: connaissance_carte != "vue"
on-arrival: connaissance_carte = "vue"

C'est une carte du port et de ses environs. Vous remarquez
qu'un **passage secret** est indiqué qui mène directement à
**la forteresse** !

Examinons un peu le code que je viens de rajouter. J’ai créé une sous-scène dans la capitainerie que j’ai appelée examiner_carte. Les noms des sous-scènes commencent par un @.

Ensuite, j’ai rajouté un choix à la fin du contenu principal.

La sous-scène possède deux métadonnées :

  • view-if pour l’empêcher d’être disponible si vous avez déjà examiné la carte
  • on-arrival définit l’instruction à déclencher lorsque l’on affiche une scène (une instruction similaire on-departure se déclenche au moment où l’on quitte une scène, et dans le cas qui nous occupe, on aurait pu mettre l’une ou l’autre, ce n’est pas important)

Désormais, lorsque vous retournez sur la place principale après avoir examiné la carte dans la Capitainerie, le choix de suivre le passage secret devient disponible !

Ajoutons une dernière subtilité : un choix peut être visible sans pour autant être disponible. Dans la description au moment de l’examen de la carte, ajoutons l’indice qu’il faudra une clé pour y accéder.

# capitainerie.scene.dry

C'est une carte du port et de ses environs. Vous remarquez
qu'un **passage secret** est indiqué qui mène directement à
**la forteresse** ! Le symbole d'une **clé** est dessiné juste
à côté du passage.

Une syntaxe simplifiée permet d’ajouter du formattage simple aux textes :

  • *en italique* en italique
  • **en gras** en gras
  • > indentation
  • = titre

Il ne nous reste plus qu’à rajouter quelques métadonnées dans la scène passage_secret.scene.dry :

# passage_secret.scene.dry

title: Passage secret
subtitle: Un passage secret vers la forteress
tags: port
view-if: connaissance_carte = "vue"
**choose-if: possede_cle = true
unavailable-subtitle: Vous n'avez pas la clé nécessaire pour y accéder**

Désormais, le choix est grisé lorsque vous revenez sur la place principale :

Les qualités et leur affichage (20min)

Une qualité est grosso modo une variable.
Si vous essayez d’utiliser une qualité que vous n’avez pas préalablement définie, elle est initialisée à 0.

Pour définir une qualité au démarrage du jeu, il est possible de la définir dans un fichier nommé ma_qualite.quality.dry

# age_du_capitaine.quality.dry

name: age_du_capitaine
initial: 54

Une bonne pratique consiste à regrouper les scènes dans un dossier scenes et les qualités dans un dossier qualities mais ce n’est en aucun cas obligatoire. Si votre jeu a de plus en plus de scènes et de qualité, ou si plusieurs auteurs collaborent, il peut être opportun de choisir une structure plus adéquate.

Les qualités sont très pratiques pour suivre l’évolution d’une valeur numérique.
Nous allons essayer avec la faim de notre personnage.

name: faim
initial: 3
min: 0
max: 5

Au départ du jeu, le personnage débarque sur l’île avec un niveau de faim de 3. Vous ne pouvez jamais dépasser 5. Après un bon repas, vous serez à 1 et si vous reprenez du dessert, vous atteindrez 0.

On peut afficher ce niveau de faim lorsque la joueuse visite la Taverne.

# taverne.scene.dry

title: Taverne
new-page: true
tags: port

La Taverne de l'Île au Trésor est un endroit où les pirates 
peuvent se retrouver pour manger, boire un coup et discuter. C'est aussi 
un lieu de rencontre pour les pirates qui cherchent à 
recruter de nouveaux membres pour leur équipage.

Votre niveau de faim est de [+ faim +].

Définissons maintenant deux sous-scènes pour montrer ce que le personnage peut accomplir :

  • @manger pour diminuer le niveau de faim
  • @insulter qui augmente le niveau de faim après avoir déclenché une bagarre dans la taverne
# taverne.scene.dry

recruter de nouveaux membres pour leur équipage.

Votre niveau de faim est de [+ faim +].

- @manger : Manger un bout
- @insulter : Insulter un pirate

@manger
new-page: false
choose-if: faim > 0
unavailable-subtitle: Vous n'avez pas faim
on-departure: faim -= 1
go-to: @fin-activite

Vous mangez un bout, ça vous cale un peu.

@insulter
new-page: false
choose-if: faim < 5
unavailable-subtitle: Vous avez trop faim pour ça
on-departure: faim += 1
go-to: @fin-activite

Vous insultez un pirate au hasard. 
Cette bagarre vous a donné un peu faim.

@fin-activite

- @taverne : Continue...

Cet exemple est volontairement un peu touffu pour montrer plusieurs possibilités offertes par Dendry

  • Les sous-scènes peuvent avoir une métadonnée new-page: false(on a déjà vu ce comportement lorsque l’on examine la clé dans la capitainerie)
  • Les scènes (et sous-scènes) peuvent avoir une métadonnée go-to: @nom_scene : Dendry va alors afficher le contenu textuel de la scène puis ignorer tous les choix avant de rediriger vers la scène spécifiée. Ce go-to peut être conditionné par une condition sur une qualité, par exemple @scene_a if ma_quality > une_autre_qualite : si la condition échoue, la scène se comporte comme d’habitude.
  • Les opérateurs += n (augmenter de n) et -= n (diminuer de n) sont disponibles.

Afficher le niveau de faim sous forme numérique n’est pas très satisfaisant, après tout, nous sommes des auteurs de jeux textuels et nos joueuses espèrent mieux de nous que des chiffres balancés tous nus ! Entrent en jeu les qdisplay (quality display = afficheur de qualité). Il s’agit du dernier type de fichier possible, de la forme nom_du_display.qdisplay.dry.

# niveau_faim

(-1..0) : écœuré
(0..1) : repu
(1..2) : en appétit
(2..3) : affamé
(3..4) : en inanition
(4..5) : prêt à tout pour de la nourriture

En raison d’un bug, les fichiers de qdisplay doivent commencer par une ligne vide (ici entre le commentaire et la liste)

Il ne reste plus qu’à modifier la scène de la Taverne :

Dans taverne.scene.dry Remplacez :

Votre niveau de faim est de [+ faim +].

Par :

Vous êtes [+ faim : niveau_faim +].

Quelques sources publiques de jeux

Les jeux publiés le sont pour le moment uniquement en anglais. Mais peut-être bientôt le vôtre ?

Pour en savoir plus