The pack
controlFeels
function controlFeels(): Pack<{ controlFeels: ControlFeelsApi }>
The control-feels pack. Pure logic, so it needs no backend and runs headless.
Helpers
CAR_FEELS
const CAR_FEELS: { polePosition: { gears: number[]; accel: number; brake: number; steer: number; auto: false; }; }
Road cars from the gallery: roadCar(CAR_FEELS.polePosition).
directMove
function directMove(o: { speed: number; eightWay?: boolean }): DirectMove
Creates a DirectMove.
inertialShip
function inertialShip(o: InertialOptions): InertialShip
Creates an InertialShip.
roadCar
function roadCar(o: CarOptions): RoadCar
Creates a RoadCar.
SHIP_FEELS
const SHIP_FEELS: { asteroids: { thrust: number; turn: number; drag: number; maxSpeed: number; }; starCastle: { thrust: number; turn: number; drag: number; maxSpeed: number; }; mineStorm: { thrust: number; turn: number; drag: number; maxSpeed: number; }; mineStorm3d: { ...; }; solarQuest: { ...; }; ripOff: { ...; }; webWars: { ...; }... …
Inertial ships, tuned by hand in the gallery tributes (not measured from the originals): spread one into
inertialShip and override what differs, inertialShip({ ...SHIP_FEELS.mineStorm, maxSpeed: 500 }).
Higher drag settles faster; turn is radians a second.
spinner
function spinner(o: { keyRate?: number; sensitivity?: number; wrap?: number } = {}): Spinner
Creates a Spinner.
trackball
function trackball(o: TrackballOptions = {}): Trackball
Creates a Trackball.
TRACKBALL_FEELS
const TRACKBALL_FEELS: { quantum: { drag: number; sensitivity: number; maxSpeed: number; }; }
Trackballs from the gallery: trackball({ ...TRACKBALL_FEELS.quantum, x, y }).
WALK_FEELS
const WALK_FEELS: { cosmicChasm: { speed: number; eightWay: true; }; }
Direct movers from the gallery: directMove(WALK_FEELS.cosmicChasm).
Classes
DirectMove
class DirectMove
Eight-way movement with no momentum — Berzerk, Clean Sweep, Armor Attack.
heading is the last direction actually moved, which is what "fire in the
direction you are walking" means and why it must survive letting go.
speed: number— Movement speed, in units each second.eightWay: boolean— Snap movement to 8 compass directions. Default true.heading: number— Last movement heading in radians, kept when the stick centres.moving: boolean— True while any direction is held.constructor(o: { speed: number; eightWay?: boolean })update(a: Mover, input: DirectInput, dt: number): void— Moves the mover for one frame from the held directions.
InertialShip
class InertialShip
Rotate / thrust / drift — Asteroids, Mine Storm, Space Wars, Solar Quest, Gravitar, Lunar Lander. The single most common control scheme of the era.
thrust: number— Acceleration under thrust, in units/s².turn: number— Turn rate, in radians each second.drag: number— Fraction of speed shed each second, 0..1. Default 0.25.maxSpeed: number— Speed ceiling, in units each second. Default Infinity.reverse: number— Reverse thrust as a fraction of forward thrust. 0 = no reverse.thrusting: boolean— True on the frames the engine is lit — for the exhaust plume.constructor(o: InertialOptions)update(a: Mover, input: ShipInput, dt: number): void— Applies one frame of turn, thrust and drag to the mover.muzzle(a: Mover, offset: number, shotSpeed: number, inherit = true): Mover— Where a shot fired now should start and how fast it goes.
RoadCar
class RoadCar
A racing gearbox: gear, gas, brake, steer, speedo. steer is scaled by speed
because a stationary car does not corner, which is the difference between a
racer and a top-down shooter that happens to have a car in it.
readonly gears: number[]— Top speed in each gear, slowest first.gear: number— The current gear, from 0 (first gear).speed: number— The current forward speed, in units each second.drift: number— Lateral rate this frame, units/s — feed it to the road position.accel: number— Acceleration under gas, in units/s².braking: number— Deceleration under brake, in units/s².coast: number— Deceleration when neither gas nor brake is held, in units/s².steer: number— Lateral rate at full speed, in units each second.auto: boolean— True for an automatic gearbox that shifts itself.constructor(o: CarOptions)get topSpeed(): number— The top speed of the current gear, in units each second.get revs(): number— 0..1 within the current gear — the rev counter, and the engine pitch.update(input: CarInput, dt: number): void— Applies one frame of shifting, speed and steering from the held keys.crash(): void— A crash: stop dead and drop to first.
Spinner
class Spinner
A spinner (Tempest, Omega Race): RELATIVE input that accumulates. A mouse, a
trackball axis and a held key all feed the same nudge, so the game reads one
value whatever the hardware is.
value: number— Accumulated position. Unbounded unlesswrapis set.keyRate: number— Units per second of held key.sensitivity: number— Multiplier on pointer deltas.wrap: number— Wrapvalueinto 0..wrap. 0 = free.constructor(o: { keyRate?: number; sensitivity?: number; wrap?: number } = {})nudge(delta: number): number— A pointer or trackball delta.update(dir: number, dt: number): number— Held keys: -1, 0 or +1.set(v: number): number— Sets the value directly, wrapped into 0..wrap whenwrapis set. Returns the new value.
Trackball
class Trackball
The trackball state. Feed pointer deltas to roll and call update each frame.
x: number— The x position of the cursor.y: number— The y position of the cursor.vx: number— The roll velocity on x, in units each second.vy: number— The roll velocity on y, in units each second.drag: number— Fraction of the roll shed each second, 0..1. Default 0.9.sensitivity: number— Multiplier on pointer deltas. Default 1.maxSpeed: number— Ceiling on the roll speed, in units each second. Default Infinity.bounds: { x: number; y: number; w: number; h: number } | null— Rectangle that limits the position, or null for no limit.constructor(o: TrackballOptions = {})roll(dx: number, dy: number, dt: number): void— A pointer delta this frame.update(dt: number): void— Applies drag, the speed ceiling and the bounds, then moves the cursor. Call each frame withdtin seconds.
Types
CarInput
interface CarInput
Held-key state for a RoadCar. The game maps its own keys.
gas?: boolean— True while the gas is held.brake?: boolean— True while the brake is held.left?: boolean— True while steering left.right?: boolean— True while steering right.shiftUp?: boolean— Momentary: shift up / down one gear.shiftDown?: boolean— Momentary: shift down one gear.
CarOptions
interface CarOptions
Configures a RoadCar.
gears: number[]— Top speed in each gear, slowest first.accel: number— Acceleration, units/s².brake: number— Braking, units/s².drag?: number— Coasting deceleration, units/s². Defaultaccel / 3.steer: number— Lateral units/s at full speed.auto?: boolean— Automatic gearbox — shifts itself at the top of each gear. Default false.
ControlFeelsApi
type ControlFeelsApi = typeof kit
game.controlFeels — every function, class and preset this pack carries, as one namespace.
DirectInput
interface DirectInput
Held-key state for DirectMove. The game maps its own keys.
up?: boolean— True while up is held.down?: boolean— True while down is held.left?: boolean— True while left is held.right?: boolean— True while right is held.
InertialOptions
interface InertialOptions
Configures an InertialShip.
thrust: number— Acceleration under thrust, units/s².turn: number— Turn rate, rad/s.drag?: number— Fraction of speed shed per second. Default 0.25.maxSpeed?: number— Speed ceiling, in units each second. Default Infinity.reverse?: number— Reverse thrust as a fraction of forward. Default 0 (no reverse).
ShipInput
interface ShipInput
Held-key state. The game maps its own keys; the profile never reads input.
left?: boolean— True while turning left.right?: boolean— True while turning right.thrust?: boolean— True while forward thrust is held.reverse?: boolean— True while reverse thrust is held.
TrackballOptions
interface TrackballOptions
A trackball with momentum — Quantum's probe, Missile Command's crosshair. The roll continues after the ball is released and decays; that carry is the whole feel of the device.
x?: number— The start x position. Default 0.y?: number— The start y position. Default 0.drag?: number— Fraction of roll shed per second. Default 0.9.sensitivity?: number— Multiplier on pointer deltas. Default 1.maxSpeed?: number— Ceiling on the roll, units/s. Default Infinity — set one.rolldivides bydt, so one fast flick of a mouse is thousands of units a second and the cursor is gone off the far side before the next frame.bounds?: { x: number; y: number; w: number; h: number }— Rectangle that limits the position. The cursor stops at the edge.


