Skip to content

The ship_data module

Load and query the ship-definition database, and let an add-on declare ships of its own.

Overview

Cosmos ships are defined in data/shipData.yaml, keyed by art ID (e.g. "tsn_battle_cruiser"). This module loads that database, merges anything a mission or add-on has contributed, and answers queries against the result.

get_ship_data() returns the whole merged database (a dict with a #ship-list). get_ship_data_for(key) returns one ship's entry. get_ship_name(key) is the display name, and filter_ship_data_by_side is how a prefab finds "a Kralien warship".

The database is loaded lazily and cached. Mission scripts do not normally need this module directly - spawn functions look ship data up for you.

Quick example

== pick_ship ==
    entry = ship_data_get_ship_data_for("tsn_battle_cruiser")
    display_name = entry.get("name", "Unknown")
    log(f"Spawning a {display_name}")
    ->END
from sbs_utils.procedural.ship_data import (
    get_ship_data, get_ship_data_for, get_ship_name,
)

entry = get_ship_data_for("tsn_battle_cruiser")
name = entry.get("name", "Unknown")

# every ship the database knows
for ship in get_ship_data().get("#ship-list", []):
    print(ship.get("key"), ship.get("name"))

The MAST names carry a ship_data_ prefix

The prelude registers this module with a prefix, so Python's get_ship_data_for is ship_data_get_ship_data_for in MAST, and add_extra is ship_data_add_extra.

Adding ships from an add-on

An add-on ships a ship-data file and names it. Nothing is written:

ship_data_add_extra("turrets/extraShipData_turrets", mod="MyMod")
from sbs_utils.procedural.ship_data import add_extra

add_extra("turrets/extraShipData_turrets", mod="MyMod")

add_extra points both readers at the one file: sbs_utils' own table (which is what filter_ship_data_by_side and get_ship_name read) and the engine (which is what resolves artfileroot, and what makes a behav_station fire at all).

The name takes no extension - the engine tries .yaml then .json itself - and may include a logical folder. With no path it is looked for where the media system already looks: this mission, then each media pack it pinned.

Put the file in a media pack, not a mastlib

A mastlib is a zip, and the engine cannot read inside one. A media pack is unpacked to disk once, so the engine can be handed a real folder. That is the whole reason this used to involve writing a file.

The older generating route is broken

ship_data_merge_mod reaches the engine by generating extraShipData.json in the mission folder. get_ship_data() then prepends that file whole on the next run - #mod entries and all - while the add-on declares the same entries again. Measured at 51 hulls becoming 102 from run 2 onward. Use add_extra.

extra_enable(False) turns off only the engine half, leaving the library merge in place - useful while the engine side is in flux, since headless and the mock keep behaving identically.

API

add_extra(name, path=None, mod=None)

Load another ship-data file for this mission.

name has no extension - .yaml or .json is found here, so a mod can change format without the caller changing. It may include a logical folder ("turrets/extraShipData_turrets"). The engine now wants the fully-pathed file WITH its suffix, so the extension search that used to be the engine's job happens in _read_extra_ship_data and its answer is what the engine is handed - one decision, not two that can disagree.

With no path, the file is looked for where the media system already looks: this mission's folder first, then each media pack it pinned. That matters because an ADD-ON cannot put a file where the engine can read it - a mastlib is a zip - while a media pack is unpacked to disk once. So an addon ships its hulls in its media pack and names them here, and neither it nor the library has to write anything.

Returns True when the engine was told, False when only the library was. Missing files are not fatal, matching the engine's habit: a mod with a broken path should be a ship with no stats, not a dead mission.

add_ship_data(entry, mod=None, prepend=True)

Add a single entry to the in-memory ship data.

Inserts entry into the #ship-list so it is returned by :func:get_ship_data, :func:get_ship_index, :func:get_ship_data_for, :func:filter_ship_data_by_side, and the *_keys helpers -- letting a script register a ship/terrain/pickup type at runtime without editing shipData or shipping an extraShipData.json.

The entry is prepended by default, matching how extraShipData.json is merged (script data ahead of built-in data). The derived caches (ship_index and every *_keys cache) are cleared so the new entry shows up on the next lookup; the loaded ship_data_cache itself is preserved.

Parameters:

Name Type Description Default
entry dict

A ship data dict. Must include a "key" (used to index it); typically also "name", "side", "roles", and "artfileroot".

required
mod str

Name of the mod/addon this entry comes from; stamped on the entry as #mod so the low-level spawn post-processes it (see :func:mod_ship_data_process). Defaults to None (untagged).

None
prepend bool

Insert at the front of the list (script priority) when True (default); append to the end when False.

True

Returns:

Type Description

dict | None: The updated ship data cache, or None if ship data could not be loaded.

alien_keys()

Return all pickup keys containing "alien" (cached).

Returns:

Type Description

list[str]: Alien pickup type keys.

arvonian_ship_keys()

Return all Arvonian ship keys (cached).

Returns:

Type Description

list[str]: Arvonian ship type keys.

arvonian_starbase_keys()

Return all Arvonian starbase keys (cached).

Returns:

Type Description

list[str]: Arvonian starbase type keys.

asteroid_keys()

Return all asteroid ship keys from the ship data (cached).

Returns:

Type Description

list[str]: Asteroid type keys.

container_keys()

Return all pickup keys containing "container" (cached).

Returns:

Type Description

list[str]: Container pickup type keys.

crystal_asteroid_keys()

Return all crystal asteroid keys, excluding plain asteroids (cached).

Returns:

Type Description

list[str]: Crystal asteroid type keys.

danger_keys()

Return all pickup keys containing "danger" (cached).

Returns:

Type Description

list[str]: Danger pickup type keys.

extra_enable(enabled=True)

Allow or forbid the ENGINE side of add_extra.

Off, the ships are still merged into sbs_utils, so headless runs and every library lookup behave the same; only the engine is not told. Use it to take the engine path out of play without touching any caller.

extra_enabled()

Is the engine call currently allowed?

extra_loaded()

[(filename, path, reached_engine, engine_arg)] for every call so far, so a report can say what was loaded, what exact file the ENGINE was pointed at, and whether it heard about it. engine_arg is None when no file was found.

extra_replay()

Tell the engine again about every extra ship data file it has been given.

create_new_sim() REBUILDS the engine's ship data table - it reads the mission's extraShipData.json inside that call - and everything add_extra_ship_data registered beforehand is gone. Nothing reports it. The library keeps its own merged copy, so the ships still have stats everywhere sbs_utils can see, and the loss surfaces later as MemoryError: bad allocation from a spawn, against whichever mission line asked for one of those hulls.

Missions register at story load, which is BEFORE the first map calls sim_create(), so this is the ordinary case rather than an edge one. LegendaryMissions declares its monsters with a top-level shared, which by design runs once and then becomes a no-op, so nothing ever re-issued them: every monster in the game was unspawnable from the first map start onward, and had been for as long as anyone could remember (measured 2026-08-14 - inside LM every hull fails and re-issuing this exact call fixes all five).

Replayed from the record rather than from the files: the library merge already happened and only the engine forgot.

extra_reset()

Forget the record. Called by the per-mission reset, not by missions.

filter_ship_data_by_side(test_ship_key, sides, role=None, ret_key_only=False)

Return ship data entries matching a key substring, side filter, and optional role.

Parameters:

Name Type Description Default
test_ship_key str | None

Substring that must appear in the ship key, or None to match all keys.

required
sides str

Comma-separated side names to include (case-insensitive).

required
role str

Single role that must be in the ship's role list. Defaults to None (no role filter).

None
ret_key_only bool

Return a list of key strings instead of full data dicts. Defaults to False.

False

Returns:

Type Description

list[str | dict]: Matching ship keys or data entries.

get_mod(key_or_entry)

Return the source mod of a ship data entry, or None if it is engine-known.

Exposed to MAST as ship_data_get_mod (the ship_data_ prelude prefix); named get_mod here so it doesn't double-prefix to ship_data_ship_data_get_mod.

Parameters:

Name Type Description Default
key_or_entry str | dict

A ship key, or a ship data entry dict.

required

Returns:

Type Description

str | None: The mod name stamped at merge time, or None for built-in data.

get_ship_data()

Load and cache the full ship data, merging extraShipData.json if present.

Results are cached after the first call. The mission-directory extraShipData.json is prepended to the #ship-list so mission ships take priority over built-in data.

Returns:

Name Type Description
dict

The merged ship data dictionary.

get_ship_data_for(ship_key)

Return the full ship data entry for a given key.

Parameters:

Name Type Description Default
ship_key str

The ship type key.

required

Returns:

Type Description

dict | None: Ship data dict, or None if not found.

get_ship_index()

Return ship data indexed by ship key for fast O(1) lookup.

Returns:

Type Description

dict[str, dict]: Mapping of ship key → ship data entry.

get_ship_name(ship_key)

Return the display name of a ship type by key.

Parameters:

Name Type Description Default
ship_key str

The ship type key.

required

Returns:

Type Description

str | None: Ship display name, or None if the key is not found.

kralien_ship_keys()

Return all Kralien ship keys (cached).

Returns:

Type Description

list[str]: Kralien ship type keys.

kralien_starbase_keys()

Return all Kralien starbase keys (cached).

Returns:

Type Description

list[str]: Kralien starbase type keys.

merge_mod_ship_data(mod, file=None)

Merge a mod folder's extra ship data (YAML or JSON) into the ship data cache.

Parameters:

Name Type Description Default
mod str

Mod directory name (resolved via get_mod_dir).

required
file str

The data file within the mod folder. Defaults to the base name "extraShipData", which loads extraShipData.yaml or extraShipData.json (YAML preferred). Pass a name WITH a .yaml/.yml/.json extension to force a specific format.

None

Returns:

Name Type Description
dict

The updated ship data cache.

merge_mod_ship_yaml(content, mod=None)

Merge ship data supplied as a YAML/JSON string into the ship data cache.

Companion to :func:sbs_utils.procedural.media.media_read_relative_file, which returns a data file's CONTENTS relative to the current addon -- working whether the addon is a loose folder (dev) or a packaged .mastlib zip, where a plain filesystem path can't reach the file. So an addon can ship its own ship/terrain data next to its prefabs and load it in one line:

merge_mod_ship_yaml(media_read_relative_file("shipData_monsters.yaml"), "MyMod")

The parsed #ship-list is prepended (addon data ahead of built-in). Each entry is stamped with mod (the #mod key) so the low-level spawn can tell these engine-unknown entries apart and post-process them (see :func:mod_ship_data_process). The derived caches (ship_index and the *_keys caches) are cleared so the new entries are visible on the next lookup.

Parameters:

Name Type Description Default
content str

YAML or JSON text (YAML is a JSON superset, so both parse).

required
mod str

Name of the mod/addon these entries come from; stamped on each entry as #mod. Defaults to None (untagged).

None

Returns:

Type Description

dict | None: The updated ship data cache, or None if content was empty or carried no #ship-list.

mod_ship_data_process(so, entry)

Apply a runtime-merged (mod) ship data entry to a freshly spawned object.

The engine's built-in shipData table doesn't contain entries merged at runtime (:func:merge_mod_ship_yaml / :func:merge_mod_ship_data / :func:add_ship_data), so create_space_object returns a bare object that never got the values the engine normally derives from a KNOWN shipData entry. The low-level spawn (spawn_common) calls this for such objects to reproduce that derivation.

The shipData-field -> object mapping mirrors cosmos_dev.mock.sbs's reverse-engineered _apply_ship_data_to_object (the engine's data_set names are NOT the shipData spellings):

  • Art via set_ship_data_key(artfileroot) when the modded key differs from its art (the engine picks the mesh from data_tag, not a data_set field). meshscale / radarscale are engine-internal render props with NO data_set key -- not applied.
  • exclusionradius -> the physics attribute engine_object.exclusion_radius (not a data_set field).
  • 1-to-1 float scalars (turn_rate, speed_coeff, interactionradius, ...).
  • hullpoints -> armor / armorMax (stations only; ships use another system).
  • baycount -> bay_count; tubecount -> torpedo_tube_count.
  • shields array -> shield_count + shield_val / shield_max_val per facing.
  • hull_port_sets beams -> beamCount + beamRange / beamDamage (coeff * 6.0) / beamCycleTime / beamArcWidth / beamBarrelAngle per port.
  • torpedostart -> {Type}_NUM / _MAX / _VAL + torpedo_types_available.

