Back to Freeze Frame Response
SOURCE / PINNED RELEASE

Made of little things.

Freeze Frame Response

Release
3e45de6048a3…
Author-recorded commit
d04885dad630…
License
LICENSE
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-ba52e30d5f2

Archive hash verified: e747ab4f9e35a11f…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

src/levels.ts
export type Material = 'masonry' | 'furniture' | 'glass';
export type DeviceType = 'trampoline' | 'funnel' | 'cushion';
export type BodyKind = 'pedestrian' | 'debris' | 'volatile';

export interface BodySeed {
  kind: BodyKind;
  material?: Material;
  x: number;
  y: number;
  vx: number;
  vy: number;
  radius: number;
}

export interface MattressTarget {
  id: string;
  x1: number;
  x2: number;
  label: string;
}

export interface BinTarget {
  id: string;
  material: Material;
  x1: number;
  x2: number;
}

export interface Kit {
  trampoline: number;
  funnel: number;
  cushion: number;
}

export interface Level {
  id: string;
  name: string;
  tagline: string;
  briefing: string;
  hint: string;
  freezeSeconds: number;
  source: { x: number; y: number; power: number };
  bodies: BodySeed[];
  mattresses: MattressTarget[];
  bins: BinTarget[];
  kit: Kit;
}

export const GRAVITY = 700;
export const GROUND_Y = 520;
export const WORLD_WIDTH = 960;
export const WORLD_HEIGHT = 600;

const ped = (x: number, y: number, vx: number, vy: number): BodySeed => ({
  kind: 'pedestrian',
  x,
  y,
  vx,
  vy,
  radius: 10,
});

const debris = (
  material: Material,
  x: number,
  y: number,
  vx: number,
  vy: number,
): BodySeed => ({
  kind: 'debris',
  material,
  x,
  y,
  vx,
  vy,
  radius: material === 'glass' ? 7 : material === 'furniture' ? 9 : 8,
});

const gas = (x: number, y: number, vx: number, vy: number): BodySeed => ({
  kind: 'volatile',
  x,
  y,
  vx,
  vy,
  radius: 11,
});

