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.
The three components
Section titled βThe three componentsββββββββββββββββββββββββββββββββββββββββββββββ 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 βββββββββββββββββββββββββββββββββββββββββββββLayer 1: CentralInformationCenter
Section titled βLayer 1: CentralInformationCenterβ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:
| Field | Meaning |
|---|---|
| Attacker | Object doing the attacking |
| Attacked | Object being attacked |
| Position | Where itβs happening (zone + sector + cluster) |
| Time | When the call came in |
Old entries (> $Time_DataObsolete minutes) are trimmed β the master list is recency-weighted.
Layer 2: Per-faction PCS instance
Section titled βLayer 2: Per-faction PCS instanceβEach active faction registers its own PCS instance via Register_Factions. The instance:
- Processes signals (
Process_Signal) β when an incident is added or updated, evaluate relevance - Designates scouts (
Designate_Scout) β sends recon ships to verify - Reinforces positions (
Reinforce_Position) β main response coordinator - Sends patrols (
Send_Patrol) β dispatches specific ships - Analyzes threats (
Analyze_Threat) β weighs force vs incident - 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.
Decision weights
Section titled βDecision weightsβ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.
Layer 3: Send_Patrol
Section titled βLayer 3: Send_Patrolβ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β.
Why this architecture matters
Section titled βWhy this architecture mattersβGalaxy-wide visibility
Section titled βGalaxy-wide visibilityβ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).
Faction independence
Section titled βFaction independenceβ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.
Priority queue prevents starvation
Section titled βPriority queue prevents starvationβ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.
Decoupled dispatch
Section titled βDecoupled dispatchβ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.
Subscription / signal pattern
Section titled βSubscription / signal patternβ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_PatrolThe βmost factions ignore most incidentsβ property is what makes this scalable β each PCS only deeply evaluates incidents within its scope.
Distress call data structure
Section titled βDistress call data structureβ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.
Why this matters for modders
Section titled βWhy this matters for moddersβAdding custom factions
Section titled βAdding custom factionsβ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.
Tuning response intensity
Section titled βTuning response intensityβ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.
Custom response types
Section titled βCustom response typesβSend_Patrol is the dispatch primitive. Custom responses (e.g. βdeploy lasertowersβ, βspawn defence fleetβ) should mirror its arrival/disruption tracking pattern.
Performance
Section titled βPerformanceβ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.
Cross-references
Section titled βCross-referencesβ- Faction goals β parent system (PCS is a goal)
- Faction β owner of the PCS
- Sector β where incidents happen
- Ship β what patrols dispatch
- NPC orders β what patrols receive
Related architectural overviews
Section titled βRelated architectural overviewsβ- Faction goals β Patrol is part of the goal registry
- NPC orders β what patrol ships execute
- Mission framework β separate but parallel βships doing thingsβ system