Getting started
Scripts are plain Lua files run by the bot's Scripts module.
Put your .lua files in this folder. They show up in the Scripts window (with an editor and console), and you run them with one click. Errors land in the console, not in a crash.
-- my_script.lua — runs every time you click it in the Scripts window local x, y, z = zorbitGetPos() zorbitLog("hello from " .. x .. "," .. y .. "," .. z)
How scripts run
Your script gets one context object with everything pre-wired.
Every script runs inside the client's own Lua environment, wrapped by the bot's compatibility layer. That means you get the bot's zorbit* functions plus the game interface of your client (g_game, g_map, g_ui…) in the same script, with errors routed to the Scripts console instead of crashing your session.
scheduler
macro(timeout, fn) repeats your function every timeout ms; schedule(ms, fn) runs it once after a delay. Never busy-loop — use the scheduler.
state shortcuts
Read-only fields like hp, mana, pos, cap are refreshed every tick — plain variables, zero setup.
events
Override an on*Callback (e.g. onTalk, onTextMessage) to react to game events instantly.
Engine & toggles
Turn the bot and its modules on and off, and read the status line shown in the UI.
Enables or disables one engine toggle. Returns true if the name is valid.
zorbitSetToggle("heal", true) -- enable the Healbot engine zorbitSetToggle("cavebot", false) -- stop walking the route
Reads a toggle's current state. Returns true or false.
if zorbitGetToggle("alarm") then zorbitLog("alarms are on") end
Returns the engine's status line — the same text the UI shows.
The master switch — turns the whole bot engine on or off at once.
| name | controls |
|---|---|
| "heal" | healbot rules |
| "mana" | mana rules |
| "target" | targeting |
| "loot" | looting |
| "cavebot" | walking waypoints |
| "antiIdle" | anti-idle |
| "alarm" | alarms module |
| "conditions" | conditions module |
| "friendhealer" | friend healer module |
| "follow" | following a player |
Character & profile
Read where you are, switch profiles and manage following.
Returns your position as three values. When offline you get 0, 0, 0.
local x, y, z = zorbitGetPos() zorbitLog("pos: " .. x .. "," .. y .. "," .. z)
Profiles are numbered sets of settings (rules, waypoints, priorities). zorbitGetProfile() returns the active number, zorbitSetProfile(n) switches to profile n (1-based).
zorbitSetProfile(n) → bool
-- swap to the second profile for a different hunt spot zorbitSetProfile(2)
The name of the player the Follow module walks after.
zorbitSetFollowName(name) → bool
Cavebot runtime state — which waypoint is active, whether the bot is walking or on a delay.
Targeting
Priority order of creature names used by targeting.
A comma-separated list of creature names — the bot attacks in this order. Unknown names fall last.
zorbitSetTargetPriority(csv) → bool
zorbitSetTargetPriority("Demon, Dragon Lord, Dragon")
Player state
Live fields and condition checks about your own character. All of them are plain values — read them straight from the script context.
Refreshed every engine tick. Use them directly — if hp < 50 then ... end.
| field | description |
|---|---|
| name | character name |
| hp / hpmax / hpercent | health, max health and health percent |
| mana / manamax / manapercent | mana, max mana and mana percent |
| cap / maxcap / freecap | capacity, max capacity and free capacity |
| level / lvl / exp | level, alias and experience points |
| mlev / mlevel / magic | magic level and its aliases |
| soul | soul points |
| speed | walk speed |
| stamina | stamina (seconds) |
| direction | facing direction (see Directions below) |
| pos / posx / posy / posz | position as a table or separate numbers |
| skull | skull type (0 = none) |
| outfit | current outfit table |
| voc / vocation | vocation name |
| blessings / blesses | blessing count / bitfield |
Each returns true/false — perfect for alarms and auto-reactions in your scripts.
-- refresh haste whenever it drops macro(500, "autoHaste", function() if hpercent > 80 and not hasHaste() then say("utani hur") end end)
Creatures
Find and inspect players, monsters and NPCs around you.
Returns every creature visible around you — players, monsters and NPCs included.
-- warn when an unknown player walks into your screen macro(1000, "playerWatch", function() for _, c in ipairs(getSpectators()) do if c:isPlayer() and not c:isLocalPlayer() then zorbitLog("player nearby: " .. c:getName()) end end end)
Look up a specific creature by name or id, and measure distances between two positions.
getCreatureByName(name) → creature
getCreatureById(id) → creature
getDistanceBetween(posA, posB) → number
Methods available on any creature you hold.
| call | description |
|---|---|
| :getName() | creature name |
| :getHealthPercent() | health percent (0–100) |
| :getPosition() | position table {x, y, z} |
| :getDirection() | facing direction |
| :isPlayer() / :isMonster() / :isNpc() | kind checks |
| :isLocalPlayer() | true for yourself |
| :isDead() | dead flag |
Items & containers
Search your inventory and open containers, count supplies and move items.
Find an item anywhere in open containers, or read a specific equipment slot. findItem(itemId) returns an item you can pass to use / usewith.
getInventoryItem(slot) → item
getSlot(slot) → item
List every open container (or a specific one by index) and read your money pouch.
getContainer(index) → container
getPurse() → item
-- count strong health potions across all open backpacks local function countPot(id) local n = 0 for _, c in ipairs(getContainers()) do for _, it in ipairs(c:getItems()) do if it:getId() == id then n = n + it:getCount() end end end return n end zorbitLog("SHP left: " .. countPot(236))
Move an item into a specific equipment slot — e.g. swap amulets, rings or the ammo slot.
Equipment slots usable with getInventoryItem / moveToSlot.
Map & movement
Walk, path and inspect tiles around you.
Walk somewhere with the client's pathfinder, or plan first and decide later. findAllPaths returns several alternatives; translateEveryPathToPath / translateAllPathsToPath normalize path formats.
findPath(from, to) → path
findAllPaths(from, to) → table of paths
getPath(from, to) → path
-- walk to the depot one screen away autoWalk({ x = posx + 8, y = posy - 6, z = posz })
Turn your character in place without stepping.
Numeric directions for turn, direction fields and path steps.
For deeper checks, the client's map module is available: g_map.getTile(pos) (then tile:getThings(), tile:getTopCreature(), tile:isWalkable()) and g_map.isSightClear(from, to) to see if a straight shot is unobstructed.
g_map.isSightClear(fromPos, toPos) → bool
Actions
Everything your character can actively do — from scripts or macros.
say(text) speaks on the default channel; saySpell(text) is the spell-flavored variant that respects the spell timeout.
saySpell(text) → void
Use an item, use it on a target, or throw a rune at a creature.
usewith(item, target, subtype?) → void -- alias useWith
useRune(itemId, target) → void
-- emergency SMP from the script layer local smp = findItem(237) if smp then use(smp) end -- throw an SD at your current target useRune(3155, target)
Combat control — attack a creature, follow a player, and cancel either or both.
follow(creature) → bool
cancelAttack() · cancelFollow() · cancelAttackAndFollow()
Leave the game when you decide to — safeLogout waits for a safe moment, canLogout tells you whether the server allows it right now.
logout() · safeLogout()
Save a screenshot of the client window — nice for level-up moments or evidence after a bug.
Events
React to the game as it happens: assign a function to any of these callbacks. Events arrive on the client thread — keep handlers short and wrap risky work in pcall.
| call | description |
|---|---|
| onPlayerHealthChange | your HP changed (heals and damage) |
| onManaChange | your mana changed |
| onPlayerPositionChange | you stepped or teleported |
| onPlayerInventoryChange | an equipment slot changed |
| onStatesChange | icon states changed (poison, burning, PZ…) |
| onSpellCooldown / onGroupSpellCooldown | spell cooldown started |
| onAttackingCreatureChange | your attack target changed |
| onTurn / onWalk | you turned or completed a step |
| onUse / onUseWith | an item use happened |
| call | description |
|---|---|
| onCreatureAppear | a creature entered your view |
| onCreatureDisappear | a creature left your view |
| onCreatureHealthPercentChange | a creature's HP% changed |
| onCreaturePositionChange | a creature moved |
| call | description |
|---|---|
| onTalk | anyone spoke in any channel |
| onTextMessage | server text messages (loot, info, warnings) |
| onChannelEvent / onOpenChannel / onCloseChannel / onChannelList | channel lifecycle |
| onGameEditText | readable text windows (books, letters) |
| onModalDialog | NPC dialogs / server dialogs |
| onAnimatedText / onStaticText / onMissle | floating texts and projectiles |
| call | description |
|---|---|
| onContainerOpen / onContainerClose / onContainerUpdateItem | backpack lifecycle and item updates |
| onAddItem / onRemoveItem / onAddThing / onRemoveThing | things appearing or vanishing on tiles |
| onKeyDown / onKeyPress / onKeyUp | keyboard events |
| onMarketBrowse / onMarketReadOffer | market data |
| onImbuementWindow / onImbuementItem / onOpenImbuementWindow / onCloseImbuementWindow | imbuement dialog data |
-- auto-responder for the NPC trade phrase function onTalk(name, level, mode, text, channelId, pos) if text == "hi" and name == "Jack Staff" then schedule(600, function() say("trade") end) end end
Scheduling & storage
Repeat work, delay it, and keep values between ticks and sessions.
macro(timeout, name, fn) runs fn every timeout ms — the heartbeat of most scripts (it also appears on the dashboard as a toggleable hotkey entry). schedule(ms, fn) fires once. removeEvent cancels a scheduled event.
schedule(delayMs, callback) → void -- alias scheduleEvent
removeEvent(handle) → void
-- every 2 s: log stamina drops below 14 h macro(2000, "staminaWatch", function() if stamina > 0 and stamina < 14 * 3600 then zorbitLog("stamina low!") end end)
delay(ms) sleeps the current coroutine, now() / time() give you current engine and wall-clock timestamps.
Register your own hotkeys inside the bot's hotkey panel: hotkey(text, fn) adds a switch, singlehotkey fires once per press, listen taps chat lines.
singlehotkey(label, callback) → handle
listen(text, callback) → void
storage is a free-form Lua table that survives script restarts and is autosaved with the bot config. Call saveConfig() to force a save.
saveConfig() → void
-- remember the last deposit position between sessions storage = storage or {} storage.lastDepot = storage.lastDepot or { x = 0, y = 0, z = 0 } zorbitLog("last depot: " .. storage.lastDepot.x)
The bot's own session key–value store — values live in memory for the current session only.
zorbitStorageRead(key) → string
Key–value pairs persisted with the bot settings — a permanent place for your script's options.
zorbitSetExtra(key, value) → void
Healbot
Two layers: quick band rules (legacy, simplest) and the priority list the Healbot window edits.
Adds a spell rule for kind "heal" or "mana". The spell fires when HP (or MP) drops below maxPct; minPct is the lower band edge. Cooldown is in milliseconds (default 1000).
zorbitAddRule("heal", 0, 70, "exura gran mas res", 1000) zorbitAddRule("mana", 0, 60, "utani gran hur", 1000)
Read or clear the band rules for "heal" / "mana". zorbitGetRules returns one rule per line, e.g. 0-70%: exura gran mas res.
zorbitClearRules(kind) → bool
The full priority list behind the Healbot window — one row per line, in priority order (line 1 fires first). Each row: trig|mode|value|kind|action|cooldown|manaCost, where trig is hp|mp, mode is b (below) or a (above), kind is spell|item, and action is the spell words or the item id.
zorbitHealSetRules(rows) → bool
-- hp below 70% → cast exura gran mas res (1 s cooldown, 20 mana) local rows = "hp|b|70|spell|exura gran mas res|1000|20\n" .. "hp|b|50|item|266|1000|0" zorbitHealSetRules(rows)
Cavebot & waypoints
Build routes programmatically and read them back.
Appends a waypoint to the active profile. Returns false for invalid arguments (e.g. a 0,0,0 position).
| param | description |
|---|---|
| kind | "node" / "stand" — walk to (or stand at) x, y, z · "say" says text · "goto" jumps to a labelled waypoint · "delay" waits x milliseconds |
| x, y, z | coordinates for node/stand; for delay only x (ms); ignored for say/goto |
| text | chat text for say, waypoint label for goto |
local x, y, z = zorbitGetPos() zorbitAddWaypoint("node", x, y, z) zorbitAddWaypoint("stand", x + 3, y, z) zorbitAddWaypoint("say", 0, 0, 0, "hi") zorbitAddWaypoint("delay", 2000)
zorbitGetWaypoints() returns a numbered list, one waypoint per line ("1. Stand 32310,32220,7"). zorbitClearWaypoints() empties the route and resets the cavebot index.
zorbitClearWaypoints() → bool
Loot list
Item IDs the bot should pick up.
Manage the loot ID list. IDs come back as a comma-separated string.
zorbitGetLootIds() → string -- e.g. "266, 237"
zorbitClearLootIds() → bool
zorbitAddLootId(3031) -- gold coin zorbitAddLootId(3035) -- platinum coin
Alarms
17 alarms with sounds and anti-GM detection. Each alarm has a key, an on/off flag and a numeric parameter (threshold, seconds, item id…).
Returns the state of every alarm as rows of key|on|param — handy to snapshot and restore configurations.
Sets one alarm. Important: pass on as a number (1/0), not a boolean. The param meaning depends on the alarm (see the table below).
zorbitAlarmSet("hp", 1, 50) -- low health alarm at 50% zorbitAlarmSet("posc", 1, 0) -- player on screen: on
Fires the sound of an alarm right now — useful for custom triggers in your scripts.
Plays the alarm's sound like zorbitAlarmNotify but bypasses the enabled check — handy for testing volumes.
setFriends takes a comma-separated whitelist — names on it are not counted as enemies (the "enemy" alarm ignores them). setCmsg sets the phrase the Custom message alarm watches for in default chat.
zorbitAlarmSetCmsg(phrase) → bool
zorbitAlarmSetFriends("Tankmaster, Bestfriend") zorbitAlarmSetCmsg("sell")
| key | alarm | param |
|---|---|---|
| "disc" | Disconnected | — |
| "dmg" | Damage taken | — |
| "hp" | Low health | HP % threshold |
| "mp" | Low mana | MP % threshold |
| "cap" | Low capacity | capacity threshold |
| "pm" | Private message | — |
| "dmsg" | Default message | — |
| "cmsg" | Custom message | phrase via zorbitAlarmSetCmsg |
| "posc" | Player on screen | — |
| "mosc" | Monster on screen | — |
| "stuck" | Player stuck | seconds without movement |
| "skull" | Player with skull | — |
| "gm" | GM detected | — |
| "gmitem" | Detect ID | item id watched in the viewport |
| "gmtp" | Teleport detect | — |
| "enemy" | Enemy (not on the friends list) | — |
| "flash" | Flash client taskbar | — |
Conditions
Extra reactions with a switch, threshold and option each.
Returns every condition as rows of key|on|param|opt — the keys match the Conditions window.
Sets one condition. Pass on as a number (1/0). Valid keys are whatever zorbitCondGet() lists for your version.
Friend Healer
Healing rules for other players — party and friends.
Returns the Friend Healer state — rule rows, healing mode and the friends list — as CSV.
Sets one Friend Healer entry (switch, rule parameter or option). Pass on as a number (1/0); keys match zorbitFHGet() output.
Scripts & files
Drive the Scripts module from code and keep small session values.
Work with files from Documents\ZorbitBot\scripts\: list them, read a source, execute one by name, or open the folder in Explorer.
zorbitReadScript(name) → string
zorbitExecScript(name) → bool
zorbitOpenScriptsFolder() → void
zorbitExecScript("hunt_rotworms")
Writes a line to the bot log — your best friend while debugging scripts.
Settings & extras
Persistence and user key–value extras.
Force-save the current profile, reload settings from disk, or open the settings folder in Explorer.
zorbitLoadSettings() → void
zorbitOpenSettingsFolder() → void
Starts key capture for the settings window's hotkey fields — the next key (or mouse-wheel move, for kind "MW") is recorded; Escape cancels. Normally driven by the settings UI itself.
Script UI helpers
Build a quick control panel for your script — switches, buttons and labels land in your script's own panel in the bot UI.
One call per widget — no layout math. Switches and buttons take callbacks; addTab groups widgets on a named tab.
addButton(text, onClick)
addLabel(text)
addTextEdit(text, onChange)
addSeparator()
addTab(name)
addIcon(itemId, onClick)
local extraHeal = false addSwitch("Extra heal", function(on) extraHeal = on end, false) addButton("Reset loot list", function() zorbitClearLootIds() zorbitLog("loot cleared") end)
setupUI(ui, parent) builds a whole UI from a table definition, importStyle(name) loads an extra OTUI style, and displayGeneralBox pops a Tibia-style message box.
importStyle(name) → bool
displayGeneralBox(title, message, buttons) → void
UI Kit (advanced)
The library the bot's own windows are built with — use it to add Tibia-styled widgets to your script windows. Widget work must happen in the bot's UI context; keep it simple and pcall everything.
| call | description |
|---|---|
| zorbitKit.window(id, w, h) | creates a miniwindow docked in the client's panel |
| zorbitKit.panel(parent, w, h, bg, x, y) | opaque panel |
| zorbitKit.button(parent, text, x, y, w, h, onClick) | button with client styling |
| zorbitKit.label(parent, text, color, x, y, w, h) | text label |
| zorbitKit.edit(parent, text, x, y, w) | single-line text edit |
| zorbitKit.vscroll(parent, x, y, w, h, onValue) | vertical scrollbar (13 px, Tibia-like) |
| zorbitKit.radio(parent, text, x, y) | round radio/checkbox — read with :zorbitGet(), set with :zorbitSet(on) |
| zorbitKit.dim(w, dim) | enable/disable + fade a widget (grey-out) |
| zorbitKit.place(w, x, y) | absolute placement helper |
| zorbitKit.make({names}, parent) | creates widgets from client style names with fallbacks |
| zorbitKit.importStyles() | imports the bot's embedded styles once per session (called automatically) |
local win = zorbitKit.window("myScriptWin", 220, 120) local btn = zorbitKit.button(win, "Loot+", 10, 30, 80, 22, function() zorbitAddLootId(3031) zorbitLog("gold coin added to loot") end)
g_game / g_map bridge
The client's own game interface, proxied safely into your script. These are the low-level calls the bot modules themselves use — reach for them when the shortcuts above aren't enough.
| call | description |
|---|---|
| g_game.isOnline() | true while logged into a character |
| g_game.getLocalPlayer() | your LocalPlayer object (then :getHealth(), :getMana(), :getLevel(), :getFreeCapacity(), :getStamina(), :getSoul(), :getSpeed(), :getPosition(), :getInventoryItem(slot)…) |
| g_game.getCreatures() | every creature known to the client |
| g_game.getContainers() | all open containers |
| g_game.findItemInContainers(itemId, subType) | first item matching the id (-1 = any subtype) |
| g_game.talk(text) | say on the default channel |
| g_game.use(thing) / g_game.useWith(item, thing) | use / use-on |
| g_game.useInventoryItem(id) / g_game.useInventoryItemWith(id, thing) | use an inventory item directly |
| g_game.look(thing) / g_game.lookAt(pos, stack) | look at things and ground objects |
| g_game.move(thing, toPos, count) | move items between tiles/containers |
| g_game.walk(dir) / g_game.turn(dir) / g_game.stop() | movement control |
| g_game.attack(creature) / g_game.follow(creature) | combat & follow |
| g_game.cancelAttack() / g_game.cancelFollow() / g_game.cancelAttackAndFollow() | stop combat / following |
| g_game.isAttackingAlive() / g_game.isFollowing() | current combat state |
| g_game.open(thing) / g_game.openParent(container) | open containers and corpses |
| g_game.getPing() / g_game.getWorldName() | connection info |
Beyond g_game and g_map, the proxied context exposes g_ui (create widgets), g_keyboard (bind hotkeys), g_settings, g_resources, g_sounds, g_things and modules (all loaded client modules). Power-tools: handle with care and pcall everything.
g_keyboard.bindKeyPress(key, fn)
modules.game_console -- …and every other client module
Recipes
Three complete scripts you can paste into Documents\ZorbitBot\scripts\ and run right away.
A hotkey that stops everything and plays the alarm — for when you come back to a surprise.
-- stops the bot and screams when you press the "PANIC" hotkey singlehotkey("PANIC", function() zorbitMasterToggle(false) cancelAttackAndFollow() zorbitAlarmNotify("gm") zorbitLog("PANIC — bot stopped, target cleared") end)
Counts your potions every 5 s, logs a warning and flashes the taskbar when you run low.
local function countPot(id) local n = 0 for _, c in ipairs(getContainers()) do for _, it in ipairs(c:getItems()) do if it:getId() == id then n = n + it:getCount() end end end return n end macro(5000, "supplyWatch", function() if countPot(266) < 10 then zorbitLog("LOW SUPPLIES: strong health potions!") zorbitAlarmNotify("cap") end end)
Switches to profile 2 (a safe, passive setup) whenever an unknown player appears on screen, and back when the screen is clear.
macro(1500, "safeSpot", function() local intruder = false for _, c in ipairs(getSpectators()) do if c:isPlayer() and not c:isLocalPlayer() then intruder = true break end end if intruder and zorbitGetProfile() ~= 2 then zorbitSetProfile(2) zorbitLog("player detected — safe profile ON") elseif not intruder and zorbitGetProfile() == 2 then zorbitSetProfile(1) zorbitLog("screen clear — normal profile ON") end end)