The pack
sectorMap
function sectorMap(): Pack<{ sectorMap: typeof kit; }>
Compose the sector map, its format, kit-bash assembly and mesh builder under game.sectorMap.
Helpers
area
const area = (p: readonly Point[]): number=> …
Area.
assembleMap
function assembleMap(layout: MapLayout, kit: Record<string, KitPiece> = {}): SectorMapData
Put a map together from pieces. Each placement copies a piece in, turned, mirrored, moved and raised; its ids get
the placement's id in front (room1.floor), except tags written with a leading @, which stay shared across the
whole map. A join snaps a socket onto an earlier piece's socket, at any angle. Where pieces' edges touch they open
onto each other, as in any sector map. Fixtures, signs, screens and flyers turn and move with their piece. Throws on
an unknown piece or socket, a join that cannot be met, or sockets of different widths.
buildMeshes
function buildMeshes(map: SectorMap, opts: MeshOptions = {}): SectorMeshes
Build the meshes of a sector map.
centroid
function centroid(p: readonly Point[]): Point
The centroid of a polygon's area.
checkMap
function checkMap(data: SectorMapData): string[]
Check a map without building it: every problem found, as sentences an editor can show. Empty when the map is sound.
createSectorMap throws on the first few.
createSectorMap
function createSectorMap(data: SectorMapData): SectorMap
Build a sector map from its data. Throws, naming the problems, on a map checkMap finds fault with.
fill
function fill(rings: readonly (readonly Point[])[]): { points: Point[]; tris: number[] }
Triangles filling the area inside an odd number of rings (even–odd): a floor outline with the shapes drawn inside
it cut out, however those shapes touch one another. A sweep across z in slabs between corner heights, each slab
split into trapezoids between pairs of crossing edges. More triangles than ear clipping, and never confused.
gridMap
function gridMap(g: GridMap): SectorMapData
A sector map from a grid of characters: each run of one character becomes as few rectangular sectors as it takes, with that character's settings, so a person (or a tile-painting editor) draws heights, doors, lifts, slime and secrets as easily as walls. Things come from the legend or by cell; so do windows, hinged doors and cabinets. Signs, screens and flyers are placed by cell (a screen's camera too).
inside
function inside(p: readonly Point[], x: number, z: number): boolean
Whether a point is strictly inside a polygon (even–odd), points on the edge counting as outside.
pieces
const pieces: { room(w: number, d: number, o?: PieceLook & { sockets?: Partial<Record<Facing, number>>; pool?: { w: number; d: number; depth: number; damage?: number; texture?: string; light?: number; }; dais?: { ...; }; pillars?: { ...; }; width?: number; }): KitPiece; ... 15 more ...; liftLedge(w: number, d: number, o?: PieceLo... …
Pieces made to measure. Every piece's origin is its centre, north is −z, and its sockets are named n, e, s, w (and top, bottom for stairs and lifts).
standardKit
function standardKit(): Record<string, KitPiece>
A ready kit of pieces at sensible sizes for a first-person shooter (a player about 1.8 tall): rooms, a hall, a
courtyard, corridors, stairs, doors (plain, red, blue and yellow keyed, secret), a lift, an exit alcove, a slime
room and a pillared hall; an octagonal hub, angled and curved bends, window walls (straight and zig-zag), a balcony
room, curved and spiral stairs, a courtyard with a walkway, a slime canal with a bridge, a computer room, a toilet
block, an office and a lift up to a ledge. Change the sizes by calling pieces yourself.
triangulate
function triangulate(outer: readonly Point[], holes: readonly (readonly Point[])[] = []): { points: Point[]; tris: number[] }
Triangles covering outer with holes cut out, as index triples into the returned points. The outer ring and the
holes may wind either way; holes must lie inside the outer ring and not touch one another. Ear clipping after
bridging each hole to the outer ring: quadratic, which is fine for the dozens of points a sector has.
Classes
SectorMap
class SectorMap
A 2.5D map of sectors, as the early-1990s shooters had: polygons with floor and ceiling heights and light, walls
worked out from where the polygons touch, steps and lips between them, doors, lifts, crushers and platforms,
light effects, damaging floors, secrets, and triggers on entering, using, crossing and shooting. It answers what a
game asks each step: can this body move here (steps, headroom, drops), what is the floor under it, can these two
points see each other through the openings, what does a shot hit, who hears a noise, and the way from here to
there. Draw it with buildMeshes. Get one from createSectorMap.
readonly sectors: Sector[]— The sectors, in the order given.readonly lines: Line[]— Walls and openings.readonly bounds: [number, number, number, number]— The map's bounds: min x, min z, max x, max z.constructor(data: SectorMapData, check = true)placementProblems(): string[]— Problems with where fixtures, signs and screens are, as sentences (forcheckMap).fixture(id: string): number— A fixture's index by id, or −1.hitFixture(id: string, damage: number, at?: { x: number; y: number; z: number }): MapEvent[]— Damage a fixture by id: a window losesdamagehit points and breaks at none (abrokenevent, itsbreaktriggers, and the way through opened for routes); doors and cabinets shrug it off. Returns the events.blastFixtures(x: number, y: number, z: number, radius: number, damage: number): MapEvent[]— A blast atx, y, z: every whole window withinradiusit can reach takes up todamage, less with distance.shotBlocker(ax: number, ay: number, az: number, bx: number, by: number, bz: number): { fixture: string; x: number; y: number; z: number } | null— The nearest fixture that stops a shot between two points in 3D (a window between an enemy and the player), or null.fixtures(): FixtureView[]— Fixtures as they are now, for a presenter: hinged doors, cabinets and windows with what each needs to be drawn.fixtureState(index: number): FixtureState | null— Fixture state by index (a copy), or null.signs(): SignView[]— Signs as they are now, for a presenter: where each hangs, its words, colours, frame and brightness.screens(): ScreenView[]— Screens as they are now, for a presenter: where each is, what it shows, a terminal's lines on show and a camera's view.flyers(): FlyerView[]— Flyers as they are now, for a presenter: where each is, its heading and climb, and its light.get source(): SectorMapData— The data the map was built from (a copy).sector(id: string): number— A sector's index by id, or −1.sectorAt(x: number, z: number): number— The innermost sector a point is in (solid ones included), or −1 outside the map.neighbours(index: number): number[]— Sectors sharing an opening with one.linesOf(index: number): number[]— The lines (walls and openings) around a sector, by index.floor(index: number): number— Floor height of a sector now.ceiling(index: number): number— Ceiling height of a sector now.light(index: number): number— Light of a sector now, 0 to 1, with its glow.revision(index: number): number— How many times a sector's heights have changed: a presenter rebuilds its meshes when this moves.floorAt(x: number, z: number, radius = 0): number— The floor under a point (the highest floor among sectors a body ofradiustouches there), or −Infinity outside.ceilingAt(x: number, z: number, radius = 0): number— The ceiling over a point (the lowest a body ofradiustouches), or −Infinity outside.lights(): (Required<Pick<MapLight, 'x' | 'z' | 'y' | 'color' | 'intensity' | 'radius' | 'flicker' | 'kind'>> & { cone: number | null; yaw: number; pitch: number; tag: string | null; on: boolean; sector: number; index: number })[]— The map's lights as they are now: each with its height worked out (0.4 below the ceiling where it stands, unless it says), the engine's yaw for a spot, the sector it is in, and whether it is lit. A spot'sconestays in degrees as written;yawandpitchare radians.things(skill?: string): (MapThing & { yaw: number; floorY: number; sector: number })[]— Things on the map for a skill (all when none is given), with the engine's yaw and their floor height.gravityAt(x: number, z: number): number— Gravity at a point as a share of the game's: its sector'sgravity(default 1), or 1 outside the map.pushAt(x: number, y: number, z: number): { x: number; z: number }— The push on a body whose feet are atx, y, z, in units per second: a wind's anywhere in its sector, a conveyor's while the body stands on that sector's floor (feet within 0.05 of it).{ x: 0, z: 0 }when nothing pushes.launch(x: number, y: number, z: number, gravity: number): { x: number; y: number; z: number; sector: string; sound: string | null } | null— What a jump pad does to a body whose feet are atx, y, z: the velocity it throws it at (units per second, y up), the pad's sector id and its sound, or null unless the body stands on a pad's floor (feet within 0.05 of it).gravityis the game's pull in units per second squared, before this map's scale, which is taken where the body starts. A pad withtoworks out an arc that comes down there under that pull; with no pull at all, a straight line that takes a second. Throws on agravitythat is not a number.opening(i: number): { low: number; high: number }— The opening of a line: the highest floor and the lowest ceiling of its two sides (closed for a wall).fits(x: number, z: number, body: MoveBody): { ok: boolean; floor: number; ceiling: number }— Whether a body fits at a point: inside an open sector, clear of walls and blocking edges, with headroom, not stepping up more than its step and (an enemy) not standing over a drop deeper than its step. With the floor and ceiling it would have there.move(x: number, z: number, dx: number, dz: number, body: MoveBody): { x: number; z: number; floor: number; ceiling: number; blocked: boolean }— Move a body bydx, dz, sliding along walls: the whole move if it fits, else along x or z alone, else not at all. Long moves go in steps no longer than the body's radius. Returns where it ends up, the floor and ceiling there, and whether anything stopped it. A fixture the body already overlaps (a door swung into it) lets it out.sees(ax: number, az: number, bx: number, bz: number, ay?: number, by?: number): boolean— Whether two points see each other: no wall, closed door or step in the way, and no fixture that hides what is behind it (a hinged door's leaf, a cabinet, frosted glass). With heights (eyes), the line must pass through every opening it crosses and over or under fixtures; without, only walls, shut openings and fixtures block.ray(x: number, y: number, z: number, yaw: number, pitch: number, max: number): MapRayHit | null— What a ray fromx, y, zalongyaw(the engine's) andpitch(positive looks down) hits withinmaxunits along the ground: a wall, a floor, a ceiling or a fixture (a door's leaf, a cabinet, a whole window:fixturenames it), or null (it went out under the sky, or nothing in reach). WithyNaN the ray is flat and only walls, shut openings and fixtures stop it.hears(x: number, z: number, radius: number): Set<number>— The sectors a noise atx, zreaches withinradiusunits, through openings that are not shut or quiet.reach(fx: number, fz: number, body: MoveBody): (x: number, z: number) => number— How far a body would walk fromfx, fzto anywhere, by one search: a function giving the walking distance in units to a point (Infinity where there is no way). For choosing among many goals at once (the nearest pickup);routethen gives the way to the one chosen.route(fx: number, fz: number, tx: number, tz: number, body: MoveBody): { x: number; z: number; door?: string }[] | null— The way from one point to another for a body, as points to walk to, next first, each that enters a door naming it (a door sector's id, or a hinged fixture's), or null when there is none. Unlocked doors count as open (and a running crusher as up: the walker times it), whole windows as shut, and a hinged door standing open is in the way where its leaf is; steps, drops and headroom are the body's; floors that hurt cost more, so the way goes round slime when it can. Fields toward a goal are shared for half a second of map time, so a crowd chasing the player costs one.use(x: number, z: number, yaw: number, keys: readonly string[] = [], reach = 1.2): { result: MapUse; events: MapEvent[] }— Press use fromx, zfacingyaw, withinreachunits: a door or lift whose edge is ahead works (whenkeysholds its key), ausetrigger on the wall ahead fires, and a fixture ahead (a hinged door, a cabinet) opens or shuts, firing itsusetriggers. Returns what happened and the events it caused.visit(who: 'player' | 'enemy', fromX: number, fromZ: number, toX: number, toZ: number): MapEvent[]— A body moved from one point to another: fireenter,leaveandcrosstriggers it set off, start a lift it stepped onto, and (the player) report a secret sector entered for the first time.shot(line: number): MapEvent[]— The player shot lineline: fireshoottriggers on it.killed(tag: string): MapEvent[]— Something the game noticed: the last thing taggedtagdied. Fireskilltriggers for that tag.fireTrigger(id: string, events: MapEvent[] = []): boolean— Fire a trigger by id, as if it had been set off (a script, a cut-scene). False when there is none.tick(dt: number, bodies: readonly MapBody[] = []): MapEvent[]— Run the map on bydtseconds (0 to 10): start triggers on the first step, delayed actions, doors, lifts, crushers, platforms and moves, hinged doors and cabinets, and flyers.bodiesare what stands in the map: a door going down on one goes back up, a hinged door swinging shut into one swings open again, a crusher hurts one it catches, and a floor that does damage hurts one standing on it.enemyWorld(): SectorEnemyWorld— The map asfps-enemiesneeds it: pass it tocreateHorde(config, map.enemyWorld()). Doors are named by sector id, hinged doors by fixture id; enemies open hinged doors that are neither locked nor keyed.get time(): number— Seconds the map has run.get secretsFound(): string[]— Secret sectors found so far, by id.get secretCount(): number— How many sectors are secrets.mover(index: number): MoverState | null— A mover's state by sector index, or null.get state(): SectorMapState— A copy of the state: everything a save needs.restore(state: SectorMapState): void— Put back a saved state of this map. Throws on one that does not fit.
SectorMeshes
class SectorMeshes
The meshes of a sector map. groups lists every piece; verts(key) builds a piece's triangles at the map's current
heights; changed() names the pieces whose sectors have moved since it was last asked.
readonly groups: MeshGroup[]— Every piece.constructor(map: SectorMap, opts: MeshOptions = {})changed(): string[]— The pieces whose sectors' heights have changed since the last call (or since the meshes were built).verts(key: string): Float32Array— A piece's triangles at the current heights: 8 floats a vertex (x, y, z, normal x, y, z, u, v). Empty when it has nothing to show now.
Types
AirPoint
type AirPoint = [number, number, number]
A point in the air: [x, z, y], y the height above the map's zero.
CabinetFixture
interface CabinetFixture extends FixtureBase
A box that opens: a cabinet, a locker, a filing drawer, a chest. It stands in the way of bodies, sight and shots like
a block of its size, and the first time it opens the game is told its contents, to spawn in front of it.
kind: 'cabinet'—'cabinet'.at: Point— The middle of its footprint,[x, z].facing?: number— The way its front faces, degrees clockwise from north; default 0.size?: [number, number, number]— Width along the front, depth and height, each above 0 and at most 20; default[0.8, 0.5, 1.8](a locker).y?: number— Its base above the floor (a cupboard hung on a wall); default 0.opens?: 'door' | 'drawer' | 'lid'— How it opens:'door'(on a hinge at its left edge),'drawer'(slides out) or'lid'(lifts); default'door'.look?: string— What it looks like, for the presenter:'locker','cabinet','filing','safe','chest'or the game's own; default'cabinet'.contents?: string[]— Thing types it holds (pickups, or an enemy that jumps out), given to the game the first time it opens.time?: number— Seconds it takes to open or shut, above 0; default 0.4.open?: boolean— Starts open (its contents already taken). Default false.
CabinetView
interface CabinetView extends FixtureViewBase
A cabinet, as a presenter draws it: a box size at at, its front toward facing, opening by opens.
kind: 'cabinet'—'cabinet'.at: Point— The middle of its footprint.facing: number— The way its front faces, compass degrees.yaw: number— The engine's yaw of the way its front faces.size: [number, number, number]— Width, depth, height.y: number— Its base height in the world.opens: 'door' | 'drawer' | 'lid'—'door','drawer'or'lid'.look: string— What it looks like.contents: string[]— What is still inside: empty once it has been opened.
Facing
type Facing = 'n' | 'e' | 's' | 'w'
A compass direction: which way a socket faces out of its piece.
FixtureBase
interface FixtureBase
What every fixture has.
id: string— Unique id among the fixtures; triggers and actions name it.tag?: string— A group name: an action naming it reaches every fixture with it.key?: string— A key the player needs to open it, by name ('red'); default none.locked?: boolean— Starts locked: use does nothing until anunlockaction. Default false.sounds?: FixtureSounds— Sounds.texture?: string— Its texture, for the presenter.
FixturePhase
type FixturePhase = 'opening' | 'open' | 'closing' | 'closed' | 'hit' | 'broken' | 'locked' | 'unlocked' | 'refused'
What happened to a fixture: it started opening, came open, started closing, came closed; a window was
hit and held, or broken; it was locked or unlocked by an action; or use found it refused (locked).
FixtureSounds
interface FixtureSounds
Sounds a fixture makes, by name, played where it is.
open?: string— As it starts to open.close?: string— As it starts to shut.locked?: string— Used while locked.break?: string— A window breaking.hit?: string— A window struck but holding.
FixtureState
interface FixtureState
A fixture's state.
open: number— How far open, 0 (shut) to 1 (open); a window is 0 whole and 1 broken.target: 0 | 1— Where it is heading: 0 (shutting) or 1 (opening).side: 1 | -1— A hinged door's swing: 1 clockwise, −1 the other way.locked: boolean— Locked bylockedor alockaction (a key is separate).hp: number— A window's hit points left.timer: number— Seconds before an open door withautoCloseshuts.given: boolean— A cabinet's contents have been handed out.
FixtureView
type FixtureView = HingedView | CabinetView | WindowView
A fixture as a presenter sees it now.
FlyerView
interface FlyerView
A flyer as a presenter draws it.
index: number— Its index.id: string— Its id.tag: string | null— Its tag, or null.kind: string— What it is.x: number— Where it is now (a beam's: where it lands).y: number— Height.z: number— Where it is now.heading: number— The way it is heading, compass degrees (a beam: the way it shines).yaw: number— The engine's yaw of its heading.pitch: number— Its climb, radians, positive down (a beam: how steeply it shines down).on: boolean— Moving now.size: number— Scale.from: AirPoint | null— A beam's fixed end, or null.light: { color: string; intensity: number; radius: number; cone: number | null } | null— The light it carries, or null.sound: string | null— A looping sound, or null.
GridCell
interface GridCell
What a grid character means: the sector settings its cells get, and maybe a thing standing on it.
floor?: number— Floor height.ceiling?: number— Ceiling height.light?: number— Light.wall?: string— Wall texture.side?: string— Step and lip texture.floorTexture?: string— Floor texture.ceilingTexture?: string— Ceiling texture.sky?: boolean— Open to the sky.solid?: boolean— Solid (a pillar inside the map).mover?: SectorMover— A door, lift, crusher or platform.damage?: number— Damage per second.secret?: boolean— A secret.tag?: string— A tag for triggers.glow?: SectorGlow— A light effect.jump?: SectorJump & { cell?: [number, number] }— A jump pad, landing attoin world units or at the middle ofcell([column, row]).gravity?: number— Gravity as a share of the game's.push?: SectorPush— A conveyor or a wind.liquid?: boolean— The floor is a liquid.floorFx?: SurfaceFx— How the floor moves and shimmers.ceilingFx?: SurfaceFx— How the ceiling moves and shimmers.wallFx?: SurfaceFx— How the walls move and shimmer.window?: { texture?: string }— A window: give the cell a sill and lintel by itsfloorandceiling, and this hangstexture(default'glass') across its openings, which bodies cannot cross.hinged?: Omit<HingedFixture, 'id' | 'kind' | 'hinge' | 'width' | 'facing' | 'direction'> & { side: Facing; hinge?: 'start' | 'end'; outward?: boolean }— A hinged door on every cell of this character, along the cell'sside, hinged at itshingeend ('start', the west or north end, or'end'), swinging into the cell (or out of it,outward). Ids are the area's id with-door,-door-2… after it.cabinet?: Omit<CabinetFixture, 'id' | 'kind' | 'at'> & { against?: Facing }— A cabinet on every cell of this character: in its middle, or pushedagainstone side and facing away from it. Ids as forhinged, with-cabinet.id?: string— A sector id for this character's first area (later ones get-2,-3…); default from the character.thing?: string— A thing on every cell of this character, by type, facing north unlessfacingsays.facing?: number— The thing's facing in degrees.
GridMap
interface GridMap
A map painted as rows of characters: each character a kind of cell, cell units square.
rows: string[]— The rows, north first. A space, or a character the legend leaves out, is solid rock.legend: Record<string, GridCell>— What each character means.cell?: number— Cell size in world units; default 2.things?: (Omit<MapThing, 'x' | 'z'> & { at: [number, number] })[]— Things placed by cell:at: [column, row], standing in the cell's middle.triggers?: MapTrigger[]— Triggers, naming sectors by the legend's ids and tags.lights?: (Omit<MapLight, 'x' | 'z'> & { at: [number, number] })[]— Lights placed by cell:at: [column, row], in the cell's middle.signs?: ByCell<MapSign>[]— Signs placed by cell, standing or hanging at the cell's middle.screens?: ByCell<MapScreen>[]— Screens placed by cell.flyers?: (Omit<MapFlyer, 'path' | 'from'> & { path: AirPoint[]; from?: AirPoint })[]— Flyers whose path points are[column, row, y], in the cells' middles.fixtures?: MapFixture[]— Fixtures, in world units (a hinge belongs on a cell's edge, not its middle).name?: string— A name for the map.sky?: string— The sky texture.defaults?: MapDefaults— Defaults.meta?: Record<string, unknown>— Anything else to keep with the map (par time, music).
HingedFixture
interface HingedFixture extends FixtureBase
A door on a hinge: a toilet stall's, a gate, one leaf of a saloon door, a cupboard's. The leaf runs width from the
hinge toward facing while shut and swings swing degrees open. While shut or part open, the leaf blocks bodies and
sight, and shots too unless it is a grate.
kind: 'hinged'—'hinged'.hinge: Point— The hinge,[x, z].width: number— The leaf's length from the hinge, above 0 and at most 20.facing: number— The way the shut leaf runs from the hinge, degrees clockwise from north.swing?: number— How far it swings open, degrees, above 0 and at most 180; default 90.direction?: 'cw' | 'ccw' | 'both'— Which way it swings seen from above:'cw'(clockwise),'ccw', or'both'(away from whoever uses it); default'cw'.height?: number— The leaf's height, above 0; default 2.1.bottom?: number— The gap under the leaf, 0 or more (a stall door has about 0.25); default 0.thickness?: number— The leaf's thickness, above 0 and at most 1; default 0.08.speed?: number— Degrees per second it swings, above 0; default 240.autoClose?: number— Seconds it stays open before it swings shut by itself, above 0; default never.open?: boolean— Starts open. Default false.grate?: boolean— Bars or a grille: it blocks bodies, but sight and shots pass. Default false.
HingedView
interface HingedView extends FixtureViewBase
A hinged door, as a presenter draws it: a box width × height × thickness from hinge to tip.
kind: 'hinged'—'hinged'.hinge: Point— The hinge.tip: Point— The far end of the leaf now.facing: number— The way the leaf runs from the hinge now, compass degrees.yaw: number— The engine's yaw of the way the leaf's face looks now (the leaf's run turned 90° clockwise).centre: [number, number, number]— The middle of the leaf now: x, y (half-way up it) and z.width: number— Its length, height, the gap under it and its thickness.height: number— Leaf height.bottom: number— Gap under it.thickness: number— Leaf thickness.y: number— The floor under it.grate: boolean— Bars: sight and shots pass.
KitPiece
interface KitPiece
A reusable piece of map: sectors, things and triggers in its own coordinates, and its sockets.
sectors: SectorDef[]— Sectors, ids local to the piece.things?: MapThing[]— Things.triggers?: MapTrigger[]— Triggers; they name the piece's own sectors, tags and fixtures.lights?: MapLight[]— Lights, in the piece's own coordinates.fixtures?: MapFixture[]— Fixtures (hinged doors, cabinets, windows), ids local to the piece.signs?: MapSign[]— Signs, in the piece's own coordinates.screens?: MapScreen[]— Screens, in the piece's own coordinates.flyers?: MapFlyer[]— Flyers, their paths in the piece's own coordinates and heights.sockets?: Record<string, Socket>— Sockets by name.
Line
interface Line
A wall or opening between two sectors, worked out from where their edges touch.
a: Point— One end.b: Point— The other end. Thefrontsector lies to the left going fromatob, seen from above with z up the page.front: number— The sector in front (never −1).back: number— The sector behind, or −1 for a solid wall.owners: { sector: number; edge: number }[]— The polygon edges it came from:{ sector, edge }.block: boolean— Bodies may not cross.blockEnemies: boolean— Enemies may not cross.quiet: boolean— Sound does not cross.middle: string— A see-through texture hung across the opening, or ''.middleHeight: number | null— How highmiddlereaches from the bottom of the opening, or null for all of it.
MapBody
interface MapBody
A body the map moves and pushes: the player, an enemy.
id: string— Who it is, for crush events.x: number— Position.z: number— Position.y: number— Feet height.radius: number— Radius.height: number— Height.
MapDefaults
interface MapDefaults
Map-wide defaults and settings.
floor?: number— Floor height; default 0.ceiling?: number— Ceiling height; default 4.light?: number— Light; default 1.wall?: string— Wall texture; default'wall'.floorTexture?: string— Floor texture; default'floor'.ceilingTexture?: string— Ceiling texture; default'ceiling'.
MapEvent
type MapEvent = | { type: 'mover'; sector: string; phase: string } | { type: 'sound'; sound: string; x: number; y: number; z: number } | { type: 'crush'; body: string; damage: number } | { type: 'secret'; sector: string } | { type: 'trigger'; trigger: string } | { type: 'action'; trigger: string; action: TriggerAction } | { type: 'locked'; key: string } | { type: 'hurt'; body: string; damage: number } | { type: 'fixture'; fixture: string; phase: FixturePhase; x: number; y: number; z: number } | { type: 'contents'; fixture: string; things: string[]; x: number; y: number; z: number; facing: number } | { type: 'sign'; sign: string } | { type: 'screen'; screen: string } | { type: 'flyer'; flyer: string; phase: 'start' | 'stop' | 'end' }
What a step of the map did.
MapFixture
type MapFixture = HingedFixture | CabinetFixture | WindowFixture
Something that opens or breaks.
MapFlyer
interface MapFlyer
Something moving along a path through the air or the sky: a ship, a drone, birds, a blimp, or a searchlight's beam sweeping along the ground. The map moves it, deterministically, and keeps where it is in its state.
id?: string— Unique id, forstartandstopactions; default its place in the list.tag?: string— A group name forstartandstop.kind: string— What it is, by name, for the presenter:'ship','drone','bird','blimp','searchlight'or the game's own.path: AirPoint[]— The way it flies, 2 or more points.speed?: number— Units per second, above 0; default 4.mode?: 'loop' | 'pingpong' | 'once'—'loop'(back to the first point and round again),'pingpong'(back and forth) or'once'(stops at the end); default'loop'.curve?: boolean— Fly a smooth curve through the points rather than straight between them. Default false.on?: boolean— Moving at the start. Default true.start?: number— How far along the path it starts, 0 to 1; default 0.from?: AirPoint— A beam: it stands at this fixed point and shines at the moving point (a searchlight sweeping the yard).light?: { color?: string; intensity?: number; radius?: number; cone?: number }— A light it carries (a beam's shines fromfromtoward the moving point).size?: number— Scale for the presenter's model, above 0; default 1.sound?: string— A looping sound by name, to play where it is.
MapLayout
interface MapLayout
A map put together from pieces.
name?: string— A name for the map.sky?: string— The sky texture.defaults?: MapDefaults— Defaults for sectors.pieces?: Record<string, KitPiece>— Pieces this layout defines for itself, beside the kit's.place: Placement[]— The pieces placed, in order: a join may only name a piece placed before it.sectors?: SectorDef[]— More sectors, with ids of their own.things?: MapThing[]— More things.triggers?: MapTrigger[]— More triggers: they name pieces' sectors as<placement id>.<local id>.lights?: MapLight[]— More lights, in map coordinates.fixtures?: MapFixture[]— More fixtures, in map coordinates.signs?: MapSign[]— More signs, in map coordinates.screens?: MapScreen[]— More screens, in map coordinates.flyers?: MapFlyer[]— More flyers, in map coordinates.meta?: Record<string, unknown>— Anything else to keep with the map.
MapLight
interface MapLight
A light the map places: a lamp over a door, a torch on a wall, a green glow over slime, a red emergency light. The map keeps it (on or off); the game turns it into a real light.
x: number— Position in world units.z: number— Position in world units.y?: number— Height; default 0.4 below the ceiling of the sector it is in.color?: string— Colour, CSS; default'#ffffff'.intensity?: number— Brightness (inverse-square units: a lamp lighting a room 6 units across wants about 4 to 10); default 6.radius?: number— How far it reaches; default 8.flicker?: number— Flicker, 0 (steady) to 1 (a guttering torch); default 0.cone?: number— A spot: the cone's half-angle in degrees; default none (a point light).facing?: number— A spot's direction, degrees clockwise from north; default 0.pitch?: number— A spot's tilt down, degrees; default 0.tag?: string— A group name, forlampactions.on?: boolean— Lit at the start; default true.kind?: 'lamp' | 'torch' | 'glow'— What it looks like, for the presenter: a'lamp'fixture, a'torch'flame, or a'glow'with no visible source; default'lamp'.
MapRayHit
interface MapRayHit
Where a ray stopped.
dist: number— Distance along the ground.x: number— Where it stopped.y: number— Where it stopped.z: number— Where it stopped.kind: 'wall' | 'floor' | 'ceiling' | 'fixture'— What it hit.line: number— The line hit, or −1 for a floor, a ceiling or a fixture.sector: number— The sector whose surface it hit (for a wall, the side it came from; for a fixture, the sector it stands in).fixture?: string— The fixture hit (a door's leaf, a cabinet, a window), by id; only with kind'fixture'.
MapScreen
interface MapScreen extends MountPlace
A screen on a wall, or a hologram standing in the air.
id?: string— Unique id, forscreenactions; default its place in the list.tag?: string— A group name forscreenactions.kind: ScreenKind— -'terminal':linesof text scrolling up,rowsat a time. -'video': a video fromsrc. -'static': snow. -'image': the picturepicture. -'hologram':picture(orlines) standing in the air, see-through, turningspindegrees a second. -'map': the level from above, theregionof it. -'camera': whatcamerasees.lines?: string[]— Terminal and hologram: the text.rows?: number— Terminal: lines shown at once, 1 to 60; default 6.scroll?: number— Terminal: lines per second it scrolls, 0 (still) to 60; default 0.5.src?: string— Video: its address.picture?: string— Image and hologram: a picture name.region?: [number, number, number, number]— Map: the part of the level it shows,[x0, z0, x1, z1]; default all of it.camera?: ScreenCamera— Camera: where it looks from.color?: string— Text or tint colour, CSS; default'#7dffb0'.background?: string— Background colour, CSS; default'#04100a'.glow?: number— How much light of its own it gives, 0 to 1; default 0.9.flicker?: number— Flicker, 0 to 1; default 0.spin?: number— Hologram: degrees a second it turns; default 0.on?: boolean— On at the start. Default true.
MapSign
interface MapSign extends MountPlace
Text on a wall or hanging: a room number, a warning, an exit sign, a neon name.
id?: string— Unique id, forsignactions; default its place in the list.tag?: string— A group name forsignactions.text: string— The words;\nbreaks a line.color?: string— Text colour, CSS; default'#ffffff'.background?: string— Background colour, CSS; default'#22262c','transparent'for none (letters on the wall).glow?: number— How much light of its own it gives, 0 (lit by the room) to 1; default 0, or 0.8 forneonandscreenframes.flicker?: number— Flicker, 0 (steady) to 1 (a failing tube); default 0.frame?: 'plate' | 'neon' | 'screen' | 'poster'—'plate'(a fixed panel),'neon'(glowing tubes),'screen'(a lit panel) or'poster'(paper); default'plate'.picture?: string— A picture drawn beside the text (an arrow, a hazard mark, a person), by name, the presenter's.hanging?: boolean— Hanging from the ceiling, readable from both sides. Default false.on?: boolean— Lit at the start. Default true.
MapThing
interface MapThing
A thing on the map: where the player starts, an enemy, a pickup, a prop, a decoration, by type.
type: string— What it is, by name:'start', an enemy kind, an item, a prop (a barrel, a crate), anything the game knows.x: number— Position in world units.z: number— Position in world units.facing?: number— Which way it faces, degrees clockwise from north (0 north, 90 east); default 0.y?: number— Height above the floor (a floating pickup); default 0.skill?: string[]— The skill levels it appears on, by name (['normal', 'hard']); default all.ambush?: boolean— An enemy that waits for sight, deaf to shots. Default false.patrol?: boolean— An enemy that walks a beat. Default false.tag?: string— A group name, forkilltriggers (the last enemy or breakable prop with it gone).
MapTrigger
interface MapTrigger
When something happens, and what.
id?: string— Unique id, forenableanddisable; default its place in the list.on: TriggerOn— -'enter'/'leave': a body enters or leavessector, any sector taggedtag, or theregionpolygon. -'use': the player presses use facingwall. -'cross': a body crosseswallorline. -'shoot': the player shootswall. -'start': when the map starts. -'kill': the last thing taggedtagdies (the game reports it). -'use'withfixture: the player uses that fixture (a cabinet, a hinged door) and it opens or shuts. -'break': the windowfixturebreaks.fixture?: string— A fixture's id, for use and break.sector?: string— A sector, for enter and leave.tag?: string— A sector tag (enter, leave) or a thing tag (kill).region?: Point[]— A polygon, for enter and leave.wall?: { sector: string; edge: number }— A wall: a sector's edge.line?: [Point, Point]— A line anywhere, for cross.by?: 'player' | 'enemy' | 'any'— Who sets it off:'player'(default),'enemy'or'any'.once?: boolean— Fires once, then is spent; default true.delay?: number— Seconds after it fires that its actions happen; default 0.key?: string— A key the player needs for it to work (use).disabled?: boolean— Starts switched off until anenableaction; default false.do: TriggerAction[]— What happens.
MapUse
type MapUse = | { type: 'door'; sector: string; opening: boolean } | { type: 'lift'; sector: string } | { type: 'locked'; key: string } | { type: 'switch'; trigger: string } | { type: 'fixture'; fixture: string; opening: boolean } | { type: 'nothing' }
What pressing use did.
MeshGroup
interface MeshGroup
One drawable piece of the map.
key: string— A stable key:'<sector>:<part>:<texture>', with~and its motion after it when it scrolls, animates, has an effect or is a liquid.sector: number— The sector it belongs to (its light).part: 'floor' | 'ceiling' | 'wall' | 'middle'—'floor','ceiling','wall', or'middle'(a see-through texture hung across an opening, drawn both sides).texture: string— The texture name.deps: number[]— Sectors whose heights change it.scroll: [number, number]— Texture repeats per second it scrolls along u and v ([0, 0]for still): addscroll × secondsto its uvs.animate: number— Frames per second of an animated texture; 0 for a still one.effect: SurfaceEffect | null— A surface effect to draw it with (ripple,shine,holo,glow,frost,fire), or null.liquid: boolean— A floor that is a liquid (water, slime, lava).
MeshOptions
interface MeshOptions
How meshes are built.
wallScale?: number— World units one repeat of a wall texture covers; default 2.flatScale?: number— World units one repeat of a floor or ceiling texture covers; default 2.
MountPlace
interface MountPlace
Where a sign or screen is: on a wall, or standing (or hanging) at a point.
wall?: { sector: string; edge: number }— On a wall: a sector's edge; it faces into that sector.along?: number— Withwall: how far along the edge its middle is, 0 to 1; default 0.5.at?: Point— Instead ofwall: its middle,[x, z].facing?: number— Withat: the way its face looks, degrees clockwise from north; default 0.y?: number— Height of its middle above the floor where it is.size?: [number, number]— Width and height in units, each above 0 and at most 40.
MountView
interface MountView
Where a sign or a screen is, as a presenter places it: a quad width × height at x, y, z, facing yaw.
index: number— Its index in the list.id: string— Its id.tag: string | null— Its tag, or null.x: number— Its middle.y: number— Its middle's height in the world.z: number— Its middle.facing: number— The way its face looks, compass degrees.yaw: number— The engine's yaw of the way its face looks.width: number— Width.height: number— Height.on: boolean— On now.brightness: number— How bright now, 0 to 1, with its flicker (0 while off).glow: number— How much light of its own it gives.sector: number— The sector it is in.rev: number— How many times it has changed: redraw its face when this moves.
MoveBody
interface MoveBody
How a body moves: its size and how it climbs.
radius: number— Radius, above 0.height: number— Height, above 0.step: number— Highest step it walks up.y: number— Feet height now.fly?: boolean— A flyer: no step limit, no drop limit.enemy?: boolean— An enemy: kept offblockEnemiesedges and away from drops deeper than its step.keys?: readonly string[]— For routes: keys it holds, so doors locked with them count as a way through.drop?: number— For routes: the deepest drop it will walk off; default its step (the player can drop any height: pass Infinity).
MoverState
interface MoverState
A mover's state.
kind: SectorMover['kind']— Its kind.phase: string—'closed' | 'opening' | 'open' | 'closing'for doors,'up' | 'lowering' | 'down' | 'raising'otherwise.timer: number— Seconds left waiting.on: boolean— Crushers and platforms: running.top: number— The top height it moves to.bottom: number— The bottom height it moves to.
PieceLook
interface PieceLook
Surfaces and settings any piece may take.
floor?: number— Floor height; default 0.ceiling?: number— Ceiling height; default 4 (stairs: their top step plus 4).light?: number— Light 0..1; default 1.wall?: string— Wall texture.floorTexture?: string— Floor texture.ceilingTexture?: string— Ceiling texture.sky?: boolean— Open to the sky.tag?: string— A tag for triggers.glow?: SectorGlow— A light effect.secret?: boolean— Counts as a secret when entered.damage?: number— Damage per second on the floor.jump?: SectorJump— A jump pad on the floor, in the piece's own coordinates.gravity?: number— Gravity as a share of the game's.push?: SectorPush— A conveyor or a wind, heading in the piece's own degrees.liquid?: boolean— The floor is a liquid.floorFx?: SurfaceFx— How the floor moves and shimmers.ceilingFx?: SurfaceFx— How the ceiling moves and shimmers.wallFx?: SurfaceFx— How the walls move and shimmer.
Placement
interface Placement
One piece put into a map.
id: string— Unique id: the piece's sector, tag, trigger and fixture ids become<id>.<local id>.piece: string | KitPiece— A piece name in the kit, or a piece itself.at?: Point— Where its origin goes; default [0, 0], or worked out byjoin.turn?: number— Degrees clockwise seen from above, any angle (0, 90, 180 and 270 keep whole-number corners); default 0, or worked out byjoin.mirror?: boolean— Mirror it east to west before turning. Default false.join?: { socket: string; to: string }— Snap itssocketonto socketto('<placement id>.<socket>') of a piece placed earlier: turned to face it, moved onto it and raised to its height.raise?: number— Added to every height in the piece; default 0, or the joined socket's height.light?: number— Every sector's light, overriding the piece's.textures?: Record<string, string>— Textures renamed:{ stone: 'brick' }.things?: MapThing[]— Things placed in this piece, in its own coordinates (they turn and move with it): how a level puts enemies and pickups in a room.triggers?: MapTrigger[]— Triggers belonging to this piece, naming its sectors by their local ids, in its own coordinates.lights?: MapLight[]— Lights placed in this piece, in its own coordinates: a lamp over the door, torches along the walls.fixtures?: MapFixture[]— Fixtures placed in this piece, in its own coordinates.signs?: MapSign[]— Signs placed in this piece, in its own coordinates (a wall sign names the piece's sector by its local id).screens?: MapScreen[]— Screens placed in this piece, in its own coordinates.flyers?: MapFlyer[]— Flyers placed in this piece, in its own coordinates.set?: Record<string, Partial<SectorDef>>— Settings merged into some of its sectors, by local id:{ room: { light: 0.4, glow: { kind: 'flicker' } } }.
Point
type Point = [number, number]
A point seen from above: [x, z] in world units, x east, z south.
ScreenCamera
interface ScreenCamera
A security camera's view, for a 'camera' screen.
at: Point— Where the camera is,[x, z].y?: number— Its height above the floor there; default 0.3 below the ceiling.facing: number— The way it looks, degrees clockwise from north.pitch?: number— How far it looks down, degrees; default 20.fov?: number— Its field of view, degrees, above 0 and below 180; default 70.
ScreenKind
type ScreenKind = 'terminal' | 'video' | 'static' | 'image' | 'hologram' | 'map' | 'camera'
What a screen shows.
ScreenView
interface ScreenView extends MountView
A screen as a presenter draws it.
kind: ScreenKind— What it shows now.lines: string[]— Its text lines.rows: number— Terminal rows shown at once.offset: number— Lines scrolled so far (a terminal's), fractional: the presenter may slide the text by the fraction.shown: string[]— Therowslines on show now, a terminal's (the text wraps round).src: string— Video address, or ''.picture: string— Picture name, or ''.region: [number, number, number, number]— Map: the region shown,[x0, z0, x1, z1].camera: { x: number; y: number; z: number; yaw: number; pitch: number; fov: number } | null— Camera: where it looks from, the engine's yaw and pitch (radians, positive down) and its field of view in degrees; null for other kinds.color: string— Text or tint colour.background: string— Background colour.
Sector
interface Sector
A sector as compiled: its authored settings and where it sits among the others.
id: string— Its id.index: number— Its index insectors.points: Point[]— Its outline.def: SectorDef— The authored settings.parent: number— The sector it was drawn inside, or −1.children: number[]— Sectors drawn inside it.centre: Point— Its centre.area: number— Area, square units.
SectorDef
interface SectorDef
One area of the map: a polygon with a floor and a ceiling.
id: string— Unique id; triggers and things refer to it.points: Point[]— The outline, 3 or more points, either way round, not crossing itself.floor?: number— Floor height; default the map's (0).ceiling?: number— Ceiling height; default the map's (4). For a door, its open height.light?: number— Light, 0 (black) to 1 (full); default the map's (1).floorTexture?: string— Floor texture.ceilingTexture?: string— Ceiling texture.wall?: string— Wall texture for its edges.side?: string— Texture for the faces it shows its neighbours where it is higher or lower: a dais's sides, a door's face; defaultwall.edges?: Record<string, SectorEdge>— Per-edge settings by edge index ("0","1"…).sky?: boolean— Open to the sky: no ceiling is drawn, and an upper face between two sky sectors is not. Default false.solid?: boolean— Solid through (a pillar, a block of rock): nothing enters it. Default false.mover?: SectorMover— A door, lift, crusher or platform.damage?: number— Damage per second to a body standing on its floor (slime, lava); default 0.secret?: boolean— Counts as a secret when the player first enters it. Default false.tag?: string— A group name for triggers and actions (several sectors may share one).glow?: SectorGlow— A light effect.jump?: SectorJump— A jump pad on its floor.gravity?: number— Gravity in it as a share of the game's, 0 (none) to 10; default 1. Below 1 is a low-gravity room.push?: SectorPush— A conveyor on its floor, or a wind through it.liquid?: boolean— Its floor is a liquid (water, slime, lava): for the presenter, which may draw it see-through or wavy. Default false.floorFx?: SurfaceFx— How its floor moves and shimmers.ceilingFx?: SurfaceFx— How its ceiling moves and shimmers.wallFx?: SurfaceFx— How its walls (and the step faces it shows) move and shimmer, unless an edge'sfxsays otherwise.
SectorEdge
interface SectorEdge
What one edge of a sector looks like and does. Edge i runs from points[i] to points[i + 1] (the last back to the first).
wall?: string— The texture of the wall on this edge, and of the step and lip faces this sector shows its neighbour there.middle?: string— A texture hung across the opening (bars, a grate, a window): drawn on both faces, see-through where clear.block?: boolean— No body may cross, though sight and shots pass (a railing, a window, bars). Default false.blockEnemies?: boolean— Enemies may not cross (the edge of a ledge they should keep to). Default false.quiet?: boolean— Sound does not carry across. Default false.middleHeight?: number— How highmiddlereaches from the bottom of the opening (a railing 1 tall); default the whole opening.fx?: SurfaceFx— How the faces on this edge (its wall, steps, lintels andmiddle) move and shimmer.
SectorEnemyWorld
interface SectorEnemyWorld
The map as enemies see it, for fps-enemies: sight through openings, moves with steps and drops, routes through
doors (named by sector id), hearing, rays, doors to open and floor heights.
sees(ax: number, az: number, bx: number, bz: number, ay?: number, by?: number): boolean— An unbroken line between two points, through openings at the given heights.move(x: number, z: number, dx: number, dz: number, body: { radius: number; height: number; step: number; fly: boolean }, y?: number): { x: number; z: number; y: number }— Move a body, sliding, with its step and drop limits.route(fx: number, fz: number, tx: number, tz: number, body: { radius: number; height: number; step: number; fly: boolean }): { x: number; z: number; door?: string }[] | null— The way between two points.hearing(x: number, z: number, radius: number): (x: number, z: number) => boolean— Who hears a noise.wallDistance(x: number, z: number, yaw: number, max: number, y?: number): number | null— How far a flat ray goes before a wall.doorShut(door: string): boolean— Whether a door blocks the way.openDoor(door: string): boolean— Open a door; true when it starts opening.floorAt(x: number, z: number): number— The floor at a point.
SectorGlow
interface SectorGlow
A light effect on a sector.
kind: 'flicker' | 'pulse' | 'strobe'—'flicker'(random),'pulse'(smooth) or'strobe'(on and off).low?: number— The lowest light as a fraction of the sector's, 0 to 1; default 0.3.speed?: number— Cycles per second; default 1 (flicker: changes per second, default 8).
SectorJump
interface SectorJump
A jump pad: a body standing on the sector's floor is thrown into the air, straight up and along a heading, or on an
arc worked out to land on a point. The game moves the body; the map works out the throw (SectorMap.launch).
up?: number— Upward speed in units per second, above 0. Needed unlesstois given, which works out its own.speed?: number— Speed alongfacing, units per second, 0 or more; default 0 (straight up). Ignored withto.facing?: number— The wayspeedthrows, degrees clockwise from north; default 0. Ignored withto.to?: Point— A point to land on,[x, z]: the throw is worked out from the gravity where the body starts, to come down there.y?: number— Withto: the height to land at; default the floor there.peak?: number— Withto: how far the arc rises above the higher of its two ends, in units, above 0; default 2.sound?: string— A sound by name, played on the pad as it throws.
SectorMapData
interface SectorMapData
A whole map, as JSON.
format?: 'sector-map'—'sector-map', so a file says what it is. Optional.version?: 1— The format version: 1. Optional.name?: string— A name for the map.sky?: string— The sky texture, for sky sectors.defaults?: MapDefaults— Defaults for sectors.sectors: SectorDef[]— The sectors.things?: MapThing[]— Things on the map.triggers?: MapTrigger[]— Triggers.lights?: MapLight[]— Lights.fixtures?: MapFixture[]— Things that open or break: hinged doors, cabinets, windows.signs?: MapSign[]— Signs on walls and hanging.screens?: MapScreen[]— Screens on walls and standing in the air.flyers?: MapFlyer[]— Things moving along paths through the air.meta?: Record<string, unknown>— Anything else the game wants to keep with the map (music, par time, a title).
SectorMapState
interface SectorMapState
Everything that changes; a save holds exactly this.
time: number— Seconds the map has run.floors: number[]— Floor heights by sector index.ceilings: number[]— Ceiling heights by sector index.lights: number[]— Base light by sector index.movers: (MoverState | null)[]— Movers by sector index (null for none).moves: ({ floor?: { to: number; speed: number }; ceiling?: { to: number; speed: number } } | null)[]— Moves started by actions, by sector index.spent: boolean[]— Triggers: whether each is spent and whether each is enabled.enabled: boolean[]— Triggers switched on.queue: { at: number; trigger: number }[]— Actions waiting for their delay.secrets: string[]— Secret sectors found.started: boolean— Whether the start triggers have run.lamps: boolean[]— Each map light: lit or not.rng: number[]— The random stream (flicker).fixtures: FixtureState[]— Each fixture, in the map's order.signs: { text: string; on: boolean; rev: number }[]— Each sign: its text now, lit or not, and how many times it has changed.screens: { on: boolean; kind: ScreenKind; lines: string[]; src: string; picture: string; since: number; rev: number }[]— Each screen: on or not, what it shows now, when that began (for scrolling), and how many times it has changed.flyers: { at: number; dir: 1 | -1; on: boolean }[]— Each flyer: how far along its path, which way it is going (1 or −1) and whether it is moving.
SectorMover
interface SectorMover
Something that moves a sector's floor or ceiling: a door, a lift, a crusher, a platform.
kind: 'door' | 'lift' | 'crusher' | 'platform'— -'door': the ceiling is drawn at its open height and starts shut on the floor; using an edge (or a trigger) raises it, it waitswaitseconds and comes down, going back up if anything is underneath. -'lift': the floor is drawn at its top; it goes down toto(default the lowest neighbouring floor), waits and comes back up. -'crusher': once started, the ceiling comes down togapabove the floor and back, over and over, hurting what is caught. -'platform': once started, the floor moves between its height andto, over and over.to?: number— Lift and platform: the other floor height.speed?: number— Units per second; default door 4, lift 3, crusher 1.5, platform 2.wait?: number— Seconds it waits open or down; default 3. 0 for a door keeps it open.key?: string— A key it needs, by name ('red'); default none.use?: boolean— Pressing use on one of its edges works it; default true for doors and lifts.walk?: boolean— Stepping onto it works it (a lift at the top); default false.on?: boolean— Crusher and platform: running from the start; default false. Door: starts open.gap?: number— Crusher: the gap it closes to; default 0.2.damage?: number— Crusher: damage per second to what is caught; default 50.sounds?: { start?: string; stop?: string }— Sounds by name, played where it is: as it starts moving and as it stops.
SectorPush
interface SectorPush
A push on bodies in a sector: a conveyor under their feet, or a wind that blows through it.
speed: number— Units per second, 0 or more.facing: number— The way it pushes, degrees clockwise from north (0 north, 90 east).air?: boolean— A wind: it pushes bodies anywhere in the sector, in the air too. Default false: a conveyor, which pushes only bodies standing on its floor.
SignView
interface SignView extends MountView
A sign as a presenter draws it.
text: string— The words now.color: string— Text colour.background: string— Background colour ('transparent'for none).flicker: number— Flicker, 0 to 1.frame: 'plate' | 'neon' | 'screen' | 'poster'—'plate','neon','screen'or'poster'.picture: string | null— A picture name, or null.hanging: boolean— Hanging: readable from both sides.
Socket
interface Socket
Where a piece joins another: a point on its outline, the way out, the opening's width and the floor height there.
at: Point— The middle of the opening, in the piece's own coordinates.facing: Facing | number— Which way it faces out of the piece: a compass letter, or degrees clockwise from north (45 is north-east).width?: number— The opening's width; joined sockets of different widths are an error. Default unchecked.y?: number— The floor height there, in the piece's own heights; default 0.
SurfaceEffect
interface SurfaceEffect
One of the renderer's surface effects.
kind: 'ripple' | 'shine' | 'holo' | 'glow' | 'frost' | 'fire'— -'ripple': a liquid's moving ripples and refraction. -'shine': a glint sweeping across (polished metal, wet tiles). -'holo': scan lines and shimmer (a hologram, a force field). -'glow': lit from inside, breathing (a computer wall, a light panel). -'frost': rime that creeps and sparkles. -'fire': flames licking upward.amount?: number— How strong, 0 to 1; default 0.5.speed?: number— How fast, 0 to 10, 1 being the effect's own pace; default 1.color?: string— A tint, CSS; default none (the texture's own colours).
SurfaceFx
interface SurfaceFx
How a surface moves and shimmers: its texture scrolling, animated frames and a renderer effect.
scroll?: [number, number]— Texture repeats per second along u and v; default[0, 0]. On a wall u runs along it and v down it, so[0, -0.5]runs a waterfall's texture down; on a floor or ceiling u is east and v south.animate?: number— Frames per second of an animated texture, 0 to 60 (the presenter's texture has the frames); default 0.effect?: SurfaceEffect— A surface effect.
TriggerAction
type TriggerAction = | { open: string } | { close: string } | { lock: string } | { unlock: string } | { break: string } | { sign: string; text?: string; on?: boolean } | { screen: string; on?: boolean; kind?: ScreenKind; lines?: string[]; src?: string; picture?: string } | { start: string } | { stop: string } | { floor: string; to: number; speed?: number } | { ceiling: string; to: number; speed?: number } | { light: string; to: number } | { lamp: string; on: boolean } | { sound: string; at?: Point; y?: number } | { message: string } | { spawn: string; at: Point; facing?: number; tag?: string } | { teleport: Point; facing?: number } | { exit: 'normal' | 'secret' } | { enable: string } | { disable: string } | { event: string; data?: unknown }
Something that happens when a trigger fires. Targets name an id or a tag: open and close reach doors (sectors)
and fixtures, start and stop movers and flyers, lock, unlock and break fixtures, sign signs and
screen screens.
TriggerOn
type TriggerOn = 'enter' | 'leave' | 'use' | 'cross' | 'shoot' | 'start' | 'kill' | 'break'
What sets a trigger off.
WindowFixture
interface WindowFixture extends FixtureBase
A pane of glass across a sector edge that opens onto another sector. While whole it stops bodies and shots, and sight
passes unless it is frosted; shots and blasts take its hit points and at none it breaks, leaving the opening clear.
kind: 'window'—'window'.wall: { sector: string; edge: number }— The edge it fills: one that opens onto another sector.hp?: number— Hit points, above 0; default 10.frosted?: boolean— Frosted: sight does not pass while it is whole. Default false.broken?: boolean— Starts broken. Default false.
WindowView
interface WindowView extends FixtureViewBase
A window pane, as a presenter draws it: a quad from a to b between heights low and high.
kind: 'window'—'window'.a: Point— One end.b: Point— The other end.low: number— The pane's bottom, in the world.high: number— The pane's top.hp: number— Hit points left.broken: boolean— Broken: draw the empty frame (and shards when it broke).frosted: boolean— Frosted glass.yaw: number— The engine's yaw of the side facing into the sector it was set on.


