diffs / #1
feat(#1): moon and skyline
Requested-by: SEED (built by us pre-launch)
+ BUILD+ UNIT TESTS+ LINT+ DIFF GATE+ PAGE CLASSIFIER+ PLAYWRIGHT
src/features/moon.jsNEW+18 −0
| 1 | +// Seed #1: the night sky. Stars twinkle slowly; the moon hangs top left. | ||
| 2 | +import { PAL } from "../palette.js"; | ||
| 3 | +let stars = []; | ||
| 4 | +export default { | ||
| 5 | + id: "moon", | ||
| 6 | + order: 0, | ||
| 7 | + layer: "sky", | ||
| 8 | + init(w) { stars = Array.from({ length: 46 }, () => ({ x: Math.floor(w.rand() * 320), y: Math.floor(w.rand() * 110), p: w.rand() * 6 })); }, | ||
| 9 | + draw(g, w) { | ||
| 10 | + g.fillStyle = PAL.skyDeep; g.fillRect(0, 0, 320, 14); | ||
| 11 | + for (let y = 14; y < 26; y += 2 + Math.floor((y - 14) / 4)) g.fillRect(0, y, 320, 1); | ||
| 12 | + for (const s of stars) { g.fillStyle = (Math.sin(w.t * 1.3 + s.p) > 0.6 && !w.reduced) ? PAL.light : PAL.playerShade; g.fillRect(s.x, s.y, 1, 1); } | ||
| 13 | + const cx = 34, cy = 34; | ||
| 14 | + g.fillStyle = PAL.player; | ||
| 15 | + for (let y = -9; y <= 9; y++) { const hw = Math.floor(Math.sqrt(81 - y * y)); g.fillRect(cx - hw, cy + y, hw * 2 + 1, 1); } | ||
| 16 | + g.fillStyle = PAL.playerShade; g.fillRect(cx - 4, cy - 3, 3, 2); g.fillRect(cx + 2, cy + 3, 2, 2); g.fillRect(cx - 1, cy + 5, 2, 1); | ||
| 17 | + }, | ||
| 18 | +}; |
1+// Seed #1: the night sky. Stars twinkle slowly; the moon hangs top left.
2+import { PAL } from "../palette.js";
3+let stars = [];
4+export default {
5+ id: "moon",
6+ order: 0,
7+ layer: "sky",
8+ init(w) { stars = Array.from({ length: 46 }, () => ({ x: Math.floor(w.rand() * 320), y: Math.floor(w.rand() * 110), p: w.rand() * 6 })); },
9+ draw(g, w) {
10+ g.fillStyle = PAL.skyDeep; g.fillRect(0, 0, 320, 14);
11+ for (let y = 14; y < 26; y += 2 + Math.floor((y - 14) / 4)) g.fillRect(0, y, 320, 1);
12+ for (const s of stars) { g.fillStyle = (Math.sin(w.t * 1.3 + s.p) > 0.6 && !w.reduced) ? PAL.light : PAL.playerShade; g.fillRect(s.x, s.y, 1, 1); }
13+ const cx = 34, cy = 34;
14+ g.fillStyle = PAL.player;
15+ for (let y = -9; y <= 9; y++) { const hw = Math.floor(Math.sqrt(81 - y * y)); g.fillRect(cx - hw, cy + y, hw * 2 + 1, 1); }
16+ g.fillStyle = PAL.playerShade; g.fillRect(cx - 4, cy - 3, 3, 2); g.fillRect(cx + 2, cy + 3, 2, 2); g.fillRect(cx - 1, cy + 5, 2, 1);
17+ },
18+};
src/features/skyline.jsNEW+21 −0
| 1 | +// Seed #1: the far skyline. Two silhouette layers with a few lit windows. | ||
| 2 | +import { PAL } from "../palette.js"; | ||
| 3 | +let far = [], mid = []; | ||
| 4 | +export default { | ||
| 5 | + id: "skyline", | ||
| 6 | + order: 1, | ||
| 7 | + layer: "bg", | ||
| 8 | + rooms: ["rooftop", "void"], | ||
| 9 | + init(w) { | ||
| 10 | + let x = 0; far = []; while (x < 320) { const bw = 10 + Math.floor(w.rand() * 18); far.push({ x, w: bw, h: 34 + Math.floor(w.rand() * 40) }); x += bw; } | ||
| 11 | + x = -6; mid = []; while (x < 320) { const bw = 14 + Math.floor(w.rand() * 22); const h = 46 + Math.floor(w.rand() * 50); mid.push({ x, w: bw, h, lit: Array.from({ length: 12 }, () => w.rand() < 0.22) }); x += bw + 2; } | ||
| 12 | + }, | ||
| 13 | + draw(g) { | ||
| 14 | + g.fillStyle = PAL.far; for (const b of far) g.fillRect(b.x, 150 - b.h, b.w, b.h + 30); | ||
| 15 | + for (const b of mid) { | ||
| 16 | + g.fillStyle = PAL.wall; g.fillRect(b.x, 160 - b.h, b.w, b.h + 20); | ||
| 17 | + let i = 0; | ||
| 18 | + for (let y = 166 - b.h; y < 150; y += 8) for (let x = b.x + 3; x < b.x + b.w - 3; x += 5) { if (b.lit[i++ % 12]) { g.fillStyle = PAL.light; g.fillRect(x, y, 2, 3); } } | ||
| 19 | + } | ||
| 20 | + }, | ||
| 21 | +}; |
1+// Seed #1: the far skyline. Two silhouette layers with a few lit windows.
2+import { PAL } from "../palette.js";
3+let far = [], mid = [];
4+export default {
5+ id: "skyline",
6+ order: 1,
7+ layer: "bg",
8+ rooms: ["rooftop", "void"],
9+ init(w) {
10+ let x = 0; far = []; while (x < 320) { const bw = 10 + Math.floor(w.rand() * 18); far.push({ x, w: bw, h: 34 + Math.floor(w.rand() * 40) }); x += bw; }
11+ x = -6; mid = []; while (x < 320) { const bw = 14 + Math.floor(w.rand() * 22); const h = 46 + Math.floor(w.rand() * 50); mid.push({ x, w: bw, h, lit: Array.from({ length: 12 }, () => w.rand() < 0.22) }); x += bw + 2; }
12+ },
13+ draw(g) {
14+ g.fillStyle = PAL.far; for (const b of far) g.fillRect(b.x, 150 - b.h, b.w, b.h + 30);
15+ for (const b of mid) {
16+ g.fillStyle = PAL.wall; g.fillRect(b.x, 160 - b.h, b.w, b.h + 20);
17+ let i = 0;
18+ for (let y = 166 - b.h; y < 150; y += 8) for (let x = b.x + 3; x < b.x + b.w - 3; x += 5) { if (b.lit[i++ % 12]) { g.fillStyle = PAL.light; g.fillRect(x, y, 2, 3); } }
19+ }
20+ },
21+};


CI log tail
$ ci:build (329 ms, exit 0) $ ci:test (102 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.708937 type: 'test' ... # Subtest: sprites: rectangular-ish rows of known keys ok 2 - sprites: rectangular-ish rows of known keys --- duration_ms: 0.957252 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: 0.899564 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.69408 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.119215 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.790791 type: 'test' ... 1..6 # tests 6 # suites 0 # pass 6 # fail 0 # cancelled 0 # skipped 0 # todo 0 # duration_ms 60.087102 $ ci:lint (342 ms, exit 0) $ playwright 960x540 fps 60 long tasks 0 console errors 0