Back to Supersonic RC Revive
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.

tools/assets/README.md
# Custom asset bundle

The art and sound every normal build ships. All of it is made from scratch for this
project: no geometry, texture, layout or sample comes from the 2004 game. The
originals stay local (`../README.md`).

```sh
pnpm assets:build   # writes src/game/assets-custom/ (commit the result)
```

The output has the same file names and formats as `tools/extract/build_level.py`,
so the game runs on either bundle unchanged:

| File | What |
| --- | --- |
| `level.glb` | One node per level piece, world space (Z-up, inches). `COLOR_0` holds baked light |
| `collision.json` | Havok static bodies: convex pieces as hulls, the room and quarter pipe as trimeshes |
| `raymesh.bin` | The coarse faces the wheels and camera raycast against |
| `markers.json` | `checkpoint_1..10`, `prizes_1..40` and `ssrc_start` (the car's start, which the original lacks) |
| `car.glb`, `star.glb`, `prize.glb`, `arrow2.glb` | Props in the frames `src/game/render/world.ts` expects (see `props.ts`) |
| `materials.json` | Shader colours for the props (the arrow is unlit, so it shows only emissive colour) |
| `ui/carshadow.png` | The blob shadow |

## Shipping: a separate pack on Blossom

Release builds (`pnpm build`, `soyli build`/`publish`) do not embed this folder. The build
packs it into one `.ssrcpack` file, writes it to `dist-pack/<sha256>.ssrcpack` (gitignored),
and bakes only the hash into `dist/index.html`: 2.0 MB, 1.07 MB gzipped. At startup the
napplet downloads `blossom:sha256:<hash>` through NAP-RESOURCE, and the host verifies the
hash. The pack is deterministic, so a rebuild gives the same hash until the assets change.

The pack is a managed soyli asset, `game-pack` in `napplet.assets.json`, with its original
stored under `assets/` and uploaded by `soyli publish` to the project's Blossom destination
(`soyli publish` verifies the uploaded bytes before it announces the release). After changing
this folder, register the new pack and commit the result:

```sh
pnpm assets:build      # regenerate this folder
pnpm assets:register   # replace "game-pack" with the new pack (soyli assets remove + add)
git add assets napplet.assets.json soy-assets.js soy-assets.d.ts src/game/assets-custom
soyli publish
```

A release build fails while `napplet.assets.json` registers a different pack, so soyli cannot
publish a build whose pack it never uploads. `pnpm assets:register --check` only reports
whether the registered pack is current. The script deletes the replaced original from
`assets/`, since every tracked file counts toward soyli's 128-file source limit.

If the download fails, or the host has no resource capability, the napplet shows a "Game
data" panel. It offers "Try again" and two other ways to get a pack (10 MiB at most):

- With the `resource` domain, a field for a Blossom link or hash. It accepts a bare
  SHA-256, `blossom:sha256:<hash>`, a BUD-10 `blossom:<hash>.ssrcpack?xs=<server>` URI, or a
  blob URL such as `https://blossom.example/<hash>.ssrcpack`. The napplet asks the host for
  `blossom:sha256:<hash>` first, passing every server the link names (the blob URL's
  server and each `xs` hint) as Blossom server hints, so the host verifies the hash. If
  that fails, it tries the direct URLs in turn (the pasted blob URL, then the hash on each
  `xs` server) and checks the SHA-256 itself.
- With the `fs` domain, a file button that side-loads a `.ssrcpack` through the host's
  file picker.

Any pack works: the release file from `dist-pack/`, or one made with
`pnpm assets:pack <folder> <out.ssrcpack>`. To share a pack, upload it to any Blossom server
and send the link. Never upload packs built from the original game's files.

Once the game is running, the title screen shows an "Asset pack…" button under "Click to
Start" (in the corner over the original title art), when the host has either domain. It
opens the same panel, with Cancel, to load another pack in place of the current one: the
game rebuilds the track, props and sounds and returns to the title screen. A file that is
not a pack, or a pack without track data, is refused and the previous pack stays. The choice
lasts for the session; a reload starts from the build's own pack again.

These builds still embed the pack: `soyli dev` and other watch builds (the local preview serves
only registered soyli assets), builds with the originals, and `SSRC_EMBED_ASSETS=1`. To try the
download and side-load path in `soyli dev`, put `SSRC_EXTERNAL_ASSETS=1` in `.env.local`.

The bundle has no sound files. `src/game/synth.ts` renders every cast-member sound at load
time: the engine, tyre chirps, crashes, stings and five interchangeable music loops.

## Sources

- `level.ts`: the "Bedroom Grand Prix" layout, its pieces, checkpoints and prizes. Its header
  comment gives the lap.
- `mesh.ts`: boxes, ramps, cylinders and quarter pipes. Faces are subdivided for the visual copy
  and kept coarse for physics and rays.
- `bake.ts`: per-vertex sun with shadows, sky ambient occlusion, a fill light and a floor bounce.
- `props.ts`: the RC buggy, star, gift box and arrow.
- `textures.ts`: planks, rug, checker and hazard stripes.
- `files.ts`: the GLB and PNG writers.

Check the level headlessly with `SIM_LEVEL=custom node tools/sim/drive.mjs "u:6,ul:2"`.

## Visual direction

The level is a sunny kid's bedroom seen from toy height: warm honey floorboards, pale blue
walls, toys in saturated primaries (red, blue, yellow, green), and soft baked shadows instead
of dynamic lights. Ramps are blue with yellow-and-black hazard stripes on lips and kickers. The
car is a blue buggy with a yellow wing and hubs, so it reads against the floor. Gameplay
markers glow: a gold star for checkpoints, a pink gift box for prizes and a gold arrow. Their
colours are app-owned (`FOLLOW_HOST_THEME` stays false).

Still to do: custom menu art (the HTML fallback screens show for now) and touch-control styling
to match.