# How the code works

A plain-English tour of the `mvp/` game — enough that you can follow what your engineer
agent changes, and know which file to point it at. No prior orbital-mechanics knowledge
assumed.

## The one big idea: "patched conics on rails"

Real spaceflight is the gravity of *every* body pulling on you at once — too expensive and
too jittery to simulate by brute force. So this game uses the same trick Kerbal Space
Program does: **at any moment the ship is influenced by exactly one body** — whichever
"sphere of influence" (SOI) it's currently inside. Around that one body, the path of a
coasting ship is a perfect, exact mathematical curve — a *conic section* (circle, ellipse,
parabola, or hyperbola). We don't integrate it step by step; we just **evaluate the
formula** for "where are you at time t." That's what "on rails" means: the ship rides an
exact curve like a train on a track.

When you cross from one body's SOI into another's (Earth → Moon), we hand the orbit off to
the new body and compute a fresh conic. Stitching these conic segments together is the
"patched" part. **Patched conics = a chain of exact curves, one per body.**

The payoff: the blue trajectory line you see is computed from the *same* orbit object the
ship actually flies — so the prediction is never a guess. It's exactly where you'll go.

There's one exception to "on rails": when thrust, atmosphere, or the ground are involved,
exact curves don't apply, so we briefly switch to step-by-step physics. That's the "two
regimes" idea below.

## The files, and what each one owns

```
config.js  →  world.js  →  game.js
   (data)     (physics)    (everything you see + touch)
   orbit.js  ←  the math both physics and the predictor lean on
```

### `config.js` — the world as data
Pure numbers, no logic. The Sun/Earth/Moon (real masses, radii, distances in SI units), the
ship parts (mass, thrust, fuel), atmosphere settings, tuning knobs. **Adding a planet or a
new part is a one-line edit here** — everything else (orbits, SOIs, rendering) is derived
from this list automatically. This is the first file to open when you want to change *what's
in the world* rather than *how it behaves*.

### `orbit.js` — one orbit, explained
A single class, `Orbit`. You hand it a position + velocity at some instant; it figures out
the conic curve that passes through that state and can then answer "where + how fast are you
at any time t?" and "what's the shape of this curve?" (for drawing). This is the exact-math
core. It's dense because the formulas are dense, but it's self-contained — it knows nothing
about ships, rendering, or the game. The heavy comments explain the numerical landmines
(near-vertical falls, escape trajectories) and how they're dodged.

### `world.js` — the physics and the rules
The bridge between the pure math and the game. It owns:
- **`bodyStates(t)`** — where every planet/moon is at time t (they ride simple circular rails).
- **`advanceShip(...)`** — move the ship forward by a tiny slice of time. This is where the
  **two regimes** live (see below).
- **`predictPath(...)`** — compute the blue trajectory line: follow the current orbit forward
  until the first "event" (hit the ground, escape to another SOI, enter a moon's SOI).
- **`spawnShip(...)`** — drop a freshly-built ship into a low orbit.

### `game.js` — input, the builder, and all the drawing
The biggest file (~750 lines) and the one you'll touch most for *feel* and *UI*. It handles:
keyboard/mouse, the ship **build screen** (the grid where you snap parts together), the
**flight HUD** (telemetry, throttle, fuel/heat bars), the **map view**, the starfield, and
the main loop that ties it together. It's a "do-everything" file right now — fine at this
size, but it's the first candidate to split as the game grows (see TODO).

## The two regimes (the heart of `advanceShip`)

Every frame, the ship is in one of two modes:

1. **On rails (analytic Kepler).** The default when you're coasting in space. We jump the
   ship along its exact conic curve. Cheap, perfectly stable, and it lets you **time-warp**
   thousands of times faster without the simulation falling apart.

2. **Physics (numerical integration).** Kicks in when *exact curves don't apply*: you're
   **thrusting**, you're **in atmosphere** (drag + re-entry heat), or you're **about to
   touch / resting on the ground**. Here we step forces forward in small increments. It's
   more expensive, so time-warp is capped while it's active (you can't warp through a burn
   or a landing).

The function `needsPhysics(...)` decides which regime applies, and the warp limiter uses the
*same* function — so the two can never disagree about what mode you're in.

## How a single frame flows

```
frame():
  1. read held keys → turn / throttle
  2. pick a time-warp-aware time step
  3. advanceShip()  →  rails OR physics, possibly several sub-steps per frame
       ↳ handle SOI crossings, ground contact, re-entry heat, fuel burn
  4. predictPath()  →  recompute the blue trajectory line
  5. draw everything (build screen, or fly/map view + HUD)
  6. requestAnimationFrame(frame)   // ~60×/second
```

## "I want to change X — where do I look?"

| You want to… | Open | Roughly where |
|---|---|---|
| Add/edit a planet or moon | `config.js` | the `bodies` array |
| Add/edit a ship part | `config.js` | the `parts` object |
| Tune flight feel (thrust, drag, turn rate, heat) | `config.js` | the top-level knobs |
| Change the orbit/trajectory math | `orbit.js` / `world.js` | `Orbit`, `predictPath` |
| Change when physics vs rails kicks in | `world.js` | `needsPhysics`, `advanceShip` |
| Change the build screen / HUD / map look | `game.js` | `drawBuild`, `drawHUD`, `drawFly` |
| Change controls / keybinds | `game.js` | the `keydown` handler near the top |

## Sanity net

`node mvp/test.mjs` runs 15 fast checks on the physics/orbit model (orbits stay bounded,
the drawn path actually passes through the ship, landings don't fall through the planet,
etc.). Run it after any change to `orbit.js` / `world.js`. If it goes red, something real
broke.
