# @ferret/interaction: API

Interactables with focus and prompts, inventory and stock props: doors, level doors, pickups, levers.

22 exports. Generated from the source (`packages/interaction/src`), so it matches the code.

## Classes

### Container

*class* · `packages/interaction/src/container.ts:19`

```ts
class Container extends Component<ContainerParams>
```

Somewhere to keep items (a locker, a chest): the counterpart of `Inventory`. If the entity has a `Door` the container works only while it is open.
`put(actor, item)` moves an item from the actor's `Inventory`; `take(actor, item)` moves it to the actor's `Inventory`. Both return false and change nothing
when the door is shut, the actor lacks the item, the container is full or does not hold it. Events: `container:put` / `container:take` { container, actor, item, count }.

- `schema: { readonly capacity: { readonly type: "number"; readonly min: 0; }; readonly items: { readonly type: "object"; }; readonly alwaysOpen: { readonly type: "boolean"; }; }`
- `readonly contents: Map<string, number>`
- `onAttach()`
- `get usable(): boolean` — is it open for use?
- `get total(): number`
- `count(item: string): number`
- `put(actor: Entity, item: string, n = 1): boolean`
- `take(actor: Entity, item: string, n = 1): boolean`
- `toJSON(): Record<string, number>`
- `load(data: Record<string, number> | undefined)`

### Door

*class* · `packages/interaction/src/props.ts:51`

```ts
class Door extends Component<DoorParams>
```

A door that opens and closes with a hinge or a slide. It blocks characters while it is (mostly) closed: its meshes keep
`checkCollisions` on until it is nearly open. Events: `door:open`, `door:close`, `door:locked` { door, actor }, `door:unlocked`.

- `schema: { readonly open: { readonly type: "boolean"; }; readonly locked: { readonly type: "boolean"; }; readonly key: { readonly type: "string"; }; readonly consumeKey: { readonly type: "boolean"; }; readonly angle: { readonly type: "number"; readonly min: -360; readonly max: 360; }; readonly slide: { readonly type: "array"; }; readonly duration: { readonly type: "number"; readonly min: 0.05; readonly max: 30; }; readonly openPrompt: { readonly type: "string"; }; readonly closePrompt: { readonly type: "string"; }; readonly with: { readonly type: "array"; }; readonly range: { readonly type: "number"; readonly min: 0.5; readonly max: 50; }; readonly interact: { readonly type: "boolean"; }; readonly overlap: { readonly type: "object"; }; }` — what the editor checks `params` against
- `isOpen: boolean`
- `locked: boolean`
- `t: number` — 0 closed .. 1 open
- `onAttach()`
- `use(actor: Entity)`
- `unlock()`
- `lock()`
- `setOpen(open: boolean, instant = false, together = true)`
- `onFixedUpdate(dt: number)`

### Grabbable

*class* · `packages/interaction/src/grab.ts:43`

```ts
class Grabbable extends Component<GrabbableParams>
```

A physics body that a hand takes: it follows the hand by a velocity drive, so heavy objects lag and a wall in the way stops them;
on release it keeps the hand's velocity of the last 100 ms (a throw). On the screen the `use` action holds it in front of the camera.
Needs a `RigidBody` on the entity. Events: `grab:take` { target, by }, `grab:release` { target, by, velocity }.

- `schema: { readonly maxMass: { readonly type: "number"; readonly min: 0; }; readonly hands: { readonly type: "string"; }; readonly twoHanded: { readonly type: "boolean"; }; readonly snapPoints: { readonly type: "array"; }; readonly stiffness: { readonly type: "number"; readonly min: 0; }; readonly maxSpeed: { readonly type: "number"; readonly min: 0; }; readonly screenHold: { readonly type: "number"; readonly min: 0; }; }`
- `onAttach()`
- `get held(): boolean`
- `get holders(): string[]`
- `canGrab(id: string): boolean` — May this hand take it now?
- `grab(id: string, holder: Holder): boolean` — Take it with a holder (a hand). Returns false when it cannot be taken.
- `release(id: string, throwIt = true): boolean` — Let go; the body keeps the hand's velocity from the last 100 ms.
- `onFixedUpdate(dt: number)`
- `onDetach()`

### Interactable

*class* · `packages/interaction/src/interact.ts:19`

```ts
class Interactable extends Component<InteractableParams>
```

Something the player can use. Behaviours (Door, Pickup ...) hook in with `onUse`; games can listen to the `interact:use` event.

- `available: boolean` — Set false to make it unavailable (a locked-out prompt, a finished quest).
- `prompt: string` — a prompt the owner changes at run time ("Close door" when it is open)
- `onAttach()`
- `get range(): number`
- `get usable(): boolean`
- `onUse(fn: UseHandler): this`
- `interact(actor: Entity): boolean` — Use it. Returns false if it could not be used now.

