API reference¶
Every public module hangs off the framework table (RCSF / RCStructureFramework) and can
also be required directly: require("RCStructureFramework/<Module>"). Data structures
(RCSFPlan, RCSFPiece, RCSFStructureDef, …) live in Data contracts
and Structure definition.
The three calls you’ll use most
RCSF.build(structureId, plan, character, opts?) - one-call headless build.
RCSF.defineStructure(def) - validating one-call registration.
RCSF.enable(key) / RCSF.disable(key) - toggle an auto-system.
Note
Several modules can be copied into your own mod and used standalone, without depending on the framework. The Vendoring guide lists which ones.
Modules¶
Group |
Modules |
|---|---|
Core |
|
Headless API |
Build · Rooms · Introspect · Events |
Plans & geometry |
Plans · Footprints · Geometry |
Materials |
|
Placement & validation |
|
Rooms & auto-systems |
RoomPersistence · PlannedConstructions · PiecePresence · RoomLighting · SpritePropertyPatcher |
Presets, config & UI |
Core¶
Registry¶
Structure-definition registry. Validates defs on registration (see how validation works).
- registerStructure(def) -> boolean
Register an
RCSFStructureDef. Returnsfalseonly ifidis missing.
- defineStructure(def) -> RCSFStructureDef?
Validate, default, and register in one call; an inline
def.piecesarray is batch-registered, and a single{ default = true }variant is filled in when none is given. AlsoRCSF.defineStructure(def).
- registerPieces(structureId, entries) -> integer
Batch-register pieces; returns the count. Auto-fills each piece’s
structureIdandid.
- getStructure(structureId) -> RCSFStructureDef?
- requireStructure(structureId) -> RCSFStructureDef
Look up a def.
requireStructureerrors if the id is unknown.
- getStructureByRoomName(roomName) -> RCSFStructureDef?
Resolve a def from a live IsoRoom name (handles
_Nsuffixes).
- getPieceSpriteName(structureId, variant, pieceType, north) -> string?
Resolve a piece sprite.
- getAllStructures() -> table<string, RCSFStructureDef>
The live registry table. Prefer Introspect for safe, copied queries.
PieceLibrary¶
Catalog of pickable pieces, indexed by category / tag / structure, with unlock gating.
- register(piece) -> boolean
- unregister(id)
- unregisterStructure(structureId)
Add, remove, or bulk-remove pieces.
- get(id) -> RCSFPiece?
Look up one piece.
- getByCategory(category) -> RCSFPiece[]
- getByCategoryAndTag(category, tag) -> RCSFPiece[]
- getByCategoryGroup(group) -> RCSFPiece[]
Bucket queries.
- find(predicate) -> RCSFPiece?
predicateis a function, or a descriptor table (fast path forstructureId + variant + pieceType + north).
- findSpriteName(structureId, variant, pieceType, north) -> string?
Resolve a sprite by descriptor.
- all() -> table<string, RCSFPiece>
- iter() -> fun(): RCSFPiece?
Iterate every registered piece.
- isUnlockedFor(piece, player) -> boolean
Is this piece unlocked for the player (skill / magazine / research)?
- addRecipeKnowledgeProvider(name, fn)
Register
fn(player) -> table<string, bool>?of known recipe keys. Use this to feed magazine / research unlocks; multiple providers OR together.
- makeResearchKey(research) -> string?
Stable key (
item:/sprite:/entity:form) for a research source.
- rebuildBuckets()
Advanced. Rebuild the indexes after mutating a piece in place.
- setKnownRecipesProvider(fn)
Deprecated. Single-slot shim for
addRecipeKnowledgeProvider.
Builder¶
Generic per-piece builder, piece-kind registry, and disassembly. Returns
RCSFBuildOutcome / RCSFDisassembleOutcome; a failed build rolls back
every piece placed this call.
- buildFromPlan(structureId, character, materialSource, plan, options) -> RCSFBuildOutcome
The main build entry.
options = { configureWallObject?, configureCellObject?, configureRoofObject?, container? }.
- disassembleFromPlan(structureId, character, options) -> RCSFDisassembleOutcome
Tear down and refund.
options = { data, objects?, materialSource? }.
- buildFromContainer(structureId, character, container, plan) -> boolean
Legacy delegation to
def.buildFromContainer.
- buildCompletion(structureId, object, character) -> boolean
Finalize a placed structure.
- registerPieceKind(name, handler) -> boolean
Add your own piece kind to the dispatch loop.
handler = { arrayKey, place(piece, ctx) -> obj, errorReason? }.
- unregisterPieceKind(name)
- getPieceKindHandler(name) -> table?
- getPieceKinds() -> string[]
Manage / inspect registered piece kinds.
- getGableAxis(...) / getRoofPieceCount(...) / getRoofPreview(...) / getMinimumContainerMaterialCount(structureId)
Roof-geometry and material-gating helpers (forward to the def’s callbacks).
Built-in piece kinds, in iteration order: wall, cell, roof, furniture, appliance,
decorative, vegetation.
Headless API¶
The first-class, no-UI entry points. Build and Rooms are server-authoritative - they run
on the MP server / in singleplayer and no-op (or fail) on an MP client (see
Multiplayer).
Build¶
One-call programmatic build for prefab spawners, quest rewards, and tests. The caller’s plan
is never mutated. Guide: Build without the UI.
- RCSF.build(structureId, plan, character, opts?) -> RCSFBuildOutcome
Also
RCSF.Build.build.opts = { variant?, materialSource?, free?, container?, builderOptions?, createRoom?=true, allowClient? }.freeconsumes nothing;createRoom(default true) materializes the IsoRoom when the def names a room and the plan has rects (setsoutcome.roomCreated).
Rooms¶
Claim an arbitrary area as a persisted, MP-synced IsoRoom, decoupled from the build pipeline.
Fires OnRCSFRoomAssigned / OnRCSFRoomUnassigned. Guide: Assign a room.
- RCSF.Rooms.assign(rectOrRects, name, opts?) -> RCSFRoomAssignment?
rectOrRectsis a single{x,y,z,w,h}, a list of them, or{ rects = {...} }.opts = { id?, stairs? }(iddefaults to a namespaced derivation ofname). Returns{ id, name, rects }.
- RCSF.Rooms.unassign(target, opts?) -> boolean
targetis the descriptor fromassign, or a rect / rect-list plusopts = { id? | name? }. Tears down the IsoBuilding(s) and forgets the record.
Introspect¶
Documented, mutation-safe cross-mod queries over the registry. Returns plain summaries / shallow copies, never the live def. Guide: Introspect the registry.
- hasStructure(id) -> boolean
- listStructureIds() -> string[]
Existence check; sorted id list.
- getStructure(id) -> table?
- listStructures() -> table[]
Summary
{ id, roomName, variantIds[], useGenericBuilder, materialSource, materialSourceKind, buildMode, hasValidation, pieceCount }.
- listVariants(id) -> string[]
A structure’s variant ids.
- getPiece(pieceId) -> table?
One piece (shallow copy).
- listPieces(filter?) -> table[]
- countPieces(filter?) -> integer
filter = { structureId?, category?, categoryGroup?, variant?, pieceType?, tag? }.
- listCategories() -> string[]
- listCategoryGroups() -> string[]
Distinct values in use.
Events¶
The framework’s custom Lua events, registered via LuaEventManager.AddEvent. Subscribe with
Events.OnRCSF*.Add(handler); each delivers one descriptor table. Full reference:
Events.
- OnRCSFStructureBuilt(info)
info = { structureId, plan, character, placed }.
- OnRCSFStructureDisassembled(info)
info = { structureId, character, removed }.
- OnRCSFRoomAssigned(info) / OnRCSFRoomUnassigned(info)
info = { id, name, rects }.
RCSF.Events.NAMES holds the four names; the fire* helpers are the dispatchers the framework
calls internally.
Plans & geometry¶
Plans¶
Plan construction, keys, deep-copy, and normalization.
- normalizePlan(plan) -> RCSFPlan
Call this first. Stamps
schemaVersion, fills every array, and lifts a legacy single rect intorects[1]. Idempotent.
- getSelectionRect(startX, startY, endX, endY, z) -> RCSFRect
- getSelection(startX, startY, endX, endY, z, existingRects?) -> table
Build a single-rect or multi-rect selection.
- getRectanglePerimeterWalls(rect, pieceType) -> RCSFWall[]
The perimeter walls for a rect - handy when building a plan by hand.
- wallSlotIsInsideRect(rect, x, y, north) -> boolean
Slot-containment test.
- copyPlan(plan) / copyWall / copyRoof / copyStair / copyFurniture / copyAppliance / copyDecorative / copyVegetation
Deep-copy helpers (the authoritative field lists).
- wallKey / makeWallKey / roofKey / makeRoofKey / buildWallMap / buildRoofMap
Keying and lookup-map helpers.
- getRoofZ(rectIndex, plan) -> integer?
- getStairLinks(plan) -> {fromZ, toZ, x, y}[]
Derived geometry.
Footprints¶
- getFootprintFromRoomRect(rect, gableAxis?) / getFootprintFromRects(rects, gableAxis?) / getFootprintFromCells(cells, z?) / getFootprintFromPlan(structureId, plan) -> table?
Derive a footprint from a rect, rect list, cells, or a whole plan.
Geometry¶
Coordinate and rect utilities.
- squareKey(x, y, z) / roomRecordKey(rect) / numberFromValue(v)
Stable keys and value coercion.
- ensureSquare(x, y, z) -> IsoGridSquare
Get-or-create a grid square.
- rectsOverlap(a, b) / rectsEdgeAdjacent4(a, b) / rectContainsCell(r, x, y) / cellInOrAdjacentToRect(r, x, y) -> boolean
Rect / cell predicates.
- getStairLandingTile(stair) -> x, y, z
The Z+1 tile a stair leads onto.
- isInteriorSquare(...) / isAdjacentToFootprint(...) / findNearestOutsideSquare(...) / findNearestAdjacentFootprintSquare(...) / findNearestAdjacentFootprintWalkTarget(...)
Footprint adjacency and nearest-square helpers.
Materials¶
See Concepts → Material sources for the model.
MaterialSource¶
Factory registry for material consumption. A source implements
canConsume / consume / refund / availableSummary / describe.
- register(kind, factory)
- create(kind, ctx) -> source?
Register / instantiate a source factory
fn(ctx) -> source.
- fromDef(structureId, character, container, plan) -> source?
Resolve from
def.materialSource/def.createMaterialSource.
Built-in kinds: "raw" (player inventory), "universal" (one container holding per-piece
counts), "bag" (a bag of variant containers).
RecipeSource¶
Atomic heterogeneous-recipe consumption (items + tags, with keep for tools).
- countAvailable(recipe, character, containers?) -> table
How many of each requirement are available.
- hasAll(recipe, character, containers?) -> boolean, table?
Returns
(ok, missing?).
- consumeAtomic(recipe, character, containers?) -> boolean, table
Validate the whole recipe, then consume all-or-nothing.
MaterialContainers¶
Legacy container / loose-material tracking (item modData). Server-authoritative operations
route via OnClientCommand.
- isContainer(structureId, item) -> boolean / isLooseMaterial(structureId, item) -> boolean
Classify an item.
- getMaterialCount(structureId, item) / getVariant(...) / getContainerVariantFromItem(...) / getMaterialVariantFromItem(...) / setState(structureId, item, variant, count)
Read and write container state.
- packLooseMaterials(...) / takeMaterials(...) / addLooseMaterials(...)
Inventory operations.
Placement & validation¶
PlacementHelpers¶
Low-level object placement and tagging (used by the builder).
- placeWallObject(square, north, spriteName, slotKind, options) -> IsoObject?
Place a wall / door / window slot object.
- placeFloorObject(...) / placeRugObject(...) / placeRoofObject(...) / placeDoor(...) / placeWindow(...) / placeStair(...) / placeFurniture(...) / placeAppliance(...) / placeDecorative(...) / placeVegetation(...)
Per-kind placement helpers, each returning the placed
IsoObject.
- placeLightSwitch(...) / isLightSwitchSprite(spriteName) / squareHasRug(square) / ensureSquare(x, y, z) / getStairLandingTile(...)
Specialized helpers.
- removeObject(object) -> boolean
Remove a placed object cleanly.
PlacementValidation¶
- validateContainerPlacement(structureId, character, container, plan) -> boolean, string?, table?
Runs the default validators, then the def’s
validate*hook. Returns(ok, reasonKey?, data?).
- validateCompletion(structureId, character, object) / validateDisassembly(structureId, character, object)
Completion and disassembly gates.
- getPlacementSummary(structureId, plan) -> table / getPieceSpriteName(...) / getRemovableObjects(structureId, data)
UI summary and lookups.
DefaultValidators¶
Each takes (plan) and returns boolean, string?. Opt in by name via
def.validation.useDefaults.
Available validators: noEmptyPlan, noOverlap, slotKindCompatible, roofNeedsWallUnder,
floorNeedsCell, zAboveEmpty, minimumRoomRectSize, stairLinks, obstructionFree,
footprintFitsInRect, multiRectEdgeConnectivity.
- runAll(plan, names) -> boolean, string?
Run a named set yourself.
Rooms & auto-systems¶
RoomPersistence¶
Runtime IsoRoom / IsoBuilding creation, persistence, and MP sync. Server-authoritative
(see Authority & rooms).
MOD_DATA_KEY = "RCStructureFrameworkRooms".
- createRoom(structureId, rectOrFootprint, loading?) -> boolean
Idempotent room creation.
- createAssignedRoom(structureId, rectOrFootprint, loading?) -> boolean
Like
createRoom, but flags the record as a standalone assignment (backsRCSF.Rooms).
- removeRoomByRect(structureId, rect, clearRecord?, loading?) -> boolean
- removeRoomByRects(structureId, rects, clearRecord?, loading?) -> boolean
Single / multi-rect teardown.
- rememberRoom(...) / rememberRoomFromRects(...) / forgetRoom(...) / forgetRoomByRects(...) / ensureAssignmentDefs(records?)
Record bookkeeping;
ensureAssignmentDefsreconstitutes synthetic assignment defs on load.
- getRoomDef(...) / hasInteriorRoom(rect) / markRoomRuntimeOnly(...) / getRoomRecords() / getRectFromRecord(record) / partitionRectsByConnectivity(rects, stairs?)
Queries and helpers.
- transmitRoomRecords() / syncRoomRecords(rooms?, loading?) / restorePersistedRooms(loading?) / restoreLoadedRoomDefs(loading?)
Sync and restore.
PlannedConstructions¶
Server-authoritative ghost-preview store (persists unbuilt plans across relogs).
MOD_DATA_KEY = "RCStructureFrameworkPlanned".
- register(params) -> string?
params = { ownerId, blueprintItemId?, plan }. Server-only; clients getnil.
- cancel(recordId, requesterId) -> boolean
Authorization: the owner, or
"ADMIN".
- markBuilt(recordId, pieceIndex, builtBy?) -> boolean
May trigger room creation when a connected group completes.
- getRecord(...) / getRecordsForChunk(...) / intersects(candidatePlan) / getNextUnbuiltPieceFor(player, opts) / getRequiredMaterials(recordId, opts)
Queries.
PiecePresence¶
“Is this slot already a real object?” detection (for ghost rendering / dedup).
- hasRealWallAt(...) / hasRealFloorAt(...) / hasRealStairAt(...) / hasObjectWithSpriteAt(...) -> boolean / isPieceRealized(piece) -> boolean
Presence checks.
- wallIsoOrder / pieceIsoOrder(a, b) / sortedWallIndices(walls) / sortedPieceIndices(pieces) / inZPass(panel, pieceZ)
Render-ordering helpers.
RoomLighting¶
Auto-system that gives runtime-created framework rooms working light switches (vanilla only
wires a switch to its room glow at chunk-load, which has already passed for a room you build at
runtime). The server owns light state; clients render. It runs on its own when enabled - toggle
the whole system with RCSF.enable("roomLighting") / RCSF.disable("roomLighting"). The one
thing you may want to configure:
- RoomLighting.setRoomFilter(fn)
Set the predicate
fn(roomName) -> booleanthat decides which IsoRooms RoomLighting manages. The framework injects a default matching registered structure rooms; override it only if you vendored RoomLighting standalone (so it has no Registry to consult) or want custom room detection.
SpritePropertyPatcher¶
Auto-system that marks door / window-frame sprites traversable in runtime rooms (vanilla relies
on map-baked sprite properties a runtime build doesn’t have). Toggle with
RCSF.enable("spritePatcher") / RCSF.disable("spritePatcher") - this only gates the save-load
re-patch sweep; the patch on the live build path always runs.
- SpritePropertyPatcher.applyToSprite(spriteName, north, slotKind)
Apply the sprite-property patch directly. Always available, even with the auto-system disabled.
Presets, config & UI¶
Presets¶
Save / load / transform layout presets (versioned JSON, auto-migrated). The JSON codec itself
is the standalone Json module.
- toRelative(structureId, plan) -> preset
- toPlanAt(structureId, preset, anchorX, anchorY, z) -> RCSFPlan
Convert between an absolute plan and a re-anchorable preset.
- load(structureId) -> preset[] / save(structureId, list) / add(...) / remove(...) / rename(...)
CRUD on a structure’s presets file.
- jsonEncode(value) -> string / jsonDecode(text) -> any?
Thin wrappers over the
Jsoncodec.
Config / EventRegistration¶
- RCSF_Config
Load-time config table.
RCSF_Config.systems.<key> = falsedisables an auto-system;RCSF_Config.validateDefs = falsedisables def validation.
- RCSF.enable(key) / RCSF.disable(key)
Runtime toggle for an auto-system. Keys:
roomLighting,spritePatcher,roomSync,materialContainers,plannedConstructions. See Vendoring.
Each auto-system also exposes registerEvents() / unregisterEvents() (e.g.
RoomLighting.registerEvents()) - the bind / unbind lifecycle that RCSF.enable / RCSF.disable
call for you. Prefer the enable / disable shortcuts; reach for the raw lifecycle only when
driving a vendored module yourself.
Client UI entry points¶
- RCStructurePlacementUI.open(structureId, playerIndex, character, container)
The builder panel (
require("RCStructureFramework/PlacementUI")first).RCStructurePlacementPanelis subclassable.
- RCStructureDisassemblyUI.open(structureId, object, character) / RCStructurePresetsWindow.openFor(...) / RCStructurePieceCatalogPanel.openFor(opts) / RCStructureSavePresetDialog.openFor(panel)
The other UI entry points.
Internal modules¶
Not part of the supported surface: Migrations (preset versions; use Presets.load),
System (room-sync event glue), and BuildRecipeCallbacks (routes def.buildRecipeCallbacks).