Skip to content

Movement & Travel Times

Canonical model for how ships traverse sectors, what Ship.current_speed actually does, what a warp tunnel costs to traverse, and how wall-clock travel time emerges from the turn-regen pipeline. This doc is the single source of truth on the movement model — ship-roster.md carries the per-ship speed values, ship-systems.md covers the Engine upgrade, sectors.md covers warp-tunnel discovery and one-way mechanics, and turns.md covers the turn pool itself; this doc explains how those pieces compose.

The model in one paragraph

Movement is turn-gated and wall-clock-instantaneous. A traversal costs a fixed number of turns (1 for adjacent-sector direct warp, 1–3 for warp tunnels based on length, 0 for player-built warp gates, 50 for a Quantum Jump). The traversal commits instantly at the moment the player has the turns — there is no "in transit" wall-clock delay between origin and destination. Wall-clock time enters the picture only because the turn pool refills lazily at a fixed regen rate (ADR-0004): a player with a banked turn pool moves immediately, a player with an empty pool waits for turns to regenerate. Ship.current_speed does not change the turn cost of any traversal — it feeds Fleet.average_speed and is reserved for future combat-side hooks. Combat evasion (today's hit-probability driver) and escape success use other inputs entirely; see What Ship.current_speed does and does not do below.

What Ship.current_speed does and does not do

Ship.current_speed is a Float on Ship (column source: models/ship.py:Ship.current_speed, Ship.base_speed). The Engine upgrade adds +0.5 per level. Its actual consumers are narrower than legacy text suggested:

  1. Fleet stat denormalizationFleet.average_speed = mean(member.current_speed) is computed and stored on the Fleet row by _recalculate_fleet_stats (see fleet-tactics.md, fleet-coordination.md). Used by the fleet-pacing UI and as input to fleet-vs-fleet relative-speed mechanics where those exist.
  2. Future combat-side hooks (📐 Design-only). Today's combat-resolver escape formula (combat-resolver.md:122-126) reads ship type (FAST_ESCAPE_SHIP_TYPES flag for Fast Courier and Scout), hull damage, sector-edge proximity, and pursuer class — not current_speed directly. Today's combat-resolver evasion is driven by Ship.evasion (a separate column scaled by Sensor upgrades per ship-systems.md), not by current_speed. Wiring current_speed into either formula is a 📐 Design-only future change; this doc reflects what the column actually does today.

It does not:

  • Reduce sector turn cost. A Scout (speed 2.5) and a Cargo Hauler (speed 0.5) both pay the same WarpTunnel.turn_cost to traverse the same tunnel.
  • Add a real-time delay between origin and destination. Movement commits atomically when the player has the turns.
  • Affect WarpTunnel.properties.length or traversal_cost reads.
  • Drive combat hit probability on the ship — that's the Ship.evasion column scaled by Sensor upgrades, not Engine.
  • Drive escape success — that's the FAST_ESCAPE_SHIP_TYPES flag plus hull/edge/pursuer-class factors per the canonical formula in combat-resolver.md:122-126.

Ship speed values

From ship-roster.md → Capacity & speed:

Ship Base speed Combat-archetype rationale
Scout Ship 2.5 Recon — fast, hard to hit, escapes well
Fast Courier 2.0 Time-critical cargo runs
Light Freighter 1.0 Generalist baseline
Defender 1.0 Combat platform — speed is sufficient, not the strength
Carrier 0.75 Capital hull — slow but devastating
Cargo Hauler 0.5 Capacity over agility
Colony Ship 0.4 Slowest non-pod hull — large, fragile
Warp Jumper jump-only No conventional speed; uses Quantum Jump
Escape Pod very low Survival shell, not a real ship

Engine upgrade levels add +0.5 each. Practical caps are set by ShipSpecification.max_upgrade_levels.engine.

Movement primitives and their turn costs

Five primitives exist. Each has a different turn cost; the choice between them is a strategic decision the player makes per traversal.

Primitive Turn cost Distance Real-time gate Notes
Direct warp (adjacent sector) Ship.turn_cost (≈1) 1 sector None beyond turn regen Standard sector-by-sector navigation
Warp tunnel (natural or generator-placed ARTIFICIAL) WarpTunnel.turn_cost (1–3) 2+ sectors (skips intermediate) None beyond turn regen Discovery-gated; long tunnels cost more
Player warp gate (player-built one-way) 0 turns Cross-region or long-distance None The speed advantage that justifies the gate's 1.1M cr build cost
Quantum Jump (Warp Jumper only) 50 turns at commit 5–15 sector hops along bearing 24h jump cooldown + 4h scan cooldown Multi-phase ritual; consumes 1 Quantum Charge per jump
Tractor tow through any of the above Base cost + size surcharge (+1 / +2 / +3 / +5 turns by towed-ship size) Same as the underlying primitive Same Hauler + towed pair as one combined contact

Long warp tunnels

Warp tunnels carry a length attribute on WarpTunnel.properties JSONB (per ../../DATA_MODELS/galaxy.md#warptunnel). Length is the in-fiction physical extent of the tunnel — short tunnels link nearby sectors, long tunnels span across clusters or zones.

WarpTunnel.turn_cost (and its mirror properties.traversal_cost) is set by the generator from a length-based table:

Length band traversal_cost Typical use
Short (≤ 5 hop-units) 1 turn Cluster-internal links; the dominant case
Medium (6–10 hop-units) 2 turns Cluster-to-cluster within a zone
Long (11+ hop-units) 3 turns Zone-to-zone; cross-region long-haul; rare ESCAPE_HATCH inbound

Hop-units approximate Euclidean distance between the tunnel's origin and destination sectors using their 3D coordinates. The mapping is generator-time and persisted on the row; gameplay reads turn_cost directly without recomputing.

Why turn cost caps at 3 instead of scaling indefinitely. Tunnels longer than the band 3 cap exist in the schema (the length field is not bounded), but the gameplay cost is clamped at 3 turns to keep the player's turn-pool budget predictable. Beyond ~3-turn cost, the design intent is that the tunnel should instead be a Quantum Jump destination (covered separately, with its own 50-turn commit cost and 24h cooldown gating) rather than a routine multi-turn traversal.

Real-time experience for a long tunnel. A 3-turn long tunnel costs the same number of turns whether the player is in the Scout (speed 2.5) or the Colony Ship (speed 0.4). Speed does not buy a faster traversal of long tunnels — only the gate-mediated 0-turn warp gate or the Quantum Jump primitive bypass the tunnel cost.

Quantum Jump

✅ Shipped. The Warp Jumper's signature long-range primitive. Pipeline at services/gameserver/src/services/quantum_jump_service.py.

The Quantum Jump bypasses the warp graph and arrives at a sector several hops away along a player-chosen bearing. It is a three-phase, multi-step commit — scan, commit, resolve — not a one-click teleport. Each phase has its own resource cost, and the resolve step is allowed to fail when the player commits to a bearing with nothing reachable on it. Only the Warp Jumper hull can Quantum Jump.

Phase 1 — Bearing and scan

The player opens the Quantum Jump panel on a Warp Jumper and sees a 3D minimap of sectors within roughly 25 hop-units Euclidean distance. Selection is two-axis — yaw (horizontal bearing) and pitch (vertical bearing) — plus a range band:

Range band Distance Gate
Near 5–6 sectors base scan cost
Mid 7–8 sectors base scan cost
Far 9–10 sectors +1 Quantum Shard to scan in this band
Extended 12–15 sectors locked behind Sensor L3; +1 Quantum Shard to scan in this band

A scan costs 5 turns. The server projects a 15° half-angle cone along the chosen bearing out to the chosen range band and returns fuzzy intelligence — a resonance band, a texture word, and a binary hostile-presence echo — under the disclosure rules in ../galaxy/sectors.md. Scans can be repeated with adjusted bearings to triangulate; results expire after 10 real-minutes, so triangulation is bounded by a usable window. A 15% misread probability applies per scan, reduced 5 percentage points per Sensor level (L1 = 10%, L2 = 5%, L3 = 0%). A 4-hour scan cooldown runs on the Warp Jumper, decoupled from the jump cooldown so scanning cannot be spammed on the way back from a jump.

Each scan emits an anonymous "long-range quantum sweep detected" event in the destination cone. The event's bearing precision is degraded to ±45° and delivery is delayed 30 seconds before it reaches any listeners in that area. The defender learns someone is sweeping, but not who or exactly where from.

Phase 2 — Commit

The player commits to a chosen bearing and range band. Commit consumes:

  • 1 Quantum Charge — a refined consumable produced 1-Shard-to-1-Charge at any Class-3+ station or SpaceDock up front (the same venue rule as Quantum Crystal assembly). Quantum Charges sit in the Warp Jumper's special-equipment slot, not regular cargo, and are spent at commit time.
  • 50 turns.
  • A 24-hour Quantum Jump cooldown on the player, beginning from the commit timestamp.
  • A lock on the Warp Jumper for the duration of resolve — it cannot move, dock, or transfer until resolve completes.

Once committed, the bearing and range band are final; there is no abort.

Phase 3 — Resolve

The server projects the committed bearing to the committed range and selects a destination:

  1. Find candidates within an accuracy radius of 1.5 inter-sector spacings of the projected point.
  2. If candidates exist, pick one weighted by inverse distance to the projection point.
  3. If none exist, expand the radius once by 1.5× and repeat.
  4. If still none, the jump misfires: the Warp Jumper arrives in the nearest existing sector on the bearing line at a distance shorter than the committed range, taking a flat 5% hull damage that insurance does not cover (a navigation hazard, not a destruction event). The Quantum Charge, the 50 turns, and the 24-hour cooldown are all consumed regardless.

On a successful resolve the Warp Jumper arrives at the picked candidate sector and the lock releases.

Quantum Jump is also the only primitive that reaches the lost-formation set — see Cross-region travel below.

Wall-clock travel time

Wall-clock time is a function of:

  1. Turn cost of the chosen primitive (above table).
  2. Player's banked turns at the moment of attempt.
  3. Turn-regen rate while waiting for turns to refill if banked is insufficient.

Turn-regen rate

From turns.md:

base_rate       = 1000 / 86400  ≈ 0.01157 turns/sec
effective_rate  = base_rate × Player.aria_bonus_multiplier
seconds_per_turn = 1.0 / effective_rate

aria_bonus_multiplier is set by the consciousness-level table — 1.0 at L1 Dormant up to 1.5 at L5 Transcendent. So:

ARIA level Multiplier Seconds per turn Turns per hour
L1 Dormant 1.0× 86.4 sec 41.7
L2 Aware 1.1× 78.5 sec 45.8
L3 Awakened 1.2× 72.0 sec 50.0
L4 Sentient 1.35× 64.0 sec 56.3
L5 Transcendent 1.5× 57.6 sec 62.5

From turn cost to wall clock

wall_clock_seconds = max(0, (turn_cost − banked_turns) × seconds_per_turn)

Worked examples (assume L1 Dormant, banked = 0):

Traversal Turn cost Wall-clock seconds Wall-clock minutes
Direct warp 1 86 1.4 min
Short warp tunnel 1 86 1.4 min
Medium warp tunnel 2 173 2.9 min
Long warp tunnel 3 259 4.3 min
Quantum Jump commit 50 4,320 72.0 min (1.2 hrs)
Warp gate 0 0 instant

If the player is at L5 Transcendent ARIA with the same empty turn pool, divide by 1.5: a long tunnel falls to 173 seconds (~2.9 min); a Quantum Jump commit falls to 2,880 seconds (~48 min).

If the player has a full turn pool (1,000 turns banked at the default cap), every primitive except Quantum Jump is wall-clock instantaneous, and even a 50-turn Quantum Jump commit only costs the next 50-turn segment of regen time (since the 50 was already banked). Banked turns are the entire point of the turn pool — they're what lets a player burst through a multi-tunnel route in a single play session.

When wall-clock matters

Most regular play is unaffected: a player with 50+ turns banked moves through ordinary navigation with zero perceptible delay. Wall-clock starts to bite when:

  • A new player has just spent down their starter pool and is rebuilding it.
  • A player chains multiple Quantum Jumps in succession (each commit eats 50 banked turns and the cooldown wall is real-time, not turn-based).
  • A player attempts a long-tunnel-heavy route after a combat encounter that drained the pool.
  • A player is at low ARIA consciousness and high regional taxes have eroded their effective time budget.

The wall-clock regen design is intentional: it caps the per-day playtime depth without explicit time-gating, makes high-tier ARIA progression valuable, and gives Quantum Jump's cooldown teeth.

Fleet movement

Fleets move as a single combined contact. The fleet endpoint POST /api/v1/fleets/{id}/move (see fleet-coordination.md) routes the entire fleet together; per-ship movement is suppressed while in a fleet.

Fleet turn cost. A fleet pays the maximum turn cost any single member would pay — i.e., the slowest-tunnel-eligible ship in the fleet sets the pace, never the average. This means a Scout in a fleet with a Cargo Hauler still pays the Cargo Hauler's costs while in formation. Players who want Scout-pace mobility leave the fleet first.

Fleet.average_speed. Computed and stored on the fleet row, but used for fleet-vs-fleet pursuit and combat-escape calculations, not for fleet movement turn cost. A faster average_speed lets the fleet escape combat better and pursue routed enemies more reliably; it does not reduce traversal turn cost.

Quantum Jump in fleets. Only the Warp Jumper itself can Quantum Jump, and a Warp Jumper in a fleet must leave the fleet to jump (the jump is a per-ship primitive, not a fleet primitive). The post-jump pilot ejects to an escape pod at the destination per ADR-0029 only at Phase 3 of warp-gate construction; standalone Quantum Jumps don't sacrifice the hull.

Cross-region travel

Three primitives cross region boundaries; pick by what the player has available.

  1. Central Nexus warp-gate path (free for free-tier). Every player region attaches to the Central Nexus via a generator-placed warp gate (per ../../SYSTEMS/galaxy-generator-design.md#phase-14--region-attachment--statistics-rollup). Free-tier players can traverse home-region → Nexus → home-region (Nexus is universal). Galactic Citizens ($5/mo) extend this to Nexus → other-player-region. Each gate traversal costs 0 turns; the path is a chain of one-way gates (see ../galaxy/warp-gates.md).
  2. Long-distance ESCAPE_HATCH inbound. Some ESCAPE_HATCH formations stamp a long-distance one-way WarpTunnel from a far sector into a nearby sector (per ADR-0034). The arriving player learns they came from "an unknown sector" — origin discovery is investigation gameplay. This is not a chosen movement; it's a consequence of formation interaction.
  3. Quantum Jump out of a stranded sector — when stranded behind a one-way warp, the Warp Jumper's Slipdrive can self-rescue (multi-turn charge, fuel cost scaled by graph distance to the nearest non-sink sector). Federation distress beacon is the alternative for non-Warp-Jumper hulls (free transport to nearest fedspace sector at the cost of −10 Terran Federation reputation, 24h cooldown, per ADR-0034).

Quantum Jump and lost formations (per ADR-0070). Quantum Jump is the only primitive that reaches the lost-formation set — LOST_SECTOR, LOST_CLUSTER, and ARCHIPELAGO formations (see ../galaxy/special-formations.md#lost-worlds-quantum-jump-only-content). The two Warp-Jumper primitives split cleanly: Quantum Jump is the discovery tool (it punches through the warp-graph wall to find lost formations), and the Slipdrive is the stranded-recovery tool (it self-rescues from WARP_SINK and similar one-way traps). A lost formation reached by Quantum Jump may still need Slipdrive to leave if no exit warp surfaced; the two primitives are complementary, not redundant.

Worked example: a typical journey

A Galactic Citizen owns a Light Freighter (speed 1.0, base turn cost 1). Their home region is 600 sectors deep; their target is a high-end commodity station in another player-owned region.

Route: 1. Home region sector 47 → Capital sector (47 hops via the proximity warp graph). Most hops are 1-turn direct warps; ~5 are 2-turn medium tunnels. Total: ~57 turns. 2. Capital sector → Nexus warp gate sector (in starter cluster). 1-turn direct warp. Total: 1 turn. 3. Nexus warp gate (home region → Nexus): 0 turns. 4. Nexus internal traversal to the destination region's gate (avg 8 hops, mix of 1-turn and 2-turn). Total: ~12 turns. 5. Nexus warp gate (Nexus → destination region): 0 turns. 6. Destination Capital → target station (~30 hops, similar mix). Total: ~38 turns.

Aggregate: ~108 turns one-way. At L3 Awakened ARIA (1.2× rate, 72 sec/turn), assuming the player started with a near-full 1,000-turn pool: trivially banked, wall-clock instantaneous if the player chains the move without combat or trading interruptions. If the player's pool is empty, ~108 × 72 = 7,776 seconds wall-clock = ~2 hours of regen wait — but realistically players bank turns and play in bursts, so the typical experience is "click, arrive, click, arrive."

A Cargo Hauler (speed 0.5) on the same route pays the same turn cost. The Cargo Hauler's 4× larger cargo hold is the trade for its lower combat evasion, not for any travel-time difference.

Status

🚧 Partial. Ship.base_speed and Ship.current_speed are columns; the Engine-upgrade +0.5 delta is in the upgrade-spec target (per ./ship-systems.md). Combat-escape and fleet-pacing reads of current_speed are partly wired (per combat-resolver.md). The length-band → WarpTunnel.turn_cost mapping in the Long warp tunnels section is the design target — current generator code uses a flat 1-turn cost for natural tunnels and the schema's properties.length is not yet populated end-to-end. The wall-clock formulas above follow the ADR-0004 continuous-regen model: MovementService lazily regenerates turns on each move (per ../../SYSTEMS/turn-regeneration.md).

Source map

Topic Path (target)
Movement entry point services/gameserver/src/services/movement_service.py:MovementService.move_player_to_sector
Fleet movement services/gameserver/src/services/fleet_service.py:FleetService.move_fleet
Quantum Jump pipeline services/gameserver/src/services/quantum_jump_service.py (✅ Shipped)
Turn-regen lazy entry MovementService._regenerate_turns (per ADR-0004)
Ship.current_speed reads combat_service.py (escape, evasion); fleet_service.py (_recalculate_fleet_stats)
Engine upgrade ship_upgrade_service.py

Cross-references