world.build#
Building pieces: walls, floors, beams and roofs that players raise themselves. They snap to each other, they hold each other up, and they come down when what was under them is taken away.
Use it to
- turn objects you built into pieces players can raise: walls, floors, roofs
- decide who may build, and how many pieces each
- know what holds a piece up, and hear when one collapses
local world = require("world") world.build:define("wall", { from = "WoodWall", kind = "wall", material = "wood" })world.build:define("floor", { from = "WoodFloor", kind = "floor", material = "wood" })world.build:allow(player_id, { wall = -1, floor = -1 })Those three lines are a working building set. The rest of this page is what each word does.
A piece is an object you already built#
There is no piece file and no importer. You build the wall with the ordinary editor and then name it:
world.build:define("wall", { from = "WoodWall", kind = "wall" })from is the object's display name. The region takes a snapshot of that object's whole subtree, meaning its parts, its properties, its contents and its scripts, and the definition owns a copy from then on, so editing the original later does not silently rewrite every wall anybody built.
What you see is what gets raised. The piece is remembered in the pose you left the prototype in. If you made your wall by standing a slab on its edge, every wall raised from it stands the same way. Where the prototype is parked makes no difference.
Keep prototypes out of the way, or delete them once the definition exists. The catalogue is saved with the world and outlives them.
Tip
The behaviour travels. A door that opens, a chest that holds, a workbench that crafts: whatever scripts are on the prototype are re-attached to every copy that gets built, with the builder as their creator. It is their wall and their code.
Where a piece snaps#
Every piece offers sockets, points on its own body where a neighbour can arrive. You do not author them; they are read off the shape.
| The piece is | It gets |
|---|---|
| flat (a wall, a floor, a roof panel) | eight around the rim of its flat face, being four edge midpoints and four corners, plus five on each large face: the centre and the four rim midpoints |
| long (a beam, a pillar, a post) | one on each end, plus four around its waist so a floor can land halfway up |
| anything else (a crate, a workbench) | one per face |
Two pieces join when a socket of one lands on a socket of the other. No grid and no cell size is involved, so pieces of different sizes work together.
The rim sockets on a flat face are what let a wall stand on the edge of a floor and not only in the middle of it.
What holds a piece up#
Every standing piece carries a support from 1 (on the ground) down to 0 (nothing holding it). It is computed from the joints: a piece on the ground has full support, and every joint crossed from there loses some.
Sideways costs more than upward. That one asymmetry is the whole mechanic: a stone tower goes up comfortably, a wooden walkway sags after a few spans and needs a pillar.
material | up | out |
|---|---|---|
"straw" | 4 pieces | 2 |
"wood" (default) | 8 | 5 |
"stone" | 16 | 8 |
"iron" | 32 | 12 |
When support reaches zero the piece comes down, and so does whatever it was holding. Nobody can raise a piece that would fall immediately. The placement is refused with nothing would hold that up.
define#
world.build:define(name, opts) → true, or nil, reason
Defines or replaces one piece.
| Option | Meaning |
|---|---|
from | display name of the object to snapshot. Required the first time; leave it out afterwards to change numbers without re-snapshotting the geometry |
kind | "wall", "floor", "roof", "beam" and so on. What shape rules it follows |
material | "straw", "wood" (default), "stone", "iron". How far it carries |
label | what the player sees in their build bar |
health | how much damage it takes to destroy |
vertical_loss | support lost crossing one joint straight up. Overrides what the material says |
horizontal_loss | support lost crossing one joint sideways. Overrides what the material says |
min_support | the piece comes down below this instead of at zero |
The last three are the dials behind material. Name a material and leave them alone unless a piece has to carry further, or less far, than its material does.
forget#
world.build:forget(name) → true, or nil, reason
Removes a piece from the catalogue.
What people already built keeps standing, because those are ordinary objects, but it stops taking part in the structure, so anything that was only being held up by it comes down.
list#
world.build:list() → list
Every piece this world defines, sorted by name. Each entry has name, label, kind and health.
Always a list. A world with no pieces answers an empty one. Never fails: the catalogue is public, the same way the block palette is.
allow#
world.build:allow(player, counts) → true, or nil, reason
Opens somebody's build bar and says how many of each piece they may raise. This is the verb that turns building on for a player.
world.build:allow(id, { wall = 10, floor = 4, roof = 0 })world.build:allow(id, { wall = -1 }) -- as many as they likeworld.build:allow(id, {}) -- closes the bar| Count | The player sees |
|---|---|
-1 | the piece, with no number |
n | the piece, with n left |
0 | the piece, greyed out. How somebody learns a piece exists before they can afford it |
| absent | nothing at all; the piece is not in their bar |
Careful
An allowance is a count, never a recipe. The engine does not know what wood is, what a wall costs, or whether the player is holding a hammer. Those are your game's questions, and every game answers them differently.
What the engine does is show the number and refuse the eleventh wall without a round trip. You decide when to hand out more:
events:listen("world.build", function(e) local left = spend(e.player_uuid, e.piece) world.build:allow(e.player_uuid, left)end)place#
world.build:place(name, opts) → true, or nil, reason
Raises a piece from a script: a starter shelter at spawn, a quest structure, a ruin the world begins with.
world.build:place("wall", { pos = { x = 4, y = 0, z = -2 }, rot = { x = 0, y = 90, z = 0 } })The structural rules still apply. A script cannot hang a wall in mid-air either: the support solve would knock it down on the same tick, so refusing it now is the honest version of what was going to happen anyway.
If you want to put something anywhere at all, you want world:spawn.
remove#
world.build:remove(id) → true, or nil, reason
Takes a standing piece down, with whatever it was holding up.
support#
world.build:support(id) → support, band
What is holding that piece up (0 to 1), and the band the game paints it with.
| Band | Support | Reads as |
|---|---|---|
0 | ≥ 0.75 | solid |
1 | ≥ 0.5 | fine |
2 | ≥ 0.25 | strained |
3 | < 0.25 | about to go |
Never fails: an object that is not a standing piece answers 0, 3, which is true of a rock.
The band travels so that your own HUD and the built-in support view can never disagree about what a colour means, because they read the same function.
Events#
Two channels report what the structure is doing. Listen with events:listen.
world.build#
Somebody raised a piece.
events:listen("world.build", function(e) print(e.player_uuid .. " raised a " .. e.piece)end)world.collapse#
A piece came down because it lost its support. Different from world.destroyed, which is about damage.
See also#
- world:spawn is for anything that does not need to stand up
Updated 17 October 2026