SOURCE / PINNED RELEASE
Made of little things.
Supersonic RC Revive
- Release
- 372375d18e3f…
- Author-recorded commit
- 6fbf09f88a0a…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.napplet.soy%2F/n-143146b0d6f
Archive hash verified: 76e4eb30c67a6f45…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
// Procedural textures for the custom level (RGBA8, sRGB). Kept small and
// low-noise so the PNGs stay compact; the baked lighting adds the depth.
import { writePng } from './files.ts';
export interface TextureOut {
name: string;
png: Buffer;
}
/** Deterministic PRNG (mulberry32). */
export function rng(seed: number): () => number {
let a = seed >>> 0;
return () => {
a = (a + 0x6d2b79f5) >>> 0;
let t = a;
t = Math.imul(t ^ (t >>> 15), t | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
function image(w: number, h: number, f: (x: number, y: number) => [number, number, number]): Uint8Array {
const out = new Uint8Array(w * h * 4);
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
const [r, g, b] = f(x, y);
const i = (y * w + x) * 4;
out[i] = Math.max(0, Math.min(255, Math.round(r)));
out[i + 1] = Math.max(0, Math.min(255, Math.round(g)));
out[i + 2] = Math.max(0, Math.min(255, Math.round(b)));
out[i + 3] = 255;
}
}
return out;
}
/** Four staggered planks across, honey-coloured, with soft grain and dark seams. */
function planks(): Uint8Array {
const W = 128;
const H = 256;
const r = rng(7);
const tones = Array.from({ length: 8 }, () => 0.9 + r() * 0.2);
const grainPhase = Array.from({ length: 8 }, () => r() * 6.28);
return image(W, H, (x, y) => {
const plank = Math.floor(x / 32);
const offset = (plank % 2) * 128 + plank * 37;
const yy = (y + offset) % H;
const seg = plank * 2 + (yy < 128 ? 0 : 1);
const t = tones[seg];
const seamX = x % 32 === 0 || x % 32 === 31;
const seamY = yy === 0 || yy === 128;
const grain = 0.05 * Math.sin(yy * 0.21 + Math.sin((x % 32) * 0.4 + grainPhase[seg]) * 2.5);
const k = (seamX || seamY ? 0.62 : 1) * (t + grain);
return [222 * k, 168 * k, 112 * k];
});
}
/** A round-cornered rug: border bands and a diamond field. */
function rug(): Uint8Array {
const S = 128;
return image(S, S, (x, y) => {
const u = (x + 0.5) / S;
const v = (y + 0.5) / S;
const edge = Math.min(u, v, 1 - u, 1 - v);
if (edge < 0.06) return [44, 72, 128];
if (edge < 0.1) return [240, 214, 120];
if (edge < 0.14) return [44, 72, 128];
const d = Math.abs(((u * 6) % 1) - 0.5) + Math.abs(((v * 6) % 1) - 0.5);
return d < 0.3 ? [214, 88, 70] : [236, 226, 204];
});
}
/** Start/finish checker. */
function checker(): Uint8Array {
return image(32, 32, (x, y) => ((Math.floor(x / 8) + Math.floor(y / 8)) % 2 ? [24, 24, 28] : [244, 244, 240]));
}
/** Hazard stripes for ramp lips and kickers. */
function stripes(): Uint8Array {
return image(64, 64, (x, y) => ((x + y) % 32 < 16 ? [255, 196, 32] : [36, 36, 40]));
}
/** The blob shadow under the car (ui/carshadow): black, soft-edged alpha. */
export function carShadow(): Buffer {
const W = 32;
const H = 64;
const out = new Uint8Array(W * H * 4);
for (let y = 0; y < H; y++) {
for (let x = 0; x < W; x++) {
// Distance outside a rounded rectangle, in normalised units.
const dx = Math.max(0, Math.abs((x + 0.5) / W - 0.5) * 2 - 0.45) / 0.55;
const dy = Math.max(0, Math.abs((y + 0.5) / H - 0.5) * 2 - 0.7) / 0.3;
const d = Math.min(1, Math.hypot(dx, dy));
out[(y * W + x) * 4 + 3] = Math.round(255 * (1 - d * d * (3 - 2 * d)));
}
}
return writePng(W, H, out);
}
export function buildTextures(): TextureOut[] {
return [
{ name: 'planks', png: writePng(128, 256, planks()) },
{ name: 'rug', png: writePng(128, 128, rug()) },
{ name: 'checker', png: writePng(32, 32, checker()) },
{ name: 'stripes', png: writePng(64, 64, stripes()) },
];
}
