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 has a floor, and that is the useful part
The tank drains only while the throttle is up —
min(thr,1) * ship_energy_cost + max(0, thr-1) * warp_energy_cost, warp weighted about
double — and the auxiliary power unit trickles it back unconditionally whenever energy
is below ship_apu_ceiling. Docking refills it fast on top of that.
There is no unrecoverable energy state. A ship that strands is a ship that never stopped burning.
That is why helm_throttle consults a reserve before allowing warp, and why
helm_energy_reserve exists at all. Three rules follow, and together they make stranding
impossible rather than unlikely:
- Never enter warp without the reserve to reach help. Warp is the only thing that outruns the APU.
- Below the reserve, drop to impulse or stop. The APU then refills with certainty.
- Dock when a station is in reach, because it is faster than waiting.
helm_energy_reserve(ship, target) asks the question a flat threshold cannot: can I afford
to get there and still have something left? "Dock below 300 energy" says nothing about
whether the station is 2,000 units away or 40,000.
This is what let LegendaryMissions' autoplay delete its energy refill cheat rather than keep hiding real energy bugs behind it. A cheat put there so an unattended run would not stall also guarantees the run can never find an energy bug.
Prove it, because a plain run does not
Neither the old nor the new autoplayer dropped below ~730 energy in 300 sim-seconds on siege — the cheat never fired, so the run proved nothing either way. Draining the tank to 20 (below the old threshold) and watching it climb back to 596 unaided is the test that means something.
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. The tank drains only while the throttle is up -
min(thr,1) * ship_energy_cost + max(0, thr-1) * warp_energy_cost, warp weighted about
double - and the auxiliary power unit trickles it back unconditionally whenever energy
is below ship_apu_ceiling. Docking refills fast on top of that.
There is no unrecoverable energy state. A ship that strands is a ship that never stopped burning.
That is why helm_throttle consults a reserve before allowing warp, and why
helm_energy_reserve exists at all. A bot that respects them cannot strand itself, which
means the "refill the tank so the test doesn't stall" cheat can be deleted rather than
hidden. 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_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, which is why sustained warp is the only thing that
outruns the auxiliary power unit.
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.
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.
Also the recovery move: with the throttle at zero nothing drains the tank, so the auxiliary power unit refills it. Stopping is always a way out.
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 is the one thing that drains faster than the auxiliary power unit refills. Clamping to impulse lets the APU win.
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.