/** * Data-driven modular build-piece snap system: a catalog of piece types, each declaring named * sockets (local position + outward facing + a generic "kind"), plus a connection-rule table saying * which socket kinds may mate. Given a placed piece and a candidate type it resolves every valid * snap — the world transform that lands the candidate's socket exactly on the placed piece's socket, * facing back into it. Genre-agnostic: foundation/wall/ceiling/pillar/edge snapping falls out of the * catalog data, with no game-specific piece names baked in. Pure and serializable; composes with * `world/footprintGrid` occupancy (via an injected `isFree` predicate — never imported) and with * `scene/modelSockets` named attach points (via {@link pieceSocketsFromModel}). */ /** A local- or world-space position/offset, matching the `[x, y, z]` triple used across core. */ export type SocketVec3 = readonly [number, number, number]; /** One named socket on a piece: where it sits and which horizontal direction it faces (yaw about Y). */ export interface SocketDef { /** Stable socket name, unique within its piece. */ name: string; /** Generic connection kind — e.g. "top", "bottom", "edge", "side". Matched by the rule table. */ kind: string; /** Local offset from the piece origin. */ position: SocketVec3; /** Outward-facing yaw in radians (rotation about +Y), local to the piece. Default `0`. */ yaw?: number; } /** A rectangular footprint (world units) a piece occupies on the build grid — the occupancy bridge. */ export interface BuildFootprint { w: number; d: number; } /** A registered piece type: its named sockets and, optionally, the footprint it claims when placed. */ export interface BuildPieceDef { /** Stable piece-type id, unique within the catalog. */ type: string; sockets: readonly SocketDef[]; /** Grid footprint for occupancy checks; omit for pieces that don't reserve cells. */ footprint?: BuildFootprint; } /** Declares that two socket kinds may snap together; `mutual` (default `true`) also allows `b`→`a`. */ export interface ConnectionRule { a: string; b: string; /** When `false` the rule is directional: an `a`-kind candidate socket may mate onto a `b`-kind placed socket, but not the reverse. */ mutual?: boolean; } /** World transform of a placed piece: position plus a yaw about +Y. */ export interface PieceTransform { position: SocketVec3; /** Rotation about +Y in radians. Default `0`. */ yaw?: number; } /** An already-placed piece the resolver snaps candidates against. */ export interface PlacedPiece { type: string; transform: PieceTransform; } /** A piece's socket resolved into world space. */ export interface PlacedSocket { name: string; kind: string; /** World-space socket position. */ position: SocketVec3; /** World-space outward facing yaw (radians). */ yaw: number; } /** One integer grid cell — structurally the `GridCell` of `world/footprintGrid` (no import needed). */ export interface BuildCell { col: number; row: number; } /** A resolved snap: which sockets mate and the world transform that lands the candidate onto the target. */ export interface SnapCandidate { candidateType: string; /** Name of the socket on the already-placed piece. */ targetSocket: string; /** Name of the candidate's socket that mates onto it. */ candidateSocket: string; /** Where to place the candidate so its socket aligns onto the target socket. */ transform: { position: SocketVec3; yaw: number; }; /** Shared world position of the mated sockets. */ point: SocketVec3; /** Distance from the resolve `cursor` to `point`, or `0` when no cursor was given. */ distance: number; } /** Options for {@link BuildSocketCatalog.resolveSnaps}. */ export interface ResolveSnapsOptions { /** Cursor point; when given, candidates are ordered nearest-first by mate-point distance. */ cursor?: SocketVec3; /** * Occupancy predicate — return `false` to reject a snap whose target cells are taken. Only applied * to candidate pieces that declare a `footprint`. Compose with `world/footprintGrid` via * `(cell) => grid.isFree([cell])`; the catalog never imports the grid. */ isFree?: (cell: BuildCell) => boolean; } /** Serializable catalog config; also the shape returned by {@link BuildSocketCatalog.toJSON}. */ export interface BuildSocketCatalogConfig { pieces: readonly BuildPieceDef[]; rules: readonly ConnectionRule[]; /** World units per grid cell for footprint occupancy. Default `1`. */ cellSize?: number; } /** Handle returned by {@link createBuildSocketCatalog}. */ export interface BuildSocketCatalog { readonly cellSize: number; has(type: string): boolean; piece(type: string): BuildPieceDef | null; /** Registered piece-type ids, in declaration order. */ types(): readonly string[]; /** True when a `candidateKind` socket may mate onto a `placedKind` socket. */ canConnect(candidateKind: string, placedKind: string): boolean; /** Every socket of `placed` resolved into world space. */ worldSockets(placed: PlacedPiece): PlacedSocket[]; /** Every valid snap of `candidateType` onto `placed`, ordered deterministically. */ resolveSnaps(placed: PlacedPiece, candidateType: string, options?: ResolveSnapsOptions): SnapCandidate[]; /** Plain, serializable snapshot — feed straight back into {@link createBuildSocketCatalog}. */ toJSON(): BuildSocketCatalogConfig; } /** * The grid cells a piece `footprint` centered on `position` (world units) covers, honoring `yaw` * (snapped to the nearest quarter turn). Mirrors `world/footprintGrid`'s `cellsFor` so a game can * feed the result straight into `grid.isFree`/`grid.reserve` after committing a resolved snap — * without this module importing the grid. * * @capability build-footprint-cells grid cells a snapped build piece footprint covers (footprintGrid occupancy bridge) */ export declare function footprintCells(position: SocketVec3, footprint: BuildFootprint, yaw?: number, cellSize?: number): BuildCell[]; /** A `{ name, offset }` attach point — structurally the `ModelSocket` of `scene/modelSockets`. */ export interface ModelSocketLike { name: string; offset: SocketVec3; } /** * Turn a model's named attach points (from `scene/modelSockets`' `readNamedSockets`) into * {@link SocketDef}s, classifying each into a connection `kind` via `kindOf` and, optionally, an * outward `yaw` via `yawOf`. Lets an artist author snap sockets as empties in a GLB instead of by * hand in the catalog. Pure and structural — no three.js or scene import. * * @capability piece-sockets-from-model derive build-piece snap sockets from a model's named attach points */ export declare function pieceSocketsFromModel(sockets: readonly ModelSocketLike[], kindOf: (name: string) => string, yawOf?: (name: string) => number): SocketDef[]; /** * Build a modular snap catalog from piece definitions and a connection-rule table. Resolves the set * of valid snap transforms when hovering a candidate piece near a placed one: for every compatible * socket pair it computes the world transform that seats the candidate's socket onto the placed * socket, oriented to face back into it. Deterministic ordering (nearest a cursor when given, else * declaration order) and an optional occupancy predicate keep it drop-in for a build-mode controller. * * @capability build-socket-catalog data-driven named-socket snap catalog for modular base building */ export declare function createBuildSocketCatalog(config: BuildSocketCatalogConfig): BuildSocketCatalog;