Using Loreline with C#

Loreline provides a C# library compatible with any C# environment that supports .NET Standard 2.1 (including Unity). This guide shows how to set up a project, load a .lor script, and handle dialogue, choices, and script completion.

Setup for .NET projects

Download loreline-csharp.zip from the GitHub releases page. The archive contains:

You can either reference the DLL directly in your .csproj:

<Reference Include="Loreline">
  <HintPath>path/to/Loreline.dll</HintPath>
</Reference>

Or add the included Loreline.csproj to your solution if you prefer to compile from source:

<ProjectReference Include="path/to/Loreline.csproj" />

Setup for Unity

Download loreline-unity.zip from the GitHub releases page. It contains a complete Unity project with the Loreline plugin that you can use as a starting point, or as a reference for integrating Loreline into your own project.

To add Loreline to an existing Unity project:

  1. From the archive, copy the Assets/Plugins/Loreline/ folder into Assets/Plugins/Loreline/ in your own Unity project
  2. Store your .lor files in Assets/Resources/ with a .lor.txt extension (Unity requires the .txt suffix for TextAsset loading)

Loading a script

Use Engine.Parse() to parse a .lor string. The third argument is a file handler for resolving import statements:

using Loreline;
using System.IO;

string content = File.ReadAllText("story/CoffeeShop.lor");

void HandleFile(string path, Engine.ImportsFileCallback callback)
{
    string text = File.ReadAllText("story/" + path);
    callback(text);
}

Script script = Engine.Parse(content, "CoffeeShop.lor", HandleFile);
Engine.Play(script, OnDialogue, OnChoice, OnFinish);

In Unity, use Resources.Load<TextAsset>() instead of file I/O:

using Loreline;
using UnityEngine;

TextAsset mainAsset = Resources.Load<TextAsset>("CoffeeShop.lor");

void HandleFile(string path, Engine.ImportsFileCallback callback)
{
    string name = Path.GetFileNameWithoutExtension(path) + ".lor";
    TextAsset asset = Resources.Load<TextAsset>(name);
    callback(asset != null ? asset.text : null);
}

Script script = Engine.Parse(mainAsset.text, "CoffeeShop.lor", HandleFile);
Engine.Play(script, OnDialogue, OnChoice, OnFinish);

If your script has no import statements, you can omit the file handler:

Script script = Engine.Parse(content);

Handling dialogue

The dialogue handler receives an Interpreter.Dialogue struct with properties for the character, text, tags, interpreter, and a callback to advance the script:

void OnDialogue(Interpreter.Dialogue dialogue)
{
    string character = dialogue.Character;

    if (character != null)
    {
        // Resolve display name from character definition
        string displayName = (string)dialogue.Interpreter.GetCharacterField(character, "name");
        if (displayName != null) character = displayName;
        Console.WriteLine(character + ": " + dialogue.Text);
    }
    else
    {
        // Narrative text (no character)
        Console.WriteLine(dialogue.Text);
    }

    dialogue.Callback();
}

Handling choices

The choice handler receives an Interpreter.Choice struct with an array of ChoiceOption items. Each option has a Text field and an Enabled field. Call the callback with the index of the selected choice:

void OnChoice(Interpreter.Choice choice)
{
    for (int i = 0; i < choice.Options.Length; i++)
    {
        if (choice.Options[i].Enabled)
        {
            Console.WriteLine("  [" + (i + 1) + "] " + choice.Options[i].Text);
        }
    }

    // Read player input
    string input = Console.ReadLine();
    if (int.TryParse(input, out int selected) && selected >= 1 && selected <= choice.Options.Length)
    {
        choice.Callback(selected - 1);
    }
}

Handling script completion

The finish handler is called when the script reaches its end:

void OnFinish(Interpreter.Finish finish)
{
    Console.WriteLine("--- The End ---");
}

Complete example

Here is a complete .NET console application that loads and plays a Loreline script:

using System;
using System.IO;
using Loreline;

class Program
{
    static string storyDir = "story/";

    static void Main(string[] args)
    {
        string content = File.ReadAllText(storyDir + "CoffeeShop.lor");
        Script script = Engine.Parse(content, "CoffeeShop.lor", HandleFile);

        if (script != null)
        {
            Engine.Play(script, OnDialogue, OnChoice, OnFinish);
        }
    }

    static void HandleFile(string path, Engine.ImportsFileCallback callback)
    {
        try
        {
            callback(File.ReadAllText(storyDir + path));
        }
        catch
        {
            callback(null);
        }
    }

    static void OnDialogue(Interpreter.Dialogue dialogue)
    {
        string character = dialogue.Character;
        if (character != null)
        {
            string name = (string)dialogue.Interpreter.GetCharacterField(character, "name");
            if (name != null) character = name;
            Console.WriteLine(character + ": " + dialogue.Text);
        }
        else
        {
            Console.WriteLine(dialogue.Text);
        }
        Console.WriteLine();
        dialogue.Callback();
    }

    static void OnChoice(Interpreter.Choice choice)
    {
        for (int i = 0; i < choice.Options.Length; i++)
        {
            if (choice.Options[i].Enabled)
            {
                Console.WriteLine("  [" + (i + 1) + "] " + choice.Options[i].Text);
            }
        }
        Console.Write("> ");
        string input = Console.ReadLine();
        if (int.TryParse(input, out int selected) && selected >= 1 && selected <= choice.Options.Length)
        {
            choice.Callback(selected - 1);
        }
    }

    static void OnFinish(Interpreter.Finish finish)
    {
        Console.WriteLine("--- The End ---");
    }
}

Going further

For a full Unity project with UI Toolkit, animations, and styled output, the loreline-unity.zip download from the GitHub releases includes a complete working sample.