Skip to content

Build Macros with AI

Nicholas van de Walle edited this page Jun 21, 2026 · 2 revisions

Build Macros with AI

You can have an LLM (Claude, ChatGPT, Gemini, …) write TrowlLord Lua macros for you. The catch: these models don't know this game's API, so left alone they invent functions that don't exist. Fix that by pasting the prompt below first — it gives the model the exact API and command list — then describe the macro you want.

Workflow: copy the whole prompt block, paste it into a fresh chat, then add your request at the bottom where it says MACRO REQUEST. Paste any error back into the same chat to iterate.


The prompt (copy everything in this box)

You are writing a macro for TrowlLord, a third-party client for the game Clan Lord. Macros are
written in Lua 5.4. Follow these rules exactly. Only use the `cl.*` functions and the `/commands`
listed below — anything else does not exist and will error.

# Environment
- All `.lua` files in the user's LuaMacros folder load into ONE shared Lua world. A function
  defined in any file is callable from the others.
- A script's top level runs ONCE at load. Put your `cl.bind_*` / `cl.on_*` / `cl.register` calls
  there. The functions you pass to them run later, each time the event fires.
- Handlers run as coroutines. `cl.pause(n)` yields for `n` server frames (5 frames ≈ 1 second);
  the game keeps running while you wait. A handler that loops MUST `cl.pause(1)` each iteration or
  the engine aborts it ("ran too long without pausing"). Never write `while true do end` with no
  pause.
- Lua truthiness: only `nil` and `false` are false. `0` and `""` are TRUE.
- Errors print to the local text log prefixed `Lua:`. `cl.message(...)` is your print/debug; it is
  local-only and never sent to the server.
- Standard Lua 5.4 is available: `string`, `table`, `math`, `ipairs`, `pairs`, `tonumber`, etc.
  `math.random` is seeded per session.

# The cl.* API (the ONLY engine functions that exist)

## Output — do something
- cl.send(text)            -- send a command/chat line to the server, as if typed + entered.
                              Use this for any "/command" below. Queued one per server frame.
- cl.message(text)         -- show a local note in your text log (never sent).
- cl.move(dir [, speed])   -- dir: "n","ne","e","se","s","sw","w","nw","stop" (long forms ok).
                              speed: "walk" (default) or "run".
- cl.play_sound(id [, volume [, bypass]]) -- play CL_Sounds effect `id` (a number). volume: "70%"
                              string or number (<=1 = fraction, else percent). bypass=true plays
                              at exactly volume, ignoring the volume slider and mute (loud alert).
- cl.select(name)          -- select/target a player by full or partial name; no arg clears.
- cl.select_item(name)     -- select an inventory item by name; no arg clears.
- cl.equip_num(n)          -- equip inventory number slot n (0-9), like pressing Alt+n.
- cl.use_num(n)            -- "use" inventory number slot n (0-9), hand-aware (equips if needed).
- cl.bubble_fade([on])     -- speech/thought-bubble fade-out: true/false, or no arg to toggle.

## Read state — current frame; returns nil if unknown
- cl.my_name()             -- your full character name.
- cl.my_simple_name()      -- your name, letters/digits only.
- cl.selected_player()     -- your currently selected/targeted player (set by cl.select).
- cl.selected_item()       -- the highlighted inventory item (set by cl.select_item).
- cl.my_item(slot)         -- item equipped in a slot, or nil. slot is one of:
                              right, left, head, neck, finger, forehead, shoulder, arms, gloves,
                              coat, cloak, torso, waist, legs, feet, hands (case-insensitive;
                              "left_hand"/"right_hand"/"both_hands" also work).
- cl.num_item(n)           -- inventory number slot n (0-9) as {name=, equipped=, right_hand=},
                              or nil if empty.
- cl.frame()               -- current server frame number (5 per second).
- cl.textlog()             -- array of the last ~10 lines, oldest first; each {frame=, text=}.
- cl.textlog_latest()      -- the most recent text-log line's text, or nil.
- cl.simple_name(name)     -- reduce any name string to letters/digits only.

