Skip to content

Production factory

A Production factory is the most common station type in the X4 universe — wheat farms, energy cells plants, hull parts factories, microchip refineries. Every NPC faction’s economy is built on hundreds of them. They consume input wares from suppliers, run Production modules to make output wares, and offer those outputs via trade subscriptions.

Unlike Shipyard, Wharf, Equipment dock, and Trade station, the “production factory” status is not a single flag — it’s defined by exclusion: a station that is not any of the special subtypes, and that has at least one production module.

This page documents production-factory-specific behavior. For all general station behavior (creation, ownership, hull, dockingbays, events), see the parent Station page.

There is no .isproductionfactory flag. Vanilla and mods identify production factories by what they are not:

<do_if value="not $Station.istradestation
and not $Station.isshipyard
and not $Station.iswharf
and not $Station.isequipmentdock
and not $Station.isdefencestation
and not $Station.isfactionheadquarters
and $Station.resources.count gt 0">
<!-- normal production factory -->
</do_if>

The $Station.resources.count gt 0 check is essential — a station with zero consumable resources is a pure storage / pier station, not a production factory.

This composite filter is the canonical idiom used by mods like Buy and Sell Stations and recurring NPC economy logic. Copy it rather than reinventing — new DLC may add station-type flags, and not .istradestation and not .isshipyard ... will continue to work.

Properties — production-factory-relevant

Section titled “Properties — production-factory-relevant”

All properties live on the parent station and container datatypes. The most-used:

PropertyTypeDescription
.productswarelistWares this station produces (aggregated across all production modules)
.resourceswarelistWares this station consumes
.resources.purewarelistResource wares that are NOT intermediate
.resources.intermediatewarelistResource wares produced by upstream modules on this station
.resources.primarywarelistAlways-required inputs
.resources.secondarywarelistRace-specific / optional inputs
.cargocontainercargolistWares currently in storage
.cargo.{ware}.targetintTarget stock level for a ware
.workforce.amountintCurrent workforce
.workforce.optimalintWorkforce needed for peak production
.workforce.bonusfloatMultiplier applied to production speed
.buyprices / .sellpricestablePer-ware prices for trade
.istraderestrictedboolTrades only with owner faction (global setting)
.istraderestricted.{ware}boolTrade restricted for this specific ware
<do_for_each name="$product" in="$Station.products.list">
<write_to_logbook
text="$Station.knownname + ' produces '
+ $product.name + ' at '
+ $Station.sellprice.{$product} + 'Cr'"/>
</do_for_each>
<do_for_each name="$res" in="$Station.resources.list">
<write_to_logbook
text="$Station.knownname + ' needs '
+ $res.name + ' (buy price: '
+ $Station.buyprice.{$res} + 'Cr)'"/>
</do_for_each>
<set_value name="$starved" exact="false"/>
<do_for_each name="$module" in="$Station.modules">
<do_if value="$module.isclass.{class.production}
and $module.iswaitingforresources">
<set_value name="$starved" exact="true"/>
<break/>
</do_if>
</do_for_each>

For deeper module-level analysis see Production module.

”Find production factories that need this ware"

Section titled “”Find production factories that need this ware"”
<find_station_by_true_owner name="$All"
space="player.galaxy"
multiple="true"/>
<create_list name="$Buyers"/>
<do_for_each name="$station" in="$All">
<do_if value="not $station.istradestation
and not $station.isshipyard
and not $station.iswharf
and not $station.isequipmentdock
and not $station.isdefencestation
and not $station.isfactionheadquarters
and $station.resources.indexof.{$ware} gt 0">
<append_to_list name="$Buyers" exact="$station"/>
</do_if>
</do_for_each>
<write_to_logbook
text="$Buyers.count + ' factories buy ' + $ware.name"/>

"Player just bought a production factory”

Section titled “"Player just bought a production factory””
<cue name="OnPlayerBoughtFactory" instantiate="true">
<conditions>
<event_object_changed_owner/>
<check_value
value="event.object.isclass.{class.station}
and event.param == faction.player
and not event.object.istradestation
and not event.object.isshipyard
and not event.object.iswharf
and not event.object.isequipmentdock
and not event.object.isdefencestation
and not event.object.isfactionheadquarters
and event.object.resources.count gt 0"/>
</conditions>
<actions>
<write_to_logbook
text="'Player bought production factory: '
+ event.object.knownname"/>
</actions>
</cue>

A production factory’s output rate depends on:

  1. Loadout level — set at construction via set_module_loadout_level.
  2. Workforce bonus.workforce.bonus multiplier (0..1+) from filled habitation modules.
  3. Input availability — modules pause when starved (iswaitingforresources=true).

To estimate output per hour: see Architectural overview Production cycle math (planned).

<set_value name="$WorkforceMultiplier"
exact="if @$Station.workforce.bonus then $Station.workforce.bonus else 1.0f"/>
<write_to_logbook
text="$Station.knownname + ' workforce bonus: ' + $WorkforceMultiplier"/>

Production factories are heterogeneous — one per ware they produce. Naming convention:

  • Group: factory_<faction>_<ware> (e.g. factory_arg_energycells).
  • Plan: e.g. arg_factory_energycells in libraries/constructionplans.xml.

Each plan typically combines: 1+ production module(s), 1+ storage modules of matching transport types, habitation modules for workforce, optionally defence modules.

  • No .isproductionfactory flag. Use the exclusion filter above. New DLC may add station-type flags; the exclusion idiom stays correct.
  • .resources.count is critical in the filter. Without it, pure storage / pier / venture stations slip through and look like production factories. They have no .resources (nothing to consume).
  • Iterate .products.list / .resources.list inline. wareamountlist degrades when stored in a variable — see Ware gotchas.
  • .products aggregates ALL production modules. A two-module station producing hull parts + advanced electronics shows BOTH wares in .products.list. To trace which module produces what, iterate modules with .isclass.{class.production}.
  • .workforce.bonus may be null on player stations without workforce setup. Default to 1.0f when reading.
  • Recycling stations look like production factories but use processing modules instead of production modules. Vanilla flags them with .isrecyclingfacility. Add not .isrecyclingfacility to the exclusion filter if your logic shouldn’t touch them.
  • How NPC factions place new factories: Architectural overview Faction economyEcon_Manager per-sector evaluates ware shortages → picks ware → builds factory.
  • Production rate math: Architectural overview Production cycle math — cycle duration × workforce bonus × loadout × input availability.
  • Trade route formation: Architectural overview Trade routing — factories register buy/sell offers; trade subscription propagates them to trade stations.