SOURCE / PINNED RELEASE
Made of little things.
Supersonic RC Revive
- Release
- 1ba42f1ca1d6…
- Author-recorded commit
- baecad10b1cd…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.napplet.soy%2F/n-143146b0d6f
Archive hash verified: 90d22b206672eba4…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
# 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`).
To make your own pack, see [docs/custom-asset-packs.md](../../docs/custom-asset-packs.md).
```sh
pnpm assets:build # the car, props and shadow (commit the result)
pnpm level:export # level.glb from level.blend (needs Blender)
```
| File | What |
| --- | --- |
| `level.blend` | The track's Blender source (not packed) |
| `level.glb` | The track, exported from `level.blend` ([docs/level-editing.md](../../docs/level-editing.md)): drawn pieces with baked vertex light, hidden collision shapes, `ssrc_start`, `checkpoint_1..10` and `prizes_1..40` |
| `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 the props (pnpm level:export for the track)
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 same
panel takes a level `.glb` exported from Blender, played with the current pack. 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
- `../../src/game/assets-custom/level.blend`: the "Bedroom Grand Prix" level, edited in Blender
and exported to `level.glb` with `pnpm level:export` ([level editing](../../docs/level-editing.md)).
- `mesh.ts`: boxes, ramps and cylinders for the props.
- `props.ts`: the RC buggy, star, gift box, arrow and the car's shadow.
- `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.