### Interactor

*class* · `packages/interaction/src/interact.ts:89`

```ts
class Interactor extends Component<InteractorParams>
```

Put on the player. Each fixed step it picks the usable thing the player is closest to looking at (within the thing's range),
announces changes with `interact:focus` / `interact:blur`, and uses the focus when the action is pressed.

- `focus: Interactable | null`
- `view: () => { origin: BABYLON.Vector3; dir: BABYLON.Vector3 }` — The view the focus is chosen by. Follows the camera rig of a PlayerController; replace it to drive the view yourself.
- `readonly sources: FocusSource[]` — focus sources besides the view cone, in order of priority
- `via: string` — which source holds the focus now
- `onFixedUpdate()`

### Inventory

*class* · `packages/interaction/src/inventory.ts:4`

```ts
class Inventory extends Component<{ items?: Record<string, number> }>
```

What an actor carries: item id -> count. Doors ask it for keys, pickups fill it. Events: `inventory:change` { entity, item, count }.

- `readonly items: Map<string, number>`
- `onAttach()`
- `count(item: string): number`
- `has(item: string, n = 1): boolean`
- `add(item: string, n = 1): number`
- `remove(item: string, n = 1): boolean` — Take n of an item; false (and nothing taken) if there are fewer.
- `toJSON(): Record<string, number>` — Plain data for a save (`levels.state.inventory = inv.toJSON()`).
- `load(data: Record<string, number> | undefined)`

### LevelDoor

*class* · `packages/interaction/src/props.ts:167`

```ts
class LevelDoor extends Component<LevelDoorParams>
```

A way into another level: using it travels there (see

- `schema: { readonly level: { readonly type: "string"; readonly required: true; }; readonly spawn: { readonly type: "string"; }; readonly prompt: { readonly type: "string"; }; readonly range: { readonly type: "number"; readonly min: 0.5; readonly max: 50; }; readonly key: { readonly type: "string"; }; }`
- `onAttach()`

### Lever

*class* · `packages/interaction/src/props.ts:212`

```ts
class Lever extends Component<LeverParams>
```

A switch. Using it flips it and toggles the doors it points at (opening a locked one unlocks it). Event: `lever:toggle` { lever, on }.

- `schema: { readonly targets: { readonly type: "array"; }; readonly on: { readonly type: "boolean"; }; readonly onPrompt: { readonly type: "string"; }; readonly offPrompt: { readonly type: "string"; }; readonly range: { readonly type: "number"; readonly min: 0.5; readonly max: 50; }; }`
- `on: boolean`
- `onAttach()`
- `set(on: boolean)`

### Pickup

*class* · `packages/interaction/src/props.ts:188`

```ts
class Pickup extends Component<PickupParams>
```

An item lying around: using it moves it into the actor's Inventory (made if missing) and removes it. Event: `pickup:collected` { item, count, actor, pickup (the entity's id) }.

- `schema: { readonly item: { readonly type: "string"; readonly required: true; }; readonly count: { readonly type: "number"; readonly min: 1; }; readonly prompt: { readonly type: "string"; }; readonly range: { readonly type: "number"; readonly min: 0.5; readonly max: 50; }; }`
- `onAttach()`

## Functions

### installInteraction

*function* · `packages/interaction/src/index.ts:15`

```ts
function installInteraction(world: World): void
```

Register the interaction components with a world so manifests and `entity.add('Door', ...)` can name them.

## Interfaces

### ContainerParams

*interface* · `packages/interaction/src/container.ts:5`

```ts
interface ContainerParams
```

- `capacity?: number` — most items it holds, counting every unit (default: unlimited)
- `items?: Record<string, number>` — what it holds to begin with: item id -> count
- `alwaysOpen?: boolean` — let it be used with the door shut (default false: with a `Door` on the same entity, only while that is open)

### DoorParams

*interface* · `packages/interaction/src/props.ts:12`

```ts
interface DoorParams
```

