Skip to content

Patrol coordination

When an Argon trade ship is attacked by Xenon in Argon Prime, the Argon faction sends reinforcements β€” but not just any reinforcements. Vanilla decides WHICH ships, from WHERE, with WHAT priority. That decision is made by the Patrol Coordination Service β€” a sub-system of Faction goals implementing X4’s galaxy-wide combat reaction.

factiongoal_patrolcoordinationservice.xml is 1260 lines, the longest single subsystem in faction logic.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Layer 1: CentralInformationCenter (CIC) β”‚
β”‚ - Singleton (one per galaxy) β”‚
β”‚ - Listens for ALL faction distress calls β”‚
β”‚ - Maintains master list of incidents β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
↓ broadcasts to
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Layer 2: Per-faction PCS instance β”‚
β”‚ - One instance per active faction β”‚
β”‚ - Evaluates each incident against β”‚
β”‚ own resources / interests β”‚
β”‚ - Selects responses β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
↓ dispatches
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Layer 3: Per-patrol Send_Patrol β”‚
β”‚ - Spawns or redirects specific ships β”‚
β”‚ - Tracks arrival / disruption β”‚
β”‚ - Reports outcomes back β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

CentralInformationCenter is a single galaxy-wide cue that:

  • Listens for distress calls from any faction (Listen_DistressCalls)
  • Adds each incident to MasterList_DistressCalls
  • Periodically trims old entries (Trim_MasterList, every 780s = 13 min)
  • Broadcasts to per-faction instances

The master list is the β€œcombat news ticker” for the whole galaxy. Every faction’s PCS reads from it.

The data per incident:

FieldMeaning
AttackerObject doing the attacking
AttackedObject being attacked
PositionWhere it’s happening (zone + sector + cluster)
TimeWhen the call came in

Old entries (> $Time_DataObsolete minutes) are trimmed β€” the master list is recency-weighted.

Each active faction registers its own PCS instance via Register_Factions. The instance:

  1. Processes signals (Process_Signal) β€” when an incident is added or updated, evaluate relevance
  2. Designates scouts (Designate_Scout) β€” sends recon ships to verify
  3. Reinforces positions (Reinforce_Position) β€” main response coordinator
  4. Sends patrols (Send_Patrol) β€” dispatches specific ships
  5. Analyzes threats (Analyze_Threat) β€” weighs force vs incident
  6. Acts (Act) β€” executes chosen response

The instance maintains its own priority queue of incidents β€” high-priority threats (e.g. attack on a HQ) jump the queue regardless of recency.

The Analyze_Threat cue evaluates multiple factors:

  • Distance from response ships β€” closer = higher priority
  • Threat strength β€” bigger attackers need bigger response
  • Faction relations β€” defending allies vs ignoring strangers
  • Available patrol ships β€” what’s in range
  • Strategic value of target β€” HQ > standard station > civilian ship

These weights feed Act, which selects 0..N patrol responses.

For each selected response, Send_Patrol spawns or redirects ships:

Send_Patrol
↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Identify available ships β”‚
β”‚ - Existing patrols in area β”‚
β”‚ - Defending fleets β”‚
β”‚ - On-call reinforcements β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Issue orders β”‚
β”‚ - Patrol_Arrived (success) β”‚
β”‚ - Patrol_Disrupted (failure) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Each patrol dispatch is tracked as a sub-instance. The PCS waits for outcome events (arrival, engagement, ship loss) before considering the incident β€œaddressed”.

Without a CIC, each faction would only react to threats in its own visible space. The CIC means Argon knows about Boron’s distress (and may choose to help).

Each faction’s PCS evaluates incidents in isolation. Argon’s PCS doesn’t know what Teladi’s PCS plans β€” they may both respond to the same incident, or neither, depending on their independent calculations.

Without a priority queue, low-importance incidents could starve high-importance ones if they kept arriving. The queue ensures HQ attacks always get response, even mid-firefight.

Send_Patrol is decoupled from Reinforce_Position β€” the latter decides β€œI want a response”, the former handles the mechanics. This lets vanilla swap out dispatch implementations without changing decision logic.

The PCS uses a publisher-subscriber model:

Ship attacks happen
↓
Engine fires event_object_attacked
↓
Listen_DistressCalls catches it
↓
CIC adds to MasterList_DistressCalls
↓
Each faction's PCS instance receives broadcast
↓
Process_Signal evaluates relevance
(most factions ignore most incidents)
↓
If relevant: Analyze_Threat β†’ Act β†’ Send_Patrol

The β€œmost factions ignore most incidents” property is what makes this scalable β€” each PCS only deeply evaluates incidents within its scope.

The $DistressCalls variable holds per-incident records:

$DistressCalls.{N} = [
{1}: AttackedObject (ship/station)
{2}: AttackedSector
{3}: TimeStamp
...
]

Vanilla factiongoal_patrolcoordinationservice.xml:300 shows the lookup pattern. Modders extending PCS should follow this shape.

A new faction needs Register_Factions to wire it into the CIC. Without registration, the faction never receives distress signals β€” its territory will go undefended in incidents it can’t see.

Mods that want β€œArgon responds harder” can adjust the weights in Analyze_Threat β€” but be careful: the system is balanced for the existing weights. Overly aggressive response inflates combat traffic.

Send_Patrol is the dispatch primitive. Custom responses (e.g. β€œdeploy lasertowers”, β€œspawn defence fleet”) should mirror its arrival/disruption tracking pattern.

CIC + per-faction PCS scales linearly with active factions. Mods adding many factions (e.g. ~24 active) add 2Γ— the PCS workload. Vanilla tunes for 10-12 active factions.