Skip to content

Station construction sequence

How does a station go from β€œlist of planned modules” to β€œphysical modules visible in space”? Every multi-module station build β€” initial seeding, NPC expansion, player plot construction β€” runs through the construction sequence pipeline. This overview explains the four-step engine ↔ MD dance.

This overview is the runtime mechanic counterpart to Galaxy seeding (which only covers the initial spawn). Construction sequence runs both at game start AND during gameplay.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Caller (MD script) β”‚
β”‚ <create_construction_sequence ...> β”‚
β”‚ station=$Station β”‚
β”‚ macros=[$mod1, $mod2, ...] β”‚
β”‚ base=$existing_sequence (or empty) β”‚
β”‚ </create_construction_sequence> β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Engine β”‚
β”‚ - validates macros + connections β”‚
β”‚ - computes geometry / build order β”‚
β”‚ - emits event_object_construction_sequence_ β”‚
β”‚ created with event.param = sequence β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Listener cue β”‚
β”‚ <event_object_construction_sequence_created/> β”‚
β”‚ <apply_construction_sequence β”‚
β”‚ station=$Station β”‚
β”‚ sequence=event.param/> β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Engine β”‚
β”‚ - materializes modules β”‚
β”‚ - sets up build modules / processors β”‚
β”‚ - fires <signal_objects 'init station'> β”‚
β”‚ on the new station β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Station operational β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The caller (typically a faction-logic or finalisation cue) requests a sequence:

<create_construction_sequence
station="$Station"
macros="$NewPlannedModules"
connectors="$Conn_PlannedConnectors"
base="$BaseSequence"/>

Parameters:

ParamPurpose
station=The target station object
macros=List of module macros to include
connectors=Connection modules between them
base=Existing sequence to extend (null for fresh start)

The engine takes these as a request β€” it doesn’t immediately build anything. It validates, computes, and fires the next-step event.

The engine emits this event once the sequence is ready. Vanilla cues listen for it to know β€œthe sequence I requested is ready to apply”:

<cue name="ApplyAfterCreate" instantiate="true">
<conditions>
<event_object_construction_sequence_created
object="$Station"/>
</conditions>
<actions>
<apply_construction_sequence
station="$Station"
sequence="event.param"/>
</actions>
</cue>

Pattern from vanilla finalisestations.xml (multiple cues use this).

The event.param holds the constructed sequence β€” a Construction sequence datatype value. The listener immediately calls <apply_construction_sequence> to materialize.

<apply_construction_sequence
station="$Station"
sequence="event.param"/>

The engine now physically builds the modules. This involves:

  1. Module placement β€” each module rendered at its computed position
  2. Connection wiring β€” connectors linked between adjacent modules
  3. Build module activation β€” if this is a player station, build modules get cargo storage
  4. Initial state setup β€” production paused, defence modules unarmed, etc.

The sequence’s modules become visible in the world.

After all modules exist, the engine fires a signal_objects on the station with the parameter 'init station'. This is the canonical β€œstation is now fully operational” signal:

<signal_objects object="$Station" param="'init station'"/>

Listeners for event_object_signalled with param="'init station'" can react β€” vanilla uses this for:

  • Trade subscription registration
  • Faction logic notification
  • NPC patrol assignment

After this signal, the station appears in find_station_by_true_owner queries, accepts trade orders, can be boarded, etc.

X4 9.0 added staged construction β€” player can specify β€œbuild these first, those later”. The sequence tracks the current stage:

sequence
β”œβ”€β”€ stage 1: pier + dock area + storage ← current
β”œβ”€β”€ stage 2: production module 1
└── stage 3: production module 2

Sequence.stage.current indicates the current stage. The engine builds modules in the current stage, then waits for player approval before advancing.

<create_construction_sequence> errors on stations with staged construction. For staged stations, use <add_build_to_expand_station> instead:

<do_if value="not $Station.hasstagedconstruction">
<create_construction_sequence ...>
</do_if>
<do_else>
<add_build_to_expand_station
object="$Station.buildstorage"
buildobject="$Station"
constructionplan="$Plan"
result="$BuildID"/>
</do_else>

Pattern from factionsubgoal_buildstation.xml:211 + finalisestations.xml:359. See Build module β†’ Stage-aware build appending.

Galaxy seeding is the primary user. Every NPC station at game start runs through this pipeline.

When factionlogic_economy decides to build a new factory:

  1. <create_construction_sequence> with the new factory’s modules
  2. Listener catches the event
  3. <apply_construction_sequence> materializes
  4. signal_objects 'init station' registers it with trade

Player Plot UI adds modules β†’ MD <add_build_to_expand_station> (X4 9.0+) β†’ engine emits sequence events β†’ engine materializes.

  • Galaxy seeding β€” initial spawn (parent context)
  • Faction economy β€” drives runtime construction requests
  • Plot expansion UX β€” player-side construction flow