diff --git a/Content.Omu.Server/CrewManifest/OmuCrewManifestSystem.cs b/Content.Omu.Server/CrewManifest/OmuCrewManifestSystem.cs
new file mode 100644
index 00000000000..7d052d661ee
--- /dev/null
+++ b/Content.Omu.Server/CrewManifest/OmuCrewManifestSystem.cs
@@ -0,0 +1,111 @@
+using Content.Server.CrewManifest;
+using Content.Server.Roles;
+using Content.Server.Roles.Jobs;
+using Content.Server.Silicons.StationAi;
+using Content.Server.Station.Systems;
+using Content.Shared.CrewManifest;
+using Content.Shared.Mind;
+using Content.Shared.NPC.Prototypes;
+using Content.Shared.NPC.Systems;
+using Content.Shared.Roles;
+using Content.Shared.Roles.Jobs;
+using Content.Shared.Silicons.Borgs.Components;
+using Content.Shared.Silicons.StationAi;
+using Content.Shared._DV.Silicons.Laws;
+using Robust.Shared.Prototypes;
+
+namespace Content.Omu.Server.CrewManifest;
+
+///
+/// Omu-specific manifest additions for silicon crew that do not create normal station records.
+///
+public sealed class OmuCrewManifestSystem : EntitySystem
+{
+ private static readonly ProtoId BorgJobId = "Borg";
+ private static readonly ProtoId StationAiJobId = "StationAi";
+ private static readonly ProtoId NanoTrasenFactionId = "NanoTrasen";
+
+ [Dependency] private readonly StationSystem _stationSystem = default!;
+ [Dependency] private readonly StationAiSystem _stationAiSystem = default!;
+ [Dependency] private readonly SharedMindSystem _mindSystem = default!;
+ [Dependency] private readonly NpcFactionSystem _npcFactionSystem = default!;
+ [Dependency] private readonly SharedRoleSystem _roleSystem = default!;
+ [Dependency] private readonly JobSystem _jobSystem = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(OnCollectManifestEntries);
+ }
+
+ private void OnCollectManifestEntries(CrewManifestEntriesCollectEvent args)
+ {
+ var borgJob = _prototypeManager.Index(BorgJobId);
+ var stationAiJob = _prototypeManager.Index(StationAiJobId);
+
+ var borgQuery = EntityQueryEnumerator();
+ while (borgQuery.MoveNext(out var uid, out var chassis))
+ {
+ if (_stationSystem.GetOwningStation(uid) != args.Station)
+ continue;
+
+ if (chassis.BrainEntity == null)
+ continue;
+
+ if (!_npcFactionSystem.IsMember(uid, NanoTrasenFactionId))
+ continue;
+
+ if (!HasComp(uid))
+ continue;
+
+ if (!IsCrewSiliconMind(uid, BorgJobId, requireSlavedBorg: true))
+ continue;
+
+ args.Entries.Add((borgJob, BuildSiliconEntry(uid, borgJob)));
+ }
+
+ var aiQuery = EntityQueryEnumerator();
+ while (aiQuery.MoveNext(out var uid, out var core))
+ {
+ if (_stationSystem.GetOwningStation(uid) != args.Station)
+ continue;
+
+ if (!_stationAiSystem.TryGetHeld((uid, core), out var held))
+ continue;
+
+ if (!IsCrewSiliconMind(held, StationAiJobId))
+ continue;
+
+ args.Entries.Add((stationAiJob, BuildSiliconEntry(held, stationAiJob)));
+ }
+ }
+
+ private bool IsCrewSiliconMind(EntityUid uid, ProtoId expectedJob, bool requireSlavedBorg = false)
+ {
+ if (!_mindSystem.TryGetMind(uid, out var mindId, out _))
+ return false;
+
+ if (!_roleSystem.MindHasRole(mindId))
+ return false;
+
+ if (_roleSystem.MindHasRole(mindId))
+ return false;
+
+ if (requireSlavedBorg && !HasComp(uid))
+ return false;
+
+ if (_jobSystem.MindHasJobWithId(mindId, expectedJob))
+ return true;
+
+ return expectedJob == BorgJobId;
+ }
+
+ private CrewManifestEntry BuildSiliconEntry(EntityUid uid, JobPrototype job)
+ {
+ return new CrewManifestEntry(
+ MetaData(uid).EntityName,
+ job.LocalizedName,
+ job.Icon.ToString(),
+ job.ID);
+ }
+}
diff --git a/Content.Server/CrewManifest/CrewManifestEntriesCollectEvent.cs b/Content.Server/CrewManifest/CrewManifestEntriesCollectEvent.cs
new file mode 100644
index 00000000000..a07fdcd2594
--- /dev/null
+++ b/Content.Server/CrewManifest/CrewManifestEntriesCollectEvent.cs
@@ -0,0 +1,11 @@
+using Content.Shared.CrewManifest;
+using Content.Shared.Roles;
+
+namespace Content.Server.CrewManifest;
+
+///
+/// Allows fork modules to append extra crew manifest rows before the final sort/cache step.
+///
+public readonly record struct CrewManifestEntriesCollectEvent(
+ EntityUid Station,
+ List<(JobPrototype? job, CrewManifestEntry entry)> Entries);
diff --git a/Content.Server/CrewManifest/CrewManifestSystem.cs b/Content.Server/CrewManifest/CrewManifestSystem.cs
index 08bf7b53f1c..86f995f0151 100644
--- a/Content.Server/CrewManifest/CrewManifestSystem.cs
+++ b/Content.Server/CrewManifest/CrewManifestSystem.cs
@@ -129,6 +129,8 @@ private void OnBoundUiClose(EntityUid uid, CrewManifestViewerComponent component
/// The name and crew manifest entries (unordered) of the station.
public (string name, CrewManifestEntries? entries) GetCrewManifest(EntityUid station)
{
+ BuildCrewManifest(station);
+
var valid = _cachedEntries.TryGetValue(station, out var manifest);
return (valid ? MetaData(station).EntityName : string.Empty, valid ? manifest : null);
}
@@ -239,7 +241,6 @@ private void BuildCrewManifest(EntityUid station)
var iter = _recordsSystem.GetRecordsOfType(station);
var entries = new CrewManifestEntries();
-
var entriesSort = new List<(JobPrototype? job, CrewManifestEntry entry)>();
foreach (var recordObject in iter)
{
@@ -250,6 +251,10 @@ private void BuildCrewManifest(EntityUid station)
entriesSort.Add((job, entry));
}
+ // Allow downstream modules to inject extra manifest rows without rewriting this system.
+ var ev = new CrewManifestEntriesCollectEvent(station, entriesSort);
+ RaiseLocalEvent(ev);
+
entriesSort.Sort((a, b) =>
{
var cmp = JobUIComparer.Instance.Compare(a.job, b.job);
@@ -309,4 +314,4 @@ public override CompletionResult GetCompletion(IConsoleShell shell, string[] arg
return CompletionResult.FromHintOptions(stations, null);
}
-}
\ No newline at end of file
+}