Voxel Game Maker

Docs Quick code

Functions

Writing functions, return values, closures, functions as values, and methods on tables.

#Writing one

function heal_party(amount)
  heal(amount)
  popup("Healed " .. amount)
end

heal_party(20)
  • Functions written at the top of the code (not inside anything) can be called from anywhere in that code — even above where they're written — and from the script's other Code nodes: write helpers once per script.
  • Missing values are nil; extra ones are dropped (Check warns when you pass too many).
  • A function can call itself (recursion). Calls more than 200 deep are stopped — usually a function calling itself forever.

#return

function damage_for(level)
  if level <= 1 then return 5 end
  return 5 + level * 3
end

local d = damage_for(4)   -- 17

A function gives back one value (nil without a return). For several, return a list or a table, and take it apart again:

function min_max(list)
  local lo, hi = list[0], list[0]
  for v in list do
    lo = min(lo, v)
    hi = max(hi, v)
  end
  return [lo, hi]
end

local lo, hi = min_max([4, 9, 1])   -- 1, 9 (several names from a list take its items)

return on its own leaves the function (or the whole Code node, at the top) early.

#Functions can wait

A function can wait(), glide objects, or ask the player a choice() — the code pauses inside it and carries on later exactly where it was:

function flash(obj, times)
  for i = 1, times do
    obj.hide()
    wait(0.2)
    obj.show()
    wait(0.2)
  end
end

flash(object("beacon"), 3)
popup("Done flashing")

The one exception: functions the language calls for you — the compare function of sort, and the ones given to map, filter and pcall — run all at once (a wait inside them is skipped).

#Local functions

local function square(x) return x * x end   -- only this code sees it

A local function can call itself, and is the way to keep a helper private to one Code node.

#Functions are values

A function can be put in a variable, a list or a table, passed to another function and returned from one:

local say = popup          -- a game function as a value
say("Hi")

local double = function(x) return x * 2 end
local squares = [1, 2, 3]:map(function(x) return x * x end)   -- [1, 4, 9]

every(2, function() popup("Two seconds passed") end)          -- see Waiting and tasks

#Closures

A function made inside another keeps the locals it was made with — each copy its own:

function make_counter()
  local n = 0
  return function()
    n += 1
    return n
  end
end

local next_id = make_counter()
next_id()          -- 1
next_id()          -- 2

This is how timers, handlers and tasks keep their own state:

for i = 1, 3 do
  local lamp = object("lamp" .. i)
  after(i * 0.5, function() lamp.show() end)   -- each one remembers its own lamp
end

#Methods: functions in tables

local enemy = {hp = 10}

function enemy.hit(self, n)     -- a function stored in the table
  self.hp -= n
end

function enemy:heal(n)          -- the colon adds a hidden first parameter, self
  self.hp += n
end

enemy:hit(4)                    -- the colon passes the table itself as self
enemy.hit(enemy, 1)             -- the same, written out
enemy:heal(2)                   -- hp is now 7

Objects work the same way: self.move(…) and self:move(…) are the same thing.

#Catching errors

local ok, result = pcall(function()
  return risky_thing()
end)
if not ok then
  print("That didn't work: " .. result)
end

pcall(fn, …) runs fn and gives [true, its result], or [false, the error message] instead of stopping the code. error("message") stops with your own message; assert(condition, "message") stops when the condition is false.