The pack
bubbleTrap
function bubbleTrap(): Pack<{ bubbleTrap: typeof api }>
Install the bubble-trap API (game.bubbleTrap).
Helpers
bubbleSettings
function bubbleSettings(o: Partial<BubbleSettings> = {}): BubbleSettings
Defaults: Bubble Bobble on 8 px tiles.
createBubbles
function createBubbles(o: BubblesOptions): BubbleField
Create a bubble field over a walker world and an enemy director.
createCurrents
function createCurrents(map: Pick<PlatformMap, "cols" | "rows" | "col" | "row"> & { wrapY?: boolean }, rows?: readonly string[] | null, fill: CurrentChar = "^"): CurrentGrid
Build a current grid for a map. rows gives one string per map row ('^' '>' 'v' '<' '.'); missing rows and
characters fall back to fill (default '^': everything floats up, the common case).
createElements
function createElements(o: { world: WalkerWorld; enemies: EnemyDirector; settings?: Partial<ElementSettings>; canKill?: (enemyId: string, kind: string) => boolean }): Elements
Create the elemental effects. canKill filters which enemies they kill (default every live enemy).
decodeCurrents
function decodeCurrents(rle: string, cols: number, rows: number): string[]
Expand a run-length encoded current grid ("v32>14..." : a direction followed by an optional count) into rows.
elementSettings
function elementSettings(o: Partial<ElementSettings> = {}): ElementSettings
Defaults: Bubble Bobble (estimates from play).
Types
Bolt
interface Bolt
A lightning bolt.
id: string— Stable id.x: number— Centre x.y: number— Centre y.dir: -1 | 1— Direction.age: number— Seconds alive.by: string— Who released it.
Bubble
interface Bubble
A bubble, as read by renderers and rules.
id: string— Stable id ('bubble-N'). Its surface id isbubble:<id>.kind: BubbleKind— What it holds.x: number— Centre x.y: number— Centre y.vx: number— Velocity x, units per second.vy: number— Velocity y, units per second.r: number— Radius.phase: "shot" | "rise" | "float"— 'shot' while travelling from the blower, 'rise' while floating straight up into the play area from below (special bubbles entering through a gap), then 'float' on the currents.riseTo: number— Height a rising bubble climbs to before the currents take it.age: number— Seconds alive.life: number— Seconds until it pops by itself (empty, special) or its enemy escapes (enemy).warn: number— 0 until the warning starts, rising to 1 at the pop or escape.enemy: string— Trapped enemy id ('' none).enemyKind: string— Trapped enemy kind ('' none).letter: string— Letter carried by a letter bubble ('' none).owner: string— Body that blew it ('' for spawned bubbles).dir: -1 | 1— Travel direction of a shot.travelled: number— Distance travelled as a shot.range: number— Distance a shot travels before floating.speed: number— Shot speed, units per second.snapped: boolean— Moved discontinuously this step (wrapped): renderers should snap.
BubbleEvent
type BubbleEvent = | { kind: "blown"; id: string; owner: string; x: number; y: number } | { kind: "spawned"; id: string; bubble: BubbleKind; x: number; y: number } | { kind: "captured"; id: string; enemy: string; enemyKind: string; x: number; y: number } | { kind: "escaped"; id: string; enemy: string; enemyKind: string; x: number; y: number } | { kind: "popped"; id: string; bubble: BubbleKind; by: string; x: number; y: number; enemy: string; letter: string; chain: number } | { kind: "expired"; id: string; bubble: BubbleKind; x: number; y: number } | { kind: "wall-pop"; id: string; owner: string; x: number; y: number } | { kind: "wrapped"; id: string }
Bubble events from drain().
BubbleField
interface BubbleField extends SurfaceProvider
The bubble field: a SurfaceProvider plus blowing, trapping, popping and queries.
readonly settings: Readonly<BubbleSettings>— Settings in force (mutable throughconfigure).readonly bubbles: Bubble[]— Copies of every bubble.bubble(id: string): Bubble | null— One bubble or null.count(kind?: BubbleKind): number— Live bubbles, optionally of one kind.configure(o: Partial<BubbleSettings>): void— Change settings (per round: float speed, trap time ...).setCurrents(currents: readonly string[] | CurrentGrid | null): void— Replace the currents.readonly currents: CurrentGrid— The current grid.blow(bodyId: string, o?: { range?: number; speed?: number }): string | null— Blow a bubble from a body (in front of it, in its facing direction). Returns the id, or null when full.spawn(kind: BubbleKind, x: number, y: number, o?: { letter?: string; vx?: number; vy?: number; riseTo?: number }): string | null— Spawn a floating bubble (special, letter, empty). WithriseToit first rises straight up to that height, ignoring the currents (entering from below the floor). Returns the id, or null when full.trap(enemyId: string, o?: { force?: boolean; life?: number }): string | null— Trap an enemy in a new floating bubble at its position (a beaten boss, a magic item).forceignorescanCapture;lifeoverrides the escape time. Returns the bubble id or null.pop(id: string, by?: string): PopResult | null— Pop a bubble and every bubble touching it (a chain). Trapped enemies are removed from the director.touching(bodyId: string, inset?: number): string[]— Bubbles a body touches (popping contact), excluding bubbles it has just bounced on and its own fresh shots.noteBounce(bodyId: string, bubbleId: string): void— Record that a body bounced on a bubble (it is not popped by that body for a moment).release(id: string): void— Release a trapped enemy now (it escapes).clear(release?: boolean): void— Remove every bubble (trapped enemies are released whenreleaseis true, otherwise removed).drain(): BubbleEvent[]— Events since the last drain.snapshot(): BubblesSnapshot— Serialisable state.restore(s: BubblesSnapshot): void— Restore a snapshot (restore the world and director too).
BubbleKind
type BubbleKind = "empty" | "enemy" | "water" | "fire" | "lightning" | "letter"
What a bubble holds.
BubbleSettings
interface BubbleSettings
Bubble rules. Distances in world units, times in seconds.
radius: number— Bubble radius. Default 8 (a 16 px bubble on 8 px tiles).shotSpeed: number— Speed of a freshly blown bubble, units per second. Default 192.shotRange: number— Distance a blown bubble travels before it starts to float. Default 40.floatSpeed: number— Floating speed along the currents, units per second. Default 18.emptyLife: number— Seconds an empty bubble floats before it pops by itself (the lastwarnseconds flash). Default 14.trapTime: number— Seconds a trapped enemy stays before it starts to break out (thenwarnseconds of warning). Default 20.warn: number— Warning seconds before a trapped enemy escapes (its bubble changes colour). Default 3.emptyWarn: number— Warning seconds before an empty, special or letter bubble pops by itself (a short fast flash). Default 1.specialLife: number— Seconds special and letter bubbles float before they pop by themselves. Default 24.riseSpeed: number— Speed of a bubble rising in from below a floor gap (the 'rise' phase), units per second. Default 60.bounce: number— Launch speed for a rider that lands on a bubble while holding jump. Default 230. 0 disables riding.chain: number— Bubbles closer than this many diameters pop together. Default 1.15.separation: number— Speed at which overlapping floating bubbles push apart, units per second. Default 36.max: number— Most bubbles at once (resource bound; blowing more fails). Default 64.wallPop: boolean— A blown bubble that hits a wall straight away pops at once (Bubble Bobble). Default true.wrap: boolean— Bubbles that float out of a gap in the top or bottom re-enter at the other side when the map wraps. Default true.riderTag: string— Only bodies with this tag ride bubbles. Default 'player' ('' for everyone).captureWhileFloating: boolean— Floating bubbles trap enemies too (not in Bubble Bobble: only a blown bubble traps). Default false.
BubblesOptions
interface BubblesOptions
Options for createBubbles.
world: WalkerWorld— The walker world (bodies are trapped, frozen and released through it).enemies: EnemyDirector— The enemy director (trapped enemies are suspended, resumed and removed through it).settings?: Partial<BubbleSettings>— Rules (partial; defaults are Bubble Bobble's).currents?: readonly string[] | CurrentGrid | null— Air currents as strings over the map ('^' '>' 'v' '<' '.'); default everything up.canCapture?: (enemyId: string, kind: string) => boolean— Which enemies a bubble may trap (default: any active enemy).id?: string— Provider id. Default 'bubbles'.attach?: boolean— Add itself to the world as a surface provider. Default true.
BubblesSnapshot
interface BubblesSnapshot
Serialisable state.
bubbles: Bubble[]— Bubbles.seq: number— Id counter.chains: number— Chain counter.settings: BubbleSettings— Settings in force.bounces: [string, string, number][]— Recent bounces (body id, bubble id, seconds left).events: BubbleEvent[]— Undrained events.
CurrentChar
type CurrentChar = "^" | ">" | "v" | "<" | "."
One current direction: up, right, down, left or still.
CurrentGrid
interface CurrentGrid
A direction field laid over a platform map's cells.
readonly cols: number— Columns (the map's).readonly rows: number— Rows (the map's).at(col: number, row: number): CurrentChar— The direction character of a cell (columns wrap; rows wrap when the map wraps vertically, otherwise outside rows are '^').dirAt(x: number, y: number): [number, number]— Unit direction at a world point: [dx, dy] with each in {-1, 0, 1}.toRows(): string[]— The grid as strings (for renderers and debugging).
ElementEvent
type ElementEvent = | { kind: "element-kill"; element: "water" | "fire" | "lightning"; enemy: string; enemyKind: string; x: number; y: number; by: string } | { kind: "ride-start"; player: string; flow: string } | { kind: "ride-end"; player: string; x: number; feet: number; escaped: boolean } | { kind: "burned"; player: string; fire: string } | { kind: "flow-end"; flow: string }
Element events.
Elements
interface Elements
Elemental effects over a walker world.
readonly settings: Readonly<ElementSettings>— Settings.configure(o: Partial<ElementSettings>): void— Change settings (applies at once).readonly flows: WaterFlow[]— Copies of the flows.readonly fires: FireDrop[]— Copies of the fires.readonly bolts: Bolt[]— Copies of the bolts.water(x: number, y: number, dir: -1 | 1, by?: string): string— Start a water flow at a point, running indironce it reaches a floor.fire(x: number, y: number, by?: string): string— Drop a fireball from a point.lightning(x: number, y: number, dir: -1 | 1, by?: string): string— Release a lightning bolt from a point.readonly active: boolean— Whether any effect is running.step(dt: number): void— Advance one step (call after the walker world steps).owner(id: string): string— Who released an effect ('' unknown).release(bodyId: string): boolean— Set a carried player down now (it died, or the rules need it free). Returns whether it was riding.clear(): void— Stop everything (riders are released).drain(): ElementEvent[]— Events since the last drain.snapshot(): ElementsSnapshot— Serialisable state.restore(s: ElementsSnapshot): void— Restore.
ElementSettings
interface ElementSettings
Element rules. Speeds in units per second, times in seconds.
waterSpeed: number— Water flow speed. Default 110.waterLength: number— Trail points kept behind the head (spacingwaterStep). Default 24.waterStep: number— Spacing of trail points. Default 4.waterLife: number— Optional bound: seconds a flow runs before it dries up (0: no limit). Default 0: a flow ends only by draining out of the bottom of the map, as Bubble Bobble's water does. For games that want a shorter or bounded flood.waterTurns: number— Optional bound: wall turns before the flow drains away (0: no limit). Default 0.fireFall: number— Fireball drop speed. Default 160.fireSpread: number— Cells the fire spreads either side along the floor it lands on. Default 3.fireBurn: number— Seconds the fire burns. Default 4.fireStun: number— Seconds a player who touches the fire is stunned (0: no effect). Default 1.2.boltSpeed: number— Lightning bolt speed. Default 260.boltSize: number— Lightning bolt half size. Default 7.playerTag: string— Player tag (riders and the stunned). Default 'player'.waterEscape: "none" | "jump"— What a player caught in a water flow can do: 'none' (carried until the flow drains, as the sources describe Bubble Bobble's water) or 'jump' (a fresh jump press hops out of the stream: a house rule). Default 'none'.waterHop: number— Upward speed of a hop out of the water ('jump'), units per second. Default 220.
ElementsSnapshot
interface ElementsSnapshot
Serialisable state.
flows: WaterFlow[]— Flows.fires: FireDrop[]— Fires.bolts: Bolt[]— Bolts.seq: number— Id counter.owners: [string, string][]— Who released each element.immune: [string, number][]— Players briefly immune to being caught again after hopping out.settings: ElementSettings— Settings in force.events: ElementEvent[]— Undrained events.
FireDrop
interface FireDrop
A dropped fireball and its burning patch.
id: string— Stable id.x: number— Drop x.y: number— Drop y (centre while falling, floor line once landed).landed: boolean— Landed and burning.cells: number[]— Burning cells: centre x of each cell on the floor liney.age: number— Seconds burning.burned: string[]— Players already stunned by it.
PoppedEnemy
interface PoppedEnemy
A popped enemy, for the rules (score, fruit).
id: string— Enemy id (already removed from the director).kind: string— Enemy kind.x: number— Where its bubble was.y: number— Where its bubble was.
PopResult
interface PopResult
The result of one pop (a chain).
bubbles: Bubble[]— Every bubble popped together (copies, before removal).enemies: PoppedEnemy[]— Trapped enemies in the chain, in pop order.by: string— Who popped it ('' for none: expiry, effects).
WaterFlow
interface WaterFlow
A water flow.
id: string— Stable id.x: number— Head x.feet: number— Head feet (the floor line it runs on).dir: -1 | 1— Direction along floors.falling: boolean— Falling down a gap.trail: [number, number][]— Trail points [x, feet], oldest first.age: number— Seconds alive.turns: number— Wall turns so far.ended: boolean— The head has stopped (drained or dried up); the trail drains away.riders: string[]— Player bodies riding it.held: string[]— Riders still holding the jump they held when caught (a new press is needed to hop out).