## Register — set up at file top level; handlers fire later
- cl.bind_key(spec, fn)        -- spec: "f1", "control-f2", "shift-option-a". Modifiers:
                                  command/cmd/win, control/ctrl, option/alt, shift, numpad. fn().
- cl.bind_click(button, fn)    -- button 1..5. fn(player_or_nil, button) — player is the clicked
                                  player's name, or nil for empty ground.
- cl.bind_double_click(button, fn) -- same signature, on double-click.
- cl.bind_text(word, fn)       -- you type a line starting with `word` + return; the line is NOT
                                  sent. fn(rest_of_line). Exact first word, case-sensitive.
- cl.on_text([filter,] fn)     -- runs on each NEW incoming text-log line. filter (optional): a
                                  Lua pattern string, or {pattern=, category=}; omit for every
                                  line. fn(text, frame, category). Every matching handler fires.
                                  categories: speech, myspeech, thought, logon, logoff, share,
                                  host, obit, who, info, bard, timestamp, error, default.
- cl.register(name, fn)        -- expose a macro by name (e.g. to map to a gamepad button). fn().
- cl.on_login(fn)              -- fn() runs once each login.
- cl.on_logout(fn)             -- fn() runs once each logout.

## Timing — only inside a handler
- cl.pause(frames)             -- yield for N server frames (5 ≈ 1 second). Defaults to 1.

# Plain speech
To SAY something out loud, send the text with NO leading slash: cl.send("hello there"). Speech is
local (people nearby hear it). /speak does NOT say text — it sets your language (see below).

# Server/client commands (send these as text with cl.send)
There is no `cl.*` wrapper for most game actions — you trigger them by sending the command text,
e.g. cl.send("/think hello"). Use a leading slash. (In-game `/help <command>` shows exact syntax;
the game spells them with a backslash, e.g. `\THINK` — both prefixes work.) `<>` = required,
`[]` = optional. These descriptions are from the in-game help.

Speech & thought:
  (plain text, no slash)        say it aloud to those nearby.
  /yell <text>                  heard for a good distance.
  /whisper <text>               only people very close hear it.
  /speak <language>             say everything in <language> until changed; COMMON = understood by all.
                                Does NOT affect /think, /thinkto, /ponder.
  /ponder <text>                show what you're thinking to those near you.
  /think <text>                 heard by everyone wearing a sunstone (requires a sunstone).
  /thinkto <player> <text>      private thought to one player (requires a sunstone).
  /thinkclan <text>             thought to your clan (requires a suncrystal/sungem).
  /thinkgroup <text>            thought to your group (requires a sungem).
  /narrate <text>               put text in a narration bubble.
  /action <text>                put "<yourname> <text>" in an action bubble (emote).

Poses / status pose:
  /pose <position> [direction]  positions: STAND SIT SEATED KNEEL LIE BOW LEANRIGHT LEANLEFT
                                THOUGHTFUL CELEBRATE AKIMBO BLESS ANGRY SALUTE CRY SURPRISED
                                WALKLEFT WALKRIGHT ATTACK (some take no direction).
  /sleep                        signal you're away from keyboard.
  /sky                          gaze skyward.

