Skip to content

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.

luau
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:

luau
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 — 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.

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 isIt gets
flat (a wall, a floor, a roof panel)eight around the rim of its flat face — 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.

materialupout
"straw"4 pieces2
"wood" (default)85
"stone"168
"iron"3212

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.

OptionMeaning
fromdisplay 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"… — what shape rules it follows
material"straw", "wood" (default), "stone", "iron" — how far it carries
labelwhat the player sees in their build bar
healthhow much damage it takes to destroy

forget

world.build:forget(name)true, or nil, reason

Removes a piece from the catalogue.

What people already built keeps standing — 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.

luau
world.build:allow(id, { wall = 10, floor = 4, roof = 0 })
world.build:allow(id, { wall = -1 })   -- as many as they like
world.build:allow(id, {})              -- closes the bar
CountThe player sees
-1the piece, with no number
nthe piece, with n left
0the piece, greyed out — how somebody learns a piece exists before they can afford it
absentnothing at all; the piece is not in their bar

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:

luau
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.

luau
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.

BandSupportReads as
0≥ 0.75solid
1≥ 0.5fine
2≥ 0.25strained
3< 0.25about 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 — they read the same function.


Events

Two channels report what the structure is doing. Listen with events:listen.

world.build

Somebody raised a piece.

luau
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

Hungrit scripting documentation.