Back to Napplet Machines V8
SOURCE / PINNED RELEASE

Made of little things.

Napplet Machines V8

Release
b3f614e739f0…
Author-recorded commit
534f19acb4ba…
License
LICENSE
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-b5572362d4a

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

src/scenery.ts
import { Road, clamp } from './track.js';

export type Building = {
  x: number;
  y: number;
  z: number;
  angle: number;
  width: number;
  length: number;
  radius: number;
  kind: 'garage' | 'stand' | 'tower';
  color: number;
};

// Geometry-derived seed: guests regenerate identical scenery without network traffic.
export function raceBuildings(road: Road): Building[] {
  let seed = 2166136261;
  for (const p of road.track.points)
    for (const n of [p.x, p.y, p.z, p.width])
      seed = Math.imul(seed ^ Math.round(n * 100), 16777619) >>> 0;
  const random = () => {
    seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0;
    return seed / 4294967296;
  };
  const result: Building[] = [];
  const count = Math.min(12, Math.max(5, Math.floor(road.length / 28)));
  for (let attempt = 0; attempt < 160 && result.length < count; attempt++) {
    const p = road.at(12 + random() * Math.max(1, road.length - 24));
    const kind = (['garage', 'stand', 'tower'] as const)[result.length % 3];
    const length =
      kind === 'tower' ? 3.5 : kind === 'stand' ? 7.5 : 5.8 + random() * 1.5;
    const width = kind === 'stand' ? 4.4 : 3.8;
    const radius = Math.hypot(length / 2 + 0.5, width / 2 + 0.5);
    const side = random() < 0.5 ? -1 : 1;
    const offset =
      (p.width / 2 + p.wallThickness / 2 + radius + 2 + random() * 1.8) * side;
    const x = p.x - p.ty * offset,
      y = p.y + p.tx * offset;
    if (
      Math.abs(x) + radius >
        Math.max(
          76,
          Math.abs(road.bounds.minX) + 17,
          Math.abs(road.bounds.maxX) + 17,
        ) ||
      Math.abs(y) + radius >
        Math.max(
          68,
          Math.abs(road.bounds.minY) + 17,
          Math.abs(road.bounds.maxY) + 17,
        )
    )
      continue;
    if (
      result.some((b) => Math.hypot(x - b.x, y - b.y) < radius + b.radius + 2)
    )
      continue;
    // Check all road segments, including other branches and changing widths.
    const clear = road.samples.slice(0, -1).every((a, i) => {
      const b = road.samples[i + 1],
        dx = b.x - a.x,
        dy = b.y - a.y;
      const t = clamp(
        ((x - a.x) * dx + (y - a.y) * dy) / (dx * dx + dy * dy || 1),
        0,
        1,
      );
      return (
        Math.hypot(x - a.x - dx * t, y - a.y - dy * t) >
        radius +
          Math.max(a.width, b.width) / 2 +
          (a.wall ? a.wallThickness / 2 : 0) +
          1.5
      );
    });
    if (!clear) continue;
    result.push({
      x,
      y,
      z: Math.max(0, p.z - 0.6),
      angle: Math.atan2(p.ty, p.tx) + (side < 0 ? Math.PI : 0),
      width,
      length,
      radius,
      kind,
      color: Math.floor(random() * 4),
    });
  }
  return result;
}