Using Loreline with Haxe
Loreline is written in Haxe, so using it from Haxe gives you direct access to the library with no wrappers. This guide shows how to set up a Haxe project, load a .lor script, and handle dialogue, choices, and script completion.
Installing the library
Install Loreline from haxelib:
haxelib install loreline
haxelib install hscript
Or install directly from the GitHub repository using haxelib git:
haxelib git loreline https://github.com/jeremyfa/loreline
haxelib install hscript
Project setup
Create a build file (e.g. build.hxml) for your target:
# 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
Adding the hscript library and hscriptPos define is recommended, as they enable function scripting support. Without them, custom functions written in Loreline scripts are not supported.
Build defines
The -D flags above configure how Loreline compiles for each target. Here is what each one does:
| Define | Description |
|---|---|
hscriptPos |
Enables position tracking in hscript for better error messages with line and column numbers. Required for function scripting. |
js-es=6 |
Outputs ES6 JavaScript (uses class, let, arrow functions). |
loreline_use_js_types |
Uses native JavaScript types for better interop with JS code. |
loreline_use_cs_types |
Uses native .NET collection types (List<object>, Dictionary<string,object>) instead of Haxe collections. |
loreline_cs_api |
Enables the C# interop layer for direct access to .NET APIs. |
loreline_typedef_options |
Defines InterpreterOptions as a typedef instead of a class, for lighter interop on dynamic targets. |
loreline_functions_map_dynamic_access |
Uses dynamic access for the functions map, for better performance on dynamic targets. |
loreline_node_id_class |
Represents node IDs as a class instead of an abstract type, for compatibility with targets that don't inline abstracts. |
Loading a script
Use Loreline.parse() to parse a .lor file. The third argument is a file handler for resolving import statements:
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 parsed successfully, ready to play
}
});
If your script has no import statements, you can pass null for the file handler and use the synchronous return value instead:
var script = Loreline.parse(content);
Handling dialogue
The dialogue handler receives the interpreter, a character identifier (or null for narrative text), the dialogue text, any tags, and a callback to advance the script:
function handleDialogue(
interpreter:loreline.Interpreter,
character:String,
text:String,
tags:Array<loreline.Interpreter.TextTag>,
callback:() -> Void
) {
if (character != null) {
// Resolve display name from character definition
var name = interpreter.getCharacterField(character, "name");
var displayName = (name != null) ? Std.string(name) : character;
Sys.println(displayName + ": " + text);
} else {
// Narrative text (no character)
Sys.println(text);
}
callback();
}
Handling choices
The choice handler receives a list of options. Each option has a text field and an enabled field. Call the callback with the index of the selected choice:
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);
}
}
// Read player input
Sys.print("> ");
var input = Sys.stdin().readLine();
var choice = Std.parseInt(input);
if (choice != null && choice >= 1 && choice <= options.length) {
callback(choice - 1);
}
}
Handling script completion
The finish handler is called when the script reaches its end:
function handleFinish(interpreter:loreline.Interpreter) {
Sys.println("--- The End ---");
}
Complete example
Here is a complete Main.hx that loads and plays a Loreline script from the command line:
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("--- The End ---");
}
}
Going further
Since Loreline is a Haxe library, you have access to the full source code and internal APIs. Check the Loreline repository on GitHub for more advanced usage patterns, including save/restore, translations, and custom functions.