Using Loreline with Lua
Loreline provides a Lua module that works with Lua 5.1 and later, with no external dependencies. This guide shows how to set up a project, load a .lor script, and handle dialogue, choices, and script completion.
Installing the library
Download loreline-lua.zip from the GitHub releases page. The archive contains:
loreline/: the Lua module (init.luaandcore.lua)sample/: a complete sample application with story files
Copy the loreline/ folder into your project so that require("loreline") can find it.
Loading a script
Use loreline.parse() to parse a .lor string. The third argument is a file handler for resolving import statements in your script:
local loreline = require("loreline")
local function handle_file(path, provide)
local f = io.open(path, "r")
if f then
provide(f:read("*a"))
f:close()
else
provide("")
end
end
local f = io.open("story/CoffeeShop.lor", "r")
local content = f:read("*a")
f:close()
local script = loreline.parse(content, "story/CoffeeShop.lor", handle_file)
loreline.play(script, on_dialogue, on_choice, on_finish)
If your script has no import statements, you can omit the file handler:
local script = loreline.parse(content)
Handling dialogue
The dialogue handler receives the interpreter, a character identifier (or nil for narrative text), the dialogue text, any tags, and a callback to advance the script:
local function on_dialogue(interp, character, text, tags, advance)
if character ~= nil then
-- Resolve display name from character definition
local name = interp:get_character_field(character, "name")
print((name or character) .. ": " .. text)
else
-- Narrative text (no character)
print(text)
end
advance()
end
In a UI application, you would typically display the text and call advance() when the player is ready to continue.
Handling choices
The choice handler receives a list of options (a 1-indexed Lua table). Each option has a text field and an enabled field. Call the callback with the 0-based index of the selected choice:
local function on_choice(interp, options, select)
for i, opt in ipairs(options) do
if opt.enabled then
print(" [" .. i .. "] " .. opt.text)
end
end
-- In a real app, you would show buttons and call select(index) on click
select(0) -- select first option (0-based)
end
Note that while the options table is 1-indexed (standard Lua), the select callback expects a 0-based index.
Handling script completion
The finish handler is called when the script reaches its end:
local function on_finish(interp)
print("--- The End ---")
end
Complete example
Here is a complete console application that loads and plays a Loreline script:
local loreline = require("loreline")
local function read_file(path)
local f = io.open(path, "r")
if not f then return "" end
local content = f:read("*a")
f:close()
return content
end
local function script_dir()
local info = debug.getinfo(1, "S")
local path = info.source:match("^@(.*/)")
return path or "./"
end
local function handle_file(path, provide)
provide(read_file(path))
end
local function on_dialogue(interp, character, text, tags, advance)
local formatted = text:gsub("\n", "\n ")
if character ~= nil then
local name = interp:get_character_field(character, "name")
io.write(" " .. (name or character) .. ": " .. formatted .. "\n")
else
io.write(" " .. formatted .. "\n")
end
advance()
end
local function on_choice(interp, options, select)
io.write("\n")
local enabled = {}
for i, opt in ipairs(options) do
if opt.enabled then
enabled[#enabled + 1] = i - 1 -- 0-based for select()
io.write(" " .. #enabled .. ". " .. opt.text .. "\n")
end
end
while true do
io.write("\n> ")
io.flush()
local raw = io.read("*l")
if raw == nil then break end
local choice = tonumber(raw:match("^%s*(.-)%s*$"))
if choice and choice >= 1 and choice <= #enabled then
select(enabled[choice])
return
end
io.write(" Please enter a valid choice number.\n")
end
end
local function on_finish(interp)
io.write("\n--- The End ---\n")
end
local story_dir = script_dir() .. "story/"
local story_path = story_dir .. "CoffeeShop.lor"
local source = read_file(story_path)
if source == "" then
io.stderr:write("Error: could not read " .. story_path .. "\n")
os.exit(1)
end
local script = loreline.parse(source, story_path, handle_file)
if script == nil then
io.stderr:write("Error: failed to parse script\n")
os.exit(1)
end
io.write("=== CoffeeShop ===\n\n")
loreline.play(script, on_dialogue, on_choice, on_finish)
Going further
The loreline-lua.zip download includes a complete working sample with a story that uses character definitions and imports. Download it from the GitHub releases page.