claude 801f328093
ci / verify (push) Successful in 46s
Fix idle/run animation flicker and bullet cell bleed; add F1 hitbox overlay
The ship looked like it played both animations at once because it effectively
did. is_moving() read the tail of `pending`, which _reconcile drains on every
snapshot -- so it returned false about 20 times a second and the sprite
alternated between the run and idle strips. Now reads the last sampled input,
which survives the queue being emptied.

Bullets were still wrong for a reason my earlier ASCII check could not see: I
had dumped alpha only, and this pack animates as a colour shimmer over a fixed
silhouette, so identical-looking frames told me nothing. The actual defect was
that several sprites overflow their 16px cell and bleed into the neighbour
below -- the first "heavy" I picked dragged in a solid slice of the sprite
above it. Cells are re-picked to ones with empty borders in every frame, and
build_local_assets.py now asserts that rather than trusting the choice, so a
future pick that bleeds fails at build time instead of looking like a
rendering bug.

Added a hitbox overlay on F1 (src/view/debug_draw.gd). It draws what the
simulation actually collides against over what is drawn: player hitbox against
sprite radius, the muzzle point, enemy and boss radii, aggro rings, every live
bullet's radius, and terrain outlined by which of the three flags each tile
sets -- so a pit that stops feet but not bullets looks different from a wall.
It deliberately ignores fog, since hiding half the evidence would defeat the
point, and it reads every number from SimConfig/MapGrid/Content rather than
keeping its own copies, or it would just confirm its own mistakes.

Nearly every "that looked wrong" report in this project has been art and
simulation disagreeing, and each took a round trip to diagnose. This makes that
class of bug visible directly.

163 tests. check.sh, test.sh and smoke.sh pass.
2026-09-04 00:12:37 +02:00
2026-09-03 16:03:57 +02:00
2026-09-03 16:03:57 +02:00
2026-09-03 16:03:57 +02:00
2026-09-03 16:03:57 +02:00

Transcience

Top-down twin-stick bullet-hell with a dedicated, server-authoritative backend. Godot 4.7, GDScript, no external runtime dependencies.

Play

tools/server.sh                      # dedicated server on :27015
tools/client.sh --join --name ada    # connect a client

Or run one client and press Host and play for a listen server.

Controls — WASD move, mouse aim, LMB fire, E on the ring in the hub to enter a dungeon, hold F for three seconds to escape back to the hub.

Develop

tools/check.sh    # parse-check every script      ~5s
tools/test.sh     # GUT suite, headless           ~2s
tools/smoke.sh    # server + 2 bot clients, ENet  ~35s
godot --headless --path . --script tools/bench.gd

Read CLAUDE.md first — it is short, and the Godot CLI gotchas in it will otherwise cost an afternoon.

What is here

Twin-stick bullet hell Server-simulated bullet field, ~350 concurrent bullets at 0.24 ms/tick.
Authoritative multiplayer Clients send input only. Hits, damage, death and instance transfers are server decisions. Prediction + reconciliation for the local player.
Enemies Four readable behaviours (static, drift, orbit, approach, strafe) composed with bullet emitters.
Boss The Warden of the Fold: stationary, four phases, each adding one idea. Defined entirely as data.
Lobby hub Shared persistent instance with a portal into dungeon runs.
Emergency escape Three-second channel back to the hub, cancelled by damage.

Docs

S
Description
No description provided
Readme 2.4 MiB
Languages
GDScript 98.6%
Shell 1.1%
Python 0.3%