- `open?: boolean`
- `needsSight?: boolean` — false: the prompt/use works through the door's own collision (see InteractableParams.needsSight)
- `locked?: boolean` — locked until the actor holds this item (see Inventory); `locked: true` without a key never opens by use
- `key?: string`
- `consumeKey?: boolean` — use the key up when unlocking
- `angle?: number` — hinge doors: degrees the panel turns about its own y axis (default 90; negative swings the other way)
- `slide?: [number, number, number]` — sliding doors: the offset of the open position in the node's local frame, metres. Used instead of the hinge when set.
- `duration?: number` — seconds to open or close (default 0.6)
- `openPrompt?: string` — the open/close prompts
- `closePrompt?: string`
- `range?: number`
- `with?: string[]` — entity ids of doors that open and close together with this one (the two leaves of a gate); each keeps its own prompt and parameters
- `interact?: boolean` — false: no prompt and no use (a door that only `overlap` moves)
- `overlap?: { radius: number; centre?: [number, number, number]; tags?: string[]; pad?: number; /** the sphere's place relative to the door node as Unreal gives a component's relative location (cm, X forward, Y right, Z up); used when `centre` is not given */ ueOffset?: [number, number, number]; }` — Opens when an entity with one of `tags` (default player, npc) comes within `radius` m of `centre` (world position; default the door's own) and closes when one leaves: each entering opens, each leaving closes, as an Unreal sphere's begin/end overlap that plays/reverses a timeline does. `pad` (m, default 0.35) is the visitor's own radius (a capsule overlaps before its centre is inside).

### FocusEvent

*interface* · `packages/interaction/src/interact.ts:73`

```ts
interface FocusEvent
```

- `target: Entity`
- `actor: Entity`
- `prompt: string`
- `via?: string`

### FocusSource

*interface* · `packages/interaction/src/interact.ts:79`

```ts
interface FocusSource
```

Another way to focus something besides the view cone: a hand's ray, a hand touching. `pick` returns the usable thing it points at or touches;
`pressed` is its own use button (trigger, a touch) this step. A source that picks something wins over the cone.

- `name: string`
- `pick(actor: Entity): Interactable | null`
- `pressed(): boolean`

### GrabbableParams

*interface* · `packages/interaction/src/grab.ts:11`

```ts
interface GrabbableParams
```

- `maxMass?: number` — kg: heavier objects cannot be taken (default 40)
- `hands?: 'left' | 'right' | 'both'` — which hand may take it (default both)
- `twoHanded?: boolean` — two hands may hold it at once (default false)
- `snapPoints?: Array<{ position: [number, number, number]; rotation?: [number, number, number] }>` — handles in the object's frame: a hand takes the object so the nearest snap point sits in the palm (position, and an optional rotation as Euler angles)
- `stiffness?: number` — how hard the hold pulls toward the hand, 1/s (default 18); heavy objects lag because the pull speed is capped by `maxSpeed` scaled down with mass
- `maxSpeed?: number` — m/s at 1 kg or less (default 6): heavier bodies are slower, so they lag and cannot be dragged through walls by the hand
- `screenHold?: number | false` — the screen version: metres in front of the camera the object is held at (default 1.4); false = the `use` action does not pick it up

### Holder

*interface* · `packages/interaction/src/grab.ts:6`

```ts
interface Holder
```

Where a hand (or the screen's "hold in front of the camera") wants the object: world space.

- `pose(): { position: BABYLON.Vector3; rotation: BABYLON.Quaternion } | null` — null while the hand is not tracked: the object is let go of

### InteractableParams

*interface* · `packages/interaction/src/interact.ts:3`

```ts
interface InteractableParams
```

- `prompt?: string` — the text the prompt shows ("Open door"); the UI adds the button label
- `range?: number` — how close the actor must be, metres (default 2.5)
- `cooldown?: number` — seconds before it can be used again
- `once?: boolean` — can only be used once
- `needsSight?: boolean` — false: usable through a solid between the eyes and it (furniture whose own collision box surrounds it); the range still applies

### InteractorParams

*interface* · `packages/interaction/src/interact.ts:48`

```ts
interface InteractorParams
```

- `action?: string` — the button action (default "use")
- `cone?: number` — half-angle of the cone in front of the view that counts as "looking at", degrees (default 35)
- `eyeHeight?: number` — height of the eyes for the view ray when there is no camera rig, metres
- `needsSight?: boolean` — objects between the viewer and the target block it (static level geometry; default true)
- `sightSlack?: number` — metres just before the target where a hit does not block: the target's own housing (a locker door sits in the cabinet's collision box, a button in its panel). Default 0.25

### LevelDoorParams

*interface* · `packages/interaction/src/props.ts:157`

```ts
interface LevelDoorParams
```

- `level: string`
- `spawn?: string`
- `prompt?: string`
- `range?: number`
- `key?: string` — held item needed to pass

### LeverParams

*interface* · `packages/interaction/src/props.ts:202`

```ts
interface LeverParams
```

- `targets?: string[]` — entity ids whose Door is toggled
- `on?: boolean`
- `onPrompt?: string`
- `offPrompt?: string`
- `range?: number`

### PickupParams

*interface* · `packages/interaction/src/props.ts:180`

```ts
interface PickupParams
```

- `item: string`
- `count?: number`
- `prompt?: string`
- `range?: number`

## Types

### UseHandler

*type* · `packages/interaction/src/interact.ts:16`

```ts
type UseHandler = (actor: Entity) => void
```

