Skip to content

Helm

Fly a player ship from script: throttle, direction steering, docking, shields, and the engineering power/heat table.

Nothing in the library could do this before. target and target_pos write the NPC keys (target_pos_x/y/z, throttle), so every brain movement leaf is unusable on a player ship and anything that wanted to fly one hand-rolled the data_set writes — LegendaryMissions' autoplay did, and so did the headless quest pilot, in two different styles.

Everything here is a real control write, the same one the crew's console makes. So the same calls serve an attract bot and a conformance run; what differs is the policy above them.

An unset field is not a no

The engine returns None for a field nobody set, and the third argument of data_set.get is a slot index, not a default — so it does not save you. Coalescing that None to 0 is right for arithmetic and wrong for a capability question: "I have no information" silently becomes "you may never warp", for the whole mission, with no error, on a ship that flies perfectly well.

helm_warp_available therefore refuses only on positive evidence of no drive — the flag says 0 and the hull costs nothing to warp — and the energy reserve is consulted only when the energy field actually says something. This shipped the wrong way round once and the symptom was simply that a bot never warped, with nothing anywhere to read.

Warp is gated, reverse is -1, and undock needs two writes

Three engine details from ENGINE_WIDGETS.md that are easy to get subtly wrong:

  • Warp is only available when data_set warp == 1.0. A hull without a drive ignores a warp throttle — it just flies at impulse — so a bot that never checks believes it is travelling three times faster than it is. helm_throttle returns what it actually set, so the caller can tell.
  • playerThrottle of -1 is reverse. The engine's own bar tops out at 5.
  • Undocking must clear dock_base_id, not just dock_state. The engine holds a docked ship with a tractor, so rewriting only the state leaves it attached to a base it believes it has left.

Energy: waiting gets you to 200, docking gets you home

The engine's energy numbers are in data/preferences.json:

key value meaning
player-fuel-use-coeff 0.12 drain, times the ship's speed
player-base-energy-use-coeff 0.007 drain, times the power on every engineering slider — a stopped ship still draws
player-shields-raised-energy-coeff 2.0 drain multiplier while shields are up
energyCostOfOneBeamShot 1.0 per beam shot
ship_apu_assistance_per_tick 0.1 APU refill, 3/s at 30 ticks
ship_apu_assistance_ceiling 200 the APU stops here
player-no_energy-speed-coeff 0.1 an empty ship still flies, at 10%

