Lua API Reference
API level 1 (Veduta v2.0). Everything a script can use besides the standard library. In VS Code the same reference appears as completion and hover text. Game Toolkit shows the timers, bodies, screens, UI and effects at work.
Conventions: positions and sizes are in units (metres), angles in degrees, HUD coordinates
in pixels. A color is "#rrggbb", "#rrggbbaa" or an integer 0xrrggbb.
game
| Callback | Called |
|---|---|
game.init() | once, after the first scene is loaded |
game.update() | once per tick, before the entities' kinds |
game.draw() | once per frame shown, after the scene; the only place for hud.* |
| Function | Does |
|---|---|
game.pause([on]) | stops the world (true or nothing) or starts it again (false): kinds, bodies, particles, on_touch and the game's timers, tweens and tasks wait; game.update, screens, dialogs and toasts go on |
game.paused() | whether the world is paused |
kinds
kinds.<name> = { init = function(e) end, update = function(e) end }
| Function | Called |
|---|---|
init(e) | when an entity of the kind is loaded with a scene or spawned |
update(e) | once per tick, in entity id order, after game.update |
on_touch(e, other) | once when e and other start to overlap (end of the tick), for each whose kind has it |
on_despawn(e) | inside e:despawn(), before the entity goes |
Built-in kinds without behaviour: static, camera, light.
engine
| Field | Value |
|---|---|
engine.tick | the current tick: 0 in game.init, then 1, 2, … |
engine.dt | seconds per tick (0.05 at 20 ticks per second) |
engine.width, engine.height | the frame's size in pixels: the project's resolution, or in game.draw the frame being drawn |
engine.headless | true when no window shows the game (tests, simulate, render) |
engine.name, engine.title | from veduta.json |
engine.api | the runtime's API level |
Entity
Fields:
| Field | Access | Value |
|---|---|---|
id | read | integer |
name | read | string |
kind | read | string |
alive | read | false once despawned |
x, y, z | read/write | position relative to the parent |
visible | read/write | boolean |
model, material | read/write | asset name or nil |
layer | read/write | integer, draw order |
frame | read/write | integer, the frame of a sprite sheet |
anim | read/write | the clip it plays, or nil: another clip starts it, the same changes nothing, nil stops |
anim_done | read | true once a clip that does not loop has ended with no next |
parent | read/write | entity or nil; accepts an entity's name |
hitbox | read/write | {{min x, y, z}, {max x, y, z}} or nil |
color | read/write | a tint ("#40a0ff", 0x40a0ff, nil for none); read back as 0xrrggbb; "#rrggbbaa" sets alpha too |
alpha | read/write | 0 (invisible) to 1 (default); below 1 it blends |
body | read/write | {vx, vy, gravity, max_fall, solid, oneway} the engine moves each tick after the kinds, setting on_ground, on_ceiling, on_wall (−1, 0, 1); or nil |
state | read/write | table of the entity's own values |
Methods:
| Method | Returns / does |
|---|---|
e:position() | x, y, z |
e:set_position(x, y, z) | |
e:move(dx, dy, dz) | adds to the position |
e:world_position() | x, y, z in the world |
e:rotation() | x, y, z in degrees |
e:set_rotation(x, y, z) | degrees |
e:scale() | x, y, z |
e:set_scale(x, y, z) | |
e:has_tag(tag) | boolean |
e:add_tag(tag), e:remove_tag(tag) | |
e:tags() | list of strings |
e:overlapping([tag]) | list of live entities whose bounds overlap (last tick's bounds), id order |
e:bounds() | min x, y, z, max x, y, z; or nil |
e:children() | list of live child entities, id order |
e:despawn() | removed at the end of the tick, with its children; the kind's on_despawn runs first |
e:play(clip) | starts a clip from its first frame, even when it plays |
e:clips() | the clips its textures have, sorted |
e:move_and_slide(dx, dy [, solid [, oneway]]) | moves in the x, y plane, stopped by entities and map cells tagged solid ("solid"), landing on oneway ("oneway") from above; returns stopped along x, along y |
e:move_toward(x, y, speed [, solid]), e:move_toward(other, speed [, solid]) | one tick's step toward a point or entity; true once there |
e:distance_to(x, y), e:distance_to(other) | distance in the x, y plane |
e:face(dx) | mirrors the x scale to face the side dx points to; 0 keeps it |
e:flash([color [, seconds]]) | paints it color (white) for seconds (0.1) |
e:cooldown(name, seconds) | true, and starts the wait, when the cooldown is over; false while it runs |
scene
Available from game.init on.
| Function | Returns / does |
|---|---|
scene.name() | the loaded scene's name |
scene.find(name) | entity or nil |
scene.tagged(tag) | list of entities, id order |
scene.entities() | every live entity, id order |
scene.spawn{...} | the new entity; fields kind (default static), name, model, material, position, rotation, scale (each {x, y, z}), tags (list), visible, layer, frame, anim, parent (entity or name), hitbox ({{min}, {max}}), state (table) |
scene.spawn_prefab(name, x, y, z [, rotation [, prefix]]) | a prefab's entities, footprint corner at (x, y, z), turned 0/90/180/270; the table lists them and holds each under its prefab name |
scene.load(name [, {fade =, color =}]) | replaces the scene, as a reset; with fade (seconds) fades to color (black) and back, taking the input meanwhile. Cancels timers, tweens and tasks (but keep ones and the loading task), cooldowns, camera follow and shake, particles |
scene.raycast(x, y, dx, dy, distance [, tag]) | the first entity or map cell tagged tag ("solid") along the ray: {x, y, distance, nx, ny, entity} (or cell = {x, y}), or nil |
scene.nearest(x, y [, tag [, max]]) | the nearest live entity (with tag, within max) and its distance, or nil |
scene.within(x, y, radius [, tag]) | the live entities within radius, nearest first |
input
Buttons: "up", "down", "left", "right", "a", "b", "select", "cancel".
| Function | Returns |
|---|---|
input.down(button) | held this tick |
input.pressed(button) | went down this tick |
input.released(button) | went up this tick |
input.dpad() | x (−1, 0, 1: left to right), y (−1, 0, 1: down to up) |
input.repeated(button [, delay [, rate]]) | true when it goes down, then every rate (0.1 s) once held delay (0.3 s) |
input.held(button) | seconds held, 0 when up |
While a dialog is open, while a scene fades and on the tick a dialog closes, these read as nothing.
camera
| Function | Returns / does |
|---|---|
camera.get() | {position = {x, y, z}, target = {x, y, z}, ortho, fov, size, near, far} |
camera.set{...} | changes the fields given |
camera.follow2d(x, y, height) | orthographic camera looking down −Z at (x, y), height units visible |
camera.follow(entity [, {bounds =, smooth =, deadzone =, offset =, snap =}]) | moves with the entity each tick; bounds "map" or {left, bottom, right, top}, smooth seconds, deadzone {w, h}, offset {x, y}, snap = false eases in; nil stops |
camera.shake(strength [, seconds]) | shakes up to strength metres, fading over seconds (0.3); camera.get never sees it |
camera.to_screen(x, y [, z]) | the frame pixel x, y of a world point, and whether it is on the frame |
hud
Only inside game.draw. Pixels from the top left.
| Function | Returns / does |
|---|---|
hud.text(x, y, text [, color [, scale]]), hud.text(x, y, text, {color =, scale =, align =, shadow =}) | draws with the 8 × 8 font (ASCII, Latin-1, Windows-1252's €‘’“”–—…); align "center" or "right" on x, shadow a color drawn one pixel down-right; returns the width drawn |
hud.text_width(text [, scale]) | width in pixels; usable outside draw |
hud.wrap(text, width [, scale]) | text with line breaks, and the number of lines; usable outside draw |
hud.rect(x, y, w, h [, color]) | a filled rectangle |
hud.outline(x, y, w, h [, color [, thickness]]) | a rectangle's border |
hud.line(x1, y1, x2, y2 [, color [, width]]) | a line |
hud.circle(x, y, r [, color [, fill]]) | a circle, a disc when fill |
hud.bar(x, y, w, h, value, max [, options]) | a bar filled value / max; options color, back (false for none), border, low (color at low_at = 0.25 or less), vertical |
hud.text_box(x, y, w, h, text [, options]) | text wrapped to w, cut to h; options color, scale, align, valign, spacing, shadow; returns lines drawn, lines total |
hud.image(texture, x, y [, options]) | a texture or part of it; options src = {x, y, w, h}, w, h, color, flip_x, flip_y |
hud.panel(texture, x, y, w, h, border [, options]) | nine-slice panel; border a number or {left, top, right, bottom}; options src, color |
hud.image_size(texture) | width, height in texels |
world
| Function | Returns / does |
|---|---|
world.load(name, cx, cz) | replaces the scene with a streamed world around cell (cx, cz) |
world.name() | the loaded world or nil |
world.focus(x, y, z) | where chunks load; call every tick |
world.height(x, z) | ground height |
world.water(x, z) | water level or nil |
map
The scene's tile map (see Maps). Cells are (x, y): columns right, rows down, from 0.
layer is a layer's name.
| Function | Returns / does |
|---|---|
map.name() | the scene's map or nil |
map.load(name) | another map, as its file describes it (nil removes it); entities stay |
map.size() | columns, rows |
map.tile() | meters per cell |
map.layers() | layer names, bottom first |
map.cell(x, y) | the cell under a point of the world |
map.center(x, y) | the point at the center of a cell |
map.inside(x, y) | whether the cell is on the map |
map.get(x, y [, layer]) | the terrain there (default the first layer), or nil |
map.set(x, y, terrain [, layer]) | paints a cell; nil empties it |
map.has(x, y, tag [, layer]) | a terrain with the tag paints the cell (any layer unless named) |
map.tags(x, y [, layer]) | the tags there, sorted |
map.objects([tag]) | objects {name, x, y, w, h, tags, props}, file order |
map.object(name) | one object or nil |
mesh
| Function | Returns / does |
|---|---|
mesh.new() | an empty mesh |
m:part([material]) | following faces use material (or the entity's) |
m:quad(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) | a quad, counter-clockwise seen from the front |
m:triangle(x1, y1, z1, x2, y2, z2, x3, y3, z3) | a triangle |
m:box(x, y, z, w, h, d [, faces]) | a box from (x, y, z); faces like "+x-x+y-y+z-z", default all |
m:triangles() | the triangle count |
mesh.set(name, m) | makes a copy of m the model name (must contain :, not start with world:); survives scene.load |
mesh.remove(name) | forgets the model |
mesh.voxels(v, materials [, size]) | a mesh of a volume's visible faces, one part per block id, materials[id] names each; blocks size units wide (default 1) |
volume
| Function | Returns / does |
|---|---|
volume.new(x, y, z) | a grid of block ids 0–255, all 0; at most 4 194 304 cells |
v:get(x, y, z) | the id; cells count from 0 |
v:set(x, y, z, id) | |
v:fill(x1, y1, z1, x2, y2, z2, id) | every cell of the box |
v:size() | x, y, z |
save
| Function | Returns / does |
|---|---|
save.write(name, table) | true, or nil and a message when the storage fails; errors on what a save cannot hold |
save.read(name) | a new table, or nil (and a message when it cannot be read) |
save.remove(name) | true, or nil and a message |
save.list() | the save names, sorted |
save.best(key [, value [, "low"]]) | a record in the save best: with a value, stores it when it beats the record (higher, or lower with "low") and returns the record and whether it is new; without, the record or nil |
settings, lang, T
| Function | Returns / does |
|---|---|
settings.<key> = value | stores the value in the save settings at once; read back the same way |
settings.defaults{...} | values for keys never set |
settings.reset(), settings.all() | forgets every value; a copy of them all |
T(key [, vars]) | the text of lang/<code>.lua in the current language, else English, else the key; {name} takes vars.name |
lang.list(), lang.get(), lang.set(code) | the languages; the current one; changes it (kept in settings.lang) |
timer, tween, task
Count ticks; belong to the screen that made them and to an owner entity (cancelled when it
is despawned); cancelled by a scene load unless made with keep.
| Function | Returns / does |
|---|---|
timer.after(seconds, fn [, owner]) | calls fn once; a handle (h:cancel(), h:done()). owner: an entity or {owner =, keep = true} |
timer.every(seconds, fn [, owner]) | calls fn every seconds until it returns false |
timer.cancel(handle) | stops a timer, tween or task |
timer.cooldown(key, seconds) | true, and starts the wait, when the cooldown is over |
tween(target, values, seconds [, easing [, done]]) | moves numeric fields of an entity or table; easings linear, in_/out_/in_out_ + quad, cubic, quart, sine, expo, circ, back, elastic, bounce |
tween.cancel(target or handle) | |
task.start(fn [, owner]) | runs fn until it waits; a handle |
task.cancel(handle), task.current() | |
wait([seconds]), wait_for(button), wait_until(fn) | inside a task: go on after that long (next tick), on a press, when fn() is true |
screen, signal
| Function | Returns / does |
|---|---|
screen.add(name, {enter =, update =, draw =, leave =, resume =}) | defines a screen; drawn after game.draw, bottom first; only the top updates, and game.update and kinds run only while at most one screen is on the stack |
screen.go(name, ...), screen.push(name, ...), screen.pop(...) | replace every screen; enter over the current one; back to the one below (its resume(...)) |
screen.current(), screen.depth() | the top screen's name or nil; how many |
signal.on(name, fn [, owner]) | a handle; fn(...) runs on every signal.emit(name, ...) |
signal.emit(name, ...), signal.off(handle) |
ui
| Function | Returns / does |
|---|---|
ui.menu{items… [, x =, y =, w =, title =, index =, align =, scale =, wrap =, on_select =, on_cancel =, on_change =]} | a menu; items are strings or {text =, disabled =, on_select =}. m:update() returns the index and item chosen this tick, or nil (nil, "cancel" on B or Cancel); m:draw(), m.index, m:select(i), m:size() |
ui.dialog(speaker, text [, {choices =, speed =, on_done =}]) | a typed dialog box at the bottom, pages of three lines, then choices; in a task waits and returns the choice, else on_done(choice). Dialogs queue; the game's input reads as nothing while one is open |
ui.toast(text [, {sec =, color =}]) | a short message at the top (2 s) |
ui.busy() | a dialog is open or a scene is fading |
ui.box(x, y, w, h) | a box in the style |
ui.style | back, border, text, accent, dim; or panel (a texture, nine-slice) and panel_border |
particles
| Function | Returns / does |
|---|---|
particles.burst{x =, y = [, z =, count =, color =, color_end =, speed =, angle =, spread =, life =, gravity =, drag =, size =, size_end =, fade =]} | count (8) squares from (x, y, z) at speed (a number or {min, max}), spread degrees (360) around angle (90), living life s; 512 at most |
particles.clear(), particles.count() |
Globals
| Function | Does |
|---|---|
trace(name [, fields]) | adds an event to the tick's trace; scenarios count events by name |
invariant(name, predicate) | registers a check; lists in veduta.json or a scenario enable it |
require(module) | runs module.lua once (dots are folders) and returns its result |
print(...) | writes to the tool's error output / VS Code's Debug Console |
Standard library
string, table, math and utf8 as in Lua 5.4, with these additions:
| Function | Returns |
|---|---|
math.clamp(x, lo, hi), math.lerp(a, b, t), math.sign(x) | |
math.round(x [, step]) | nearest integer (halves up), or multiple of step |
math.approach(x, target, step) | x moved toward target by at most step |
math.distance(x1, y1, x2, y2), math.angle(x1, y1, x2, y2) | distance; degrees from +x, counter-clockwise |
random.int(a, b), random.float([a, b]), random.chance(p) | from the seeded generator |
random.choice(list), random.shuffle(list) | an element (nil if empty); shuffles in place |
random.weighted(t) | a key of {gold = 3, rock = 1} or an item of {{"gold", 3}, {"rock", 1}}, by weight |
table.find(list, value), table.remove_value(list, value) | first index or nil; removes it, true if it was there |
table.copy(t [, deep]) | a copy, nested tables too when deep |
and these differences:
pairsvisits keys in insertion order.math.randomandmath.randomseeduse the run's deterministic generator.coroutineas in Lua 5.4; a coroutine may also yield inside a Go callback (table.sort).- Missing:
string.pack,string.unpack,string.packsize,string.dump. - Not available by design:
io,os,debug,load,loadfile,dofile,goto.