Skip to content

Authoring Lua

EmoTracker Community edited this page Apr 8, 2026 · 2 revisions

Authoring Lua Scripts

This page is the top of the Lua scripting hierarchy for pack authors. It explains what scripts can do, how the runtime hosts them, the global objects EmoTracker exposes, and the security model. Then it links out to the per-topic sub-pages for everything that needs more space.

New to pack authoring? Read Developer Setup first to install a Lua language server in VS Code, then Authoring Items, Authoring Locations, and Authoring Layouts to get familiar with the data sides of a pack — Lua sits on top of all three.

What Lua does in a pack

Most of a pack is data: items, locations, layouts, and maps declared in JSON. Lua handles everything that can't be expressed declaratively:

  • Bootstrapping the pack. The runtime calls a single scripts/init.lua file when your pack loads, and it's your script's job from there to call back into the engine to register items, locations, layouts, and maps.
  • Custom access logic. Some checks are too complex for plain code requirements — multi-step routes, conditional dungeon entrances, runtime settings — and need a function rather than a static rule. The $ prefix in access rules drops down into Lua.
  • Autotracking. When the user has the autotracker connected, the runtime hands you raw memory bytes from the running game. A Lua callback turns those bytes into item state changes.
  • Reactive callbacks. EmoTracker calls a handful of well-known function names automatically when major events happen (pack ready, accessibility refreshed, save loaded, autotracker connected). You implement the ones you need.
  • Dynamic items. When none of the built-in item types fit, you can build a fully scripted item via ScriptHost:CreateLuaItem().

The init.lua entry point

Every pack that uses Lua has a scripts/init.lua at its root. The runtime runs this file once when the pack loads, after items / locations / layouts are available to register but before any of them are loaded. Your init script is responsible for choosing what to register, in what order.

→ See Authoring Lua — init.lua for the full pattern, ordering rules, and variant detection.

The Lua sandbox

EmoTracker hosts your scripts via NLua running an embedded Lua 5.x interpreter. The environment is a slightly restricted standard Lua:

  • Standard library is mostly availablestring, math, table, coroutine, os (with restrictions), etc.
  • io is removed entirely unless your pack is flagged as Unsafe. Scripts cannot read or write arbitrary files on the user's disk.
  • os.execute, os.exit, os.setlocale are always removed even for unsafe packs.
  • os.tmpname, os.rename, os.getenv, os.remove are removed unless the pack is flagged unsafe.
  • import is a no-op. It's stubbed out so scripts that come from an environment expecting it don't break.
  • print is rewired to write to EmoTracker's script console (the same one you see in the developer view) instead of stdout.

If your pack genuinely needs filesystem access (e.g. for cached state), you have to set enable_unsafe_scripting: true in its manifest.json. Doing so triggers a warning icon in the Package Manager and discourages most users from installing it — only do this if there's no alternative.

Globals exposed to your scripts

When the runtime constructs the Lua environment, it injects six global names that give you access to EmoTracker's machinery. The full reference for each lives on Authoring Lua — API Reference; the short version:

Global Type What it's for
Tracker object The main entry point — register items/locations/layouts/maps, look up codes, query active variant, etc.
AutoTracker object Memory-read API for autotracking. Only meaningful when an autotracking provider is connected.
Layout object Find loaded layouts and registered layout elements by name.
ScriptHost object Memory watch registration, notifications, console output, and Lua-item creation.
ImageReference object Construct image references (with optional filters / layering) you can hand back to other APIs.
AccessibilityLevel enum The set of accessibility levels that custom-logic functions can return. Has constants like AccessibilityLevel.Normal, .SequenceBreak, .Inspect.
NotificationType enum Notification severity levels for ScriptHost:PushMarkdownNotification. Has constants Message, Celebration, Warning, Error.

These globals are set up before init.lua runs, so you can use them from line one of your script.

Console output and print

EmoTracker reroutes Lua's print(...) so that anything you print goes to the Script Output view inside the developer console, not to a real stdout. This view is shown in EmoTracker's developer mode and is the primary way to debug your scripts at runtime.

You can also call into ScriptHost directly for color-coded output:

print("normal output")               -- printed in the standard color
ScriptHost:Output("dim status line")  -- dark grey
ScriptHost:OutputWarning("warning")   -- yellow
ScriptHost:OutputError("error")       -- red

The output area is capped at 500 lines; older lines drop off the top.

Sub-pages

The deeper topics each get their own page:

Topic Sub-page
Writing your scripts/init.lua and managing load order init.lua
Standard callback functions the runtime invokes by name Standard Callbacks
$-prefixed Lua functions used as custom access rules Custom Access Rules
Adding autotracking to a pack via Lua Autotracking
Building fully-scripted items with LuaItem Lua Items
Full reference for Tracker, AutoTracker, Layout, ScriptHost, and ImageReference API Reference

See also

Clone this wiki locally