The pack
horrorDirector
function horrorDirector(): Pack<{ horrorDirector: typeof kit; }>
Compose the horror director, its world adapter for sector maps, the scare library, two presets and their maps under game.horrorDirector.
Helpers
createHorrorDirector
function createHorrorDirector(config: HorrorConfig, world: HorrorWorld): HorrorDirector
Start a night: the config (a preset such as nightShift(), or your own) and the world it plays on
(sectorHorrorWorld(map), or any HorrorWorld). Throws, naming the field, on a config it cannot use.
cryptMap
function cryptMap(): SectorMapData
The Crypt Vigil crypt, about 30 by 54 metres: a long nave with four alcoves a side, a passage down to the crypt
with its altar and coffin, a chapel with a bell behind an open gate, an ossuary behind another gate with a grate
looking into an alcove, and the porch where the vigil starts. Candles (lights of kind 'torch') are tagged one by
one. Spots: start, tomb, coffin (a fixture), bell, niche, grate, oil.
cryptVigil
function cryptVigil(overrides: HorrorConfig = {}): HorrorConfig
Crypt Vigil: a night kept in a crypt by lantern light. No stalker and no running: an oil lantern that lights all
round but not far, and gutters as the oil runs low; candles that are snuffed and flare again; shades at the edge of
the light, a face behind a grate, a procession crossing the nave, gates slamming, bare feet behind, prayers whispered,
bones falling from niches, a bell tolling, a coffin lid sliding. Fear is the danger: left in the dark with no light
it climbs, and ten seconds at its height loses the night. Survive seven minutes to dawn. Plays on cryptMap().
nightShift
function nightShift(overrides: HorrorConfig = {}): HorrorConfig
Night Shift: an abandoned hospital at night. A slow walk with a short sprint, a torch on batteries that die (spares
to find), hiding in lockers, under desks and beds, leaning round corners, no weapons, the stalker (it hunts by sound
and sight, flees the torch's beam and will not follow into bright lamplight), the whole scare library, and three
fuses to find before the stairs light up and the night can be escaped. Plays on nightShiftMap(). overrides lay
over it field by field (nightShift({ seed: 7, stalker: { light: 'freeze' } })); lists and null replace.
nightShiftMap
function nightShiftMap(): SectorMapData
The Night Shift hospital floor, about 46 by 24 metres: a long corridor with a wing at each end, two wards with beds
to hide under, an office with desks, a radio and a window onto the corridor, the stairs out (dark until the fuses
are found), a washroom with three stalls and a mirror, the nurses' station with its phone, a storeroom with lockers
and shelves, and the morgue, where the stalker sleeps. Doors stay where they are put (wait: 0); two start open.
Spots: start, bed, desk, locker (hiding), stall, cabinet (fixtures), mirror, window, phone,
radio, shelf, battery, fuse, exit, lair and patrol. Lamps are tagged by room, the corridor's one by one.
prepareHorrorMap
function prepareHorrorMap(data: SectorMapData): SectorMapData
A copy of a sector map's data with the triggers the director works it by: for every lamp tag, one switching it on
and one off, and for every door, one opening and one closing it. They are switched off (disabled), so nothing in
the map sets them off; sectorHorrorWorld fires them by id. Preparing a map twice changes nothing more.
scareLibrary
function scareLibrary(): ScareDef[]
The full scare library, one of each kind and two apparitions (at corridor ends, and behind window spots): lights failing, an apparition, a figure at a window, a runner, a door slam, footsteps, a whisper, a falling object, breathing, a phone, a distortion pulse, a stall opening by itself and a mirror figure. Each takes its kind's defaults; tune or replace any of them.
sectorHorrorWorld
function sectorHorrorWorld(map: SectorMap, options: SectorWorldOptions = {}): SectorHorrorWorld
The HorrorWorld of a sector map: its enemyWorld() for sight, moves, routes and hearing, its lights as lamps,
its door sectors as doors, and its things as spots (each thing's type its kind). The map must be built from
prepareHorrorMap data for lamps to switch and doors to slam; otherwise those throw, naming the missing trigger.
Light at a point is the sector's light (times ambient) plus each lit lamp that sees the point, falling off to its
radius.
Classes
HorrorDirector
class HorrorDirector
The director of a horror night, run beside the game's own rules. Each step it reads where the player is and what
they pressed, and the world through its HorrorWorld; it runs the torch, the player's stamina, fear, hiding and
leaning, the stalker, the scares going on and the pacing (tension, phases and a scare budget that rations the next
scare), and answers with events for the presenter and the sound. Seeded and deterministic: the same seed and the
same player path give the same night. Time moves only through step. Get one from createHorrorDirector.
constructor(config: HorrorConfig, world: HorrorWorld)get config(): HorrorConfig— The config it was made with (a copy).get time(): number— Seconds the night has run.get status(): HorrorStatus—'playing', or how the night ended:'escaped','dawn'or'dead'.get phase(): HorrorPhase— The pacing's phase.get tension(): number— Tension, 0 to 1.get target(): number— The tension it heads for now, 0 to 1.get budget(): number— Scare budget points left.get landed(): number— Scares landed so far (forced ones included).get collected(): number— Things on the night's list collected.get distortion(): number— The screen distortion now, 0 to 1: drive a warp, a chromatic split or static by it.get light(): number— The light (lamps and room, without the torch) where the player stands, 0 to 1.spots(): HorrorSpot[]— The spots the director read from the world (copies).get torch(): TorchView— The torch, for the presenter's light and the HUD.get player(): PlayerView— The player, for the camera, the HUD and the sound.get stalker(): StalkerView | null— The stalker, or null when the night has none.figures(): FigureView[]— The figures to draw now: apparitions, runners, mirror figures.fixtures(): FixtureView[]— The fixtures and how far open each is.pickups(): PickupView[]— The pickups still to be taken: refills for the light and things on the night's list.active(): ScareView[]— The scares going on.beam(x: number, z: number, y: number): number— How brightly the torch lights a point at heighty, 0 to 1 (0 outside its beam or behind a wall).step(dt: number, input: HorrorInput): HorrorEvent[]— Run the night on bydtseconds (0 to 1) with the player's doings, and return what happened. Throws on adtout of range or an input without a finitex,zandyaw. Once the night has ended it returns nothing.scare(id: string): HorrorEvent[]— Land a scare now by its def's id, as a script or a map trigger would: no budget is spent and the phase and conditions are not asked, but it still needs somewhere to go. Returns the events it made (itsscareevent carries the new scare's id), or none when it found nowhere, the night is over or no step has placed the player. Throws on an unknown id.noise(x: number, z: number, radius: number): void— Tell the stalker of a noise the game made between steps, heardradiusunits away (0 to 200).get state(): HorrorState— A copy of the state: everything a save needs, with the world's own (a sector map'sstate).restore(state: HorrorState): void— Put back a saved state of this night. Throws on one that does not fit.
Types
ActiveScare
interface ActiveScare
A scare in play, as saved.
id: string— Its id,<def>-<n>.scare: string— The def's id.kind: ScareKind— Its kind.x: number— Where it is, world units.y: number— Height.z: number— Where it is, world units.yaw: number— The engine's yaw it faces.vx: number— Velocity (a runner), units a second.vz: number— Velocity (a runner), units a second.age: number— Seconds since it began.life: number— Seconds it lasts at most; 0 until it is done.target: string— What it works on: a lamp tag, a door or fixture id, a spot id; '' for none.count: number— Steps or flickers left.next: number— Seconds to the next step or flicker.stage: string— Where it is in its course:'live','out','flicker','falling'.look: number— Seconds the player has looked straight at it.yaw0: number— The player's yaw when it began.from: number— A fall's top height; footsteps' distance behind.to: number— A fall's floor height.on: boolean— A flickering lamp is lit now.
BudgetDef
interface BudgetDef
The scare budget: points that refill over time and that each scare spends, so scares are rationed.
start?: number— Points at the start, 0 to 100; default 1.max?: number— Most points held, 0 to 100; default 4.rate?: number— Points regained a second, 0 to 10; default 0.04 (one every 25 seconds).
CatchDef
interface CatchDef
What a catch does.
damage?: number— Health a catch takes; default 1 (with the player's default health, the night is lost).retreat?: [number, number]— Seconds it keeps away after a catch the player survives,[min, max]from 0 to 600; default [10, 16].
FigureView
interface FigureView
A figure to draw: an apparition, a runner, a mirror figure.
id: string— The scare's id; thescare-endevent with it removes the figure.scare: string— The scare def's id.kind: ScareKind— Its kind.show: string— What to draw.x: number— Position, world units.y: number— Feet height.z: number— Position, world units.yaw: number— The engine's yaw it faces (the player).height: number— Height in units.mirror: string | null— A mirror figure: the mirror spot to draw it in (only its reflection shows), else null.
FixtureSounds
interface FixtureSounds
Sounds fixtures make, by name; '' for none.
open?: string— Opening; default'creak'.shut?: string— Shutting; default'latch'.slam?: string— Slammed; default'slam'.
FixtureState
interface FixtureState
A fixture, as saved.
id: string— Its spot's id.open: number— How far open, 0 to 1.target: number— Where it swings to, 0 or 1.
FixtureView
interface FixtureView
A fixture: a stall door, a cabinet, a locker, anything that opens.
id: string— Its spot's id.kind: string— Its spot's kind.x: number— Position, world units.z: number— Position, world units.yaw: number— The engine's yaw it faces.open: number— How far open, 0 (shut) to 1 (open): swing its door by it.opening: boolean— Opening (true) or shutting (false).occupied: boolean— The player hides in it now.
HideDef
interface HideDef
Hiding spots: any spot kinds the player can get into, where the stalker cannot see them.
kinds?: string[]— Spot kinds that hide the player ('locker','desk','bed'); default those three. Empty for none.time?: number— Seconds getting in or out takes, 0 to 10; default 0.6.exit?: number— Units in front of the spot the player comes out, 0 to 5; default 0.9.peek?: number— Units within which the stalker sees a player peeking out, 0 to 50; default 3. 0: peeking is safe.noise?: number— How far getting in or out is heard, units, 0 to 100; default 2.
HorrorBody
interface HorrorBody
The body the world moves: its size and how it climbs.
radius: number— Radius in world units.height: number— Height in world units.step: number— Highest step up, and deepest step down, it takes.fly: boolean— Whether it flies over steps and drops.
HorrorConfig
interface HorrorConfig
Everything a night is. Every field has a default; nightShift() and cryptVigil() fill them in two ways.
seed?: number— Seed, 0 to 4294967295; default 1. A seed and the player's path give the same night.view?: ViewDef— The camera, for in and out of view.pacing?: PacingDef— Phases, tension and the scare budget.light?: LightDef— The player's light.player?: PlayerDef— The player.scares?: ScareDef[]— The scare library the director draws on; defaultscareLibrary(). Empty for none.stalker?: StalkerDef | null— The stalker; null (the default) for none.night?: NightDef— What the player must do.fixtures?: string[]— Spot kinds that open and shut, by the player's hand or a scare's; default['stall', 'cabinet', 'locker'].fixtureSpeed?: number— Units a second a fixture swings, as a share of fully open, 0.1 to 50; default 1.5.fixtureSounds?: FixtureSounds— Fixture sounds.dim?: number— Light (lamps and sectors) at or above which a figure is not placed, and goes, 0 to 1; default 0.35.dark?: number— Ambient light below which the player counts as in the dark, 0 to 1; default 0.25.
HorrorDoor
interface HorrorDoor
A door the director may slam, and whether it is shut.
id: string— The world's id for it (a sector map's door sector id).tag: string | null— Its group name, or null.x: number— Its middle, in world units.z: number— Its middle, in world units.shut: boolean— Shut, or not open enough to see through.
HorrorEvent
type HorrorEvent = | { type: 'phase'; phase: HorrorPhase; from: HorrorPhase } | { type: 'scare'; id: string; scare: string; kind: ScareKind; show: string; sound: string; x: number; y: number; z: number; duration: number; target: string } | { type: 'scare-end'; id: string; scare: string; kind: ScareKind; why: ScareEnd } | { type: 'sound'; sound: string; x: number; y: number; z: number; volume: number; source: HorrorSoundSource; id: string; loop: number } | { type: 'lamp'; tag: string; on: boolean; spark: boolean } | { type: 'sparks'; x: number; y: number; z: number; count: number } | { type: 'door'; door: string; open: boolean; by: 'scare' | 'stalker' | 'night' } | { type: 'fixture'; id: string; kind: string; open: boolean; by: 'player' | 'scare' | 'stalker' } | { type: 'fall'; id: string; show: string; x: number; z: number; from: number; to: number; time: number } | { type: 'distort'; strength: number; duration: number } | { type: 'torch'; change: TorchChange; charge: number } | { type: 'pickup'; id: string; kind: string; x: number; z: number } | { type: 'collect'; id: string; kind: string; have: number; need: number } | { type: 'objective'; done: boolean } | { type: 'hide'; spot: string; state: 'entering' | 'in' | 'leaving' | 'out'; x: number; z: number; yaw: number } | { type: 'stalker'; mode: StalkerMode; why: string; x: number; z: number } | { type: 'caught'; hidden: boolean; health: number } | { type: 'beat'; bpm: number; strength: number } | { type: 'end'; status: Exclude<HorrorStatus, 'playing'> }
What a step did, for the presenter and the sound. Every sound to play is a sound event; the others say what to show
and why. See the pack's page for what to draw and play for each.
HorrorInput
interface HorrorInput
The player's doings this step. The game moves the player (at most director.player.speed); the director reads where they are.
x: number— Where the player's feet are now, world units. Ignored while hidden, when the player is held at the spot.z: number— Where the player's feet are now, world units.y?: number— Feet height; default the floor there.yaw: number— The engine's yaw the player faces.pitch?: number— Pitch in radians, positive looking down (assector-map's rays); default 0.sprint?: boolean— The sprint key is held.crouch?: boolean— The crouch key is held.switchTorch?: boolean— The torch's switch was pressed this step.use?: boolean— Use was pressed this step: get into or out of a hiding spot, open or shut a fixture, take a pickup, answer a phone.lean?: number— Lean, −1 (left) to 1 (right), held; default 0.peek?: boolean— Peeking out of a hiding spot, held.shake?: boolean— The torch was shaken this step (withlight.shake).reload?: boolean— Put a spare in this step.noises?: HorrorNoise[]— Other noises the game made this step: a shot, a thing knocked over, a door forced.
HorrorLamp
interface HorrorLamp
A lamp, as the director sees it.
index: number— Its index among the map's lights.tag: string | null— Its group name, which lamps are switched by; null for none (such a lamp cannot be switched).x: number— Position in world units.y: number— Height in world units.z: number— Position in world units.radius: number— Units it reaches.intensity: number— Brightness (inverse-square units; 6 is a room lamp).on: boolean— Lit now.
HorrorNoise
interface HorrorNoise
A noise the game made, for the stalker's ears.
x: number— Where, in world units.z: number— Where, in world units.radius: number— Units it carries through open space and open doors, 0 to 200.
HorrorPhase
type HorrorPhase = 'calm' | 'build' | 'peak' | 'relief'
The four moods of a night, in the order they turn: calm, build, peak, relief, then calm again.
HorrorSoundSource
type HorrorSoundSource = 'scare' | 'stalker' | 'player' | 'torch' | 'fixture' | 'night'
Who a sound comes from.
HorrorSpot
interface HorrorSpot
A place the map marks: a hiding spot, a window, a mirror, a phone, a shelf, a stall, a patrol point, a pickup. What
a kind means is the config's (player.hide.kinds, a scare's spots, fixtures, light.refill, night).
id: string— Unique id: its kind and its number among that kind,'locker-2'.kind: string— What it is, by name. A sector map's spots are its things, and a thing's type is its kind.x: number— Position in world units.z: number— Position in world units.y: number— Height above the floor, units (a shelf's top, a mirror's middle); 0 when the map gives none.yaw: number— The engine's yaw it faces: out of a hiding spot, out of a mirror's glass, into a window.tag: string | null— Its group name, or null.
HorrorState
interface HorrorState
Everything that changes in a night; a save holds exactly this (with the map's own state).
time: number— Seconds the night has run.status: HorrorStatus— How it stands.phase: HorrorPhase— The phase.phaseTime: number— Seconds in the phase.phaseLength: number— Seconds the phase lasts this time.tension: number— Tension, 0 to 1.target: number— The tension it heads for.budget: number— Scare budget points.gap: number— Seconds before the director may land another scare.since: number— Seconds since the last scare.noise: number— Noise heard near the player lately, 0 to 1.landed: number— Scares landed.cooldowns: Record<string, number>— Seconds before each scare may land again, by def id.uses: Record<string, number>— Times each scare has landed, by def id.scares: ActiveScare[]— Scares going on.serial: number— Scares started, for ids.distort: { strength: number; time: number; length: number }— The screen distortion now, 0 to 1, and seconds it lasts.torch: TorchState— The torch.player: PlayerState— The player.stalker: StalkerState | null— The stalker, or null.fixtures: FixtureState[]— The fixtures.taken: string[]— Pickups taken, by spot id.collected: number— Things collected.doors: Record<string, boolean>— Doors' shut states last step, by id.worked: string[]— Doors a scare or the stalker opened or shut, so the player is not blamed for them.noises: { x: number; z: number; radius: number }[]— Noises the stalker has yet to hear.rng: number[]— The random stream.
HorrorStatus
type HorrorStatus = 'playing' | 'escaped' | 'dawn' | 'dead'
How a night ended, or that it goes on.
HorrorWaypoint
interface HorrorWaypoint
A point on a route, and the door to open before going on, if any.
x: number— Where, in world units.z: number— Where, in world units.door?: string— A door to open first, by the world's id for it.
HorrorWorld
interface HorrorWorld
What the director needs of the map. sectorHorrorWorld(map) gives one from a sector map; any map that answers these
can host a night. All positions are world units with the engine's yaw (forward is (−sin yaw, cos yaw)).
sees(ax: number, az: number, bx: number, bz: number, ay?: number, by?: number): boolean— An unbroken line from a to b; with heights, through openings in 2.5D maps.move(x: number, z: number, dx: number, dz: number, body: HorrorBody, y?: number): { x: number; z: number; y: number }— Move a body bydx, dz, sliding along walls; where it ends up and the floor under it.yis its feet now.route(fx: number, fz: number, tx: number, tz: number, body: HorrorBody): HorrorWaypoint[] | null— The way from one point to another for this body, next point first, through unlocked doors; null when there is none.hearing(x: number, z: number, radius: number): (x: number, z: number) => boolean— Who hears a noise atx, zthat carriesradiusunits through open space and open doors.wallDistance(x: number, z: number, yaw: number, max: number, y?: number): number | null— How far a flat ray goes before a wall or shut door, or null withinmax.yis its height, for 2.5D maps.doorShut(door: string): boolean— Whether a door on a route is still shut.openDoor(door: string): boolean— Start a door opening; true when it was shut and now opens.floorAt(x: number, z: number): number— The floor height at a point.open(x: number, z: number): boolean— Whether a body could stand at a point: inside the map, not in rock, a pillar or a shut door.ceilingAt(x: number, z: number): number— The ceiling height over a point.light(x: number, z: number, y?: number): number— How lit a point is by the lamps and the room, 0 (black) to 1, without the player's torch.ydefaults to a metre above the floor.lamps(): HorrorLamp[]— The lamps, as they are now.setLamps(tag: string, on: boolean): boolean— Switch every lamp with a tag on or off; false when no lamp has it.doors(): HorrorDoor[]— The doors.closeDoor(door: string): boolean— Slam a door shut; false when it is shut already or unknown.spots(): HorrorSpot[]— The marked spots. They do not move; the director reads them once.
LeanDef
interface LeanDef
Leaning round a corner.
distance?: number— Units the eye moves sideways at a full lean, 0 to 2; default 0.4.roll?: number— Roll of the camera at a full lean, radians, 0 to 1; default 0.15.speed?: number— Lean gained a second, 0.1 to 50; default 5.cover?: number— Multiplies the stalker's sight of a player leaning still, 0 to 1; default 0.7.
LightDef
interface LightDef
The player's light: a torch with a beam, or a lantern all round.
kind?: string— What it is, for the presenter ('torch','lantern','candle','phone'); default'torch'.cone?: number— Half-angle of the beam in radians, 0.05 to π; π lights all round (a lantern). Default 0.42.range?: number— Units it reaches at full charge, 0.5 to 200; default 12.lowRange?: number— Share of the range it still reaches at the bottom of its low mark, 0 to 1; default 0.5.color?: string— Colour, CSS; default'#fff2d6'.capacity?: number— Seconds of light a full charge gives, 0 to 100000; 0 never runs out. Default 240.start?: number— Charge at the start, 0 to 1; default 1.startOn?: boolean— On at the start; default true.toggle?: boolean— It can be switched off; default true.low?: number— Charge below which it flickers, 0 to 1; default 0.25.dying?: number— Charge below which it sputters and cuts out, 0 to 1; default 0.08.flicker?: number— Flickers a second at an empty-ish charge (fewer as the charge rises to the low mark), 0 to 20; default 1.5.flame?: number— A flame's steady waver, 0 to 0.5 of its strength; default 0 (a lantern or candle wants about 0.1).refill?: string— The spot kind that refills it (batteries, oil); default'battery'.refillAmount?: number— Charge one refill gives, 0 to 1; default 0.6.spares?: number— Most refills carried for later, 0 to 20; 0 uses each at once. Default 2.autoSwap?: boolean— A spare goes in by itself when the charge runs out; default true.shake?: ShakeDef | null— Shaking it to recharge; default none.sounds?: LightSounds— Its sounds.
LightSounds
interface LightSounds
Sounds the torch asks for, by name; '' for none.
on?: string— Switched on; default'torch-on'.off?: string— Switched off; default'torch-off'.flicker?: string— A flicker as it runs low; default'torch-flicker'.sputter?: string— A sputter as it dies; default'torch-sputter'.dead?: string— The charge ran out; default'torch-dead'.swap?: string— A spare put in; default'torch-swap'.shake?: string— Shaken to recharge; default'torch-shake'.
MercyDef
interface MercyDef
How the stalker fails to find the player on purpose, so it stays frightening rather than unfair.
miss?: number— Chance that going after a noise it goes astray, 0 to 1; default 0.3.offset?: [number, number]— Units it goes astray by,[min, max]from 0 to 50; default [2.5, 5].hidden?: number— Chance it leaves a hiding spot it saw the player take without pulling them out, 0 to 1; default 0.4.giveUp?: [number, number]— Seconds of hunting after which it loses the player on purpose,[min, max]from 1 to 600; default [12, 22].grace?: [number, number]— Seconds after losing the player before its sight can start a hunt again,[min, max]from 0 to 600; default [10, 18].edge?: number— Noticings a second of a player at the far edge of its sight (past 70% of it), 0 to 20; default 0.6.
NightDef
interface NightDef
What the player must do: survive until dawn, collect things, reach an exit, or a mix.
length?: number | null— Seconds until dawn, which ends the night survived, 1 to 360000; default none.collect?: { kind: string; count: number } | null— The spot kind to collect and how many; default none.exit?: string | null— A spot kind to reach once everything is collected (or at once, with nothing to collect); default none.reach?: number— Units within which pickups are taken and the exit is reached, 0.2 to 10; default 1.pickup?: 'touch' | 'use'— Pickups are taken by walking onto them ('touch', default) or by pressing use ('use').lamps?: string[]— Lamp tags switched on when everything is collected (the way out lights up); default none.doors?: string[]— Doors (ids) opened when everything is collected; default none.sounds?: NightSounds— Its sounds.
NightSounds
interface NightSounds
Sounds the night asks for, by name; '' for none.
collect?: string— Something on the list collected; default'collect'.done?: string— Everything collected; default'objective'.dawn?: string— Dawn; default'dawn'.
NoiseDef
interface NoiseDef
How far the player's doings are heard, in units: the stalker hears them through open space and open doors.
walk?: number— A walking step; default 2.5.run?: number— A running step; default 9.sneak?: number— A crouching step; default 0.8.fixture?: number— Opening or shutting a fixture; default 3.door?: number— A map door opening near the player; default 6.
PacingDef
interface PacingDef
The pacing: phases, tension and the scare budget, in the manner of a game director.
phases?: Partial<Record<HorrorPhase, PhaseDef>>— The phases, by name; each field left out keeps its default.weights?: PacingWeights— What raises tension.rise?: number— Tension gained a second toward a higher target, 0 to 10; default 0.12.fall?: number— Tension lost a second toward a lower target, 0 to 10; default 0.05.peakAt?: number— Tension at which build turns to peak, 0 to 1; default 0.7. Build also turns to peak when its time is up.wait?: number— Seconds since the last scare at which thewaitfactor is full, 1 to 3600; default 60.near?: number— Units within which the stalker counts as near, 0.5 to 200; default 12.budget?: BudgetDef— The scare budget.gap?: [number, number]— Seconds between any two scares the director lands,[min, max]from 0 to 600; default [8, 20].start?: HorrorPhase— The phase a night starts in; default'calm'.
PacingWeights
interface PacingWeights
How much each thing the player is going through raises the tension it heads for. Each factor is 0 to 1; the target is their weighted sum, at most 1. Every weight is 0 to 2.
dark?: number— Darkness where the player stands (1 minus the light there, the torch left out); default 0.2.torch?: number— The torch: 1 when off or dead, rising as the charge runs below its low mark; default 0.15.seen?: number— The stalker sees the player; default 0.55.unseen?: number— The stalker is near (withinnear) and does not see the player: hidden, or simply unseen; default 0.35.noise?: number— Noises heard near the player recently (each one fades over a few seconds); default 0.25.wait?: number— Time since the last scare, full atwaitseconds; default 0.3.speed?: number— The player's speed as a share of their sprint; default 0.1.
PatrolDef
interface PatrolDef
Patrolling between rooms.
spots?: string[]— Spot kinds it walks between; default['patrol']. With none on the map it walks between the hiding spots.wait?: [number, number]— Seconds it stands at each,[min, max]from 0 to 600; default [2, 6].
PhaseDef
interface PhaseDef
One phase of the pacing.
time?: [number, number]— Seconds it lasts,[min, max]from 1 to 3600, rolled each time it begins. Defaults: calm [25, 45], build [30, 60], peak [12, 24], relief [15, 30].rate?: number— Scares a second it tries to land at full tension, 0 to 5 (0: none in this phase). Defaults: calm 0.02, build 0.12, peak 0.25, relief 0.fall?: number— Multiplies how fast tension falls in it, 0 to 20. Defaults: relief 3, the others 1.
PickupView
interface PickupView
A pickup still to be taken: a refill for the light, or something on the night's list.
id: string— Its spot's id.kind: string— Its spot's kind.x: number— Position, world units.y: number— Height above the floor.z: number— Position, world units.
PlayerDef
interface PlayerDef
The player: movement, stamina, fear, hiding and leaning. The game moves the player; the director says how fast.
walk?: number— Walking speed, units per second, 0.1 to 20; default 1.5.sprint?: number— Sprinting speed, units per second, 0 to 40; 0 (or no more thanwalk) means no sprint. Default 3.4.crouch?: number— Crouching speed, units per second, 0 to 20; default 0.7.stamina?: number— Seconds of sprint from full stamina, 0.5 to 600; default 6.recover?: number— Stamina regained a second (a share of full), 0 to 10; default 0.15.delay?: number— Seconds after sprinting before stamina comes back, 0 to 30; default 1.again?: number— Stamina needed to sprint again after running out, 0 to 1; default 0.35.tired?: number— Multiplies walking speed while out of breath, 0.1 to 1; default 0.75.eye?: number— Eye height above the feet, 0.1 to 10; default 1.6.health?: number— Health at the start; a catch takes the stalker'scatch.damage, 0.01 to 1000. Default 1.dread?: number | null— Seconds at full fear before the night is lost to fright, 0.5 to 600; default none.fearFall?: number— Fear lost a second when calm (twice as fast in lamplight), 0 to 10; default 0.05.darkFear?: number— The fear darkness brings with no light of your own (torch off or dead, in the dark), 0 to 1; default 0.45.heartbeat?: [number, number]— Resting and racing heart rates, beats a minute,[rest, max]from 20 to 260; default [68, 165].beats?: number | null— Fear from which each heartbeat is an event (for a thump), 0 to 1; default 0.3. null for none.reach?: number— Units within which the player uses a fixture, a hiding spot or a pickup, 0.2 to 10; default 1.3.stride?: number— Units walked per footstep, 0.1 to 10; default 0.75.hide?: HideDef | null— Hiding; null for none.lean?: LeanDef | null— Leaning; null for none.noise?: NoiseDef— How far the player is heard.sounds?: PlayerSounds— The player's sounds.
PlayerSounds
interface PlayerSounds
Sounds the player asks for, by name; '' for none.
step?: string— A walking step; default ''.run?: string— A running step; default ''.pant?: string— Out of breath; default'pant'.hide?: string— Getting into a hiding spot; default'hide'.unhide?: string— Getting out; default'unhide'.gasp?: string— A gasp when a scare lands close; default'gasp'.pickup?: string— A pickup taken; default'pickup'.
PlayerState
interface PlayerState
The player, as saved.
placed: boolean— Whether the first step has placed them.x: number— Position.y: number— Feet height.z: number— Position.yaw: number— Yaw.pitch: number— Pitch.moving: number— Speed measured last step.speed: number— Speed allowed now.stamina: number— Stamina, 0 to 1.rest: number— Seconds since they last sprinted.sprinting: boolean— Sprinting.exhausted: boolean— Out of breath.crouch: boolean— Crouching.exertion: number— Exertion, 0 to 1.fear: number— Fear, 0 to 1.dread: number— Seconds at full fear.bpm: number— Heart rate.beat: number— Where in its beat the heart is, 0 to 1.health: number— Health.hidden: string | null— The spot they hide in, or null.hiding: 'entering' | 'in' | 'leaving' | null— Getting in, in, getting out, or null.hideT: number— Seconds left getting in or out.peeking: boolean— Peeking.lean: number— Lean, −1 to 1.still: number— Seconds standing still.stride: number— Units walked since the last footstep.seen: boolean— The stalker saw them last step.
PlayerView
interface PlayerView
The player, for the camera, the HUD and the sound.
x: number— Position the camera should be at (a hiding spot's, while hidden), world units.y: number— Feet height.z: number— Position, world units.yaw: number— The engine's yaw.speed: number— The fastest the game should move the player now, units a second (0 while hiding).moving: number— How fast they moved last step, units a second.stamina: number— Stamina, 0 to 1.sprinting: boolean— Sprinting now.exhausted: boolean— Out of breath: no sprint until stamina comes back toplayer.again.fear: number— Fear, 0 to 1: drive a vignette, a desaturation or a ringing.heartbeat: number— Heart rate, beats a minute.breath: number— How hard they breathe, 0 to 1, for a breathing loop.vignette: number— How dark to make the screen's edges, 0 to 1 (fear and hurt).health: number— Health left.hidden: string | null— The hiding spot they are in, getting into or out of, or null.hiding: 'entering' | 'in' | 'leaving' | null—'entering','in','leaving', or null.peeking: boolean— Peeking out of a hiding spot.lean: number— Lean, −1 to 1.leanX: number— Where the lean moves the eye, world units: add to the camera's position.leanZ: number— Where the lean moves the eye, world units.roll: number— The camera's roll for the lean, radians.seen: boolean— The stalker sees them now.still: number— Seconds they have stood still.
ScareDef
interface ScareDef
A scare, as data. Its kind says what it does and how it is placed; everything else tunes it. Fields left out take
the kind's defaults (listed on the pack's page). show and sound name what the presenter draws and plays.
id: string— Unique id.kind: ScareKind— -'lights': lamps with one tag near the player go out with sparks, and maybe flicker back. -'apparition': a figure stands just beyond the torch beam, at a corridor end or behind a window spot; it goes when lit, looked at straight on or come near. -'runner': a figure crosses a corridor end ahead, from out of sight to out of sight. -'slam': an open door or fixture near the player slams shut, by preference out of sight. -'footsteps': steps behind the player that follow, and stop when the player looks back. -'whisper': a whisper just behind the player. -'fall': a thing drops from a shelf spot nearby, or from the ceiling at the edge of view. -'breath': breathing close by, out of sight (outside the spot when the player is hidden). -'phone': a phone or radio spot rings or crackles until the player reaches it. -'distort': a screen distortion pulse. -'fixture': a shut fixture (a stall, a cabinet) opens by itself. -'mirror': a figure stands behind the player, seen in a mirror spot they face; it goes when they turn.show?: string— What the presenter shows (a figure's look, an effect); default by kind.sound?: string— The sound it plays at its place; default by kind. '' for none.sound2?: string— A second sound: a lamp's buzz as it flickers back, a phone answered, a thing landing; default by kind.cost?: number— Budget points it spends, 0 to 100; default by kind (1 or 2).cooldown?: number— Seconds before it may land again, 0 to 3600; default 40.max?: number— Most times it lands in a night, 0 to 10000; default unlimited.weight?: number— How often it is chosen against the others, 0 to 100; default 1.phases?: HorrorPhase[]— The phases it lands in; default by kind (build and peak; lights and distortion in calm too).tension?: [number, number]— The tension it lands within,[min, max]from 0 to 1; default [0, 1].jolt?: number— Tension it adds when it lands, 0 to 1; default by kind.fear?: number— Fear it adds, 0 to 1; default by kind.pulse?: number— A screen distortion pulse with it, 0 to 1; default 0 (a'distort'scare 0.8).noise?: number— How far the stalker hears it, units, 0 to 200; default 0 (a falling thing 6).range?: [number, number]— Units from the player it is placed within,[min, max]from 0 to 200; default by kind.spots?: string[]— The spot kinds it uses (windows, phones, mirrors, shelves, stalls); default by kind.tags?: string[]— Lamp tags (lights) or door ids and tags (slam) it may use; default any.duration?: [number, number]— Seconds it lasts at most,[min, max]from 0 to 600; default by kind.when?: ScareWhen— When it may land.back?: [number, number] | null— Lights: seconds out before they flicker back,[min, max]from 0 to 3600; null: they stay out. Default [6, 18].stayOut?: number— Lights: the chance they stay out anyway, 0 to 1; default 0.25.count?: number— Lights: sparks in each burst, 0 to 500; default 14. Footsteps: steps,countfrom 1 to 100 (default 6).speed?: number— Runner: units a second (default 5). Footsteps: steps a second (default 1.8). 0.1 to 50.span?: number— Runner: half the width of the crossing, units, 0.5 to 20 (default 2.5); its two ends must be out of sight.ends?: boolean— Apparition: also stand at corridor ends along the view (default true).height?: number— Figures: height in units, 0.2 to 20; default 1.8.vanish?: VanishDef— What makes a figure go.
ScareEnd
type ScareEnd = 'time' | 'lit' | 'look' | 'near' | 'turn' | 'answered' | 'done'
Why a scare ended.
ScareKind
type ScareKind = | 'lights' | 'apparition' | 'runner' | 'slam' | 'footsteps' | 'whisper' | 'fall' | 'breath' | 'phone' | 'distort' | 'fixture' | 'mirror'
What a scare does. Each kind has its own placement and its own way of ending; see ScareDef.
ScareView
interface ScareView
A scare going on.
id: string— Its id,<def>-<n>.scare: string— The def's id.kind: ScareKind— Its kind.show: string— What it shows.x: number— Where it is now, world units (a falling thing'syfalls).y: number— Height.z: number— Where, world units.age: number— Seconds since it began.life: number— Seconds it lasts at most (0: until it is done).
ScareWhen
interface ScareWhen
When a scare may land, beyond its phase, tension and budget. Every condition left out holds.
dark?: number— The light where the player stands (lamps and sectors, without the torch) is at most this, 0 to 1.torch?: 'on' | 'off' | 'low'—'on'(lit),'off'(off or dead) or'low'(below its low mark).moving?: boolean— The player is moving (true) or not (false).still?: number— The player has stood still at least this many seconds, 0 to 600.hidden?: boolean— The player is hidden (true) or not (false).chased?: boolean— The stalker is hunting the player (true) or not (false).
SearchDef
interface SearchDef
Searching.
time?: [number, number]— Seconds it searches round where it lost the player or heard them,[min, max]from 0 to 600; default [8, 14].radius?: number— Units round that place it searches, 0.5 to 50; default 5.wait?: [number, number]— Seconds it stands at each place it checks,[min, max]from 0 to 60; default [0.8, 2].
SectorHorrorWorld
interface SectorHorrorWorld extends HorrorWorld
The world a sector map gives, with the map's own events from the doors and lamps the director worked.
readonly map: SectorMap— The map.drain(): MapEvent[]— The map's events (movers starting, their sounds) caused by slams and lamps since the last call; for the game to play.
SectorWorldOptions
interface SectorWorldOptions
How sectorHorrorWorld reads a sector map.
ambient?: number— Multiplies a sector's own light as the room's share of the light at a point, 0 to 10; default 1.lampScale?: number— Multiplies each lamp's share, 0 to 10; default 1.ignore?: string[]— Thing types that are not spots; default['start'].rename?: Record<string, string>— Thing types read as another spot kind ({ wardrobe: 'locker' }); default none.
ShakeDef
interface ShakeDef
Shaking the torch to win back a little charge.
gain: number— Charge one shake gives, 0 to 1.up: number— The most charge shaking brings it to, 0 to 1.noise?: number— How far the shaking is heard, units, 0 to 100; default 0.
StalkerDef
interface StalkerDef
The stalker: one monster that hunts by sound and sight and hates the light.
show?: string— What the presenter shows; default'stalker'.speed?: StalkerSpeeds— Speeds by mode.radius?: number— Body radius, units, 0.1 to 4; default 0.35.height?: number— Height, units, 0.2 to 20; default 2. Its eye is at 90% of it.step?: number— Highest step it walks up, units, 0 to 20; default 0.5.sight?: number— Units it sees a player in light without a torch, 0 to 500; default 12.sightLit?: number— Units it sees a player whose torch is on, 0 to 500; default 22.sightDark?: number— Units it sees a player in the dark (light below 0.25) with no torch, 0 to 500; default 4.fov?: number— Half-angle it sees across, radians, 0 to π; default 1.1.hearing?: number— Multiplies how far it hears every noise, 0 to 10; default 1.light?: StalkerLight— What light does to it:'flee'runs,'avoid'steps out of the beam,'freeze'cannot move while lit or watched,'ignore'. Default'flee'.tolerance?: number— Seconds in the light before it reacts, 0 to 30; default 0.4.lamps?: boolean— Bright lamplight keeps it off: it will not follow a player standing in light of at leastbrightinto it, but waits at its edge; it never catches a player standing in it; and it keeps its beat to the darker rooms (it still passes through lit places on its way). Default true.bright?: number— Light from which lamps keep it off, 0 to 1; default 0.55.flee?: [number, number]— Seconds it runs from the light,[min, max]from 0 to 600; default [3, 6].reach?: number— Units within which it catches the player, 0.1 to 10; default 0.9.catch?: CatchDef— What a catch does.search?: SearchDef— Searching.patrol?: PatrolDef— Patrolling.wake?: number— Seconds before it first appears, 0 to 36000; default 30.wakeOnPeak?: boolean— It appears at the first peak even beforewake; default true.lair?: string— The spot kind where it appears; default'lair'. With none, the patrol spot furthest from the player.mercy?: MercyDef— How it fails on purpose.director?: boolean— The pacing steers it: away in calm, toward the player at a peak, back to its far rooms in relief; default true.stride?: number— Units walked per footstep sound, 0.1 to 10; default 0.9.ambient?: [number, number]— Seconds between its idle sounds,[min, max]from 0.5 to 600; default [7, 14].sounds?: StalkerSounds— Its sounds.
StalkerLight
type StalkerLight = 'flee' | 'avoid' | 'freeze' | 'ignore'
How the stalker reacts to light.
StalkerMode
type StalkerMode = 'dormant' | 'patrol' | 'investigate' | 'hunt' | 'search' | 'flee' | 'retreat'
What the stalker is doing.
StalkerSounds
interface StalkerSounds
Sounds the stalker makes, by name; '' for none.
step?: string— A footstep, everystrideunits; default'stalker-step'.growl?: string— It heard something; default'growl'.scream?: string— It saw the player and the hunt begins; default'scream'.sniff?: string— It checks a place or a hiding spot; default'sniff'.hiss?: string— The light hurts it; default'hiss'.idle?: string— Now and then, unaware; default'moan'.door?: string— It forces a door; default'door-bash'.catch?: string— It catches the player; default'catch'.
StalkerSpeeds
interface StalkerSpeeds
Units a second the stalker moves in each mode, 0 to 30.
patrol?: number— Walking its beat; default 1.1.search?: number— Going to a noise and searching; default 1.7.hunt?: number— Chasing the player; default 3.1.flee?: number— Running from the light; default 3.8.
StalkerState
interface StalkerState
The stalker, as saved.
x: number— Position.y: number— Feet height.z: number— Position.yaw: number— Yaw.mode: StalkerMode— Mode.timer: number— Seconds left in a timed mode (dormant, search, flee, retreat).wait: number— Seconds left standing where it is.gx: number— Where it is going.gz: number— Where it is going.route: { x: number; z: number; door?: string }[]— Its route, next first.replan: number— Seconds until it plans its route again.lx: number— Where it last saw or heard the player.lz: number— Where it last saw or heard the player.cx: number— Where it searches round.cz: number— Where it searches round.sees: boolean— It saw the player last step.sawHide: string | null— The hiding spot it saw the player take, or null.hunt: number— Seconds this hunt has run.giveUp: number— Seconds of hunting before it gives up on purpose.grace: number— Seconds before its sight may start a hunt.lit: number— Seconds it has been in the light.litNow: boolean— In the light now.frozen: boolean— Held by the light.avoiding: boolean— Stepping out of the beam.lurk: boolean— Waiting at the edge of bright lamplight it will not enter.walked: number— Units walked.stride: number— Units since its last footstep.noise: number— Seconds to its next idle sound.stuck: number— Seconds without getting nearer its goal.checking: string | null— The spot it is checking, or null.checked: string[]— Spots it has checked in this search.post: string | null— The patrol spot it walks to, or null.
StalkerView
interface StalkerView
The stalker, for the presenter.
x: number— Position, world units.y: number— Feet height.z: number— Position, world units.yaw: number— The engine's yaw it faces.mode: StalkerMode— What it is doing.show: string— What the presenter shows (stalker.show). Draw nothing whilemodeis'dormant'.lit: boolean— In the light (the torch's or a bright lamp's).frozen: boolean— Held still by the light or a look (withlight: 'freeze').avoiding: boolean— Stepping back out of the torch beam (withlight: 'avoid').lurking: boolean— Waiting at the edge of bright lamplight it will not follow the player into.walked: number— Units walked, for a walk cycle.
TorchChange
type TorchChange = 'on' | 'off' | 'low' | 'flicker' | 'sputter' | 'dead' | 'swap' | 'charge' | 'refill'
What the torch did.
TorchState
interface TorchState
The torch, as saved.
on: boolean— Switched on.charge: number— Charge, 0 to 1.spares: number— Spares carried.dip: number— How deep the current dip is, 0 to 1.dipT: number— Seconds left in the current dip.strength: number— Strength now, 0 to 1.warned: boolean— It said it was low.
TorchView
interface TorchView
The torch, for the presenter's light and the HUD.
kind: string— What it is (light.kind).on: boolean— Switched on (it may still be dark when dead).charge: number— Charge left, 0 to 1.strength: number— How bright it shines now, 0 to 1, with flicker and sputter: set the light's intensity by it (0 is dark).flicker: number— How much it is dipping now, 0 to 1 (a flicker or a sputter).cone: number— The beam's half-angle, radians (π all round).range: number— Units it reaches now.color: string— Colour, CSS.spares: number— Spare refills carried.dead: boolean— Out of charge.sputter: boolean— Sputtering (it cuts out now and then).
VanishDef
interface VanishDef
What makes a figure go.
lit?: boolean— It goes when the torch beam or a lamp lights it; default true for apparitions.look?: number— It goes after being looked at straight on this many seconds, 0 to 60; default 0.35 for apparitions. 0: never.near?: number— It goes when the player comes within this many units, 0 to 100; default 2.5. 0: never.turn?: number— A mirror figure goes when the player turns more than this many radians from where they looked, 0 to π; default 1.
ViewDef
interface ViewDef
How the presenter's camera sees, for placing things in or out of view.
fov?: number— Half the camera's horizontal field of view, radians, 0.1 to 1.5; default 0.75.look?: number— Half-angle within which the player counts as looking straight at something, radians, 0.01 to 0.6; default 0.12.


