Files
transcience/docs/ROADMAP.md
T
claude de48afcbd9
ci / verify (push) Successful in 46s
Fix aiming under a scrolling camera; add status and decisions docs
The aim bug was collateral from the camera work. "Mouse relative to the centre
of the screen" WAS the cursor's world position while the world was drawn fixed
at the origin, so subtracting the player position gave the right vector. Once
the camera scrolled, that expression became the aim vector itself, and
subtracting the player position again made the ship aim at a fixed world
location -- walking around swung the crosshair with the mouse held still.

Fixed by inverting the transform the view actually draws with (world = screen -
world_view.position, published by the game scene each frame) rather than
assuming the player is centred, so it still holds if the camera later clamps at
map edges or gets shake or look-ahead. tests/unit/test_aim.gd pins it, including
the regression directly: moving the player must not move the crosshair.

Documentation, for other sessions picking this up cold:
- docs/ROADMAP.md rewritten as the status map -- every feature in the brief
  against its state and the file implementing it, the known gaps called out
  (actor interest management is the notable one), and the ten design questions
  that are genuinely unspecified and should not be guessed at.
- docs/DECISIONS.md, new: settled decisions with their reasoning, so a session
  does not re-litigate or re-ask. Several are not the obvious default -- no
  i-frames, no contact damage, non-interruptible escape, and never sending the
  map seed.
- CLAUDE.md and README point at both.

137 tests; check.sh, test.sh and smoke.sh pass.
2026-09-03 20:58:03 +02:00

7.2 KiB
Raw Blame History

Status and roadmap

Read this first when picking up work. It maps every feature in the design brief to its current state and the files that implement it, so you can find the relevant code without re-reading the whole project.

Design decisions that are already settled — and the reasoning behind them — live in DECISIONS.md. Check there before asking the user something that may already have an answer.

Legend: done · partial (works, with a stated gap) · todo (not started)


Stage 1 — World and exploration · done

Feature State Where
Tile grid: movement / bullet / sight blocking done src/sim/map_grid.gd
Dungeon generation from (seed, depth) done src/sim/map_gen.gd
Hand-authored boss arenas and hub done src/content/rooms.gd
Walls, pillars, pits, barricades done MapGrid.Kind + the three BLOCKS_* tables
Enemies placed per room at creation done Instance._populate()
Scrolling camera done src/view/game_scene.gd
Hard fog of war done WorldView._draw_terrain / _visible
Per-peer map streaming (anti map-hack) done ServerRuntime._stream_map
Aggro: range and line of sight done SimWorld._aggro_target
Cursor-to-world aiming under a scrolling camera done ClientRuntime.screen_to_world
Boss confined to its room partial SimBoss.room exists and bounds curtain patterns, but nothing clamps the boss — every boss is stationary today. Enforcement belongs with boss movement in Stage 5.
Actor interest management todo The known gap. ServerRuntime._physics_process still encodes one snapshot and sends it to every peer in the instance, so clients are told about enemies the fog then hides. Terrain is properly withheld; actors are not. Do this first.

The invariant that matters most here

Maps are streamed per peer, and the generation seed is never sent. See NETCODE.md. MAP_STREAM_RADIUS must stay wider than FOG_VIEW_RADIUS, or the client predicts movement against terrain it does not have.


Stage 2 — Characters, persistence, levels · todo, next

Depends on nothing in Stage 1 except a place to stand. Blocked only on the identity layer, which is decided but unbuilt.

Feature Notes
Identity abstraction Shaped like Steamworks so it swaps out cleanly: opaque ticket from client → server validates → stable 64-bit account id (a SteamID64 stand-in). Local dev provider persists a generated id in user://. See DECISIONS.md.
Character store Server-side, keyed by account id. Needs a schema and a file format; nothing persistent exists in the project today.
Up to 5 characters, random colour each Colour is a placeholder for a later cosmetic system.
Last-played character auto-selected on join
Permadeath → mark inactive, never delete Archival/troubleshooting. Confirm whether the 5-character cap counts only active characters.
Death flow: pick another character or create one Replaces today's "return to hub" button, which currently just revives you.
Levels 115, +10 max HP per level Confirm whether level 1 is 100 HP (→ 240 at cap) or 110.
XP from kills, bosses worth much more First full dungeon should give slightly more than one level.

Current behaviour to replace: SimPlayer has no identity beyond a peer id; ServerRuntime.peer_names is client-supplied and trusted for display only.


Stage 3 — Upgrades · todo

Feature Notes
Lobby NPC, 3 random choices per level gained Confirm whether unclaimed level-ups queue.
Every upgrade also grants +5% damage, additive Displayed on the choice alongside its specific effects.
Upgrade list split shot, glass cannon, spread, sniper, doubleshot (rarer), poison (rare), eraser (legendary)
Rarity weights Unspecified. Needs numbers.
Damage stacking formula Unspecified. Sniper is explicitly multiplicative; the rest read as additive. Order needs pinning down before any of it is built.
"Chosen upgrades" screen

Damage is currently the constant SimConfig.PLAYER_BULLET_DAMAGE; upgrades turn it into a per-player computed stat, so SimWorld._fire_player_shot grows a stats block. Several upgrades (split, spread, doubleshot) change how many bullets spawn per shot, so they belong in the same place.


Stage 4 — Inventory and loot · todo

Feature Notes
Small always-on-screen inventory Slot count unspecified.
Health potions: rare from trash, guaranteed from bosses
World-shared loot Player-instanced loot planned later.
A unique, useless food item from bosses Exists specifically to test player-instanced loot. Confirm whether it should be instanced now or just marked for it.
Dropping items so others can pick them up

Stage 5 — Boss features and new bosses · todo

Feature State
Stationary phases done — every current phase
Roaming / chasing within the boss room todo; needs SimBoss.room clamping (see Stage 1 partial)
Phases that move to preset locations todo
Attacks spawned at a distance with a telegraph indicator todo — new event type plus a renderer, and it must survive fog
More bosses partial — Rooms.choir_vault() is authored but has no BossDef yet

The boss format itself is proven: tests/unit/test_boss.gd builds a boss from scratch and asserts the simulation needs no changes to run it. Movement will be the first thing that format has not covered, so expect BossPhase to gain a movement field rather than SimWorld gaining a branch.


Deliberate omissions

Not oversights — each was considered and rejected for now, with the reasoning in NETCODE.md or DECISIONS.md:

  • Lag compensation. Rewinding to a shooter's view means a player who dodged still gets hit; wrong trade for this genre.
  • Snapshot delta compression. Fine at current actor counts.
  • DTLS / encryption. ENetMultiplayerPeer supports it. Required before any public server, not before then.
  • Pattern-level bullet replication. A real bandwidth win that couples the client to emitter behaviour.
  • MultiplayerSynchronizer / MultiplayerSpawner. Right tools, wrong shape for a bullet hell — see ARCHITECTURE.md.

Open questions for the user

Genuinely unspecified; do not guess at these, they change the design:

  1. Damage stacking order — additive pool then multiplicative, or something else?
  2. Rarity weights for the four upgrade tiers.
  3. Split shot geometry: ±22.5° from the original heading, or 45° to each side?
  4. Poison: do applications stack, or refresh a single DoT?
  5. Eraser: does it delete enemy bullets it passes through?
  6. Inventory slot count.
  7. Do unclaimed level-ups queue at the NPC?
  8. Does the 5-character cap count only active characters?
  9. Glass cannon's 50% health: of base HP, or of levelled max HP?
  10. What advances dungeon depth? --depth is a dev flag; nothing raises it in play.