Utiliser Loreline avec Haxe

Loreline est écrit en Haxe, donc l'utiliser depuis Haxe donne un accès direct à la bibliothèque sans wrapper. Ce guide montre comment configurer un projet Haxe, charger un script .lor, et gérer les dialogues, les choix et la fin du script.

Installer la bibliothèque

Installez Loreline depuis haxelib :

haxelib install loreline
haxelib install hscript

Ou installez directement depuis le dépôt GitHub avec haxelib git :

haxelib git loreline https://github.com/jeremyfa/loreline
haxelib install hscript

Configuration du projet

Créez un fichier de build (par ex. build.hxml) pour votre cible :

# Build : haxe build.hxml
# Run :   node out/main.js

--class-path src
--main Main
--library loreline
--library hscript
-D hscriptPos
-D js-es=6
-D loreline_use_js_types
-D loreline_typedef_options
-D loreline_functions_map_dynamic_access
-D loreline_node_id_class
--js out/main.js
# Build : haxe build.hxml
# Run :   neko out/main.n

--class-path src
--main Main
--library loreline
--library hscript
-D hscriptPos
--neko out/main.n
# Build : haxe build.hxml
# Run :   cd out/cs && dotnet run

--class-path src
--main Main
--library loreline
--library hscript
--library hxcs
-D hscriptPos
-D erase-generics
-D loreline_use_cs_types
-D loreline_cs_api
--cs out/cs
# Build : haxe build.hxml
# Run :   ./out/cpp/Main

--class-path src
--main Main
--library loreline
--library hscript
--library hxcpp
-D hscriptPos
--cpp out/cpp
# Build : haxe build.hxml
# Run :   python3 out/main.py

--class-path src
--main Main
--library loreline
--library hscript
-D hscriptPos
-D loreline_typedef_options
-D loreline_functions_map_dynamic_access
-D loreline_node_id_class
--python out/main.py
# Build : haxe build.hxml
# Run :   lua out/main.lua

--class-path src
--main Main
--library loreline
--library hscript
-D hscriptPos
-D loreline_typedef_options
-D loreline_functions_map_dynamic_access
-D loreline_node_id_class
--lua out/main.lua

Ajouter la bibliothèque hscript et le define hscriptPos est recommandé, car ils activent le support du scripting de fonctions. Sans eux, les fonctions personnalisées écrites dans les scripts Loreline ne sont pas supportées.

Defines de build

Les flags -D ci-dessus configurent la façon dont Loreline est compilé pour chaque cible. Voici ce que chacun fait :

Define Description
hscriptPos Active le suivi de position dans hscript pour de meilleurs messages d'erreur avec numéros de ligne et de colonne. Requis pour le scripting de fonctions.
js-es=6 Génère du JavaScript ES6 (utilise class, let, fonctions fléchées).
loreline_use_js_types Utilise les types natifs JavaScript pour une meilleure interopérabilité avec le code JS.
loreline_use_cs_types Utilise les types de collections .NET natifs (List<object>, Dictionary<string,object>) au lieu des collections Haxe.
loreline_cs_api Active la couche d'interopérabilité C# pour un accès direct aux API .NET.
loreline_typedef_options Définit InterpreterOptions comme un typedef au lieu d'une classe, pour une interop plus légère sur les cibles dynamiques.
loreline_functions_map_dynamic_access Utilise l'accès dynamique pour la map de fonctions, pour de meilleures performances sur les cibles dynamiques.
loreline_node_id_class Représente les identifiants de nœuds comme une classe au lieu d'un type abstrait, pour la compatibilité avec les cibles qui n'inlinent pas les abstracts.

Charger un script

Utilisez Loreline.parse() pour analyser un fichier .lor. Le troisième argument est un gestionnaire de fichiers pour résoudre les instructions import :

var content = sys.io.File.getContent("story/CoffeeShop.lor");

function handleFile(path:String, callback:(String) -> Void) {
  var dir = "story/";
  callback(sys.io.File.getContent(dir + path));
}

Loreline.parse(content, "CoffeeShop.lor", handleFile, function(script) {
  if (script != null) {
    // Script analysé avec succès, prêt à jouer
  }
});

Si votre script n'a pas d'instructions import, vous pouvez passer null pour le gestionnaire de fichiers et utiliser la valeur de retour synchrone :

var script = Loreline.parse(content);

Gérer les dialogues

Le gestionnaire de dialogue reçoit l'interpréteur, un identifiant de personnage (ou null pour du texte narratif), le texte du dialogue, les tags éventuels, et un callback pour faire avancer le script :

function handleDialogue(
  interpreter:loreline.Interpreter,
  character:String,
  text:String,
  tags:Array<loreline.Interpreter.TextTag>,
  callback:() -> Void
) {
  if (character != null) {
    // Résoudre le nom d'affichage depuis la définition du personnage
    var name = interpreter.getCharacterField(character, "name");
    var displayName = (name != null) ? Std.string(name) : character;
    Sys.println(displayName + " : " + text);
  } else {
    // Texte narratif (pas de personnage)
    Sys.println(text);
  }
  callback();
}

Gérer les choix

Le gestionnaire de choix reçoit une liste d'options. Chaque option a un champ text et un champ enabled. Appelez le callback avec l'index du choix sélectionné :

function handleChoice(
  interpreter:loreline.Interpreter,
  options:Array<loreline.Interpreter.ChoiceOption>,
  callback:(Int) -> Void
) {
  for (i in 0...options.length) {
    if (options[i].enabled) {
      Sys.println("  [" + (i + 1) + "] " + options[i].text);
    }
  }

  // Lire l'entrée du joueur
  Sys.print("> ");
  var input = Sys.stdin().readLine();
  var choice = Std.parseInt(input);
  if (choice != null && choice >= 1 && choice <= options.length) {
    callback(choice - 1);
  }
}

Gérer la fin du script

Le gestionnaire de fin est appelé quand le script atteint sa fin :

function handleFinish(interpreter:loreline.Interpreter) {
  Sys.println("--- Fin ---");
}

Exemple complet

Voici un Main.hx complet qui charge et exécute un script Loreline en ligne de commande :

import loreline.Loreline;
import loreline.Interpreter;

class Main {
  static var storyDir = "story/";

  static function main() {
    var content = sys.io.File.getContent(storyDir + "CoffeeShop.lor");

    Loreline.parse(content, "CoffeeShop.lor", handleFile, function(script) {
      if (script != null) {
        Loreline.play(script, onDialogue, onChoice, onFinish);
      }
    });
  }

  static function handleFile(path:String, callback:(String) -> Void) {
    try {
      callback(sys.io.File.getContent(storyDir + path));
    } catch (e:Dynamic) {
      callback(null);
    }
  }

  static function onDialogue(
    interp:Interpreter, character:String, text:String,
    tags:Array<Interpreter.TextTag>, callback:() -> Void
  ) {
    if (character != null) {
      var name = interp.getCharacterField(character, "name");
      Sys.println((name != null ? Std.string(name) : character) + " : " + text);
    } else {
      Sys.println(text);
    }
    Sys.println("");
    callback();
  }

  static function onChoice(
    interp:Interpreter, options:Array<Interpreter.ChoiceOption>,
    callback:(Int) -> Void
  ) {
    for (i in 0...options.length) {
      if (options[i].enabled) {
        Sys.println("  [" + (i + 1) + "] " + options[i].text);
      }
    }
    Sys.print("> ");
    var input = Sys.stdin().readLine();
    var choice = Std.parseInt(input);
    if (choice != null && choice >= 1 && choice <= options.length) {
      callback(choice - 1);
    }
  }

  static function onFinish(interp:Interpreter) {
    Sys.println("--- Fin ---");
  }
}

Aller plus loin

Comme Loreline est une bibliothèque Haxe, vous avez accès au code source complet et aux API internes. Consultez le dépôt Loreline sur GitHub pour des usages avancés, notamment la sauvegarde/restauration, les traductions et les fonctions personnalisées.