helm_apu_ceiling(ship) returns that ceiling (the ship's ship_apu_ceiling when it carries one). Anything that parks a ship "until energy recovers" must aim below it, or it waits forever. Only docking refills the tank.

helm_throttle still refuses warp below DEFAULT_ENERGY_RESERVE (400), so a ship keeps enough in hand to reach a station. That is a spending gate, not a recovery target. helm_energy_reserve(ship, target) estimates whether a trip is affordable at a given throttle. Because an empty ship still moves, "unaffordable" means "slow", not "impossible".

This page used to say energy has a floor

It claimed a stopped ship always recovers, because the mock's APU refilled to 1000. LM's brain autoplayer believed it: it parked ships below 400 to wait for 400, and in the engine every ship that ran low away from a station sat still for the rest of the run. The mock now uses the engine's ceiling and rate.

The engineering table is many-to-one

helm_eng_controls walks eng_control_label once. That walk was previously written out by hand in three places — autoplay's can-turn check, autoplay's power loop, and set_engineering_value.

eng_control_type_index is the ship system a control feeds, and several controls share one. On a tsn_light_cruiser the engine reports eight controls onto four systems:

control system
BEAM, TORP 0
IMPULSE, WARP, MANEUVER 1
SENSORS 2
FRONT SHIELD, REAR SHIELD 3

So helm_set_power sets every matching control, where set_engineering_value stops at the first — which on this hull means it sets FRONT SHIELD and silently leaves REAR alone. Labels are the engine's display text in upper case, so matching folds case.

helm_can_turn consults two things on purpose

A wrecked maneuver system stops a ship turning before turn_damage_coeff bottoms out, so the check is the coefficient and the maneuver system's damage. A ship that cannot turn must not burn straight ahead: that only commits it further, and a straight-line chase after a target it cannot aim at ends in deep space.

API

Flying a PLAYER ship from script: throttle, steering, docking, shields, power.

WHY THIS EXISTS. Nothing in the library could steer a player. target() and target_pos() (space_objects.py) write the NPC keys - target_pos_x/y/z, throttle - so every brain movement leaf is unusable on a player ship, and anything that wanted to fly one hand-rolled the data_set writes. LegendaryMissions' autoplay does, and so did cosmos_dev's quest pilot, in two different styles.

The engine semantics these wrap are documented in ENGINE_WIDGETS.md and are easy to get subtly wrong:

  • playerThrottle is the throttle bar. -1 is reverse, and warp is only available when data_set warp == 1.0 - a hull without a warp drive ignores a warp throttle, so a bot that asks for one just flies at impulse while believing it is at warp.
  • Steering is steerToDirD{X,Y,Z} plus steeringToDirFlag. These were added FOR autoplay and are honored by the engine, so this is a real control path and not an emulation.
  • Docking starts by writing dock_base_id, then the state walks unknown -> docking -> docking_start -> docked. Cancelling needs dock_base_id = 0 AND dock_state = unknown - and the engine holds the dock with a tractor, so a cancel that does not delete it leaves the ship attached.

ENERGY IS THE INTERESTING PART, and the engine's numbers live in data/preferences.json:

  • the tank drains with SPEED (player-fuel-use-coeff), with the power on every engineering slider (player-base-energy-use-coeff) - so a stopped ship still draws - doubled while shields are raised (player-shields-raised-energy-coeff), and per beam shot;
  • the auxiliary power unit adds 0.1 a tick (3/s) but only up to 200 (ship_apu_assistance_ceiling). It is a get-home trickle, not a recharge;
  • an empty tank does not stop the ship - it flies at 10% (player-no_energy-speed-coeff);
  • docking refills the tank to full.

Waiting never gets a ship past ~200 energy. Only docking does.

This module used to say the opposite - "a stopped ship always recovers" - because that was true of the MOCK, whose APU refilled to 1000. LM's brain autoplayer believed it, parked ships below a 400 reserve to wait for the APU, and they sat there for the rest of the run. A threshold that waiting is meant to clear must sit under helm_apu_ceiling.

Everything here is a real control write, so the same calls serve an attract bot and a conformance run; what differs is the policy above them, not the actuation.

helm_apu_ceiling(ship)

The energy the auxiliary power unit refills to - and so the most waiting can reach.

Reads ship_apu_ceiling when the ship carries a positive one, otherwise the engine's global ship_apu_assistance_ceiling (200). Anything that parks a ship "until energy recovers" has to aim below this, or it waits forever.

helm_can_turn(ship)

Whether the ship can still steer meaningfully.

Consults BOTH the turn damage coefficient and the maneuver system's damage: a wrecked maneuver system stops the ship turning before the coefficient bottoms out. A ship that cannot turn must not burn straight ahead - that only commits it further - so callers use this to decide to hold station instead.

helm_distance(ship, target)

Distance between a ship and a target (object, id, or point), or inf.

helm_dock_request(ship, station)

Ask to dock: name the base, then start the state walk if it has not started.

The engine takes it from dock_start through to docked on its own. Docking refills the tank fast, which makes it the quick way out of a low-energy situation when a friendly station is in reach.

helm_energy(ship)

Current energy in the tank.

helm_energy_cost(ship, throttle, seconds)

Energy this ship would spend holding throttle for seconds.

Mirrors the engine's drain: impulse is charged on the part of the throttle up to 1.0 and warp on the excess, at the hull's own ship_energy_cost / warp_energy_cost. Warp costs roughly double per unit. An ESTIMATE for trip planning: the engine really charges by speed and slider power (see the module docstring), not these fields.

helm_energy_reserve(ship, target=None, throttle=IMPULSE_MAX, reserve=None)

Whether the ship can afford to run, and still have something left.

With a target, this asks the question that matters: can I get there and not be stranded when I arrive? It compares the tank against the cost of the trip at throttle plus a flat reserve. Without a target it is just "am I above the reserve".

This is what replaces a flat "dock below 300": 300 says nothing about whether the nearest station is 2,000 units away or 40,000.

helm_eng_controls(ship)

Yield (index, label, system_index) for each engineering control the ship has.

One walk of the eng_control_label array, which was written out by hand in three places: autoplay's can-turn check and its power loop, and set_engineering_value. Stops at the first empty label, which is how the engine marks the end.

helm_is_docked(ship)

True while the ship is docked.

helm_position(thing)

Position of a ship, an object, or a point-like value. None when there is none.

A PLAIN (x, y, z) COUNTS, and leaving it out was a real bug rather than a nicety. Everything geometric in the library hands positions round as TUPLES - volume_route returns them, relic_points stores them, eva_points lists them - so helm_distance(suit, place) against any of those answered inf for want of a .x. The EVA Nav app showed every destination at distance 0.

helm_set_power(ship, name, value)

Set the power level of every control whose label matches name. Returns how many.

Unlike set_engineering_value, which stops at the first match, this sets all of them - a hull can expose more than one control feeding the same system.

helm_shield_fraction(ship, facing=0)

How full one shield facing is, 0..1. 0 when the ship has no shields.

helm_shields(ship, up=True)

Raise or lower shields.

helm_speed_for(ship, throttle)

Approximate speed (units/second) this ship would make at throttle.

Player speed is hull-INDEPENDENT in Cosmos: impulse tops out the same for every hull, and warp adds a per-factor bonus on top. Used only to turn a distance into a duration for the energy estimate, so approximate is enough.

helm_steer_to_point(ship, target)

Steer toward an object, id, or point. False when either position is unknown.

helm_steer_to_vec(ship, x, y=None, z=None)

Steer along a direction vector. Accepts (vec) or (x, y, z).

helm_stop(ship)

Cut the throttle and drop direction steering.

Stopping cuts the speed drain, but the sliders and shields still draw, and the auxiliary power unit only refills to helm_apu_ceiling (~200). It buys a trickle, not a full tank.

helm_system_damage(ship, name)

Damage fraction (0..1) of the ship system a named control feeds, or 0.

name matches case-insensitively as a substring, because the engine's labels are display text ("Maneuver", "Impulse Drive") rather than keys.

helm_system_heat(ship, name)

Heat (0..1-ish) of the ship system a named control feeds, or 0.

helm_throttle(ship, level, allow_warp=True, reserve=None)

Set the throttle, refusing a warp the ship cannot actually sustain.

Returns the throttle actually set, which may be lower than asked. Two reasons it clamps, and both are silent failures otherwise:

  • the hull has no warp drive (warp != 1.0), so the engine ignores the warp band;
  • the tank is below the reserve, and warp burns it fastest. Clamping to impulse keeps enough in hand to reach a station.

Pass allow_warp=False to hold impulse regardless - a caller that has decided to conserve does not need to restate why.

helm_undock(ship)

Release the dock.

Clears dock_base_id as well as the state: the engine holds a docked ship with a TRACTOR, and a cancel that only rewrites the state leaves the ship attached to a base it believes it has left.

helm_warp_available(ship)

Whether this hull may use warp. Unknown counts as YES - see below.

The engine gates the throttle bar's WARP band on data_set warp == 1.0, so checking it stops a bot believing it is at warp on a hull that has no drive.

BUT THE POLARITY MATTERS MORE THAN THE CHECK. The engine returns None for a field nobody set, and treating that as 0 means "I have no information" silently becomes "you may never warp" - a capability disabled forever, with no error, on a ship that flies perfectly well. That is strictly worse than the thing the check was guarding against, which merely wastes a throttle write the engine ignores.

So this refuses only on POSITIVE evidence of no drive: the flag says 0 AND the hull costs nothing to warp. Anything unknown is allowed through, which is exactly how every autoplayer behaved before this function existed.