Skip to content

Lua API Cheatsheet

Nicholas van de Walle edited this page Jun 21, 2026 · 1 revision

Lua API Cheatsheet

Every function the TrowlLord Lua engine exposes. Everything lives on one global table, cl. The full Lua 5.4 standard library (string, table, math, …) is also available.

This page mirrors the in-app Cheatsheet section (Macros window → Lua Guide). If you change one, change both — see the lua-docs-sync skill.

Output — make something happen

Call Effect
cl.send(text) Send a command/chat line to the server (as if typed and entered). Queued one per server frame.
cl.message(text) Show a note in your local text log. Never sent to the server — your print/debug.
cl.move(dir [, speed]) Drive the player. dir: "n", "ne", "e", "se", "s", "sw", "w", "nw", or "stop" (also accepts long forms like "north"). speed: "walk" (default) or "run".
cl.play_sound(id [, volume [, bypass]]) Play CL_Sounds effect id. volume: "70%" string or a number (≤1 ⇒ fraction, else percent); omit for your normal sound volume. bypass = true plays at exactly volume, ignoring your volume slider and the master/background mute — a reliable loud alert.
cl.select(name) Select/target a player by full or partial name. cl.select() (no arg) clears it. Local-only /select — never sent to the server; sets what cl.selected_player() returns.
cl.select_item(name) Select an inventory item by full or partial name. cl.select_item() clears it. Local /selectitem (also syncs the selection to the server); sets what cl.selected_item() returns.
cl.equip_num(n) Equip whatever item is bound to inventory number slot n (09), exactly like pressing Alt+n. No-op if the slot is empty.
cl.use_num(n) "Use" the item in number slot n (09) with the inventory Use button's smart, hand-aware logic (equips it first if needed). No-op if empty.
cl.bubble_fade([on]) Turn speech/thought-bubble fade-out on (true) / off (false), or toggle with no argument. Local /pref bubblefade.

Read state — snapshot of the current frame (returns nil if unknown)

Call Returns
cl.my_name() Your full character name.
cl.my_simple_name() Your name with only letters/digits.
cl.selected_player() Your currently selected/targeted player.
cl.selected_item() The highlighted inventory item.
cl.my_item(slot) The item equipped in slot (see slots below).
cl.num_item(n) The item bound to inventory number slot n (09) as { name = <string>, equipped = <bool>, right_hand = <bool> }, or nil if the slot is empty. Inspect a slot before acting on it (e.g. detect a chain by name, or skip an already-equipped item).
cl.frame() Current server frame number (5 frames per second).
cl.textlog() Array of the last 10 lines, oldest first; each entry is { frame = <int>, text = <string> }.
cl.textlog_latest() The most recent text-log line's text.
cl.simple_name(name) Reduce any name string to letters/digits only.

Slots for cl.my_item: right, left, head, neck, finger, forehead, shoulder, arms, gloves, coat, cloak, torso, waist, legs, feet, hands. (Case-insensitive; "left_hand"/"right_hand" and "both_hands" also work.)

Number slots for cl.equip_num / cl.use_num / cl.num_item: 09 are the inventory Alt+digit assignments from the Inventory pane (the same slots Alt+n triggers).

Register — set up at file top level; handlers fire later

Call Fires when Handler signature
cl.bind_key(spec, fn) A hotkey is pressed. spec e.g. "f1", "control-f2", "shift-option-a". Modifiers: command/cmd/win, control/ctrl, option/alt, shift, numpad. fn()
cl.bind_click(button, fn) A mouse button (1..5) is clicked. fn(player_or_nil, button)player is the clicked player's name, or nil for empty ground.
cl.bind_double_click(button, fn) A button is double-clicked. fn(player_or_nil, button)
cl.bind_text(word, fn) You type a line starting with word and press return. The line is not sent. Case-sensitive, exact first word. fn(rest_of_line) — text after the word, trimmed.
cl.on_text([filter,] fn) A new incoming text-log line arrives. filter (optional): a Lua pattern string, or a table { pattern=, category= }; omit to match every line. Every matching handler fires (unlike bind_text). fn(text, frame, category)
cl.register(name, fn) Run on demand by name (e.g. gamepad button mapping in the Controller page). fn()
cl.on_login(fn) You finish logging in. fn()
cl.on_logout(fn) You log out / disconnect. fn()

bind_key, bind_click, bind_double_click, and register raise a Lua error on an invalid spec/button. The most-recently-registered handler for a key/word/name wins.

on_text categories (the third handler arg, and the category filter value): speech, myspeech, thought, logon, logoff, share, host, obit, who, info, bard, timestamp, error, default.

Timing — only inside a handler (handlers run as coroutines)

Call Effect
cl.pause(frames) Yield for frames server frames, then resume. 5 frames ≈ 1 second. Defaults to 1. The game and other macros keep running while you wait.

A handler that loops forever must cl.pause(1) each iteration, or the engine aborts it ("macro ran too long without pausing"). Never write while true do end with no pause.

Standard library

Full Lua 5.4. Most useful here:

  • string.find, string.match, string.gmatch, string.gsub, string.format, string.lower, string.upper, string.sub, s:lower() method form.
  • table.insert, table.remove, table.concat, table.sort, ipairs, pairs, #t.
  • math.random (seeded each session), math.floor, math.min, math.max, math.abs.
  • tonumber, tostring, select, type.

Truthiness gotcha: in Lua only nil and false are false-y. 0 and "" are true.

Errors & debugging

  • Syntax and runtime errors appear in your text log prefixed with Lua:.
  • Use cl.message(...) as a print statement — it's local-only.
  • All .lua files in the Lua macros folder load into one shared world: a helper defined in any file is callable from every other file.

Minimal example

cl.bind_key("f1", function()
    cl.send("/think Hello from Lua!")
end)

See Lua Macro Guide for worked examples of each feature, and Why Lua Macros for why this exists alongside the classic macro language.

Clone this wiki locally