Pour commencer
Installation
npm install @lsde/dialog-enginedotnet 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# 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)# 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# 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-EngineUsage 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.
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();
}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();
}#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();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 :
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
},
});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" },
},
});lsde::CheckOptions check;
check.functions = {"set_flag", "play_sound"};
check.dictionaries = {{"items", {"sword", "shield"}}};
check.cards = {"Alice", "Bob"};
engine.init({blueprint, check});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.
| Code | Ce qui s'est passé |
|---|---|
MISSING_DATA | Aucun data n'a été passé à init() |
MISMATCHED_EXPORTS | Plusieurs fichiers fusionnés qui viennent d'exports différents — project ou exportedAt divergent. Passez les fichiers d'UN seul export |
WRONG_NAMING_CONVENTION | Exporté en snake_case ou PascalCase ; le engine lit du camelCase. Paramètres du projet › Exporteurs › Convention de nommage |
INVALID_FORMAT | format 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_VERSION | version 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_SCENES | Le payload ne porte aucune scène |
DUPLICATE_SCENE | Deux scènes partagent un path ou un id stable |
MISSING_SCENE_PATH | Une scène n'a pas de path |
DUPLICATE_BLOCK_ID | Deux 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_BLOCK | La scène nomme un block de départ qui n'est pas l'un de ses blocks |
BROKEN_LINK | Un 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.
| Code | Ce que ça vous coûte |
|---|---|
NO_START_BLOCK | La scène n'a pas de block de départ, donc start() n'a nulle part où commencer |
UNKNOWN_WAIT_BLOCK | Un 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_FUNCTION | Une action a un appel sans fonction choisie. Le jeu reçoit un appel qu'il ne peut pas exécuter |
UNDECLARED_FUNCTION | Une 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_ARGUMENT | Un appel passe un argument que la fonction ne déclare pas |
UNDECLARED_DICTIONARY_KEY | Un argument dictionaryKey n'est pas une entrée du dictionnaire que nomme son paramètre |
UNDECLARED_DICTIONARY | Une condition teste un dictionnaire que l'export ne déclare pas |
UNDECLARED_ENTRY | Une condition teste une entrée que son dictionnaire ne déclare pas |
UNKNOWN_CHOICE_BLOCK | Un test sur le dictionnaire réservé choice nomme un block qui n'est pas un CHOICE de cette scène |
UNKNOWN_CHOICE_OPTION | Un test sur le dictionnaire réservé choice nomme une option que ce CHOICE n'a pas |
UNKNOWN_FUNCTION | Une action appelle un id de fonction que votre check.functions ne liste pas |
UNKNOWN_DICTIONARY | Une condition teste un id de dictionnaire que votre check.dictionaries ne liste pas |
UNKNOWN_DICTIONARY_ENTRY | Le dictionnaire est connu, la clé d'entrée non |
UNKNOWN_CARD | Un 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.
