claude d9a59fff03 Fix lobby/HUD UI pinned at (0,0); shrink player hitbox below visual size
The lobby connect menu and three pieces of the HUD (hit-flash overlay, boss
bar centering, death-message centering, hint label) were all silently broken
by the same Godot gotcha: set_anchors_preset(preset) with the default
keep_offsets=false does NOT zero the offsets to the preset's margins -- it
recomputes them to preserve the control's *current* rect, which for a
freshly constructed Control is (0,0). Anchors end up correct; the actual
rect stays pinned to the top-left corner regardless. Fixed by switching to
set_anchors_and_offsets_preset() everywhere a Control is built in code, and
by using offset_left/offset_top (anchor-relative) instead of .position
(absolute) for the HUD hint label. Documented as gotcha #5 in CLAUDE.md.

Also split PLAYER_RADIUS into two constants: PLAYER_RADIUS (6.0, the
authoritative hitbox used by SimWorld) and PLAYER_VISUAL_RADIUS (13.0,
view-only, used by world_view.gd). The client renders every ship a little
late relative to the server -- interpolation delay, reconciliation
smoothing -- so a hitbox that matched the sprite would let bullets connect
against a ship the player watched dodge clear of them. A smaller hitbox
means the occasional bullet visibly clips the sprite without a hit, which
reads as more forgiving of latency than the reverse.

Verified headlessly: a throwaway scene instantiating MainMenu/HUD under a
real 1280x720 viewport, asserting the panel is centered and _canvas.size /
hint position resolve correctly, before and after each fix. check.sh,
test.sh (78/78) and smoke.sh all pass.
2026-09-03 16:58:42 +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
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
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%