Builtin Library
README.md
Assembly
Holds what fills every slot at and under its entity: a vehicle whose wheel,
engine, seat and light slots parts fill, a mech with weapon hardpoints, a ship
or a modular building assembled from parts, a character with equipment points.
Put it on the root the slots belong to; the slots are the Slot components on
that entity and its descendants, and the role sockets of any Humanoid among
them (righthand, head, ...), which a part fills the way Humanoid:attach
seats an item.
The configuration
config maps each slot's path to the guid of the part filling it, or to
false for a slot left empty. A slot it does not name holds the slot's
default. A part's own slots nest under the slot it fills (engine/turbo), so
one table describes the whole assembled object. A path naming a slot of a part
that is not built at the moment (the engine was swapped away) stays in the
table and applies when that part is back.
config is a synced, saved field, which gives the slot family its behaviour:
- Undo. Each
fill,emptyandconfigureis one undoable step; undo puts the previous part back. - Multiplayer. The entity's owner writes it and every peer builds the same parts from it. A peer that does not own the entity is refused with the owner's id.
- Saving. The configuration is saved with the scene, and the parts are rebuilt from it when the scene loads. The parts themselves are runtime children and are never written into the scene.
Changing what one slot holds rebuilds that slot's part (and the parts in its slots) and nothing else: every other part, its entities and their state stay.
Methods
| Method | Returns | Notes |
|---|---|---|
:fill(path, part) | entity or nil | Fill a slot; part is an AssetRef, identity, guid or path, nil returns the slot to its default. Raises when the part does not fit, naming why, or when no slot answers to the path, listing the slots. |
:empty(path) | Leave a slot empty, its default included. | |
:configure(config) | Replace the whole configuration. Keys are slot paths; a value is a part, false, or { part = ..., <slot> = ... } for a part and its own slots. Nothing changes when any part does not fit; the refusal names each. | |
:resolve() | tree | Every slot: { path, name, kind, tags, from, part, guid, source, fill, slot, slots }. source is config, default, empty or none; fill the entity the part was built on; slots the slots of that part. |
:slot(path) | report or nil | One slot as resolve reports it. |
:fits(path, part) | (boolean, string) | Whether a part fits a slot, and why or why not. |
:candidates(path) | { { identity, guid, name } } | The bundles this world holds that fit a slot. |
:refresh() | Rebuild every slot whose part differs from the configuration. |
An Assembly on a part's root (so the part can be assembled on its own too)
passes its calls, once the part is built into a slot, to the Assembly holding
that part, with its paths under the slot the part fills. Any other Assembly
below this one holds its own slots: an assembled car parented onto a truck, or
an assembled item a character holds through Humanoid:attach, keeps its own
configuration, and the Assembly above leaves its slots alone.
Usage
local asm = car.component.get("Assembly")
-- One configuration for the whole car
asm:configure({
wheel_fl = "parts.SportWheel", wheel_fr = "parts.SportWheel",
wheel_rl = "parts.SportWheel", wheel_rr = "parts.SportWheel",
engine = { part = "parts.V8", turbo = "parts.Turbo" },
seat = "parts.BucketSeat",
})
-- Swap one part: only the front-left wheel is rebuilt
asm:fill("wheel_fl", "parts.OffroadWheel")
-- Why does a part not fit?
print(asm:fits("wheel_fl", "parts.V8")) -- false, "slot 'wheel_fl' takes parts of kind 'wheel'; 'parts.V8' fits { engine }"
-- What fills what
for _, s in ipairs(asm:resolve()) do
print(s.path, s.part, s.source)
for _, inner in ipairs(s.slots) do print(" ", inner.path, inner.part) end
end
-- What could go in the engine slot?
for _, p in ipairs(asm:candidates("engine")) do print(p.identity) end
The Slot README covers declaring slots and the Part README declaring what a
part fits.