Items & inventory:
  /equip <item> [n]             ready an item (n picks an individual numbered item).
  /unequip <item> | LEFT|RIGHT  store an item / empty that hand.
  /use [args]                   use your RIGHT-hand item (item-specific, e.g. /use /reflect).
  /useitem <item> [text]        use a named equipped item; /useitem left … uses a hand; many items
                                document args via /useitem <item> ?
  /drop [item|amount]           drop the selected/named item, or that many coins; /drop /mine =
                                only you can pick it back up.
  /examine                      examine the item selected in your Inventory window.
  /name <text>                  set the custom name of the selected individual (<#n>) item; /name
                                alone clears it.
  /show [player]                show the selected item to those nearby (everyone sees it).
  /bag …                        bag-of-holding overflow store: /add, /list, /remove [/exact] <name>.

Movement-ish (act on a nearby person):
  /pull <player>                swap places with a nearby person; /pull /critter pulls a pet.
  /push <player>                push a person ahead of you.

Trade & money (player-to-player):
  /sell <price> <buyer>         offer the selected inventory item to <buyer> for <price>.
  /buy <price> <seller>         accept <seller>'s offer.
  /give <player> <amount>       give coins to a player.
  /money                        show how many coins you're carrying.

Karma, sharing & social:
  /thank <person> [reason]      give good karma (anonymous without a reason, named with one).
  /curse <person> [reason]      give bad karma (anonymous without a reason, named with one).
  /anonthank <person>           give good karma, always anonymous.
  /anoncurse <person>           give bad karma, always anonymous.
  /karma [player]               show your karma, or a player's.
  /share [player]               share experience; /share /lock <player>, /share /unlock /all.
  /unshare [player]             stop sharing (everyone, or one), except locked shares.
  /affiliations [name]          show your (or a player's) groups; /affiliations /quit <org> leaves one.

Info & query:
  /who [person]                 players currently online (start the list at <person>).
  /whoclan [clan]               clan members online (yours, or a named clan).
  /info [player]                info about yourself, or another player.
  /status <text>                publish a status line to external widgets (1 per 10 min);
                                /status /remove clears yours.

Session, world & meta:
  /news                         show the news.
  /options <opt> <val>          set a preference; /options list, /options ? <opt>.
  /depart                       (only when fallen) give up and go to Purgatory — traumatic.
  /boot <player>                send an intrusive Agratis to the temple, or someone out of your home.
  /tip                          show a random gameplay tip.
  /swear                        (just try it.)

Reporting & support (to Game Masters):
  /pray <text>                  message all online GMs (out-of-character).
  /bug <text>                   report a bug or suggestion (prefix a keyword to route it).
  /report <person> <reason>     lodge a complaint about a player. Don't abuse it.
  /help [command]               in-game help.

# Output rules
- Prefer the `cl.*` helpers when one exists (e.g. cl.move, cl.select, cl.equip_num); otherwise
  cl.send the command.
- Keep loops paced with cl.pause. Don't spam cl.send — it's already throttled to one per frame,
  but tight bursts still queue up.
- Comment the macro briefly. Return ONE self-contained `.lua` snippet unless asked otherwise.
- If my request needs a function or command that isn't listed above, say so instead of inventing
  one, and suggest the closest thing that exists.

# MACRO REQUEST
<describe the macro you want here — see the hints on the wiki page if you're not sure how>

How to describe what you want

The model writes a good macro when you tell it the trigger, the action, and the edge cases. A useful request answers:

  • Trigger — what starts it? A key (F5), a typed word (/heal), a mouse click, login, an incoming message matching some text, or "run it from a gamepad button" (use cl.register).
  • Action — what should happen, step by step? Name exact items/commands ("equip Sunstone, then /use it"), exact directions, exact text to say.
  • Targeting — who/what does it act on? The selected player, a name you type, whatever you clicked, everyone?
  • Repetition & timing — once, or repeat while held / until something happens? How fast? (Give it in seconds; remind it 5 frames ≈ 1 second.)
  • Stop condition — when does it end or toggle off? (e.g. "toggle with the same key", "stop when my balance is low", "stop after 10 times".)
  • Edge cases — what if nothing is selected, the item isn't equipped, the slot is empty, you're not logged in? Say what you want then.

Tips:

  • Name things exactly as they appear in game (item names, command spelling).
  • One macro per request. Iterate: run it, and if it errors, paste the Lua: line back into the chat — the prompt told the model how the engine reports errors.
  • For "react to a message" macros, tell it the message text or pattern and which category (speech, thought, share, …) so it can use cl.on_text.
  • If you want a toggle, say so — the model will keep a boolean at file scope and flip it.

Example requests that work well

  • "Bind F5 to a toggle: while on, every 2 seconds re-/use my Sunstone if it's equipped on my finger; turn off when I press F5 again. Print a message each time it toggles."
  • "When anyone thinks a line containing my name, play sound 24 as a loud alert and print who said it."
  • "Make a /follow typed command that takes a player name, selects them, and walks toward them — actually just select them and say /think following <name>."
  • "Register a gamepad macro chug that uses inventory number slot 1, but only if slot 1 isn't already in my hand."

See also: Lua API Cheatsheet, Lua Macro Guide, Why Lua Macros.

Clone this wiki locally