Public: anyone can read this; the crew can change it.

forked from trevato/cs

The readout becomes engine: a 2D overlay the HUD draws through, and a second game can too #18

shipped production is serving it opened this has it
opened this

src/client/hud.ts is 229 lines of 2D canvas in which the panels, the scaled fonts, the crosshair, the progress bar, the faded feed lines and the column table are generic and only the layout and the words are Counter-Strike's; the generic half becomes src/engine/overlay.ts so the range in #13 can draw a clock and a score without copying a line out of the game.

Done looks like

  • [ ] src/engine/overlay.ts takes its drawing target structurally — an interface naming only the members it uses (clearRect, fillRect, beginPath, moveTo, lineTo, arc, rect, stroke, fill, fillText, textAlign, textBaseline, font, fillStyle, strokeStyle, lineWidth, globalAlpha) — so a recording stub satisfies it and no test needs a browser or a CanvasRenderingContext2D.
  • [ ] overlay(ctx, width, height) returns the tools: scale (width / 1280 clamped to 0.7–1.4), pad, font(px) building "<n>px system-ui, sans-serif" at that scale, panel(x, y, w, h, fill?), text(s, x, y, { align, font, fill, alpha }) which always restores globalAlpha to 1, crosshair(gap, arm, color), bar(x, y, w, h, fraction, fill) with the fraction clamped to 0–1, columns(labels, values, at) for a right-aligned row of numbers, and clock(seconds) → m:ss. Not one CS colour, team, weapon or phase in the module, and boundary.test.ts stays green.
  • [ ] src/engine/overlay.test.ts drives the stub and would fail without the module: the scale clamps at both ends for a 360px and a 4000px canvas, clock(115) is 1:55 and clock(-1) is 0:00, the crosshair strokes four arms starting at the gap it was given, bar fills a quarter of the width at 0.25 and the whole width at 1.4, a faded text leaves globalAlpha back at 1, and columns right-aligns at the stride it was handed.
  • [ ] src/client/hud.ts draws through it with its output unchanged: the same 4 + spread * 14 gap and 7-unit arms, the same 30/19/15px fonts, the same rgba(12, 14, 18, 0.55) panels, the same team colours, the same 8-second kill-feed fade, the same 240×44 clock bar and 260×14 use bar, the same scoreboard columns and stride. Move the numbers; do not tune one.
  • [ ] src/client/hud.test.ts (new) covers the CS layout with the same stub — the HUD is the largest module in the game with no test today: health is drawn red below 30 and white above, the clock reads --:-- outside the timed phases and red once the bomb is planted, a feed entry older than 8 seconds is not drawn, the scoreboard sorts by kills then damage and marks the player's own row, and a dead player sees the watching-out message.
  • [ ] bun test green, the bundle rebuilt with bun run scripts/build.ts and the new static/app.<hash>.js committed, and the HUD on the preview is pixel-for-pixel what production draws at 360px and at 1920px — say what you compared.

Files I expect to change

src/engine/overlay.ts, src/engine/overlay.test.ts (new), src/client/hud.ts, src/client/hud.test.ts (new), README.md, static/app.<hash>.js.

Risks

The HUD is drawing code with no test, so write the stub and pin today's behaviour before moving anything — otherwise nothing tells you what you broke. hud.ts imports MOVE from physics.ts and pull #7 renames that to CS_MOVE in src/client/movement.ts: rebase on main once #7 lands and keep the rename rather than reverting it, and rebuild the bundle rather than merging one by hand. Allocation matters here too — this runs every frame, so return the tools from one call per resize, not one per draw, and build no arrays per call.

Suggested: Vesper — canvas by hand, phone-first, and the frame cost measured.

commented

The engine half of this issue lands ahead of it as #49, and this one now waits for it: src/engine/overlay.ts, the structural Paint target, the tools (scale, pad, font, panel, text, crosshair, bar, columns, clock), overlay.test.ts against a recording stub, and the door's paragraph all move there — with the range's readout.ts as the consumer that proves it, since the range already declared that Paint type for itself.

What stays this issue's, unchanged: src/client/hud.ts draws through the tools with its output pixel-for-pixel what it is today — the same 4 + spread * 14 gap and 7-unit arms, the same 30/19/15px fonts, the same rgba(12, 14, 18, 0.55) panels, the same team colours, the same 8-second feed fade, the same 240×44 clock bar and 260×14 use bar, the same scoreboard columns and stride — plus src/client/hud.test.ts, which the largest untested module in the game still needs, and the preview compared at 360px and 1920px.

This half keeps its wait on #3 honestly: hud.ts is one of pull #7's seven conflicting files and MOVE becomes CS_MOVE in src/client/movement.ts there, so writing the HUD's tests against a name that is about to change is work done twice. #49 opens no file under src/client/.

commented

Two things now stand in front of this issue, and both make it smaller. #64 pins what the HUD draws today with a recording Paint and no browser, so the move this issue makes can be proved to have changed nothing — the same order #53 and #57 set. #62 replays the physics extraction on today's main, which is where MOVE becomes CS_MOVE in src/client/movement.ts; the edge to #3 stays, and #3 should be closed with a word when #62 lands.

What stays this issue's, unchanged: src/client/hud.ts draws through the engine's overlay tools that landed in #49 — the scale, the pad, the fonts, the panels, the crosshair, the bar, the columns and the clock — with its output pixel-for-pixel what it is today, and the HUD's own layout, colours, phases and words staying in the game. Move the numbers; do not tune one. The pixel comparison at 360px and 1920px is still the acceptance, and #64's pin is how you will know before you look.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs