- Remove the drawn slot icons (research/build/ship/menu/reserved) and their RGB ghost copies; keep the GlitchText split on labels and panel outline only. - Center button text vertically and shrink the bar height from 92 to 56 px now that there is no icon row. - Drop iconSize/iconY config and the drawIcon helper, and update README/comments to match the label-only design. |
||
|---|---|---|
| assets | ||
| data | ||
| dev | ||
| docs | ||
| js | ||
| lib | ||
| README.md | ||
| index.html | ||
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.htmlviafile://won't work, because the game uses ES modules andfetches 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 anownerseam reserved for the factions/pirates to come. The current system's dossier (name, identity, what's there) shows top-left in the game scene. - Names with a point of view (
data/naming.json): planets draw from a deep curated bank — colonial "New Denver" worlds beside alien "Klaxoria" worlds — and stations from a bank of official designations ("Deep Space SC-145") and smuggler hangouts ("Hell's Hideout"). Names are dealt out randomly per system without repeats until the bank is exhausted (a seeded shuffle, so the first N draws are N distinct names). Stars and the galaxy itself are still synthesised from syllable pools, so you never run out of those. Within a system a planet never shares a name with another planet and a station never with another station. - Game screen with a top-down ship (real spritesheet art — frame 0 of
assets/images/ships-player.png, seedata/ship.json): click anywhere to fly there in the current system's open space (system boundaries/jumps come next). Discovered worlds get a screen-edge arrow + a name tag showing the world's name (e.g.HOME WORLD · ESHKAELURA); clicking the name tag autopilots the ship there (it arrives on the keep-out rim, facing the world) - The home planet — the player's world — in the system you start in:
a 1024 px disc rendered 1:1 from the
assets/images/planets.pngspritesheet, with a proper name drawn from the planet name bank (the first name off the starting system's deck, so it never clashes with one of the other worlds). Which Terran face it shows (frames 0–2) is a seed-deterministic pick, so the same galaxy always yields the same home world. The ship spawns ~150 px (edge-to-edge) off its rim in a seed-derived direction, may fly in as close as 50 px from the rim, and can never cross it — a planet is solid (tuning indata/planets.json) - The tether — your range. You start with a level-1 tether anchored
on the home world: a circular zone, 5120 px from the planet's center
(
data/tether.json). The ship cannot leave it — anywhere in the union of its tethers' zones is open space (where zones overlap there is no line and no wall), and the rim is a hard barrier rendered as a thick, glitchy, marching dotted line (neon core + RGB fringe + ambient glitch bursts, contact shudder where the ship bumps it). Click-to-fly and autopilot targets are clamped to the union boundary, so a world outside your range is where the ship parks — on the line — until a tether grows. Upgrades and extra anchors (planets/stations) plug intoTetherField.add/setLevel(tuning indata/tether.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
│ ├── theme.json # shared UI theme: fonts, palette, CRT/glitch tuning
│ ├── ship.json # ship feel: thrust, drag, maxSpeed, …
│ ├── planets.json # home world + system layout + solid-disc rules
│ ├── tether.json # the tether (your range): level radii, barrier line, glitch, contact
│ ├── galaxy.json # galaxy scale & shape (count, radius, spiral…)
│ ├── systems.json # system archetypes: theme, attributes, distribution
│ ├── settlements.json # the lived-in layer: settlement kinds & populations
│ ├── research.json # RESEARCH: time-based, one at a time, gates builds/research
│ ├── builds.json # BUILDING: credits + minerals, ship/planet/station upgrades
│ ├── actionbar.json # the command deck: 6 slots (Research, Build, Ship, ·, ·, Menu)
│ └── naming.json # names: star/galaxy syllable pools + the curated PLANET & STATION name banks
├── assets/images/ # art: planets.png (1024×1024 frames), ships-player.png (256×256 frames)
├── assets/fonts/ # UI typefaces: Ethnocentric (headers), Centauri (body)
├── 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
│ ├── tether/ # Tether (pure range math) + TetherField (constraint + barrier line)
│ ├── ui/ # MenuButton, GlitchText, CyberShape, ActionBar, DiscoveryCompass (reusable)
│ ├── visuals/ # Starfield, CyberOverlay (CRT/glitch, shared)
│ ├── 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 indata/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).
The player's loop: research + building
Beyond flying, Orbit is built around two progression verbs — both are data layers now, with the rules and panels to come next:
- Research (
data/research.json) — time-based. A project takes a fixedduration; the player researches one thing at a time (maxConcurrent: 1). Research is the gate: it unlocks builds and further research.projectsis an empty map for now; the_templateentry documents the shape each project must have. - Building (
data/builds.json) — cost-based, paid in credits and minerals (resources). A build improves the ship, a planet, or a space station (category).buildsis an empty map; the_templateentry documents the shape (includingrepeatable, for things like extra mining rigs). - The command deck (
data/actionbar.json, rendered byjs/ui/ActionBar.js) — the cyberpunk bar across the bottom of the screen. Six evenly spaced slots: Research, Build, Ship, ·, ·, Menu. The slots fireonActionand are otherwise inert; two slots are reserved. Dressed with the menu's CRT language — scanlines over the strip and the GlitchText RGB pull-apart on labels and the panel outline during bursts. All layout, palette, and motion (boot flicker, rail comet, glitch bursts, RGB split, scanlines) live in the config.
_-prefixed keys (_comment, _template, …) are documentation, not
runtime data — loaders and tests ignore them.
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
node dev/discovery.test.mjs # discovery rules + compass geometry + chip hit test
node dev/tether.test.mjs # tether range math: union, clamp, visible arcs (no line in overlaps)
node dev/research-builds.test.mjs # data contract: research/builds/actionbar shapes + manifest
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.