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_throttlereturns what it actually set, so the caller can tell. playerThrottleof-1is reverse. The engine's own bar tops out at 5.- Undocking must clear
dock_base_id, not justdock_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:
playerThrottleis the throttle bar. -1 is reverse, and warp is only available whendata_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}plussteeringToDirFlag. 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 walksunknown -> docking -> docking_start -> docked. Cancelling needsdock_base_id = 0ANDdock_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.