Introduction à Loreline

Loreline est un langage de script open-source pour écrire de la fiction interactive. Il gère les dialogues à embranchements, les choix de la personne qui joue, les personnages et l'état de l'histoire dans une syntaxe conçue pour être lisible par les autrices et auteurs tout en offrant de vrais outils de programmation (variables, conditions, fonctions) quand l'histoire en a besoin.

Vous pouvez l'intégrer partout : moteurs de jeu, applications web ou projets autonomes. Il s'adapte à vos outils, les histoires que vous écrivez restent portables.

À quoi ça ressemble

Voici un script Loreline minimal :

Une bonne odeur de café flotte dans la salle.

barista: Salut ! Comment tu vas aujourd'hui ?

choice
  Je passe une excellente journée
    barista: Formidable ! Le café va la rendre encore meilleure.

  Besoin de caféine...
    barista: N'en dis pas plus ! Laisse-moi m'en occuper.

  Tu t'appelles Alex, non ?
    barista.name = "Alex"
    barista: Oh, je ne m'attendais pas à ce que tu t'en souviennes !

C'est le schéma de base. Le Guide d'écriture aborde cela en détail.

Simple mais explicite par design

Loreline réduit la ponctuation au strict minimum. Le texte narratif et les dialogues s'écrivent tels quels, sans aucun délimiteur particulier. Passer à une autre section, c'est une simple flèche ->. Assigner une variable, c'est juste =. Les conditions n'ont pas besoin d'un mot-clé de fermeture, l'indentation suffit. Un choix peut inclure un if en fin de ligne pour n'apparaître que si la condition est remplie.

D'un autre côté, les principaux éléments structurels (beat, choice, character, state, if, else) sont des mots-clés en anglais courant plutôt que des symboles. Ils se comprennent d'un coup d'œil, sans avoir besoin d'apprendre ce que signifie chaque caractère spécial. C'est un choix délibéré : des mots lisibles plutôt que de la ponctuation cryptique.

D'autres outils s'appuient davantage sur les symboles ou les délimiteurs : Yarn Spinner utilise <<if>>...<<endif>> et <<set>> ; Ink dispose de ses propres marqueurs pour les choix, les knots et les diverts. Loreline essaie de rester au plus proche du texte brut, et l'éditeur est suffisamment intelligent pour appliquer une coloration syntaxique correcte avec aussi peu d'indices :

Le café est calme ce matin.

choice
  Commander un café
    cafesCommandes += 1
    if cafesCommandes > 3
      barista: <concerned> Ça fait quand même beaucoup de café aujourd'hui...
    else
      barista: Un de plus, c'est parti !

  Commander un déca if cafesCommandes > 2
    barista: Bonne idée. Je t'en fais un bon.

Pour les autrices et auteurs, le résultat est un script qui se lit presque comme du langage naturel, avec moins de bruit visuel entre l'histoire et sa structure.

Structure de l'histoire

Une histoire est organisée en beats, des sections nommées qui fonctionnent comme des scènes ou des chapitres. On passe de l'une à l'autre avec ->, ou on en appelle une avec BeatName() pour faire un détour qui ramène à l'endroit de l'appel une fois terminé :

beat EnterCafe
  Le soleil du matin traverse les fenêtres du café.
  barista: Bienvenue ! Je ne crois pas t'avoir déjà vu.
  TakeOrder()
  barista: Bon café !
  -> EndScene

beat TakeOrder
  barista: Alors, qu'est-ce que je te prépare ?

Les personnages sont des éléments de premier plan : on les déclare avec des champs qui peuvent être lus et modifiés tout au long de l'histoire :

character barista
  name: Alex
  mood: friendly
  friendship: 0

Les données persistantes vivent dans un bloc state. Les conditions (if, else if, else) permettent à l'histoire de réagir aux décisions prises, et les choix peuvent porter des conditions en ligne pour afficher ou masquer des options dynamiquement.

if barista.friendship > 2
  barista: Ah ! Je suis content de te voir :)

Autres fonctionnalités

Au-delà de la syntaxe de base, Loreline propose aussi :

barista: <friendly> Je suis content de te voir !

sarah: Ça sera un <strong>café très fort</strong> pour moi, aujourd'hui.
sarah: Tu savais que c'est $barista.name qui gère le café à côté ?

james: Ah super, allons-y, il faut que je termine ma lecture de ${james.currentBooks[0]}.

barista: Tu as pris $cafesCommandes boisson|boissons aujourd'hui.
pick
  Un air de jazz s'échappe doucement d'une enceinte sur l'étagère.
--
  La machine à espresso siffle et crépite.
--
  Des rires fusent d'une table près de la fenêtre.
barista: Ta commande sera prête dans $random(2, 5) minutes !
// Affichera un nombre aléatoire entre 2 et 5
import inventaires
import personnages

// Histoire qui utilise les inventaires (inventaires.lor)
// et les personnages (personnages.lor)...
Exemple en JavaScript
// Sauvegarder l'état (par exemple à un point de contrôle)
const saveData = interpreter.save();
localStorage.setItem('save', JSON.stringify(saveData));

