Skip to content

The settings system

Mission configuration defaults loaded from settings.yaml (or legacy setup.json).

Overview

settings_get_defaults() returns a dict of built-in defaults merged with mission-specific overrides from settings.yaml in the mission directory. The result is cached after the first call, so all modules that read settings see a consistent snapshot.

Common use-cases:

  • Reading built-in settings like DIFFICULTY, PLAYER_COUNT, WORLD_SELECT, or operator-mode config.
  • Adding module-specific defaults with settings_add_defaults so that a module's settings are visible in settings_get_defaults() even when the author hasn't created a settings.yaml.

Quick example

== setup ==
    settings = settings_get_defaults()
    difficulty = settings.get("DIFFICULTY", 5)
    log(f"Difficulty is {difficulty}")
from sbs_utils.procedural.settings import settings_get_defaults, settings_add_defaults

# Read built-in settings
settings = settings_get_defaults()
difficulty = settings.get("DIFFICULTY", 5)
player_count = settings.get("PLAYER_COUNT", 1)

# Register module-specific defaults (won't override settings.yaml values)
settings_add_defaults({
    "ENEMY_COUNT": 10,
    "BOSS_ENABLED": False,
})

Built-in settings keys

Key Default Notes
DIFFICULTY 5 Mission difficulty 1–10
PLAYER_COUNT 1 Expected number of player ships
WORLD_SELECT "siege" World generation preset
TERRAIN_SELECT "some" Terrain density
LETHAL_SELECT "none" Lethal NPC density
FRIENDLY_SELECT "few" Friendly NPC density
UPGRADE_SELECT "max" Available upgrades
AUTO_START False Skip lobby and start immediately
GAME_STARTED False Set by the game engine at start
GAME_ENDED False Set by mission when over
GRID_THEME 0 Engineering grid color theme index
PLAYER_LIST list of 8 ships Player ship definitions

API

settings_add_defaults(additions)

Merge additional keys into the global settings defaults.

additions acts as a fallback — existing values from settings.yaml or setup.json take precedence, so this only fills gaps.

Parameters:

Name Type Description Default
additions dict

Default key-value pairs to add if not already present.

required

settings_add_npc_races(*races)

Add races to NPC_RACES, keeping whatever is already listed.

The NPC twin of :func:settings_add_playable_races - same semantics, same ordering caveat. Separate from the playable list on purpose: the races a player may BE and the races that raid them are different questions.

Parameters:

Name Type Description Default
*races

race names, as separate arguments, a comma-separated string, or a list.

()

Returns:

Name Type Description
list

the names actually added, in order.

settings_add_playable_races(*races)

Add races to PLAYABLE_RACES, keeping whatever is already listed.

The call a MOD that ships player-flyable hulls should make. Accepts names as separate arguments, one comma-separated string, or a list::

settings_add_playable_races("Federation", "Klingon")
settings_add_playable_races("Federation, Klingon")

ADD rather than replace, so a mod can put a Galaxy alongside a TSN crew instead of taking the mission's own races away. A total conversion stays a MISSION's choice - it sets the setting to its own races alone - rather than something installing a mastlib does to you.

WHY NOT settings_set_mod_default. That is the right tier for "a value the library ships and the mission did not override", and it deliberately returns False once the mission has spoken (_explicit_keys). Adding a race is not overriding a choice, it is widening a list, and it has to work even when the mission named the key - a mission that lists TSN, Ximni has said nothing at all about the Federation. So this edits the live settings dict, which is what every mod doing this had to hand-roll.

ORDER MATTERS, and this is the one sharp edge. Addons decide which floor plans and fleet ladders to load by READING these settings at load time, and addon load order is non-deterministic. Adding a race the mod supplies hulls for is safe, because the mod's own addon merges those. Adding a race to unlock ANOTHER addon's content is a race with that addon's own load - call this as early as possible (the first line of the mod's __init__.mast) and do not rely on it.

Parameters:

Name Type Description Default
*races

race names, as separate arguments, a comma-separated string, or a list.

()

Returns:

Name Type Description
list

the names actually added, in order. Empty when every one was already listed.

settings_get_defaults()

Return the merged default settings dict, loading settings.yaml or setup.json if present.

Results are cached after the first call. Mission-specific values from the YAML/JSON file override the built-in defaults.

Returns:

Name Type Description
dict

The default settings mapping.

settings_get_profile()

The selected profile file, PARSED - not merged.

settings_get_defaults() folds a profile's settings keys into the settings dict and then forgets the file, which is all a setting ever needed. A profile that also selects ADD-ONS has to be read as a document, by the compiler, before any settings exist to merge into - so the parse is cached here and both callers share it.

Returns:

Name Type Description
dict

the profile, or an empty dict when none was named or it did not load.

settings_npc_races()

The races that can appear as NPCs, lowercased, from NPC_RACES.

settings_playable_races()

The races a player ship may be, lowercased, from PLAYABLE_RACES.

settings_profile_addons()

The profile's addons: include/exclude, by addon FOLDER name.

settings_profile_media()

The profile's media: include/exclude, by media pack name.

settings_profile_reset()

Forget the parsed profile. Called from reset_mission_state - a reused interpreter can be pointed at a different mission, and its profile.

settings_race_is_npc(race)

Whether a race can appear as an NPC.

Used by the race_* addons to skip loading a fleet ladder for a race this mission never spawns. As with :func:settings_race_is_playable, matching ignores case and spacing, and an EMPTY setting means no restriction rather than no races.

settings_race_is_playable(race)

Whether a race may be flown as a player ship.

Used by the interiors_* addons to skip loading floor plans for a race no player can be, since an interior is only ever built for a player ship.

An EMPTY or missing PLAYABLE_RACES means "no restriction" rather than "nothing is playable" - a mission that clears the setting should get every race, not a game where no ship has an interior.

settings_seed_apply(value=None)

Seed the global RNG so a run is reproducible.

Every random draw in sbs_utils flows through Python's single global random.Random instance -- both module-level random.* calls and the from random import ... bindings (scatter, vec) resolve to it -- so one seed here makes terrain scatter, fleet-race weights, dialogue % selection, faces, and names all reproducible.

Parameters:

Name Type Description Default
value int | None

explicit seed. If None the seed_value setting is used. A falsy seed (the default 0 = "don't care") means pick one: a fresh entropy-based seed is generated, applied, and returned, so a run can always be reproduced later by passing the value back.

None

Returns:

Name Type Description
int

the seed actually applied.

settings_set_mod_default(key, value)

Set a setting on behalf of a MOD - unless the mission already spoke for it.

The tier that was missing. There are two kinds of "default" and the existing :func:settings_add_defaults only expresses the weaker one: it does additions | setting_defaults, so anything already in the built-ins wins, and a mod can therefore only fill a key sbs_utils has never heard of. For a key the library ships a value for - MUSIC_SELECT, PLAYABLE_RACES - a mod had no way to be heard at all.

So the precedence is now, strongest first::

var.NAME= (command line)  >  COSMOS_SETTINGS  >  profiles/<name>.yaml
                          >  settings.yaml / setup.json
                          >  settings_set_mod_default   <- this
                          >  the library built-in

which is the order an author would expect: a mod re-skins the game, and anything the mission or the operator actually typed still beats it.

Last mod loaded wins between two mods, and load order is non-deterministic - so two mods claiming the same key is a genuine conflict, not something to paper over. Use a key the mod owns.

Parameters:

Name Type Description Default
key str

the setting name.

required
value

the value.

required

Returns:

Name Type Description
bool

whether it applied. False means the mission (or the launch) had already set it.