The pack
fpsBosses
function fpsBosses(): Pack<{ fpsBosses: typeof kit; }>
Compose boss fights under game.fpsBosses.
Helpers
createBosses
function createBosses(defs: BossDef[], horde: Horde, world: EnemyWorld, seed = 1): Bosses
Boss fights for the bosses of defs among horde's enemies, on world (the map's enemyWorld()). Throws on a def it cannot fight.
Classes
Bosses
class Bosses
Boss fights for first-person shooters, on top of fps-enemies: an enemy of a boss kind is taken over (the horde
keeps its health, pain, death and sounds) and fought in phases set by its health. Each phase has a way of moving
(holding, chasing, strafing, keeping away) and a pattern of attacks with warning wind-ups: fanned volleys, rings of
shots, aimed bursts, charges, ground slams, summons and teleports. A new phase can bring invulnerability, healing and
reinforcements; phases can carry armour. Projectiles, summons and damage come back as events for the game to apply,
as the horde's do, so it works on any map an EnemyWorld describes. Get one from createBosses.
constructor(defs: BossDef[], horde: Horde, world: EnemyWorld, seed = 1)adopt(id: string): boolean— Take over an enemy if its kind is a boss: returns true when it now fights as one. Call it for every enemy placed.list(): BossState[]— The bosses, for a health bar.has(id: string): boolean— Whether an enemy is a boss.scale(id: string, damage: number): number— The damage a boss actually takes fromdamage: none while invulnerable, less with its phase's armour. Other enemies take it all.tick(dt: number, player: BossTarget): BossEvent[]— Run the bosses on bydtseconds (0 to 1) against the player.summoned(boss: string, enemy: string): void— Tell the bosses an enemy they summoned now stands (the game spawned it), somaxcounts it.get state(): BossesState— A copy of the state.restore(state: BossesState): void— Put back a saved state.
Types
BossAttack
type BossAttack = | { kind: 'volley'; count: number; spread: number; projectile: BossProjectile; damage: [number, number]; repeat?: number; interval?: number; windup?: number; sound?: string } | { kind: 'ring'; count: number; projectile: BossProjectile; damage: [number, number]; repeat?: number; interval?: number; windup?: number; sound?: string } | { kind: 'burst'; shots: number; interval: number; projectile: BossProjectile; damage: [number, number]; spread?: number; windup?: number; sound?: string } | { kind: 'charge'; speed: number; time: number; damage: [number, number]; windup?: number; sound?: string } | { kind: 'slam'; radius: number; damage: [number, number]; knock?: number; windup?: number; sound?: string } | { kind: 'summon'; enemy: string; count: number; radius?: number; max?: number; windup?: number; sound?: string } | { kind: 'teleport'; to?: 'near' | 'behind' | [number, number][]; windup?: number; sound?: string }
One attack in a boss's pattern. Every attack has a windup (seconds of warning, default 0.6) and may name a sound.
volley:countshots fanned acrossspreadradians at the player,repeattimesintervalapart.ring:countshots all round,repeattimesintervalapart (each ring turned half a gap).burst:shotsaimed shotsintervalapart, scattered byspread.charge: a rush at the player atspeedfor up totimeseconds, hurting on contact.slam: a blow to the ground hurting a player withinradiuswho is on the ground, and knocking them back.summon:countenemies of kindenemyaround it withinradius, while fewer thanmaxit summoned stand.teleport: to a random point near the player, behind the player, or one ofto's points.
BossDef
interface BossDef
A boss: an enemy kind that a horde already knows (its hit points, size and sprite), and how it fights.
id: string— The enemy kind it drives. Every enemy of this kind on the map becomes this boss.name?: string— Its name, for the health bar; default the id.phases: BossPhase[]— Its phases, first to last.wake?: number— How near the player must come, in sight, to wake it; default 30. It wakes when hurt too.sounds?: { wake?: string }— Sounds: as it wakes.
BossesState
interface BossesState
Everything that changes; a save holds exactly this.
bosses: Fight[]— Each boss's fight.rng: number[]— The random stream.
BossEvent
type BossEvent = | { type: 'boss-wake'; id: string; name: string } | { type: 'boss-phase'; id: string; phase: number } | { type: 'boss-windup'; id: string; attack: string; time: number } | { type: 'projectile'; id: string; x: number; y: number; z: number; yaw: number; pitch: number; speed: number; radius: number; damage: number; splash?: { radius: number; damage: [number, number]; knock?: number } } | { type: 'slam'; id: string; x: number; z: number; radius: number; damage: number; knock: number } | { type: 'charge-hit'; id: string; damage: number } | { type: 'summon'; id: string; enemy: string; x: number; z: number; yaw: number } | { type: 'teleport'; id: string; x: number; z: number } | { type: 'boss-died'; id: string } | { type: 'sound'; id: string; sound: string; x: number; y: number; z: number }
What the bosses did. Projectiles, summons and damage are the game's to apply.
BossPhase
interface BossPhase
A stretch of a fight: how the boss moves and what it does until its health falls to until.
until?: number— The share of its health (0 to 1) at which the next phase begins; the last phase has none.move?: 'hold' | 'chase' | 'strafe' | 'keep-away'— How it moves between attacks:'hold'its ground,'chase'the player,'strafe'round them, or'keep-away'atrange. Default'chase'.speed?: number— Movement speed, units per second; default 2.5.range?: number— Distance it keeps when strafing or keeping away; default 8.attacks: BossAttack[]— Its attacks.order?: 'cycle' | 'random'—'cycle'through the attacks in order (default) or pick at'random'.pause?: [number, number]— Seconds between attacks,[min, max]; default [1, 2].armor?: number— Damage it takes is multiplied by this, 0 (none) to 1 (all); default 1.enter?: { sound?: string; invulnerable?: number; summon?: { enemy: string; count: number; radius?: number }; heal?: number }— What happens as the phase begins: asound, secondsinvulnerable, asummon, a share of health healed.
BossProjectile
interface BossProjectile
A boss's shot: speed and radius in world units, and an optional blast.
speed: number— Units per second.radius: number— Radius.splash?: { radius: number; damage: [number, number]; knock?: number }— A blast where it lands.
BossState
interface BossState
A boss now.
id: string— The enemy's id.def: string— Its kind.name: string— Its name.hp: number— Hit points left.max: number— Hit points at full health.phase: number— Phase index.awake: boolean— Awake and fighting.dead: boolean— Dead.stage: 'idle' | 'windup' | 'attack'—'idle','windup'or'attack'.attack: string— The attack under way, by kind, or ''.invulnerable: number— Seconds of invulnerability left.
BossTarget
interface BossTarget
The player as a boss sees them.
x: number— Position.z: number— Position.y?: number— Feet height; default 0.height?: number— Height; default 1.6.yaw?: number— Facing (for teleporting behind them); default 0.grounded?: boolean— Standing on the ground (a slam only hurts the grounded); default true.alive: boolean— Alive.


