Go to file
Brian Fertig e7b52cc33d Add the home planet: a solid world the ship can approach but not enter
Adds the player's home planet to the starting system, rendered from the new
planets.png spritesheet. The planet is a solid disc: the ship may come within
shipClearance of its rim but can never cross it, and clicks on the planet
clamp to the keep-out rim so the ship always has a reachable destination.

- js/entities/Planet.js: solid-disc entity with a circle keep-out constraint
  (constrainShip/resolve, pure and scene-free) plus edgePoint/aimPoint helpers
- data/planets.json (+ manifest entry): texture, frame size, scale, homePlanet,
  shipClearance, spawnDistanceFromEdge
- Ship.js: exposes a radius (half the hull width) for the keep-out math
- GameScene.js: loads the spritesheet, spawns the home world at the origin,
  starts the ship ~150 px off its rim in a seed-derived direction, and applies
  the constraint each frame after the ship moves
- dev/planet.test.mjs: Node tests of the constraint against real config;
  dev/test-game.html gains a <base href> so asset paths resolve from /dev
2026-09-03 14:44:01 -06:00
assets/images Add the home planet: a solid world the ship can approach but not enter 2026-09-03 14:44:01 -06:00
data Add the home planet: a solid world the ship can approach but not enter 2026-09-03 14:44:01 -06:00
dev Add the home planet: a solid world the ship can approach but not enter 2026-09-03 14:44:01 -06:00
docs Add a lived-in layer: settlements in generated systems 2026-09-03 11:25:03 -06:00
js Add the home planet: a solid world the ship can approach but not enter 2026-09-03 14:44:01 -06:00
lib Add v0.1 foundation: menu, click-to-fly ship, and JSON config system 2026-09-02 22:31:34 -06:00
README.md Add the home planet: a solid world the ship can approach but not enter 2026-09-03 14:44:01 -06:00
index.html Add v0.1 foundation: menu, click-to-fly ship, and JSON config system 2026-09-02 22:31:34 -06:00

README.md

Orbit

A procedurally generated space RPG in the spirit of Privateer, in top-down 2D. Built with Phaser 4 as plain ES6 modules — no build step, no package managers required to run.

Run it

Any static file server works (Python, Node, Caddy, nginx, …):

cd orbit
python3 -m http.server 8080
# → http://localhost:8080

Must be served over http(s) — opening index.html via file:// won't work, because the game uses ES modules and fetches its JSON config.

Current state — v0.2: a seedable galaxy

  • Main menu with New Game and a Galaxy Seed panel: the seed is displayed, editable (click it and type), and rerollable — and the menu shows what that seed builds (the galaxy's name, system count, archetype count) before you commit. Same seed ⇒ same galaxy.
  • Procedural galaxy: 40,000 star systems in a seeded disk + core + spiral arms (data/galaxy.json), typed into six themed archetypes (data/systems.json) with per-type distribution weights and radial bands — the first "how does the galaxy lay itself out" rules.
  • Two-level generation: the whole galaxy roster is generated at New Game (~70 ms); each system's planets/moons/belts/settlements are generated lazily on arrival, deterministically (seed + system id), so lazy and eager give identical results.
  • A lived-in galaxy: the galaxy was settled long before you arrive. Systems host colonies on habitable worlds, mining stations over resource worlds, cloud bases riding gas giants, stations adrift in open space, and beacons — or report charted · unclaimed when nobody's there. Rates are per-archetype (data/systems.json) and thin out from the settled core to the wilder rim (data/galaxy.json). Each settlement has a name, population, and an owner seam reserved for the factions/pirates to come. The current system's dossier (name, identity, what's there) shows top-left in the game scene.
  • Game screen with a basic top-down ship: click anywhere to fly there in the current system's open space (system boundaries/jumps come next)
  • The home planet — the player's Terran world — in the system you start in: a 1024 px disc rendered 1:1 from frame 0 of assets/images/planets.png. The ship spawns ~150 px (edge-to-edge) off its rim in a seed-derived direction (same galaxy ⇒ same start), may fly in as close as 50 px from the rim, and can never cross it — a planet is solid (tuning in data/planets.json)
  • Camera gently trails the ship; the parallax starfield streams past while it flies and the view slowly recenters (≈1.5 s) once the ship comes to rest
  • Config-driven setup: every tunable value lives in data/*.json

Project layout

orbit/
├── index.html              # boots the game
├── data/                   # ← ALL tunable config (edit these freely)
│   ├── manifest.json       #   which config files exist
│   ├── game.json           #   dimensions, colors, starfield, …
│   ├── menu.json           #   menu text, colors, button layout, seed panel
│   ├── ship.json           #   ship feel: thrust, drag, maxSpeed, …
│   ├── galaxy.json         #   galaxy scale & shape (count, radius, spiral…)
│   ├── systems.json        #   system archetypes: theme, attributes, distribution
│   ├── settlements.json    #   the lived-in layer: settlement kinds & populations
│   └── naming.json         #   syllable pools for names
├── assets/images/          # art: planets.png (1024×1024 spritesheet frames)
├── lib/                    # vendored third-party libs (Phaser 4.2.1)
├── js/
│   ├── main.js             # entry point: load config → boot Phaser
│   ├── config/             # Config singleton, ConfigLoader, game config
│   ├── scenes/             # MenuScene, GameScene (thin, orchestration)
│   ├── entities/           # Ship (own behavior), Planet (home world, solid)
│   ├── galaxy/             # Galaxy (seeded world model), SystemGenerator, SystemReport
│   ├── ui/                 # MenuButton (reusable)
│   ├── visuals/            # Starfield (decorative)
│   ├── utils/              # small pure helpers (Color, Rng, NameGenerator)
│   └── vendor/             # shim to the vendored Phaser
└── docs/PROJECT_NOTES.md   # ← project conventions: read this

Conventions (short version)

  • Config in JSON. If a value might change, it goes in data/, not code. Add a file = one line in data/manifest.json.
  • One class per file, ES modules, scenes stay thin, entities own their behavior. Details in docs/PROJECT_NOTES.md.
  • Phaser is imported only via js/vendor/phaser.js (one-file version swap).

Dev tools

node dev/ship-behavior.test.mjs   # runs the real Ship.update() loop in Node
node dev/starfield.test.mjs       # runs the real Starfield.create() in Node
node dev/galaxy.test.mjs          # galaxy determinism, distribution, lazy vs eager

dev/test-game.html boots straight into the GameScene (no menu click), handy for manual testing of the flight feel.

Phaser

Phaser 4.2.1 is vendored at lib/phaser.min.js (UMD build, MIT license — see lib/PHASER_LICENSE.md). No internet or npm needed at runtime.