Guide d’écriture Loreline
Ce guide vous donne toutes les clés pour écrire des histoires interactives avec Loreline. Aucune expérience en programmation n’est requise. Si vous savez écrire un scénario ou une histoire à choix, vous serez en terrain connu.
Commençons par un exemple simple :
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 !
Même si ce type de script est nouveau pour vous, son fonctionnement est intuitif : il décrit une scène dans un café et propose des choix qui mènent à des résultats différents.
Concepts fondamentaux
Voyons comment Loreline vous aide à créer des histoires interactives. Nous commencerons par les éléments de base et progresserons graduellement vers des fonctionnalités plus complexes.
Structure narrative et beats
Vous pouvez écrire vos dialogues dès le début d'un script, mais à mesure que l'histoire s'étoffe, une meilleure organisation deviendra nécessaire. C’est ici qu’interviennent les « beats » : des sections regroupant des scènes ou des moments liés. Pensez aux beats comme aux chapitres ou scènes de votre histoire :
beat EntrerCafe
Le soleil du matin filtre à travers les vitres du café tandis que tu entres.
barista: <friendly> Bienvenue ! Je ne crois pas t'avoir déjà vu ici.
choice
Je regarde juste un peu
barista: Prends ton temps ! Je suis là quand tu es prêt.
-> ExplorerMenu
En fait, j'aurais bien besoin d'un café
barista: <happy> Tu es au bon endroit !
-> PrendreCommande
beat ExplorerMenu
À côté de toi, une cliente régulière sirote sa boisson avec contentement.
sarah: Leurs lattés sont incroyables. Je viens ici chaque matin.
barista: <cheerful> Sarah a raison ! Tu veux en essayer un ?
choice
Bien sûr, je prendrai la même chose qu'elle
sarah: <pleased> Excellent choix !
-> PrendreCommande
Qu'est-ce que tu recommandes d'autre ?
-> PrendreCommande
beat PrendreCommande
barista: Alors, qu'est-ce que je te prépare ?
choice
Un latté, ce serait parfait
barista: <excited>
Ça arrive tout de suite !
Je vais le préparer spécialement pour ta première visite.
sarah: <smile> Tu ne le regretteras pas.
-> FinVisite
Juste un café classique pour aujourd'hui
barista: Parfois, les classiques sont le meilleur choix !
-> FinVisite
beat FinVisite
Tu trouves un coin confortable pour déguster ta boisson.
sarah: <friendly> J'espère te revoir bientôt !
La notation en flèche (->) permet de naviguer entre les beats et de créer une intrigue à embranchements. Chaque beat peut avoir son propre flux narratif, ses choix et ses conséquences.
Vous pouvez aussi terminer l’histoire entièrement en utilisant -> . (flèche vers un point) :
beat FinVisite
barista: Merci d'être venu ! À la prochaine.
-> .
Personnages et dialogues
Pour rédiger des dialogues, commencez par définir vos personnages et leurs propriétés :
character barista
name: Alex
amitie: 0 // Niveau d'amitié
serviceCommence: true
character client
name: Sam
visits: 0
boissonPreferee: null
Une fois définis, les personnages peuvent parler via une syntaxe simple : leur identifiant suivi de deux-points.
barista: Salut ! Qu'est-ce que je te sers ?
client: Juste un café normal, s'il te plaît.
barista: Ça arrive tout de suite !
Les dialogues peuvent aussi s’étendre sur plusieurs lignes en plaçant le texte sur des lignes indentées après les deux-points :
barista:
Salut, bienvenue dans notre café !
On a un mélange spécial pour toi aujourd'hui.
Jette un œil à ces grains de café éthiopiens en édition limitée au comptoir.
Écrire du texte narratif
Dans Loreline, le texte narratif s'écrit tel quel, sans marqueur spécial :
Une bonne odeur de café flotte dans la salle. Le soleil filtre par les fenêtres, projetant de longues ombres sur le parquet.
Un doux murmure de conversation emplit l'espace.
Les balises entre chevrons (<balise>) peuvent être utilisées dans n'importe quel texte, qu'il s'agisse de dialogue ou de narration :
barista: <friendly> Content de te revoir ! Comme d'habitude ?
client: <tired> Oui s'il te plaît, j'en ai vraiment besoin aujourd'hui.
La machine <whirs>se met à ronronner</whirs> tandis que de la vapeur <hiss>s'échappe avec un sifflement</hiss>.
Ces balises permettent d’exprimer les émotions des personnages ou changer la façon dont le texte est affiché, selon les possibilités de votre jeu ou application.
Gérer l’état
Toute histoire interactive se doit de mémoriser les choix et de suivre la progression. Loreline utilise des déclarations d’état pour cela. Il existe deux types d’état : persistant et temporaire.
État persistant
L’état persistant est conservé durant toute la durée de l’histoire :
state
grainsDeCafe: 100 // Suivre l’inventaire
heureDePointe: false // Est-ce l’heure de pointe ?
numeroJour: 1 // Quel jour de l’histoire
meteo: ensoleillé // Météo actuelle
Vous pouvez modifier ces valeurs au fur et à mesure que votre histoire progresse :
grainsDeCafe -= 10 // Utiliser des grains
heureDePointe = true // Début de l’heure de pointe
numeroJour += 1 // Passer au jour suivant
meteo = "pluvieux" // Changer la météo
Assignation vs. déclaration. Dans une assignation (
=,+=,-=…), le côté droit est une expression. C’est pourquoi"pluvieux"a besoin de guillemets : sans eux,pluvieuxserait interprété comme un nom de variable. Les nombres (100), booléens (true/false) etnullsont aussi des expressions, donc pas besoin de guillemets.C’est différent des déclarations
stateetcharacter(avec:), où la valeur n’est pas une expression :meteo: ensoleilléassigne directement le texte « ensoleillé ». Pour y insérer une expression, utilisez l’interpolation :name: $nomJoueuroutotal: ${a + b}, comme dans les dialogues ou les textes narratifs.
L’état peut aussi contenir des objets imbriqués et des tableaux :
state
menu:
espresso: 3
latte: 5
cappuccino: 4
specialsDuJour: ["Torréfaction Éthiopienne", "Cold Brew Vanille"]
État local à un beat
Vous pouvez déclarer un état à l’intérieur d’un beat. Il persiste entre les visites du beat mais est limité à ce beat, afin d’éviter tout conflit avec une variable de premier niveau portant le même nom :
state
compteur: 0 // Compteur de premier niveau
beat Cafeteria
state
compteur: 0 // Compteur séparé, local à ce beat
compteur += 1
barista: Tu as commandé $compteur cafés dans ce bistrot !
État temporaire
Parfois, vous aurez besoin d’un état temporaire, existant uniquement au sein d’un beat. Utilisez le mot-clé new pour créer un état temporaire qui se réinitialise à chaque fois que vous entrez dans le beat :
beat DegustationCafe
// Ces valeurs se réinitialisent à chaque entrée dans DegustationCafe
new state
tassesGoutees: 0
torrefactionActuelle: legère
niveauPlaisir: 5
choice
Prendre une autre gorgée if tassesGoutees < 3
tassesGoutees += 1
Des notes intéressantes dans celui-ci...
Terminer la dégustation
-> CommanderBoisson
Dans cet exemple, tassesGoutees, torrefactionActuelle et niveauPlaisir reprennent leurs valeurs initiales à chaque entrée dans le beat DegustationCafe.
Rendre les choix interactifs
L’essence de la fiction interactive réside dans les choix offerts :
beat CommanderBoisson
choice
Commander un cappuccino
grainsDeCafe -= 15
barista: <happy> Un cappuccino, c'est parti !
-> PreparerBoisson
Se renseigner sur les thés
barista: Nous avons une belle sélection de thés verts et d'infusions.
-> MenuThe
Consulter le menu tranquillement
Tu prends ton temps pour lire la longue liste de boissons.
-> MenuBoissons
Les choix peuvent être conditionnels, disponibles uniquement si certains critères sont remplis :
beat MenuSpecial
choice
Commander la torréfaction spéciale if grainsDeCafe >= 20
grainsDeCafe -= 20
barista: Excellent choix ! Notre mélange éthiopien est incroyable.
-> PreparerBoisson
Discuter avec le barista if barista.amitie > 2
barista: <friendly> Tu veux que je te raconte comment je suis devenu barista ?
-> DiscussionBarista
Lorsqu’un choix effectue simplement une transition vers un autre beat sans logique supplémentaire, vous pouvez l’écrire sur une seule ligne :
choice
Rester au café -> Cafeteria
Finir la journée -> FinJournee
Rejoindre $sarah if sarah.present -> DiscussionSarah
Les choix peuvent aussi être imbriqués. Lorsqu’une branche de choix se termine sans transition ->, l’exécution reprend naturellement après le bloc de choix :
barista: Qu'est-ce qui te ferait plaisir ?
choice
Une boisson chaude
choice
Espresso
barista: Un espresso, c'est parti !
Latté
barista: Excellent choix ! Une préférence de lait ?
choice
Lait d'avoine
barista: Notre option la plus populaire !
Lait standard
barista: Un classique. Ça arrive tout de suite.
Une boisson froide
choice
Café glacé
barista: Parfait par ce temps !
Limonade
barista: Pressée maison, ma préférée.
barista: Ce sera prêt dans un instant.
La dernière ligne est jouée quel que soit le choix de boisson, toutes les branches convergent naturellement après le bloc de choix extérieur.
Composer les choix avec des insertions
À mesure que votre histoire s'agrandit, vous pourriez vouloir réutiliser des groupes de choix dans différents beats. Les insertions de choix vous permettent d’intégrer les choix d’un autre beat en utilisant le préfixe + :
beat SceneCafe
choice
+ BoissonsDeSaison
+ MenuHabituel
Rien pour moi, merci
barista: Pas de souci, fais-moi signe si tu changes d'avis.
beat BoissonsDeSaison
barista: N'oublie pas nos spécialités de saison !
choice
Chocolat chaud épicé
barista: Un choix parfait pour la saison !
Thé aux agrumes
barista: Excellent, c'est notre dernière nouveauté.
beat MenuHabituel
choice
Espresso
barista: Un espresso, c'est parti !
Latté
barista: Excellent choix !
En atteignant le choix dans SceneCafe, on verra les options de BoissonsDeSaison et MenuHabituel fusionnées avec l’option « Rien pour moi ». Chaque beat inséré peut aussi inclure un dialogue qui se joue avant que ses choix ne soient affichés.
Utiliser des beats comme sous-routines
Vous pouvez appeler un beat comme une fonction en utilisant des parenthèses. Le beat appelé s’exécute, et quand il se termine, l’exécution reprend là où il a été appelé :
character perso
name: null
beat Introspection
if !perso.name
Comment est-ce que je m'appelle ?
ChoisirNom()
-> Introspection
else
Ah oui, je me souviens, je m'appelle $perso.name !
beat ChoisirNom
choice
Alex
perso.name = "Alex"
Sam
perso.name = "Sam"
Jamie
perso.name = "Jamie"
Ici, ChoisirNom() entre dans le beat ChoisirNom, permet de choisir un nom, puis renvoie à Introspection où l'exécution continue.
Varier le texte entre les visites
Quand les joueurs revisitent un même beat, il est souvent souhaitable que le texte change. Les blocs alternatifs permettent de définir des variantes et de contrôler leur sélection. Utilisez -- pour séparer les éléments à l'intérieur du bloc :
sequence
Le café est encore calme à cette heure matinale.
--
Quelques habitués se sont installés avec leurs journaux.
--
L'affluence du matin commence à se faire sentir.
barista: Qu'est-ce que je te sers aujourd'hui ?
Le mot-clé sequence joue chaque élément dans l'ordre à chaque nouvelle visite, puis reste sur le dernier. Ici, la première visite affiche « Le café est encore calme… », la deuxième « Quelques habitués… », et à partir de la troisième, toujours « L'affluence du matin… ».
Cinq modes sont disponibles :
| Mot-clé | Comportement |
|---|---|
sequence |
Éléments dans l'ordre, puis reste sur le dernier |
cycle |
Éléments dans l'ordre, puis recommence au premier |
once |
Éléments dans l'ordre, puis plus rien |
pick |
Un élément au hasard à chaque fois |
shuffle |
Tous les éléments dans un ordre aléatoire |
Voici cycle pour un barista qui alterne ses salutations :
cycle
barista: Bonjour ! Qu'est-ce que ce sera ?
--
barista: Content de te revoir ! Comme d'habitude ?
--
barista: Salut ! On essaie quelque chose de nouveau aujourd'hui ?
once est utile pour du contenu qui ne doit apparaître qu'un nombre limité de fois :
once
Une clochette tinte quand tu pousses la porte pour la première fois.
--
L'odeur familière du café t'accueille à nouveau.
--
Tu salues d'un signe quelques habitués que tu as appris à connaître.
barista: Salut !
Après la troisième visite, le bloc once ne produit plus rien et seul « Salut ! » est joué.
Avec un seul élément et sans --, once permet simplement d'afficher quelque chose uniquement lors de la première visite :
once
Une clochette tinte quand tu pousses la porte pour la première fois.
barista: Salut !
pick choisit un élément au hasard à chaque fois :
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.
Et shuffle joue tous les éléments, mais dans un ordre aléatoire :
shuffle
Tu remarques le menu à la craie sur le mur.
--
Un chat dort sur le rebord de la fenêtre.
--
Le barista astique des verres derrière le comptoir.
Chaque élément peut contenir plusieurs lignes de texte et de dialogue, comme n'importe quel autre bloc :
cycle
barista: Le spécial du jour, c'est un macchiato caramel.
L'odeur est tout simplement irrésistible.
--
barista: On a un nouveau cold brew au menu.
Une ardoise annonce le prix.
--
barista: Essaie notre latté épicé de saison !
Les épices chaudes embaument l'air.
Texte dynamique
Rendez votre texte dynamique en utilisant le symbole $ pour l’interpolation de variables :
barista: Il nous reste $grainsDeCafe grains en stock.
barista: Ça fera ${grainsDeCafe * 2} euros pour le lot !
Les personnages peuvent aussi être référencés par leur identifiant, ce qui affichera automatiquement leur propriété name :
beat FermerBoutique
$barista commence à ranger pour la journée. // Affichera "Alex commence à ranger pour la journée"
$client salue d'un geste en partant. // Affichera "Sam salue d'un geste en partant"
Échapper les caractères spéciaux
Comme $ et < ont une signification spéciale dans Loreline, vous pouvez les échapper lorsque vous avez besoin d’afficher ces caractères tels quels :
barista: Ce café rare va te coûter 9$$. Ça te convient ?
perso: Zut, il ne me reste que 5\$...
Les deux formes $$ et \$ produisent un $ littéral dans la sortie.
Vous pouvez aussi échapper les chevrons pour éviter qu’ils soient interprétés comme des balises :
Ce texte contient une \<balise> échappée.
Texte vs. conditions
La plupart du temps, Loreline est assez intelligent pour distinguer quand if fait partie de votre texte plutôt qu’une condition. Par exemple, ceci fonctionne très bien sans aucun échappement :
beat WeatherReport
david: It's Friday once again, if you can believe it!
Loreline détecte que if you can believe it! n'est pas une condition valide, il conserve donc la ligne entière comme texte de dialogue. Cet exemple est en anglais car il illustre spécifiquement un cas propre à l'anglais, où if apparaît naturellement dans une phrase courante.
Cependant, lorsque le texte après if se trouve être une expression valide, il sera interprété comme une condition. Dans ce cas, entourez le texte de guillemets doubles si vous voulez qu’il soit traité sans ambiguïté comme du texte :
state
tired: false
choice
Go outside if tired // choix affiché uniquement quand tired est vrai
"Go outside if tired" // le texte du choix est littéralement "Go outside if tired"
Utilisez \n pour insérer un saut de ligne dans une seule ligne de dialogue :
perso: Est-ce que je peux payer...\nle reste...\ndemain ?
Cela s’affiche en trois lignes distinctes :
Est-ce que je peux payer...
le reste...
demain ?
Si vous avez besoin d'un \n littéral dans la sortie, utilisez un antislash supplémentaire : \\n.
Fonctions
Loreline permet d'utiliser des fonctions. Elles peuvent être appelées depuis des expressions, de l’interpolation de texte ou comme instructions autonomes. Une fonction est appelée par son nom suivi de parenthèses. Par exemple, random est une fonction intégrée qui renvoie un nombre aléatoire entre deux valeurs :
barista: Ta commande sera prête dans $random(2, 5) minutes !
// Affichera un nombre aléatoire entre 2 et 5
Fonctions intégrées
Loreline inclut plus de 50 fonctions intégrées couvrant les mathématiques, l’aléatoire, les chaînes, les tableaux, les dictionnaires, et plus encore. En voici quelques exemples :
sante = clamp(sante + soin, 0, santeMax)
if chance(4)
Tu trouves une gemme rare par terre !
if array_has(inventaire, "clé dorée")
Tu ouvres la porte ancienne.
salutation = " bonjour le monde ".trim().upper()
De nombreuses fonctions supportent aussi la notation par point. Par exemple, string_upper(nom) peut s’écrire nom.upper().
Pour la liste complète, consultez la référence Fonctions intégrées.
Définir vos propres fonctions
Vous pouvez définir vos propres fonctions à l’extérieur des beats. Une fonction possède un nom, des paramètres optionnels, et un corps écrit dans une syntaxe de script standard :
function additionner(a, b)
return a + b
state
pommes: 7
oranges: 3
Nous avons $pommes pommes et $oranges oranges, ce qui fait un total de $additionner(pommes, oranges) fruits !
Les fonctions peuvent accéder et modifier les variables d’état :
state
fruits: 2
function obtenirFruit()
fruits = fruits + 1
Tu as $fruits fruits.
obtenirFruit()
Tu as $fruits fruits.
Les fonctions peuvent utiliser des boucles pour construire des résultats :
function enumerer(nombre, mot)
var resultat = ""
for (i in 0...nombre)
if i > 0
resultat += ", "
resultat += "$mot ${i + 1}"
return resultat
Voici tous mes objets : $enumerer(3, "pomme")
// Sortie : Voici tous mes objets : pomme 1, pomme 2, pomme 3
Pour une référence complète de la syntaxe de script disponible dans les fonctions (variables, structures de contrôle, opérateurs et plus), consultez la page Scripting de fonctions.
Fonctions externes
Vous pouvez aussi déclarer des fonctions vides. Celles-ci servent de points d’ancrage (hooks) pour votre moteur de jeu ou application. Le script les déclare, et l’environnement hôte fournit l’implémentation réelle :
function jouerExplosion()
sarah: C'est quoi ce diamant vert ? Attends, laisse-moi le toucher...
james: Nooon ne le touche pas !
jouerExplosion()
james: Sarah ? Sarah !!
Importer des scripts
À mesure que votre histoire s'agrandit, vous pouvez la répartir sur plusieurs fichiers en utilisant des instructions d’import :
import objets
import personnages/barista
import "scenes/intro.lor"
Les imports chargent le contenu d’un autre fichier .lor dans le script courant. L’extension .lor et les guillemets sont optionnels. import personnages/barista cherchera personnages/barista.lor dans un sous-dossier personnages.
Syntaxe alternative : accolades
Tout au long de ce guide, tous les exemples utilisent l’indentation pour définir les blocs. Loreline supporte aussi les accolades comme alternative :
beat Cafeteria {
choice {
Commander un espresso {
barista: Un espresso, c'est parti !
-> TraiterCommande
}
Commander un latté if !heureDePointe {
barista: Excellent choix ! Je vais le faire bien mousseux.
-> TraiterCommande
}
Partir -> FinJournee
}
}
Les deux styles fonctionnent partout où des blocs sont utilisés (beats, choix, déclarations d’état, if/else). Vous pouvez utiliser le style que vous préférez, bien que la syntaxe basée sur l’indentation tende à être plus lisible pour le contenu narratif.
Commentaires et organisation
Gardez votre script organisé avec des commentaires :
// Suivre la fidélité du client
client.visits += 1
/* Vérifier si on doit
déclencher l'événement spécial */
if client.visits > 10
-> RecompenseFidelite
Fonctionnalités avancées
Voici un exemple complexe combinant plusieurs fonctionnalités :
beat DegustationCafe
state
tassesGoutees: 0
torrefactionPreferee: null
derniereImpression: ""
barista: <enthusiastic> Prêt à découvrir nos nouvelles torréfactions ?
choice
Essayer la torréfaction légère if tassesGoutees < 3
tassesGoutees += 1
derniereImpression = "vive et citronnée"
Les notes vives et citronnées dansent sur ton palais.
if chance(3) // 1 chance sur 3
torrefactionPreferee = "legère"
barista: <happy> Je vois cette étincelle dans tes yeux !
-> DiscuterGout
Essayer la torréfaction moyenne if tassesGoutees < 3
tassesGoutees += 1
derniereImpression = "noisette et équilibrée"
Une agréable saveur de noisette emplit ta bouche.
-> DiscuterGout
Discuter des origines du café if barista.amitie > 1
barista: <passionate> Laisse-moi te parler de nos producteurs...
-> OriginesCafe
Terminer la dégustation if tassesGoutees > 0
if torrefactionPreferee != null
-> CommanderFavori
else
-> CommandeHabituelle
beat DiscuterGout
barista: Qu'est-ce que tu penses de ces notes $derniereImpression ?
choice
Exprimer son enthousiasme
barista.amitie += 1
-> DegustationCafe
Hocher poliment la tête
-> DegustationCafe
Ce guide a passé en revue les principales fonctionnalités de Loreline, mais il y a toujours plus à découvrir en écrivant vos propres histoires. Expérimentez avec différentes combinaisons de ces fonctionnalités pour créer des récits riches.
Bonne écriture !