export const LEVELS: Level[] = [
  {
    id: 'kettle',
    name: 'The Kettle Incident',
    tagline: 'Flat 2B, 07:12. A domestic appliance has overreached.',
    briefing:
      'A pressure cooker has removed itself from Flat 2B. One resident and four pieces of municipal debris are airborne. Mattress on the left, recycling on the right. You have four items of equipment and the authority to use them.',
    hint: 'A trampoline under the resident will move them toward the mattress. A tilted funnel can nudge stubborn debris.',
    freezeSeconds: 0.8,
    source: { x: 480, y: 340, power: 300 },
    bodies: [
      ped(450, 320, -150, -300),
      debris('masonry', 500, 320, 150, -260),
      debris('masonry', 490, 330, 90, -280),
      debris('furniture', 505, 330, 230, -210),
      debris('glass', 520, 320, 330, -310),
    ],
    mattresses: [{ id: 'm1', x1: 40, x2: 180, label: 'MATTRESS 1' }],
    bins: [
      { id: 'masonry', material: 'masonry', x1: 600, x2: 710 },
      { id: 'furniture', material: 'furniture', x1: 730, x2: 840 },
      { id: 'glass', material: 'glass', x1: 860, x2: 950 },
    ],
    kit: { trampoline: 2, funnel: 1, cushion: 1 },
  },
  {
    id: 'wedding',
    name: 'The Wrong Wedding',
    tagline: 'Rooftop reception, 16:40. The cake was not the problem.',
    briefing:
      'A caterer has ignited the celebratory gas ring. Two guests and five items of wedding furniture are in the air. The gas canister is still travelling and remains enthusiastic about secondary explosions.',
    hint: 'Cushions kill momentum. Funnels preserve it but change its career path.',
    freezeSeconds: 0.9,
    source: { x: 470, y: 330, power: 330 },
    bodies: [
      ped(450, 310, -120, -330),
      ped(460, 310, -210, -240),
      debris('masonry', 500, 310, 60, -300),
      debris('masonry', 455, 310, -80, -260),
      debris('furniture', 510, 310, 170, -250),
      debris('furniture', 530, 320, 240, -230),
      debris('glass', 490, 310, 320, -290),
      gas(540, 330, 190, -330),
    ],
    mattresses: [
      { id: 'm1', x1: 30, x2: 150, label: 'MATTRESS 1' },
      { id: 'm2', x1: 170, x2: 290, label: 'MATTRESS 2' },
    ],
    bins: [
      { id: 'masonry', material: 'masonry', x1: 545, x2: 655 },
      { id: 'furniture', material: 'furniture', x1: 690, x2: 800 },
      { id: 'glass', material: 'glass', x1: 850, x2: 950 },
    ],
    kit: { trampoline: 3, funnel: 2, cushion: 1 },
  },
  {
    id: 'piano',
    name: 'Grand Piano, Minor Key',
    tagline: 'Conservatoire, 11:05. The recital is now site-specific.',
    briefing:
      'The old boiler took issue with the allegro. Three audience members and six assorted objects are in the air, plus one gas cylinder from the catering tent. Preserve the soft landing zones; the bins are fussy about materials.',
    hint: 'Place equipment early in a trajectory, not on top of the target. Let physics do the paperwork.',
    freezeSeconds: 0.75,
    source: { x: 500, y: 320, power: 340 },
    bodies: [
      ped(470, 300, -260, -340),
      ped(480, 300, -40, -300),
      ped(500, 300, -80, -240),
      debris('masonry', 520, 310, 120, -280),
      debris('masonry', 460, 320, -170, -300),
      debris('furniture', 490, 310, 250, -300),
      debris('furniture', 530, 320, 170, -200),
      debris('glass', 510, 310, 340, -330),
      debris('glass', 480, 330, 400, -260),
      gas(530, 330, 200, -300),
    ],
    mattresses: [
      { id: 'm1', x1: 30, x2: 130, label: 'MATTRESS 1' },
      { id: 'm2', x1: 330, x2: 430, label: 'MATTRESS 2' },
    ],
    bins: [
      { id: 'masonry-left', material: 'masonry', x1: 210, x2: 310 },
      { id: 'masonry', material: 'masonry', x1: 650, x2: 750 },
      { id: 'furniture', material: 'furniture', x1: 760, x2: 850 },
      { id: 'glass', material: 'glass', x1: 870, x2: 950 },
    ],
    kit: { trampoline: 3, funnel: 2, cushion: 2 },
  },
  {
    id: 'flatpack',
    name: 'IKEA Aftermath',
    tagline: 'Showroom annex, 14:25. Some assembly was required.',
    briefing:
      'A pallet of flat-pack bookcases has achieved self-assembly at speed. Three shoppers are airborne, along with eight material samples and two gas canisters. The blue bin does not accept masonry, however confident it looks.',
    hint: 'Chain reactions are not covered by the standard insurance form. Use them or regret them.',
    freezeSeconds: 0.85,
    source: { x: 480, y: 310, power: 360 },
    bodies: [
      ped(450, 300, -200, -320),
      ped(470, 300, -140, -300),
      ped(500, 300, -10, -280),
      debris('masonry', 490, 310, 130, -290),
      debris('masonry', 500, 310, 210, -250),
      debris('masonry', 470, 310, -60, -260),
      debris('furniture', 510, 310, 300, -300),
      debris('furniture', 520, 310, 360, -260),
      debris('furniture', 480, 310, -220, -300),
      debris('glass', 500, 310, 260, -340),
      debris('glass', 520, 300, 440, -240),
      gas(530, 320, 150, -320),
      gas(460, 320, -120, -310),
    ],
    mattresses: [
      { id: 'm1', x1: 40, x2: 140, label: 'MATTRESS 1' },
      { id: 'm2', x1: 200, x2: 300, label: 'MATTRESS 2' },
      { id: 'm3', x1: 360, x2: 460, label: 'MATTRESS 3' },
    ],
    bins: [
      { id: 'masonry-left', material: 'masonry', x1: 470, x2: 570 },
      { id: 'masonry', material: 'masonry', x1: 580, x2: 680 },
      { id: 'furniture', material: 'furniture', x1: 700, x2: 800 },
      { id: 'glass', material: 'glass', x1: 830, x2: 930 },
    ],
    kit: { trampoline: 4, funnel: 2, cushion: 2 },
  },
  {
    id: 'block',
    name: 'The Entire Block',
    tagline: 'Civic centre, 09:55. Everything is technically recyclable.',
    briefing:
      'The district heating plant has made a statement. Four residents, ten samples of municipal debris and three gas cylinders are in the air. This is the department\'s problem now. Make the mattress maths work.',
    hint: 'You can tilt placement with Tilt or R before clicking. Space resumes time; Z undoes a mistake.',
    freezeSeconds: 0.95,
    source: { x: 480, y: 300, power: 390 },
    bodies: [
      ped(480, 290, -260, -320),
      ped(480, 290, -80, -300),
      ped(480, 290, 70, -340),
      ped(480, 290, 240, -280),
      debris('masonry', 490, 290, 140, -300),
      debris('masonry', 490, 290, 240, -260),
      debris('masonry', 490, 290, -180, -320),
      debris('masonry', 490, 290, 280, -330),
      debris('furniture', 490, 290, 320, -300),
      debris('furniture', 490, 290, 380, -250),
      debris('furniture', 490, 290, -60, -280),
      debris('glass', 490, 290, 200, -360),
      debris('glass', 490, 290, 440, -220),
      gas(540, 300, 120, -330),
      gas(450, 300, -150, -300),
      gas(520, 300, 300, -280),
    ],
    mattresses: [
      { id: 'm1', x1: 20, x2: 130, label: 'MATTRESS 1' },
      { id: 'm2', x1: 150, x2: 260, label: 'MATTRESS 2' },
      { id: 'm3', x1: 280, x2: 390, label: 'MATTRESS 3' },
      { id: 'm4', x1: 410, x2: 520, label: 'MATTRESS 4' },
    ],
    bins: [
      { id: 'masonry', material: 'masonry', x1: 600, x2: 700 },
      { id: 'furniture', material: 'furniture', x1: 720, x2: 820 },
      { id: 'glass', material: 'glass', x1: 840, x2: 940 },
    ],
    kit: { trampoline: 4, funnel: 3, cushion: 3 },
  },
];