The pack
sectorShooter
function sectorShooter(): Pack<{ sectorShooter: typeof kit; }>
Compose the sector shooter rules, its two sets, the sample levels and the autopilot under game.sectorShooter.
Helpers
adminLayout
const adminLayout: MapLayout
Level 2 of the facility, "Administration": slower and stranger. A lobby under a skylight with a turning hologram and a curved staircase up to a mezzanine; dark office corridors whose lamps flicker; offices of filing cabinets; a locker room where not every locker holds clothes; a toilet block where something waits in a stall, and the blue key in another; a security room whose screens show the floor and whose override unlocks the director's wing (and puts the lobby's lights out); the yellow key in the director's safe; the roof, where a gunship circles.
classicEpisode
function classicEpisode(): SectorMapData[]
The classic episode's three maps, ready to play: built from the layouts and grids above with the standard kit.
classicSet
function classicSet(): SectorSet
The classic set, in the manner of the 1993 shooters: fists, a pistol, a pump shotgun, a chaingun, a rocket launcher and a plasma gun on bullets, shells, rockets and cells; grunts and gunners who shoot, fiends who throw fireballs, charging brutes, floating eyes and the towering warden; health from bonuses to the soul orb, two armours, keys in three colours, a backpack and five powers; barrels that burst (and set each other off) and crates that splinter. Every sound is named so a game can give each one its voice.
createSectorPilot
function createSectorPilot(): SectorPilot
A fresh autopilot memory.
createSectorRun
function createSectorRun(config: SectorShooterConfig): SectorRun
Start a level. Throws, naming the problem, on a config it cannot play.
facilityEpisode
function facilityEpisode(): SectorMapData[]
The facility: the Refinery and Administration, ready to play, built with the standard kit.
gatehouseLayout
const gatehouseLayout: MapLayout
Level 3, "Gatehouse": a courtyard under the sky, floating eyes, and the warden waiting in the keep; killing it opens the way out.
hangarLayout
const hangarLayout: MapLayout
Level 1, "Hangar": a pillared hall with a row of barrels along its north wall, stairs to a loft holding the red key, a slime pit, a secret closet and the exit.
modernArena
function modernArena(): SectorMapData
The modern arena, ready to play.
modernSet
function modernSet(): SectorSet
The modern set, the same rules tuned fast: double jumps, an air dash and air control, a blade, a revolver, a wide scattergun, a nailgun and a launcher whose blasts carry the player (rocket jumps); drones that fly and shoot, husks that rush, sentries that snipe from afar and hulks that soak up fire; small health and armour shards; fuel barrels whose blasts throw the player high (barrel jumps) and crates; and style scoring that rewards kills in the air, after a dash, by a barrel and in quick chains, ranked D to S.
overdriveGrid
const overdriveGrid: GridMap
The modern arena, "Overdrive": jump pads up to the perches, low gravity over the slime (double-jump up to the core), barrels by the slime, and waves that come as each is cleared.
pumpStationGrid
const pumpStationGrid: GridMap
Level 2, "Pump Station": a grid of walkways over slime with barrels along the slime's edge, a crusher to time, a blue door and an open yard.
refineryLayout
const refineryLayout: MapLayout
Level 1 of the facility, "Refinery": a control room whose zig-zag window looks down on a slime courtyard; a catwalk along a slime canal whose bridge lies sunk until the computer room raises it; the blue key on an island in the slime; an octagonal pump hub with a window into the computer room; a lift to a sniper balcony over an outdoor yard where a ship passes over and a searchlight sweeps; the red key on a rock shelf; a secret server closet behind glass.
sectorAutopilot
function sectorAutopilot(run: SectorRun, pilot: SectorPilot, dt: number, speed = 7, turn = 6): SectorInput
The controls a simple player would give this step: shoot the nearest enemy it can see with its best weapon, top up
health and ammunition when short, fetch a key when the way to the exit is locked, press switches, open doors (hinged
ones too), open cabinets for what they hold (supplies, a gun, a key), and go to the exit (a switch to use or a place to
enter). speed is units per second (default 7), turn radians per second (default 6). It sets off barrels next to
enemies, holds its fire when one close by is in the way, and waits for a crusher to rise before going under it. For
attract modes, demos and tests that prove a level can be finished; it does not jump, dash, ride lifts or aim for jump
pads.
sectorLevel
function sectorLevel(map: SectorMapData, set: SectorSet, options: { skill?: SectorSkill; par?: number; seed?: number; mutators?: SectorMutators } = {}): SectorShooterConfig
A level config from a map, a set and a skill: the map's things filtered to the skill, damage to the player and
ammunition scaled, and on nightmare the dead rising again; mutators change gravity or make every hit lethal. Every
field can be changed afterwards.
Classes
SectorRun
class SectorRun
One level of a 2.5D shooter in the manner of the 1993 games and their modern descendants, on sector-map (the map),
fps-weapons (the arsenal) and fps-enemies (the AI). A step moves the player with steps, falls, gravity, and
optionally jumps, double jumps, dashes and air control; uses doors, lifts and switches; fires, resolving pellets
against enemies (aiming up and down by itself) and walls (setting off shoot triggers), and flying projectiles both
ways in 3D with splash damage and knockback; runs the map's movers, crushers, damaging floors and triggers
(messages, spawns, teleports, exits); runs the enemies with infighting and respawning; and collects health, armour,
ammunition, weapons, keys, backpacks and powers. Props stand in the way and break when shot; some burst, hurting
and throwing what is near and setting off their neighbours in a ripple. The map's jump pads throw the player, its
low-gravity rooms let them float, and its conveyors and winds push them; mutators change gravity everywhere or make
every hit lethal. The map's fixtures play too: use opens hinged doors and cabinets (whose contents come out to be
picked up, or jump out), and glass stops shots until shots and blasts break it, enemies' shots included. Health and
armour share damage; a death restarts the level, or costs a life; the exit gives the tally. Get one from
createSectorRun.
map!: SectorMap— The map, live.arsenal!: Arsenal— The arsenal, live.horde!: Horde— The enemies, live.bosses!: Bosses— The boss fights, live:bosses.list()for a health bar.constructor(config: SectorShooterConfig)get status(): 'playing' | 'dead' | 'complete' | 'over'—'playing','dead'(waiting forrespawn),'complete'(an exit was used) or'over'(no lives left).get player(): SectorPlayer— The player (a copy).get stats(): SectorStats— The level's numbers so far.get tally(): SectorTally | null— The last finished level's tally, or null.pickups(): SectorItem[]— The pickups lying about.flying(): SectorProjectile[]— What is flying.decorations(): { type: string; x: number; y: number; z: number; yaw: number }[]— Things on the map that are none of the start, enemies, pickups and props: scenery for the presenter (lamps, trees).props(): SectorProp[]— The props: whole, fusing and broken (a presenter draws the wreck).item(id: string): ShooterItemDef | undefined— An item kind.prop(id: string): ShooterPropDef | undefined— A prop kind.get mutators(): Required<SectorMutators>— The mutators in force (a copy).mutate(m: SectorMutators): void— Change mutators during a level: the fields given replace those in force. Throws on a value out of range.get eye(): number— The player's eye height in the world.respawn(): boolean— After a death with the level to try again: the level starts over with the starting arsenal and health. Lives and score are kept.nextLevel(config: SectorShooterConfig): boolean— After an exit: go on to another level keeping health, armour, the arsenal, lives and score; keys and powers stay behind. Returns false unless the level is complete; throws on a config it cannot play.step(dt: number, input: SectorInput): SectorEvent[]— Run the level on bydtseconds (0 to 0.25) with this step's controls.
Types
PropBurst
interface PropBurst
A prop's burst: splash damage, knockback and a sound, a moment after it breaks.
radius: number— Reach in units, above 0 and at most 64.damage: [number, number]— Damage at its heart,[min, max]from 0 to 10000, falling to nothing atradius.knock?: number— How hard it throws the player, units per second at its heart; default 0.delay?: number— Seconds from breaking to bursting, 0 to 10; default 0.2. A row of props that burst ripples one link each delay.sound?: string— A sound by name as it bursts.
SectorEvent
type SectorEvent = | { type: 'shot'; weapon: string; pellets: number } | { type: 'hit'; enemy: string; damage: number; dist: number } | { type: 'wall-hit'; x: number; y: number; z: number } | { type: 'hurt'; damage: number; from: string; armor: number } | { type: 'picked-up'; item: string; id: string } | { type: 'use'; result: string } | { type: 'locked'; key: string } | { type: 'secret'; sector: string } | { type: 'message'; text: string } | { type: 'teleport'; x: number; z: number } | { type: 'exit'; tally: SectorTally } | { type: 'died'; lives: number | null } | { type: 'jump' } | { type: 'dash' } | { type: 'land'; speed: number } | { type: 'launch'; sector: string; x: number; y: number; z: number } | { type: 'prop-hit'; id: string; prop: string; damage: number; x: number; y: number; z: number } | { type: 'prop-broke'; id: string; prop: string; by: string } | { type: 'burst'; x: number; y: number; z: number; radius: number; from: string; prop?: string } | { type: 'style'; points: number; chain: number; rank: string } | { type: 'event'; name: string; data?: unknown } | { type: 'enemy'; event: HordeEvent } | { type: 'boss'; event: BossEvent } | { type: 'map'; event: MapEvent } | { type: 'sound'; sound: string; x: number; y: number; z: number }
What happened in a step.
SectorInput
interface SectorInput
The controls for one step.
move: { x: number; z: number }— Where the player wants to go this step, in units (the run handles collision, steps and air control).yaw: number— Facing, the engine's yaw.pitch?: number— Looking up or down (positive down); aims shots that do not lock on. Default 0.fire: boolean— Trigger held.use?: boolean— Use pressed.jump?: boolean— Jump pressed.dash?: boolean— Dash pressed.select?: string— A weapon to raise.cycle?: 1 | -1— Step through weapons.
SectorItem
interface SectorItem
A pickup lying in the level.
id: string— Unique id.item: string— Its item kind.x: number— Position.y: number— Height of its base.z: number— Position.
SectorMutators
interface SectorMutators
Game-wide rule changes, like an arena shooter's mutators.
gravity?: number— Multiplies gravity everywhere (falls, jumps and jump pads' arcs), 0 to 10; default 1. Below 1 is a low-gravity game.instagib?: boolean— Every hit the player lands is lethal: enemies die at once (a boss only while it can be hurt), and props that can break, break. Default false.
SectorPilot
interface SectorPilot
An autopilot's memory between steps.
route: { x: number; z: number; door?: string }[]— The points it is walking to, next first.replan: number— Seconds until it plans again.goal: string— What it is heading for:'exit','switch','key','supplies','cabinet','enemy'or''.press: { x: number; z: number } | null— A wall to press use on when it gets there, as its midpoint, or null.held: boolean— Whether it held the trigger last step.stuck: number— Seconds without progress.last: { x: number; z: number }— Where it was a moment ago.useWait: number— Seconds before it presses use again.pressed: string[]— Switches it has pressed and cabinets it has opened, by id, so it tries others.target: string— The switch or cabinet it is on its way to press, by id, or ''.engaged: { id: string; time: number; hp: number }— The enemy it is shooting at, and for how long.ignore: Record<string, number>— Enemies it has given up shooting at for now (out of reach or out of range), with seconds left.
SectorPlayer
interface SectorPlayer
The player.
x: number— Position.y: number— Feet height.z: number— Position.yaw: number— The engine's yaw.vx: number— Velocity, units per second.vy: number— Velocity, units per second (up is positive).vz: number— Velocity, units per second.onGround: boolean— Standing on a floor.health: number— Health.armor: number— Armour.absorb: number— The share of damage armour takes.keys: string[]— Keys held.powers: Record<string, number>— Powers and seconds left (Infinity for the level).alive: boolean— Alive.lives: number | null— Lives left, or null without lives.score: number— Score.style: number— The style meter, 0 to 100.rank: string— The style rank:'D','C','B','A'or'S', or '' with no style.
SectorPlayerDef
interface SectorPlayerDef
How the player moves and what they can take.
health?: number— Starting health; default 100.maxHealth?: number— Health most pickups fill to; default 100.armor?: number— Starting armour; default 0.maxArmor?: number— Most armour; default 200.radius?: number— Body radius; default 0.4.height?: number— Body height; default 1.8.eye?: number— Eye height above the feet; default 1.6.step?: number— Highest step walked up; default 0.6.gravity?: number— Gravity, units per second squared; default 20.jump?: number— Jump speed, units per second; default 0 (no jumping, as in the classic games).jumps?: number— Jumps before landing again (2 is a double jump); default 1.airControl?: number— How much the player steers in the air, 0 (none) to 1 (as on the ground); default 1.dash?: { speed: number; time: number; cooldown: number }— A dash:speedunits per second fortimeseconds, thencooldownseconds before the next; default none.lives?: number— Lives; default none (a death restarts the level, as in the classic games).
SectorProjectile
interface SectorProjectile
Something flying.
id: string— Unique id.from: string—'player'or an enemy's id.x: number— Position.y: number— Height.z: number— Position.yaw: number— The engine's yaw.pitch: number— Pitch, positive down.speed: number— Units per second.radius: number— Radius.damage: number— Damage on a direct hit.splash?: { radius: number; damage: [number, number]; knock?: number }— Blast.weapon?: string— The weapon that fired it (player shots).
SectorProp
interface SectorProp
A prop standing in the level, or what is left of it.
id: string— Unique id.prop: string— Its prop kind.x: number— Position.y: number— Height of its base.z: number— Position.yaw: number— The engine's yaw.hp: number— Hit points left; Infinity for one that cannot break.state: 'whole' | 'fusing' | 'broken'—'whole';'fusing'(broken, about to burst: it still stands in the way);'broken'(a wreck, in no one's way).fuse: number— Seconds until a fusing prop bursts.by: string— Who broke it:'player'or an enemy's id; '' while whole.
SectorSet
interface SectorSet
A set: everything a level needs besides its map.
weapons: ArsenalConfig— The arsenal.enemies: EnemyDef[]— Enemy kinds.items: ShooterItemDef[]— Pickups.props?: ShooterPropDef[]— Props the maps place: barrels that burst, crates that break.player: SectorPlayerDef— The player.style?: StyleDef— Style scoring, if the set has it.bosses?: BossDef[]— Boss fights for some of its enemy kinds.sounds: SectorSounds— Sounds.
SectorShooterConfig
interface SectorShooterConfig
One level.
map: SectorMapData— The map. Its things are'start', enemy kinds, item ids and prop ids (anything else is scenery).weapons: ArsenalConfig— The arsenal.enemies: EnemyDef[]— Enemy kinds.items: ShooterItemDef[]— Pickups.props?: ShooterPropDef[]— Props: what the map's barrels, crates and the like are. Default none.mutators?: SectorMutators— Game-wide rule changes; default none.SectorRun.mutatechanges them during a level.player?: SectorPlayerDef— The player.skill?: string— The skill level the map's things are filtered by (MapThing.skill); default all things.damageScale?: number— Multiplies damage to the player from enemies and bursting props, 0 to 10; default 1.ammoScale?: number— Multiplies ammunition picked up, 0 to 10; default 1.respawn?: number— Dead enemies rise again after this many seconds; default never.maxAttackers?: number— Most enemies aiming at once; default 3.infighting?: boolean— Enemies turn on another kind that hurts them; default true.par?: number— Par time in seconds; default 0, none.style?: StyleDef— Style scoring; default none.bosses?: BossDef[]— Boss fights for enemy kinds (fps-bosses); every enemy of a boss kind fights as that boss. Default none.sounds?: SectorSounds— Sounds.seed?: number— Seed.
SectorSkill
type SectorSkill = 'easy' | 'normal' | 'hard' | 'nightmare'
How hard: 'easy' halves damage to the player and doubles ammunition; 'nightmare' brings the dead back. Things are filtered by the same name.
SectorSounds
interface SectorSounds
Sounds the run asks for, by name, as sound events (enemies, weapons, pickups and the map's movers have their own).
hurt?: string— The player is hurt.death?: string— The player dies.locked?: string— A locked door or switch refuses.nothing?: string— Use with nothing there.secret?: string— A secret found.exit?: string— The exit.jump?: string— A jump.dash?: string— A dash.land?: string— Landing from a fall.teleport?: string— A teleport.burst?: string— A projectile bursting.
SectorStats
interface SectorStats
A level's numbers so far.
kills: number— Enemies killed.killsTotal: number— Enemies at the start.items: number— Counted items taken.itemsTotal: number— Counted items at the start.secrets: number— Secrets found.secretsTotal: number— Secrets on the map.time: number— Seconds.par: number— Par.
SectorTally
interface SectorTally
A finished level's tally.
kills: number— Kills, whole per cent (100 with none).items: number— Items, whole per cent.secrets: number— Secrets, whole per cent.time: number— Seconds, whole.par: number— Par.exit: 'normal' | 'secret'— The exit used.
ShooterItemDef
interface ShooterItemDef
What a pickup does.
id: string— Unique id; the map places it by this name.kind: 'health' | 'armor' | 'ammo' | 'weapon' | 'key' | 'backpack' | 'power' | 'treasure'— -'health':amounthealth, taken only belowupTo(default the player'smaxHealth). -'armor':amountarmour up toupTo(defaultmaxArmor), absorbingabsorbof damage (keeps the old rate when 0). -'ammo':amountofammo. -'weapon':weapon, withamountof its ammunition. -'key': the keykey. -'backpack': doubles every ammunition maximum and givesamountof each type. -'power': the powerpowerfordurationseconds ('invulnerable','berserk','radsuit','invisible','light','map', or the game's own). -'treasure': points only.amount?: number— How much.upTo?: number— Health and armour: the most it fills to.absorb?: number— Armour: the share of damage it takes, 0 to 1.ammo?: string— Ammo: the type.weapon?: string— Weapon: its id.key?: string— Key: its name.power?: string— Power: its name.duration?: number— Power: seconds it lasts; 0 for the rest of the level. Default 30.score?: number— Points; default 0.counts?: boolean— Counts toward the tally's items share. Default false.sound?: string— A sound to play when picked up.
ShooterPropDef
interface ShooterPropDef
A kind of prop: a thing on the map that stands in the way and can be shot, like a barrel that bursts or a crate that breaks. Hitscan pellets, projectiles, splash and other props' bursts damage it.
id: string— Unique id, unlike any item's or enemy's; the map places it by this name.hp?: number— Hit points, above 0; default none: it cannot be broken (a statue, a stack of girders).radius?: number— Radius in units, 0.05 to 4; default 0.4.height?: number— Height in units, 0.05 to 20; default 1. A body that gets on top stands on it.solid?: boolean— Bodies cannot pass through it and shots stop at it; default true. A prop that is not solid can still be shot while it has hit points.burst?: PropBurst— What it does when broken: bursts after a delay with splash damage and knockback. Default none.drops?: string[]— Item ids it leaves when broken.score?: number— Points to the player for breaking it; default 0.sound?: string— A sound by name as it breaks.
StyleDef
interface StyleDef
Points for killing with style (the modern preset): kills in the air and just after a dash count more, and kills close together chain.
kill?: number— Points for a kill; default 100.air?: number— Multiplier for a kill while in the air; default 2.dash?: number— Multiplier for a kill within half a second of a dash; default 1.5.chain?: number— Seconds in which kills chain; default 2.prop?: number— Multiplier for a kill by a prop the player set off (a barrel's burst); default 1.5.decay?: number— How fast the style meter (0 to 100) drains, per second; default 8.


