Skip to content

Movement & Travel Times

Status: 🚧 Partial — intrasector movement and warp traversal shipped; advanced movement modifiers partially documented vs code. (re-verified 2026-08-21 vs Sectorwars2102 46bce720.)

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 do not have a travel-as-a-unit relocate API. POST /api/v1/fleets/{id}/move / move_fleet are retired. Fleet.sector_id follows the flagship ship's sector on roster recompute (see fleet-coordination.md). Individual ships still move via MovementService; being in a fleet does not bulk-relocate members.

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).

Paid fuel delivery (the peer-assisted counterpart to self-rescue). Max's design direction (WO-GWQ-STRANDING-2, 2026-07-10): "conversely you could pay for someone to deliver you fuel." A ship with an empty fuel hold can't charge/complete a Slipdrive escape on its own — another player flies fuel cargo into the same sector and sells it directly, so the stranded player can complete their OWN Slipdrive escape (fuel lands on the recipient's ship, not their wallet; they still fly the Slipdrive themselves).

Shipped 2026-08-04 (was 🚧 Partial — primitive shipped, unwired). services/gameserver/src/services/fuel_delivery_service.py::deliver_fuel implements the full atomic handoff (same-sector requirement, fuel-cargo and payment validation, recipient cargo-capacity check, both legs — fuel and credits — in one flush), now exposed via POST /trade/deliver-fuel (api/routes/player_trade.py) — the deliverer calls it naming the recipient, fuel amount, and payment; both legs commit atomically or the whole call 400s. This is a deliberate kernel-only scope: FEATURES/economy/contracts.md's cargo_delivery contract type is the natural long-term home for a "post a fuel request, any player browses and accepts it" flow, but the entire trade-contract system is itself 📐 Design-only (WO-ECON-CONTRACT-1-KERNEL still queued) — so this stays a hand-arranged (players coordinate off-channel, meet in person) primitive for now. Once the contract kernel ships, a fuel_delivery contract type should call deliver_fuel at its complete transition rather than re-implementing the transfer.

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's +0.5 delta per level is ✅ shipped and purchasable — ship_upgrade_service.py applies it directly to Ship.current_speed on purchase and removal (per ./ship-systems.md). Combat-escape and fleet-pacing reads of current_speed are partly wired (per combat-resolver.md). ✅ Natural-tunnel length → WarpTunnel.turn_cost persistence is Shippedbang_import_service calls natural_tunnel_cost_fields and persists hop-unit properties.length plus banded turn_cost / properties.traversal_cost (c8bb6da7 / PR #636 MERGED → origin/feat 46bce720; see Long warp tunnels). Soft Partial remains for wall-clock / speed reads of those costs (speed still does not change tunnel turn cost). The wall-clock formulas above follow the ADR-0004 continuous-regen model: MovementService lazily regenerates turns on each move (per ../../SYSTEMS/turn-regeneration.md).

Player-facing affordances

  • Sector navigation mapNavigationMap.tsx on the GameDashboard CHART tab; adjacent-sector and warp-tunnel moves via the standard move endpoint (origin/feat 46bce720).
  • Course plot / autopilotAutopilotContext + AutopilotHud; POST /api/v1/nav/plot hop replay as ordinary moves (ADR-0072).
  • Quantum Jump consoleQuantumDriveConsole.tsx in the NAV monitor (Warp Jumper ritual + charge refine UI).
  • Player-built warp gate traversal — 0-turn gate moves use the same move endpoint once a gate is ACTIVE (see warp-gates.md).
  • 🚧 Fleet unit move UI — no FleetManagerPanel / POST /fleets/{id}/move player surface on tip; fleet position follows flagship only.
  • 📐 Ship.current_speed does not reduce traversal turn cost — see What Ship.current_speed does and does not do above.

Source map

Topic Path (target)
Movement entry point services/gameserver/src/services/movement_service.py:MovementService.move_player_to_sector
Player navigation map services/player-client/src/components/tactical/NavigationMap.tsx (mounted GameDashboard.tsx CHART tab)
Autopilot context services/player-client/src/contexts/AutopilotContext.tsx
Autopilot HUD services/player-client/src/components/hud/AutopilotHud.tsx
Quantum drive console services/player-client/src/components/quantum/QuantumDriveConsole.tsx
Fleet position FleetService derives Fleet.sector_id from the flagship ship (no move_fleet / POST /fleets/{id}/move)
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

Intra-system movement

Position, burns, and multiplayer pose sync inside a sector are specified in SYSTEMS/intrasystem-movement.md.

Status: ✅ Shipped — server-authoritative pose (players + NPCs), free burns with empty-space targets, leg-plan sync, activity-derived presence liveness, the server-side dock/land proximity gate, and planet-claim proximity gating (api/routes/planets.py:claim_planet, honors the same assert_dock_land_proximity gate as dock/land — WO-CLAIM-PROXIMITY, 322bd586) (re-verified 2026-08-04). 🚧 Partial — the "interactive dial/slider fills" client affordance has no concrete spec beyond this line (no range number, no field name); needs a design pass before it can be built, unless it turns out to already be covered by the existing dock/land range indicator.