// Plus tard, restaurer et reprendre
const saved = JSON.parse(localStorage.getItem('save'));
interpreter = Loreline.resume(script, onDialogue, onChoice, onFinish, saved);

Plateformes et intégration

Loreline est compatible avec n'importe quel moteur C# (y compris Unity), C++, Javascript, Typescript, Python, Lua ou encore JVM. Il s'utilise de la même manière partout : charger un script Loreline, puis le jouer en fournissant des callbacks pour les dialogues, les choix et la fin de l'exécution.

Toutes ces cibles partagent une base de code commune écrite en Haxe, ce qui garantit un comportement cohérent d'un langage à l'autre et permet de livrer les mises à jour partout simultanément sans différence majeure de fonctionnalité. Une batterie de plus de 160 fichiers de tests couvre le runtime et contribue à sa fiabilité au fil de l'évolution du langage.

Exemple en JavaScript
import { Loreline } from 'loreline';

const script = Loreline.parse(source);

Loreline.play(script,

  // Appelé pour afficher un dialogue
  (interp, character, text, tags, callback) => {
    if (character) {
      const name = interp.getCharacterField(character, 'name');
      console.log((name || character) + ': ' + text);
    } else {
      console.log(text);
    }
    callback();
  },

  // Appelé pour afficher un choix
  (interp, options, callback) => {
    options.forEach((opt, i) => {
      if (opt.enabled) console.log('  [' + (i + 1) + '] ' + opt.text);
    });
    callback(0);
  },

  // Appelé en fin d'exécution
  (interp) => {
    console.log('--- The End ---');
  }
);
Plateforme Notes
JavaScript / TypeScript Fonctionne dans Node.js et le navigateur. Disponible en tant que package npm.
C# / .NET S'intègre avec Unity, Godot (.NET) et tout projet .NET.
C++ Bibliothèque autonome sans dépendances externes.
Python Fonctionne avec Python 3.8+, sans dépendances externes.
Java / JVM Fonctionne avec Java 8+ (et Kotlin). Un seul JAR sans dépendances externes.
Lua Fonctionne avec Lua 5.1+, sans dépendances externes.
Haxe Disponible en tant que package haxelib avec accès à l'API complète.

Jetez un œil aux guides d'intégration pour les instructions de configuration dans chaque langage.

Outillage

Comparaison avec Ink et Yarn Spinner

Ink et Yarn Spinner sont les deux autres outils couramment utilisés pour le récit interactif. Chacun adopte une approche différente de la syntaxe et de la structure.

Cette comparaison ne prétend en aucun cas qu'un outil est meilleur ou moins bon qu'un autre. Ink et Yarn Spinner sont matures, bien documentés et ont été utilisés dans des jeux publiés depuis des années. Loreline est plus récent et adopte une approche différente. Le meilleur choix dépend de la familiarité de l'équipe, du moteur cible et du style de script qui correspond le mieux à la façon dont on veut écrire.

Voici le même script écrit dans les trois langages :

state
  cafesCommandes: 0

character barista
  name: Alex

beat Cafeteria
  Une bonne odeur de café flotte dans la salle.
  barista: Salut ! Qu'est-ce que je te sers ?

  choice
    Un café normal
      barista: C'est parti !

    Un déca if cafesCommandes > 2
      barista: Tu passes au déca ? Sage décision.

  cafesCommandes += 1
  if cafesCommandes > 3
    barista: Ça fait quand même beaucoup de café aujourd'hui...
VAR cafesCommandes = 0

=== Cafeteria ===
Une bonne odeur de café flotte dans la salle.
Alex: Salut ! Qu'est-ce que je te sers ?

+ Un café normal
  Alex: C'est parti !
+ {cafesCommandes > 2} Un déca
  Alex: Tu passes au déca ? Sage décision.
-
~ cafesCommandes = cafesCommandes + 1
{cafesCommandes > 3:
  Alex: Ça fait quand même beaucoup de café aujourd'hui...
}
-> DONE
title: Cafeteria
---
<<declare $cafesCommandes = 0>>

Une bonne odeur de café flotte dans la salle.
Alex: Salut ! Qu'est-ce que je te sers ?

-> Un café normal
    Alex: C'est parti !
-> Un déca <<if $cafesCommandes > 2>>
    Alex: Tu passes au déca ? Sage décision.

<<set $cafesCommandes = $cafesCommandes + 1>>
<<if $cafesCommandes > 3>>
    Alex: Ça fait quand même beaucoup de café aujourd'hui...
<<endif>>
===

Les trois scripts racontent la même histoire avec une longueur comparable mais des conventions différentes. Pour un comparatif détaillé fonctionnalité par fonctionnalité, consultez la comparaison détaillée.

Essayer Loreline

Le moyen le plus rapide de commencer est l'Atelier, où vous pouvez écrire et exécuter des scripts Loreline directement dans le navigateur, sans rien installer. Pour aller plus loin, le Guide d'écriture couvre le langage complet pas à pas.