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:
- Fleet stat denormalization —
Fleet.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. - Future combat-side hooks (📐 Design-only). Today's combat-resolver escape formula (
combat-resolver.md:122-126) reads ship type (FAST_ESCAPE_SHIP_TYPESflag for Fast Courier and Scout), hull damage, sector-edge proximity, and pursuer class — notcurrent_speeddirectly. Today's combat-resolver evasion is driven byShip.evasion(a separate column scaled by Sensor upgrades per ship-systems.md), not bycurrent_speed. Wiringcurrent_speedinto 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_costto 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.lengthortraversal_costreads. - Drive combat hit probability on the ship — that's the
Ship.evasioncolumn scaled by Sensor upgrades, not Engine. - Drive escape success — that's the
FAST_ESCAPE_SHIP_TYPESflag plus hull/edge/pursuer-class factors per the canonical formula incombat-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:
- Find candidates within an accuracy radius of 1.5 inter-sector spacings of the projected point.
- If candidates exist, pick one weighted by inverse distance to the projection point.
- If none exist, expand the radius once by 1.5× and repeat.
- 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:
- Turn cost of the chosen primitive (above table).
- Player's banked turns at the moment of attempt.
- 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.
- 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). - Long-distance ESCAPE_HATCH inbound. Some
ESCAPE_HATCHformations stamp a long-distance one-wayWarpTunnelfrom 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. - 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
−10Terran 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_speedandShip.current_speedare columns; the Engine upgrade's+0.5delta per level is ✅ shipped and purchasable —ship_upgrade_service.pyapplies it directly toShip.current_speedon purchase and removal (per./ship-systems.md). Combat-escape and fleet-pacing reads ofcurrent_speedare partly wired (per combat-resolver.md). ✅ Natural-tunnel length →WarpTunnel.turn_costpersistence is Shipped —bang_import_servicecallsnatural_tunnel_cost_fieldsand persists hop-unitproperties.lengthplus bandedturn_cost/properties.traversal_cost(c8bb6da7/ PR #636 MERGED → origin/feat46bce720; 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:MovementServicelazily regenerates turns on each move (per../../SYSTEMS/turn-regeneration.md).
Player-facing affordances¶
- ✅ Sector navigation map —
NavigationMap.tsxon the GameDashboard CHART tab; adjacent-sector and warp-tunnel moves via the standard move endpoint (origin/feat46bce720). - ✅ Course plot / autopilot —
AutopilotContext+AutopilotHud;POST /api/v1/nav/plothop replay as ordinary moves (ADR-0072). - ✅ Quantum Jump console —
QuantumDriveConsole.tsxin 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}/moveplayer surface on tip; fleet position follows flagship only. - 📐
Ship.current_speeddoes not reduce traversal turn cost — see WhatShip.current_speeddoes 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¶
./turns.md— turn pool, regen formula, turn cost table../ship-roster.md— per-ship speed values../ship-systems.md— Engine upgrade ladder, scanner / sensor evasion stack../combat.md— combat evasion (driven byShip.evasion) and escape mechanics (driven byFAST_ESCAPE_SHIP_TYPESflag + hull / sector-edge / pursuer-class factors)../fleet-tactics.md— fleetaverage_speedand formation modifiers.../galaxy/sectors.md— natural warp tunnels, one-way warps, Quantum Jump spec.../galaxy/warp-gates.md— player-built warp gate construction and traversal.../../SYSTEMS/galaxy-generator-design.md— how the warp graph is laid down at generation time, includingWarpTunnel.properties.lengthassignment.../../SYSTEMS/combat-resolver.md— escape-success formula citing speed.../../SYSTEMS/fleet-coordination.md— fleet movement endpoint, average-speed denormalization.- ADR-0004 — continuous turn regeneration model.
- ADR-0030 — Quantum Jump multi-step commit.
- ADR-0034 — one-way warp tunnel design (for stranding-recovery context).
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.