diffs / #3

feat(#3): the rooftop

SHIPPED commit 8deb236 +37 −0 request “a rooftop to stand on” SEED · built by us pre-launch

Requested-by: SEED (built by us pre-launch)
+ BUILD+ UNIT TESTS+ LINT+ DIFF GATE+ PAGE CLASSIFIER+ PLAYWRIGHT
src/levels/rooftop.jsNEW+37 −0
1+// Seed #3: the rooftop. The cat starts on the left roof, a water tower stands on the right roof.
2+// Roof tops are one-way platforms. The door on the left roof leads down to the lobby; walk off the left edge for the alley.
3+import { PAL } from "../palette.js";
4+import { blit } from "../sprites.js";
5+
6+const BLOCKS = [
7+ { x: 0, y: 96, w: 80, h: 84, rows: 5 },
8+ { x: 86, y: 128, w: 62, h: 52, rows: 3 },
9+ { x: 154, y: 114, w: 60, h: 66, rows: 4 },
10+ { x: 222, y: 92, w: 98, h: 88, rows: 5 },
11+];
12+
13+export default {
14+ name: "rooftop",
15+ label: "rooftop",
16+ platforms: BLOCKS.map((b) => ({ x: b.x, y: b.y, w: b.w })),
17+ start: { x: 30, y: 90 },
18+ bossAt: { x: 262, y: 92 },
19+ exits: { left: { to: "alley", at: { x: 300, y: 150 } } },
20+ doors: [{ x: 52, y: 84, to: "lobby", at: { x: 40, y: 150 } }],
21+ boss: null,
22+ spawn: [],
23+ music: "rooftop-a",
24+ draw(g) {
25+ for (const b of BLOCKS) {
26+ g.fillStyle = PAL.wall; g.fillRect(b.x, b.y, b.w, b.h);
27+ g.fillStyle = PAL.roof; g.fillRect(b.x - 1, b.y, b.w + 2, 4);
28+ g.fillStyle = PAL.ink; g.fillRect(b.x - 1, b.y + 4, b.w + 2, 1);
29+ let i = (b.x * 7) % 5;
30+ for (let r = 0; r < b.rows; r++) for (let x = b.x + 8; x < b.x + b.w - 8; x += 14) {
31+ i++; g.fillStyle = i % 3 === 0 ? PAL.winOff : PAL.light;
32+ g.fillRect(x, b.y + 12 + r * 16, 7, 9); g.fillStyle = PAL.ink; g.fillRect(x + 3, b.y + 12 + r * 16, 1, 9);
33+ }
34+ }
35+ blit(g, "tower", 282, 70);
36+ },
37+};
1+// Seed #3: the rooftop. The cat starts on the left roof, a water tower stands on the right roof.
2+// Roof tops are one-way platforms. The door on the left roof leads down to the lobby; walk off the left edge for the alley.
3+import { PAL } from "../palette.js";
4+import { blit } from "../sprites.js";
5+
6+const BLOCKS = [
7+ { x: 0, y: 96, w: 80, h: 84, rows: 5 },
8+ { x: 86, y: 128, w: 62, h: 52, rows: 3 },
9+ { x: 154, y: 114, w: 60, h: 66, rows: 4 },
10+ { x: 222, y: 92, w: 98, h: 88, rows: 5 },
11+];
12+
13+export default {
14+ name: "rooftop",
15+ label: "rooftop",
16+ platforms: BLOCKS.map((b) => ({ x: b.x, y: b.y, w: b.w })),
17+ start: { x: 30, y: 90 },
18+ bossAt: { x: 262, y: 92 },
19+ exits: { left: { to: "alley", at: { x: 300, y: 150 } } },
20+ doors: [{ x: 52, y: 84, to: "lobby", at: { x: 40, y: 150 } }],
21+ boss: null,
22+ spawn: [],
23+ music: "rooftop-a",
24+ draw(g) {
25+ for (const b of BLOCKS) {
26+ g.fillStyle = PAL.wall; g.fillRect(b.x, b.y, b.w, b.h);
27+ g.fillStyle = PAL.roof; g.fillRect(b.x - 1, b.y, b.w + 2, 4);
28+ g.fillStyle = PAL.ink; g.fillRect(b.x - 1, b.y + 4, b.w + 2, 1);
29+ let i = (b.x * 7) % 5;
30+ for (let r = 0; r < b.rows; r++) for (let x = b.x + 8; x < b.x + b.w - 8; x += 14) {
31+ i++; g.fillStyle = i % 3 === 0 ? PAL.winOff : PAL.light;
32+ g.fillRect(x, b.y + 12 + r * 16, 7, 9); g.fillStyle = PAL.ink; g.fillRect(x + 3, b.y + 12 + r * 16, 1, 9);
33+ }
34+ }
35+ blit(g, "tower", 282, 70);
36+ },
37+};
Before
BEFORE
After
AFTER
CI log tail
$ ci:build  (604 ms, exit 0)

$ ci:test  (154 ms, exit 0)
TAP version 13
# Subtest: palette: valid hex, at most 40 colours
ok 1 - palette: valid hex, at most 40 colours
  ---
  duration_ms: 0.992769
  type: 'test'
  ...
# Subtest: sprites: rectangular-ish rows of known keys
ok 2 - sprites: rectangular-ish rows of known keys
  ---
  duration_ms: 2.273006
  type: 'test'
  ...
# Subtest: levels: every room has a name, platforms and a draw function
ok 3 - levels: every room has a name, platforms and a draw function
  ---
  duration_ms: 1.954189
  type: 'test'
  ...
# Subtest: bosses: every file exports a class with update/draw
ok 4 - bosses: every file exports a class with update/draw
  ---
  duration_ms: 0.652021
  type: 'test'
  ...
# Subtest: features: default export with a unique id and only known hooks
ok 5 - features: default export with a unique id and only known hooks
  ---
  duration_ms: 1.00936
  type: 'test'
  ...
# Subtest: no network, storage or parent access anywhere in src
ok 6 - no network, storage or parent access anywhere in src
  ---
  duration_ms: 0.7374
  type: 'test'
  ...
1..6
# tests 6
# suites 0
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 92.27021

$ ci:lint  (798 ms, exit 0)

$ playwright 960x540  fps 60  long tasks 0  console errors 0