Skip to content

Pour commencer ​

Installation ​

bash
npm install @lsde/dialog-engine
bash
dotnet add package LsdeDialogEngine

# JSON loader — choose ONE based on your platform:
dotnet add package LsdeDialogEngine.Newtonsoft        # Unity
dotnet add package LsdeDialogEngine.SystemTextJson    # .NET 5+ / Godot .NET
bash
# Add as a git submodule
git submodule add https://github.com/jonlepage/LS-Dialog-Editor-Engine.git deps/lsde

# CMakeLists.txt
add_subdirectory(deps/lsde/lsde-cpp)
target_link_libraries(your_target PRIVATE lsde)
bash
# Copy the headers and source into your project
# e.g. Source/YourModule/ThirdParty/lsde/

cp -r lsde-cpp/include/lsde  YourProject/Source/YourModule/ThirdParty/lsde/include
cp -r lsde-cpp/src/*          YourProject/Source/YourModule/ThirdParty/lsde/src

# In your module's .Build.cs, add:
# PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "ThirdParty/lsde/include"));
# Add the .cpp files from ThirdParty/lsde/src/ to your module
bash
# Clone the repository
git clone https://github.com/jonlepage/LS-Dialog-Editor-Engine.git

# Copy the addon into the Godot project
mkdir -p addons
cp -r LS-Dialog-Editor-Engine/lsde-gdscript/addons/lsde addons/lsde
rm -rf LS-Dialog-Editor-Engine

Usage minimal ​

Le engine est une machine de traversée de graphe — il dispatch les blocks aux handlers enregistrés qui leur donnent un sens. Sans handlers, le engine n'a aucun output.

Agnostique du format

Le engine consomme un objet BlueprintExport, pas un fichier. Vous pouvez charger votre blueprint depuis JSON, XML ou YAML avec n'importe quel parseur adapté à votre plateforme. Voir Parsing & import pour les recommandations.

ts
import { DialogueEngine } from '@lsde/dialog-engine';
import blueprintJson from './blueprints.json';

const engine = new DialogueEngine();
engine.init({ data: blueprintJson });

// Unified condition resolver — evaluates game-state conditions for both
// choice visibility and condition block pre-evaluation.
engine.onResolveCondition((cond) => game.evaluateCondition(cond));

// 3 required handlers — bridge between the engine and your game
// (onCondition is optional when onResolveCondition is installed)
engine.onDialog(({ scene, block, context, next }) => {
  game
    .createDialogAuto(block, context)
    .catch(() => scene.cancel())
    .finally(() => next());
});

engine.onChoice(({ scene, block, context, next }) => {
  game
    .createChoiceAuto(block, context)
    .catch(() => scene.cancel())
    .finally(() => next());
});

engine.onAction(({ block, context, next }) => {
  game
    .executeActions(block.actions)
    .catch((err) => context.reject(err))
    .finally(() => next());
});

// Start a scene anywhere in your game code
function myGameScript(sceneId: string) {
  const scene = engine.scene(sceneId);
  scene.start();
}
csharp
using LsdeDialogEngine;
using LsdeDialogEngine.Json; // LsdeDialogEngine.SystemTextJson package
// Unity: use LsdeDialogEngine.Newtonsoft instead

var blueprint = LsdeJson.Parse(File.ReadAllText("blueprints.json"));
var engine = new DialogueEngine();
engine.Init(new InitOptions { Data = blueprint });

// Unified condition resolver — evaluates game-state conditions for both
// choice visibility and condition block pre-evaluation.
engine.OnResolveCondition(cond => Game.EvaluateCondition(cond));

// 3 required handlers — bridge between the engine and your game
engine.OnDialog(args => {
    var (scene, block, context, next) = args;
    Game.ShowDialog(block, context, onComplete: next);
});

engine.OnChoice(args => {
    var (scene, block, context, next) = args;
    Game.ShowChoices(block, context, onSelected: next);
});

engine.OnAction(args => {
    var (scene, block, context, next) = args;
    Game.ExecuteActions(block.Actions);
    context.Resolve();
    next();
});

// Start a scene anywhere in your game code
void MyGameScript(string sceneId) {
    var scene = engine.Scene(sceneId);
    scene.Start();
}
cpp
#include <lsde/engine.h>

using namespace lsde;

DialogueEngine engine;
engine.init({blueprint});

// Unified condition resolver — evaluates game-state conditions for both
// choice visibility and condition block pre-evaluation.
engine.onResolveCondition([](const ConditionTest& cond) {
    return game->evaluateCondition(cond);
});

// 3 required handlers — bridge between the engine and your game
engine.onDialog([](auto* scene, auto* block, auto* ctx, auto next) -> CleanupFn {
    game->showDialog(block, ctx, [next]() { next(); });
    return {};
});

engine.onChoice([](auto* scene, auto* block, auto* ctx, auto next) -> CleanupFn {
    game->showOptions(block, ctx, [next]() { next(); });
    return {};
});

engine.onAction([](auto*, auto* block, auto* ctx, auto next) -> CleanupFn {
    game->executeActions(block->actions);
    ctx->resolve();
    next();
    return {};
});

// Start a scene anywhere in your game code
auto scene = engine.scene(sceneId);
scene->start();
gdscript
var engine = LsdeDialogueEngine.new()
engine.init({"data": blueprint})

# Unified condition resolver — evaluates game-state conditions for both
# choice visibility and condition block pre-evaluation.
engine.on_resolve_condition(func(cond):
    return game.evaluate_condition(cond)
)

# 3 required handlers — bridge between the engine and your game
engine.on_dialog(func(args):
    await game.show_dialog(args["block"], args["context"])
    args["next"].call()
)

engine.on_choice(func(args):
    await game.show_options(args["block"], args["context"])
    args["next"].call()
)

engine.on_action(func(args):
    game.execute_actions(args["block"].actions)
    args["context"].resolve()
    args["next"].call()
)

# Start a scene anywhere in your game code
func my_game_script(scene_id: String):
    var scene = engine.scene(scene_id)
    scene.start()

Validation du blueprint ​

engine.init() retourne un rapport de diagnostic avec erreurs, warnings et stats. L'option check permet de cross-valider avec les capabilities du jeu :

ts
engine.init({
  data,
  check: {
    functions: ['set_flag', 'play_sound'],          // function ids your game implements
    dictionaries: { items: ['sword', 'shield'] },   // dictionary ids and their entry keys
    cards: ['Alice', 'Bob'],                        // card NAMES, never the editor id
  },
});
csharp
engine.Init(new InitOptions {
    Data = blueprint,
    Check = new CheckOptions {
        Functions = new() { "set_flag", "play_sound" },
        Dictionaries = new() { ["items"] = new() { "sword", "shield" } },
        Cards = new() { "Alice", "Bob" },
    },
});
cpp
lsde::CheckOptions check;
check.functions = {"set_flag", "play_sound"};
check.dictionaries = {{"items", {"sword", "shield"}}};
check.cards = {"Alice", "Bob"};

engine.init({blueprint, check});
gdscript
engine.init({
    "data": blueprint,
    "check": {
        "functions": ["set_flag", "play_sound"],
        "dictionaries": {"items": ["sword", "shield"]},
        "cards": ["Alice", "Bob"],
    },
})

Les vingt-cinq diagnostics ​

Erreurs — le payload est refusé et rien ne joue. errors n'est pas vide et engine.scene() n'a rien à vous rendre.

CodeCe qui s'est passé
MISSING_DATAAucun data n'a été passé à init()
MISMATCHED_EXPORTSPlusieurs fichiers fusionnés qui viennent d'exports différents — project ou exportedAt divergent. Passez les fichiers d'UN seul export
WRONG_NAMING_CONVENTIONExporté en snake_case ou PascalCase ; le engine lit du camelCase. Paramètres du projet › Exporteurs › Convention de nommage
INVALID_FORMATformat n'est pas lsde-blueprints. C'est aussi ce que rapportent C# et C++ pour le cas ci-dessus : ils valident un objet typé, les noms de clés d'origine ont déjà disparu
UNSUPPORTED_FORMAT_VERSIONversion n'est pas 1. Un projet encore sur LSDE 1.6 appartient au engine 0.3.x — il n'y a pas de lecteur double
NO_SCENESLe payload ne porte aucune scène
DUPLICATE_SCENEDeux scènes partagent un path ou un id stable
MISSING_SCENE_PATHUne scène n'a pas de path
DUPLICATE_BLOCK_IDDeux blocks de la MÊME scène partagent un id. D'une scène à l'autre c'est légal et attendu — un block est (scène, id)
INVALID_START_BLOCKLa scène nomme un block de départ qui n'est pas l'un de ses blocks
BROKEN_LINKUn fil pointe vers un block absent de la scène. La traversée n'aurait simplement nulle part où aller

Avertissements — ça joue, et quelque chose ne marchera pas en silence. Lisez-les ; aucun n'est du bruit.

CodeCe que ça vous coûte
NO_START_BLOCKLa scène n'a pas de block de départ, donc start() n'a nulle part où commencer
UNKNOWN_WAIT_BLOCKUn id de waitForBlocks n'est pas un block de la scène, donc cette piste se gare pour de bon. La vérification ne peut pas aller plus loin : un id qui existe peut n'être jamais joué
EMPTY_FUNCTIONUne action a un appel sans fonction choisie. Le jeu reçoit un appel qu'il ne peut pas exécuter
UNDECLARED_FUNCTIONUne action appelle une fonction que l'export ne déclare pas — un id v1 resté dans le projet, par exemple. Avant, ça chargeait en silence et échouait en jeu
UNDECLARED_ARGUMENTUn appel passe un argument que la fonction ne déclare pas
UNDECLARED_DICTIONARY_KEYUn argument dictionaryKey n'est pas une entrée du dictionnaire que nomme son paramètre
UNDECLARED_DICTIONARYUne condition teste un dictionnaire que l'export ne déclare pas
UNDECLARED_ENTRYUne condition teste une entrée que son dictionnaire ne déclare pas
UNKNOWN_CHOICE_BLOCKUn test sur le dictionnaire réservé choice nomme un block qui n'est pas un CHOICE de cette scène
UNKNOWN_CHOICE_OPTIONUn test sur le dictionnaire réservé choice nomme une option que ce CHOICE n'a pas
UNKNOWN_FUNCTIONUne action appelle un id de fonction que votre check.functions ne liste pas
UNKNOWN_DICTIONARYUne condition teste un id de dictionnaire que votre check.dictionaries ne liste pas
UNKNOWN_DICTIONARY_ENTRYLe dictionnaire est connu, la clé d'entrée non
UNKNOWN_CARDUn block cite un NOM de carte d'acteur que votre check.cards ne liste pas

Les quatre derniers n'apparaissent que si vous passez check — sans lui le engine n'a rien à quoi comparer. Les huit au-dessus sont toujours actifs : ils comparent ce que les blocks UTILISENT à ce que l'export DÉCLARE lui-même, donc sans check.

Un diagnostic trouvé dans une scène porte sceneId — l'id stable (sc_…), celui que rend handle.getSceneId() — et scenePath, plus blockId quand il vise un block.