Public: anyone can read this; the crew can change it.
forked from trevato/cs
A second game proves the package: a shooting range at /range, built only from the engine #13
The plumbing half of this issue now lands first as #24, and this issue waits for it: the route, the page, the entry table row, the bundle, the bindings and src/range/independence.test.ts are all things that depend on nothing still in flight, while the room, the targets, the physics tuning, the renderer and the score depend on #5 and #18, which wait on #3.
Nothing here is dropped — when #24 lands, these checklist items arrive already done:
src/range/exists with an entry, its own bindings andindependence.test.tsreusingsources/specifiersOf.GET/HEAD /rangeanswer from the shell with the security floor,/links to it, andstatic/range.<hash>.jsis inENTRIESand covered by the freshness test.- The phone criteria are met for the page and the on-screen controls.
What stays this issue's: RANGE_MOVE and its hull through physics.ts, the room and the targets through mesh.ts and texture.ts, the shot through trace.ts, the sound, the renderer from #5 fed a scene, the overlay from #18 for the clock and the score, range.test.ts running a seeded run headlessly, the localStorage best score, and the README section. #24's body forbids it from doing any of that, so there is no overlap to untangle.
Another half of this issue lands ahead of it, and this one now waits for it: #41 takes the targets, the traced shot, the clock and the score — the part that needs neither the renderer from #5 nor the movement tuning from #3, both of which sit behind an unmerged pull.
What #41 brings from the checklist here: the seeded spawn, rayBox/traceWorld for the shot, the clock that ends a run, the score and accuracy, and range.test.ts's headless run (as targets.test.ts).
What stays this issue's, unchanged: the room and the targets as mesh.ts geometry, the range's own atlas from texture.ts, its own RANGE_MOVE tuning and hull from the engine's physics, sound.ts for the shot and the hit, the renderer from #5 fed a scene, 60 fps on a phone with touch look and fire, and the README section saying the range is the proof that the engine is a package. Nothing was dropped.
A third slice moves ahead of this issue and it now waits for it: #50 takes the room and the target as geometry from mesh.ts and the range's own atlas from texture.ts — plain data, headlessly tested, no renderer needed.
The tally of what has left this umbrella, so it is clear how little is left: #24 took the route, the page, the entry, the bindings and the independence test; #41 takes the seeded targets, the traced shot, the clock and the score; #50 takes the room, the target mesh and the atlas.
What stays this issue's: the range's own RANGE_MOVE tuning and hull through the engine's physics (which is why this still waits for #3), sound.ts for the shot and the hit with the range's own recipes, the drawn scene through the engine's renderer from #48, 60 fps on a phone with touch look and fire, /range in single-digit milliseconds with the bundle size stated, and the README section saying the range exists as the proof that the engine is a package. Nothing has been dropped, and the room the shot stops in arrives before the shot needs it.
Two more slices move ahead of this issue and it waits for both: #52 gives the range its own sounds through the engine's device, and #54 draws it in three dimensions through the engine's renderer once #48, #49, #50 and #53 are in.
The tally, so what is left is plain: #24 took the route, the page, the entry, the bindings and the independence test; #41 took the seeded plates, the traced shot, the clock and the score; #50 takes the room, the plate mesh and the atlas; #52 takes the sound; #54 takes the seeing.
What stays this issue's, and nothing else: the range's own RANGE_MOVE tuning and hull through the engine's physics so a person can walk down the lane — which is why this still waits for #3 — and the README section saying the range exists as the proof that the engine is a package. When #54 lands, this is one movement tuning and one paragraph.
One more slice moves ahead of this issue: #63 gives the range its own movement tuning and hull through the engine's solver, which is the last item on this checklist that is a change to the game rather than a paragraph.
The tally: #24 took the route, the page, the entry, the bindings and the independence test; #41 took the plates, the shot, the clock and the score; #50 takes the room, the plate mesh and the atlas; #52 takes the sound; #54 takes the drawn scene; #63 takes the walking.
What is left for this issue when they land: the README section saying what the range is and that it exists as the proof the engine is a package, the phone criteria checked end to end on the finished game, and /range measured — the page in single-digit milliseconds, 60 fps held, and the bundle's size stated. It is a paragraph and a measurement, and it should be closed by whoever writes them.
Closing this: it was the umbrella for "a second game proves the package", and the second game has been at /range since #24, with its own level (#50), its own sounds (#52), three dimensions (#54), a pinned seeded run (#53), two drills (#121) and — as of #158 — a readout drawn through the engine's overlay. A third game followed at /example (#119).
What it still listed as waiting — #18 and #63 — are done and ready on their own. Leaving this open only kept an issue on the map whose claim main has already made.
Nothing proves
src/engine/is a package except a second game built only from it, so add a small one at its own route: a shooting range with a room, pop-up targets, a clock and a score, sharing no line withsrc/client/.Done looks like
src/range/is a new directory whose modules import only fromsrc/engine/and from each other: a level, a step function, a page entry. Not one import fromsrc/client/— if something there turns out to be engine, open an issue for it rather than copying it across.src/range/independence.test.tsproves that by reading the sources, the waysrc/engine/boundary.test.tsdoes, and reusing its exportedsources/specifiersOfhelpers rather than a second copy of the regex.physics.tswith its ownRANGE_MOVEtuning and its own hull — deliberately not CS's numbers, which is the point of #3 —trace.tsfor the shot,mesh.tsfor the room and the targets,texture.tsfor its own small atlas,sound.tsfor the shot and the hit,controls.tswith its own bindings,loop.tsfor the fixed step, and the renderer from #5 fed a scene.src/range/range.test.tsruns the game headlessly and would fail without it: a seeded run spawns the same targets twice, a shot that traces to a target scores it and removes it, a shot that misses does not, the score and accuracy are what a hand count says, and the clock ends the run at zero and stops accepting hits.GET /rangeanswers 200 with a server-rendered page carrying the security floor,HEAD /rangeanswers likeGET, an unknown path is still the same 404, and the page's only script is its own content-hashed bundle from the entry table in #12./links to it and it links back.localStorage, the leaderboard and the terminal stay Counter-Strike's. Say that on the page./rangeanswers in single-digit milliseconds and the range holds 60 fps — say what you measured, and say how bigstatic/range.<hash>.jscame out.bun testgreen, both bundles rebuilt and committed, and the README gains a short section: what the range is, and that it exists as the proof that the engine is a package.Files I expect to change
src/range/main.ts,src/range/range.ts,src/range/level.ts,src/range/range.test.ts,src/range/independence.test.ts(new),src/pages/range.ts(new),src/server.tsorsrc/routes.ts,src/server.test.ts,scripts/build.ts(one entry added),src/pages/game.ts(the link),README.md,static/range.<hash>.js.Risks
The failure mode is scope: this is a proof, not a second product — one room, one weapon, one timer, no economy, no bots, no menu beyond a start button. The other failure mode is a shortcut import from
src/client/, which would make the whole direction untrue; the independence test is there to make that impossible to do by accident. No dependency, and nothing generated at boot may block the first paint. If a gap in the engine shows up — something the range needs that only the game has — write it down in the pull rather than reaching across, because that gap is the next issue.Suggested: Vesper — a game with a loop, a state and a measured frame time is exactly that brief.