Fields with no known engine mapping (and the meta key/name/side/roles/#mod) are skipped; a prefab may still set anything extra afterwards (it runs after spawn, so it wins).

Parameters:

Name Type Description Default
so

The spawned SpaceObject (exposes .data_set and .set_ship_data_key).

required
entry dict

The ship data entry (as merged, carrying #mod).

required

pirate_ship_keys()

Return all pirate ship keys (cached).

Returns:

Type Description

list[str]: Pirate ship type keys.

pirate_starbase_keys()

Return all pirate starbase keys (cached).

As of v1.2.2 no pirate starbases exist in shipData; this returns an empty list.

Returns:

Type Description

list[str]: Pirate starbase type keys.

plain_asteroid_keys()

Return all plain asteroid keys, excluding crystal asteroids (cached).

Returns:

Type Description

list[str]: Plain asteroid type keys.

reset_ship_data_caches()

Clear the DERIVED ship data caches (index and key lists), not the data itself.

Called by the merge/add functions after they change the #ship-list so the next lookup sees the new entries. For the mission-boundary reset that also drops the loaded data, use :func:ship_data_reset_for_mission.

ship_data_is_loaded()

Reset-ledger probe: 1 while ship data (possibly mod-merged) is held, else 0.

ship_data_reset_for_mission()

Drop the loaded ship data ENTIRELY, including any merged mod entries.

Deliberately NOT the same as :func:reset_ship_data_caches, which clears only the DERIVED caches and is called by the merge functions themselves - clearing ship_data_cache there would throw away the entries just merged.

This is the MISSION-BOUNDARY reset. The next mission has its own mission directory (its own extraShipData) and its own set of mods, so a #ship-list carrying the previous mission's merged entries must not survive into it. The engine forks a fresh process per mission and hides this; cosmos_dev reuses one interpreter and does not. Registered in the reset ledger as ship_data_cache.

skaraan_ship_keys()

Return all Skaraan ship keys (cached).

Returns:

Type Description

list[str]: Skaraan ship type keys.

skaraan_starbase_keys()

Return all Skaraan starbase keys (cached).

Returns:

Type Description

list[str]: Skaraan starbase type keys.

terran_ship_keys()

Return all TSN ship keys (cached).

Returns:

Type Description

list[str]: Terran ship type keys.

terran_starbase_keys()

Return all USPF station (Terran starbase) keys (cached).

Returns:

Type Description

list[str]: Terran starbase type keys.

torgoth_ship_keys()

Return all Torgoth ship keys (cached).

Returns:

Type Description

list[str]: Torgoth ship type keys.

torgoth_starbase_keys()

Return all Torgoth starbase keys (cached).

Returns:

Type Description

list[str]: Torgoth starbase type keys.

ximni_ship_keys()

Return all Ximni ship keys (cached).

Returns:

Type Description

list[str]: Ximni ship type keys.

ximni_starbase_keys()

Return all Ximni starbase keys (cached).

Returns:

Type Description

list[str]: Ximni starbase type keys.