Commit Graph

3 Commits

Author SHA1 Message Date
claude 7ef972e3b3 Hub character swapping, XP percentage, passive health regeneration
ci / verify (push) Successful in 47s
Character swapping is hub-only, and refused by the SERVER rather than merely
greyed out in the menu. Allowing it inside a dungeon would be an instant,
uninterruptible exit from danger -- strictly better than the one-second escape
channel, which would make that channel pointless. Creating a character in a
dungeon is refused for the same reason. Both are covered by diag_progression.

Health regenerates at 0.5% of MAXIMUM per second. A percentage rather than a
flat rate so it does not become irrelevant at level 15: a capped character
regains 1.2 hp/s against a level 1's 0.5, and both take about 200 seconds to
heal from nothing. No out-of-combat gate -- at this rate it cannot out-heal
anything actually shooting at you, and a trickle that never stops is easier to
reason about than a timer players have to learn. A fractional carry is needed
because a tick heals well under one hit point, so truncating each tick would
heal exactly nothing; there is a test for that specifically.

The XP bar now states the percentage and the level it leads to, since a bar
answers "how far" vaguely and a number answers it exactly.

Recorded the two Stage 3 answers: 4 inventory slots, and the boss's food item
is player-instanced now so the mechanism gets exercised rather than deferred.

Writing the swap guard's test caught my own mistake: the first version created
its spare character through the store, which has no opinion about where you
are, so it bypassed the guard it was meant to prove and left a stray character
behind that broke a later assertion.

200 tests. check.sh, test.sh, smoke.sh and both diagnostics pass.
2026-09-04 00:56:29 +02:00
claude e1f9fa8096 Unlock menu after failed join; remove contact damage; cover clean disconnects
ci / verify (push) Successful in 45s
A failed connection left the connect buttons disabled forever. _show_menu()
returned early when a menu already existed, so the set_busy(false) that
re-enables them never ran on the way back from _abort_connect -- the player was
locked out of both joining and hosting with no way out but a restart. Also
guards against a second attempt stacking on an in-flight one.

Contact damage is gone as a concept: nothing in this game hurts you by touching
it, because every threat should be a bullet you can see and dodge. The Stalker
walked at you and dealt contact damage and nothing else, so it now carries a
point-blank shotgun instead -- five pellets, 62 degrees, 18-tick lifetime for
about 78px of reach, so it still has to close and leaves nothing lingering.
tests/unit/test_content.gd asserts every hostile enemy has an emitter, so a new
enemy cannot quietly reintroduce the mechanic.

The menu's "Disconnect" DOES get the anti-cheese protection -- verified, not
assumed. It calls Net.shutdown(), the socket closes, and the server takes the
same linkdead path as a SIGKILL, because the channel is keyed on the socket
closing rather than on how it closed. Made permanent: a --leave-after bot flag
and two smoke assertions covering the polite exit alongside the hard kill, so a
future "clean leave" message that skipped the channel would fail a test. The
menu now says so out loud in a dungeon -- the mechanic only reads as fair if
the cost is known before clicking.

103 tests, 12 smoke assertions; check.sh, test.sh and smoke.sh all pass.
2026-09-03 19:37:26 +02:00
claude 005679f1b5 Fix bullet/ship desync; rework death, escape, arrival and hub awareness
(1) Bullets appeared to trail the ship. Two independent causes, measured with
the new tools/diag_prediction.gd rather than guessed at:
  - ServerRuntime ticked before ClientRuntime, so input sampled on frame N was
    not consumed until frame N+1, leaving the drawn ship a constant one tick
    (4.00px at 240 u/s) ahead of the authoritative one that bullets spawn from.
    ClientRuntime now sets process_physics_priority = -10. Gap on a listen
    server: 4.00px -> 0.10px mean, 0.30px worst.
  - PLAYER_MUZZLE_OFFSET was PLAYER_RADIUS + 6 = 12px against a 13px drawn
    ship, so bullets were born inside the sprite. Regression from the previous
    commit's hitbox shrink; it now derives from PLAYER_VISUAL_RADIUS.

(2) No more timed respawn. A downed player stays down until they ask for the
hub (E), which is an ordinary input -- the server has no "revive me" message.

(3) Escape channel 3s -> 1s, and damage no longer cancels it. An interruptible
channel makes killing the process strictly better than using the button, so a
dropped connection now runs the same channel: the player stays in the world as
linkdead, still killable, and is only released once it completes. Instances
refuse to close while a linkdead body is resolving, or a solo drop would delete
it on the next tick and hand the exploit straight back.

(4) Escape opens an in-game menu: return to hub (routed through the same held-
escape channel, not a new message), disconnect, quit.

(5) Server pushes a roster so the hub shows who is online and which dungeon
they are in. Entering a dungeon grants 2s arrival protection -- invulnerable
AND weapons-cold, since invulnerability alone would make the spawn a free
firing position -- flagged in the snapshot and drawn on every protected ship.

(6) Cleared dungeons hold the party 30s (was 5s) with a visible countdown.

(7) The hub's grey circle was a 100k-HP target dummy that read as scenery. Now
drawn as a bullseye so its purpose is legible.

Protocol version 1 -> 2. 91 tests (was 78); smoke.sh gains a bot that is
SIGKILLed mid-dungeon to prove the disconnect path end to end. check.sh,
test.sh and smoke.sh all pass.
2026-09-03 18:43:19 +02:00