Skip to content
0.5.0 — latest

Starmap

View as Markdown

The world entry covers the map: star systems and the bodies inside them. It is the shared, static-ish layer that fleets move through and starbases sit in.

const system = await sage.systems.byId(systemId);
const bodies = await system.celestialBodies.all();

Systems have a numeric id as well as an account address, and you can read by either:

const byId = await sage.systems.byId(3);
const byAddress = await sage.systems.get(systemAddress);

The id is the friendlier handle when you already know which system you mean. The address is what other accounts reference, so it is what you will have when arriving from a fleet’s state.

A fleet’s location lives inside its state, so narrow first:

if (fleet.state.kind === 'docked') {
const system = await sage.systems.get(fleet.state.system.address);
}

That indirection is deliberate — see fleets for why location is not a flat field.

Systems are not floating islands — the starmap is a network, and warp lanes are its links. Every system lists its own:

const neighbours = system.connections.map((connection) => connection.systemId);

Each connection also carries the lane’s toll — an ATLAS cost per starbase level (connection.costs.level1Atlas through level5Atlas, plus the CSS tier) — so “what does this jump cost” is a read, not a guess.

Whether a lane is usable is a faction question: both ends must be held by the same faction, and a system’s controller is on its shared starbase data:

const controller = system.starbase?.owner; // 'mud' | 'oni' | 'ustur' | …

Travelling a lane is a fleet move — planFleetWarpLane in Warp & subwarp — and the fee a character actually pays can shrink with Council Rank research.

Celestial bodies are the things inside a system worth interacting with:

const planets = await system.planets.all();
const asteroids = await system.asteroids.all();

Both are projections over the same underlying body accounts, filtered to the kind you asked for. Asteroids are where mining happens; planets are where claim stakes go.

Shared starbase data — where it is, what level it is — belongs to the world. A player’s own state at that starbase is separate and lives in starbases.

const bases = await system.playerStarbases.all();

The split exists because the two have different lifetimes and different readers: the starbase itself is shared infrastructure, while your cargo sitting in it is yours.

systems.all() is a broad read. It exists, but it fetches a lot. Prefer reading the systems you actually need by id.

Bodies are not evenly distributed. A system may have no asteroids, or many. Write for both.

Coordinates are game-space, not screen-space. They position bodies relative to each other within the galaxy, and do not map onto any rendering directly.

  • world — every export in this entry
  • starbases — player state at a starbase
  • mining — what asteroids are for