Cue lifecycle#

MD state belongs to the saved game. A completed cue need not execute again after loading, so code added only to an old Init may not initialize new fields in an existing save. Separate first startup from migration to a new data version.

A new field#

The example belongs in the context that owns your settings. It initializes a missing schema version, creates a missing settings table and advances the version. It does not install a load handler; call it from your actual initialization/migration path and use the correct variable namespace.

XML
<do_if value="not $SchemaVersion?">
  <set_value name="$SchemaVersion" exact="0" />
</do_if>
<do_if value="$SchemaVersion lt 1">
  <do_if value="not $Settings?">
    <set_value name="$Settings" exact="table[]" />
  </do_if>
  <set_value name="$SchemaVersion" exact="1" />
</do_if>

Preserve the player's choices#

Write defaults only for missing fields. Do not reset settings, created objects or mission progress on each load. Check object existence before using stored references. Temporary UI handles and caches are not persistent game state.

Verify a mod update#

Create a save with the previous data version, load it with the new extension, verify that old values survive and new fields appear, save and completely restart X4, then read back the result again. Initialization must not create a second object. For ship property changes, test a new and an existing ship separately: parameter application and damage state can have different lifecycles.