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

Closed opened this
opened this

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 with src/client/.

Done looks like

  • [ ] src/range/ is a new directory whose modules import only from src/engine/ and from each other: a level, a step function, a page entry. Not one import from src/client/ — if something there turns out to be engine, open an issue for it rather than copying it across.
  • [ ] src/range/independence.test.ts proves that by reading the sources, the way src/engine/boundary.test.ts does, and reusing its exported sources/specifiersOf helpers rather than a second copy of the regex.
  • [ ] The range uses the whole package and says so in the README: physics.ts with its own RANGE_MOVE tuning and its own hull — deliberately not CS's numbers, which is the point of #3 — trace.ts for the shot, mesh.ts for the room and the targets, texture.ts for its own small atlas, sound.ts for the shot and the hit, controls.ts with its own bindings, loop.ts for the fixed step, and the renderer from #5 fed a scene.
  • [ ] src/range/range.test.ts runs 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 /range answers 200 with a server-rendered page carrying the security floor, HEAD /range answers like GET, 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.
  • [ ] No new endpoint, no new table, no new cookie: the range's best score lives in localStorage, the leaderboard and the terminal stay Counter-Strike's. Say that on the page.
  • [ ] A phone can play it: 360px wide with no horizontal scroll, on-screen controls at least 44px, touch look and fire through the same engine controls.
  • [ ] /range answers in single-digit milliseconds and the range holds 60 fps — say what you measured, and say how big static/range.<hash>.js came out.
  • [ ] bun test green, 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.ts or src/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.

commented

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 and independence.test.ts reusing sources/specifiersOf.
  • GET/HEAD /range answer from the shell with the security floor, / links to it, and static/range.<hash>.js is in ENTRIES and 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.

commented

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.

commented

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.

commented

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.

commented

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.

commented

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.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs