The roadmap was actively misleading: an earlier stage renumbering left Stage 2's completed work sitting under a "Stage 3 -- todo" heading, and the inventory table orphaned with no heading at all. Since that file is the primary handoff document, a fresh session would have started by re-implementing accounts and characters. Rewritten. Captured in full, from the original brief rather than from memory, the two stages not yet built: - Stage 3 (inventory and loot): 4 slots, potions rare from trash and guaranteed from bosses, world-shared loot, and the player-instanced food item -- with a note that two loot visibilities must exist from the start, because proving the instanced path works is the food item's entire purpose. - Stage 4 (upgrades): every upgrade with its exact stated effect, plus the two constraints it will collide with -- sniper's 2x bullet speed against the tunnelling threshold, and per-player bullet travel against the interest radius that test_interest.gd currently derives from static content. Ten open questions are listed as explicitly do-not-guess, seven of them blocking Stage 4. ARCHITECTURE.md still claimed "the arena is a rectangle" and "no tilemap collision", both untrue since Stage 1. Rewritten around MapGrid, with what the grid costs (axis-aligned, 32px-quantised, bullets under a tile per tick) rather than only what it buys. Added a "verification traps" section to WORKFLOW.md recording six mistakes made during this work, each of which cost a round trip of reporting something fixed that was not: verifying the artefact rather than the behaviour, dumping the wrong channel, measuring a configuration where the bug cannot exist, a test whose setup silently invalidated it, git checkout reverting real work alongside a probe, and a pattern edit matching in two files. They are specific enough to be actionable. Also documented the diagnostics in CLAUDE.md -- they were undiscoverable -- and recorded the current verification surface so "everything passes" has a stated meaning. 206 tests, 15 smoke assertions, both diagnostics pass.
This commit is contained in:
+38
-6
@@ -45,11 +45,32 @@ This project deliberately does not, for four reasons:
|
||||
prototyping and wrong for this shape: it would replicate per-bullet state and
|
||||
saturate the link. See [NETCODE.md](NETCODE.md).
|
||||
|
||||
The trade is that there is no physics engine — no arbitrary collision shapes, no
|
||||
tilemap collision. The arena is a rectangle and every hit is circle-vs-circle.
|
||||
For a bullet hell that is not a limitation; hitboxes in the genre are circles by
|
||||
convention anyway. A dungeon with real geometry would need a static collision
|
||||
representation added to `SimWorld`, not a switch back to nodes.
|
||||
The trade is that there is no physics engine, so collision is hand-written.
|
||||
Since Stage 1 that means a tile grid ([MapGrid](../src/sim/map_grid.gd)):
|
||||
circle-vs-tile for actors, a point test for bullets, and Bresenham line of sight
|
||||
shared by fog and aggro. Actor-vs-actor hits stay circle-vs-circle, which is the
|
||||
genre convention anyway.
|
||||
|
||||
That was the right call rather than a compromise: the same grid answers
|
||||
collision, sight, and per-peer interest management as array lookups. On arbitrary
|
||||
polygons all three become intersection tests, and the fog query in particular
|
||||
stops being cheap enough to run every frame.
|
||||
|
||||
What it costs: geometry is axis-aligned and 32px-quantised, and bullets must
|
||||
travel less than one tile per tick or the point test steps over walls (pinned by
|
||||
`test_bullet_speeds_stay_below_the_tunnelling_threshold`).
|
||||
|
||||
## Where the layers sit now
|
||||
|
||||
Stage 1 and 2 added two things worth knowing before reading any file:
|
||||
|
||||
- **Geometry is per-world.** There is no global arena. `SimWorld.map` is a
|
||||
[MapGrid](../src/sim/map_grid.gd); the hub and every dungeon have their own,
|
||||
and the client holds a *partial* copy streamed to it in chunks.
|
||||
- **`src/meta/` is server-only.** Accounts, characters, levels and experience
|
||||
live there. The simulation reads a player's level and maximum health; it never
|
||||
writes progression. One writer means a level cannot disagree with the
|
||||
experience that earned it.
|
||||
|
||||
## Tick
|
||||
|
||||
@@ -100,4 +121,15 @@ after `--`:
|
||||
| `src/net/client_runtime.gd` | Prediction, reconciliation, interpolation, bot input. |
|
||||
| `src/instances/instance.gd` | Lobby hub and dungeon progression. |
|
||||
| `src/autoload/net.gd` | ENet lifecycle, RPCs, local loopback for listen servers. |
|
||||
| `src/view/bullet_renderer.gd` | The whole bullet field in one MultiMesh draw call. |
|
||||
| `src/sim/map_grid.gd` | Tile grid: collision, line of sight, chunked streaming. |
|
||||
| `src/sim/map_gen.gd` | Dungeon generation. `build()` is the only entry point both sides use. |
|
||||
| `src/content/rooms.gd` | Hand-authored stamps: the hub and each boss arena, as text. |
|
||||
| `src/meta/progression.gd` | XP curve and what a level is worth. Pure functions. |
|
||||
| `src/meta/character.gd`, `character_store.gd` | Characters and their JSON persistence. Server-owned. |
|
||||
| `src/meta/auth_provider.gd` | Identity, shaped like Steamworks so it swaps out. |
|
||||
| `src/net/net_codec.gd` | Snapshot / event / input / roster / character / map-chunk codecs. |
|
||||
| `src/net/server_runtime.gd` | Instances, ticking, transfers, interest, progression, map streaming. |
|
||||
| `src/view/bullet_renderer.gd` | The bullet field: one MultiMesh per bullet kind. |
|
||||
| `src/view/art.gd` | Every atlas rect and sound path, in one table. |
|
||||
| `src/view/debug_draw.gd` | F1 overlay: what the simulation collides against. |
|
||||
| `src/ui/character_select.gd` | Roster screen: pick or create. |
|
||||
|
||||
Reference in New Issue
Block a user