diff --git a/Content.Server/DeltaV/StationEvents/Components/DebrisSpawnerRuleComponent.cs b/Content.Server/DeltaV/StationEvents/Components/DebrisSpawnerRuleComponent.cs
new file mode 100644
index 00000000000..0f89e161fc8
--- /dev/null
+++ b/Content.Server/DeltaV/StationEvents/Components/DebrisSpawnerRuleComponent.cs
@@ -0,0 +1,30 @@
+/*
+* Delta-V - This file is licensed under AGPLv3
+* Copyright (c) 2024 Delta-V Contributors
+* See AGPLv3.txt for details.
+*/
+
+using Content.Server.StationEvents.Events;
+
+namespace Content.Server.StationEvents.Components;
+
+///
+/// Spawns random debris in space around a loaded grid.
+/// Requires .
+///
+[RegisterComponent, Access(typeof(DebrisSpawnerRule))]
+public sealed partial class DebrisSpawnerRuleComponent : Component
+{
+ ///
+ /// How many debris grids to spawn.
+ ///
+ [DataField(required: true)]
+ public int Count;
+
+ ///
+ /// Modifier for debris distance.
+ /// Should be between 3 and 10 generally.
+ ///
+ [DataField(required: true)]
+ public float DistanceModifier;
+}
diff --git a/Content.Server/DeltaV/StationEvents/Components/LoadFarGridRuleComponent.cs b/Content.Server/DeltaV/StationEvents/Components/LoadFarGridRuleComponent.cs
new file mode 100644
index 00000000000..ca241fb071f
--- /dev/null
+++ b/Content.Server/DeltaV/StationEvents/Components/LoadFarGridRuleComponent.cs
@@ -0,0 +1,37 @@
+/*
+* Delta-V - This file is licensed under AGPLv3
+* Copyright (c) 2024 Delta-V Contributors
+* See AGPLv3.txt for details.
+*/
+
+using Content.Server.StationEvents.Events;
+using Robust.Shared.Utility;
+
+namespace Content.Server.StationEvents.Components;
+
+///
+/// Loads a grid far away from a random station.
+/// Requires .
+///
+[RegisterComponent, Access(typeof(LoadFarGridRule))]
+public sealed partial class LoadFarGridRuleComponent : Component
+{
+ ///
+ /// Path to the grid to spawn.
+ ///
+ [DataField(required: true)]
+ public ResPath Path = new();
+
+ ///
+ /// Roughly how many AABBs away
+ ///
+ [DataField(required: true)]
+ public float DistanceModifier;
+
+ ///
+ /// "Stations of Unusual Size Constant", derived from the AABB.Width of Shoukou.
+ /// This Constant is used to check the size of a station relative to the reference point
+ ///
+ [DataField]
+ public float Sousk = 123.44f;
+}
diff --git a/Content.Server/DeltaV/StationEvents/Components/PirateRadioSpawnRuleComponent.cs b/Content.Server/DeltaV/StationEvents/Components/PirateRadioSpawnRuleComponent.cs
deleted file mode 100644
index 07ea5425cf1..00000000000
--- a/Content.Server/DeltaV/StationEvents/Components/PirateRadioSpawnRuleComponent.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-* Delta-V - This file is licensed under AGPLv3
-* Copyright (c) 2024 Delta-V Contributors
-* See AGPLv3.txt for details.
-*/
-
-using Content.Server.DeltaV.StationEvents.Events;
-using Content.Server.StationEvents.Events;
-
-namespace Content.Server.StationEvents.Components;
-
-[RegisterComponent, Access(typeof(PirateRadioSpawnRule))]
-public sealed partial class PirateRadioSpawnRuleComponent : Component
-{
- [DataField("PirateRadioShuttlePath")]
- public string PirateRadioShuttlePath = "Maps/Shuttles/DeltaV/DV-pirateradio.yml";
-
- [DataField("additionalRule")]
- public EntityUid? AdditionalRule;
-
- [DataField("debrisCount")]
- public int DebrisCount { get; set; }
-
- [DataField("distanceModifier")]
- public float DistanceModifier { get; set; }
-
- [DataField("debrisDistanceModifier")]
- public float DebrisDistanceModifier { get; set; }
-
- ///
- /// "Stations of Unusual Size Constant", derived from the AABB.Width of Shoukou.
- /// This Constant is used to check the size of a station relative to the reference point
- ///
- [DataField("sousk")]
- public float SOUSK = 123.44f;
-
-}
diff --git a/Content.Server/DeltaV/StationEvents/Events/DebrisSpawnerRule.cs b/Content.Server/DeltaV/StationEvents/Events/DebrisSpawnerRule.cs
new file mode 100644
index 00000000000..41e0742fd3d
--- /dev/null
+++ b/Content.Server/DeltaV/StationEvents/Events/DebrisSpawnerRule.cs
@@ -0,0 +1,71 @@
+/*
+* Delta-V - This file is licensed under AGPLv3
+* Copyright (c) 2024 Delta-V Contributors
+* See AGPLv3.txt for details.
+*/
+
+using Content.Server.GameTicking.Rules;
+using Content.Server.Station.Components;
+using Content.Server.StationEvents.Components;
+using Content.Shared.CCVar;
+using Content.Shared.Salvage;
+using Robust.Server.GameObjects;
+using Robust.Server.Maps;
+using Robust.Shared.Configuration;
+using Robust.Shared.Map;
+using Robust.Shared.Map.Components;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+using System.Linq;
+
+namespace Content.Server.StationEvents.Events;
+
+public sealed class DebrisSpawnerRule : StationEventSystem
+{
+ [Dependency] private readonly IConfigurationManager _config = default!;
+ [Dependency] private readonly IPrototypeManager _proto = default!;
+ [Dependency] private readonly MapLoaderSystem _mapLoader = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnLoadedGrids);
+ }
+
+ private void OnLoadedGrids(Entity ent, ref RuleLoadedGridsEvent args)
+ {
+ if (_config.GetCVar(CCVars.WorldgenEnabled))
+ return;
+
+ // get world AABBs of every grid that was loaded, probably just 1 anyway
+ var boxes = new List(args.Grids.Count);
+ foreach (var gridId in args.Grids)
+ {
+ var grid = Comp(gridId);
+ var aabb = Transform(gridId).WorldMatrix.TransformBox(grid.LocalAABB);
+ boxes.Add(aabb);
+ }
+
+ // fetch all the salvage maps that can be picked
+ var salvageMaps = _proto.EnumeratePrototypes().ToList();
+
+ // spawn them!
+ for (var i = 0; i < ent.Comp.Count; i++)
+ {
+ var aabb = RobustRandom.Pick(boxes);
+ var dist = MathF.Max(aabb.Height / 2f, aabb.Width / 2f) * ent.Comp.DistanceModifier;
+
+ var offset = RobustRandom.NextVector2(dist, dist * 2.5f);
+ var randomer = RobustRandom.NextVector2(dist, dist * 5f); //Second random vector to ensure the outpost isn't perfectly centered in the debris field
+ var options = new MapLoadOptions
+ {
+ Offset = aabb.Center + offset + randomer,
+ LoadMap = false,
+ };
+
+ var salvage = RobustRandom.PickAndTake(salvageMaps);
+ _mapLoader.Load(args.Map, salvage.MapPath.ToString(), options);
+ }
+ }
+}
diff --git a/Content.Server/DeltaV/StationEvents/Events/LoadFarGridRule.cs b/Content.Server/DeltaV/StationEvents/Events/LoadFarGridRule.cs
new file mode 100644
index 00000000000..66039084f75
--- /dev/null
+++ b/Content.Server/DeltaV/StationEvents/Events/LoadFarGridRule.cs
@@ -0,0 +1,78 @@
+/*
+* Delta-V - This file is licensed under AGPLv3
+* Copyright (c) 2024 Delta-V Contributors
+* See AGPLv3.txt for details.
+*/
+
+using Content.Server.GameTicking.Components;
+using Content.Server.GameTicking.Rules;
+using Content.Server.Station.Components;
+using Content.Server.StationEvents.Components;
+using Robust.Server.GameObjects;
+using Robust.Server.Maps;
+using Robust.Shared.Map;
+using Robust.Shared.Map.Components;
+using Robust.Shared.Random;
+
+namespace Content.Server.StationEvents.Events;
+
+public sealed class LoadFarGridRule : StationEventSystem
+{
+ [Dependency] private readonly MapLoaderSystem _mapLoader = default!;
+
+ protected override void Added(EntityUid uid, LoadFarGridRuleComponent comp, GameRuleComponent rule, GameRuleAddedEvent args)
+ {
+ base.Added(uid, comp, rule, args);
+
+ if (!TryGetRandomStation(out var station) || !TryComp(station, out var data))
+ {
+ Log.Error($"{ToPrettyString(uid):rule} failed to find a station!");
+ ForceEndSelf(uid, rule);
+ return;
+ }
+
+ if (data.Grids.Count < 1)
+ {
+ Log.Error($"{ToPrettyString(uid):rule} picked station {station} which had no grids!");
+ ForceEndSelf(uid, rule);
+ return;
+ }
+
+ // get an AABB that contains all the station's grids
+ var aabb = new Box2();
+ var map = MapId.Nullspace;
+ foreach (var gridId in data.Grids)
+ {
+ // use the first grid's map id
+ if (map == MapId.Nullspace)
+ map = Transform(gridId).MapID;
+
+ var grid = Comp(gridId);
+ var gridAabb = Transform(gridId).WorldMatrix.TransformBox(grid.LocalAABB);
+ aabb = aabb.Union(gridAabb);
+ }
+
+ var scale = comp.Sousk / aabb.Width;
+ var modifier = comp.DistanceModifier * scale;
+ var dist = MathF.Max(aabb.Height / 2f, aabb.Width / 2f) * modifier;
+ var offset = RobustRandom.NextVector2(dist, dist * 2.5f);
+ var options = new MapLoadOptions
+ {
+ Offset = aabb.Center + offset,
+ LoadMap = false
+ };
+
+ var path = comp.Path.ToString();
+ Log.Debug($"Loading far grid {path} at {options.Offset}");
+ if (!_mapLoader.TryLoad(map, path, out var grids, options))
+ {
+ Log.Error($"{ToPrettyString(uid):rule} failed to load grid {path}!");
+ ForceEndSelf(uid, rule);
+ return;
+ }
+
+ // let other systems do stuff
+ var ev = new RuleLoadedGridsEvent(map, grids);
+ RaiseLocalEvent(uid, ref ev);
+ }
+}
diff --git a/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs b/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs
deleted file mode 100644
index 6fad44d2c56..00000000000
--- a/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
-* Delta-V - This file is licensed under AGPLv3
-* Copyright (c) 2024 Delta-V Contributors
-* See AGPLv3.txt for details.
-*/
-
-using System.Linq;
-using Content.Server.GameTicking.Components;
-using Content.Server.Station.Components;
-using Content.Server.StationEvents.Components;
-using Content.Server.StationEvents.Events;
-using Content.Shared.CCVar;
-using Content.Shared.Salvage;
-using Robust.Server.GameObjects;
-using Robust.Server.Maps;
-using Robust.Shared.Configuration;
-using Robust.Shared.Map;
-using Robust.Shared.Map.Components;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Random;
-
-namespace Content.Server.DeltaV.StationEvents.Events;
-
-public sealed class PirateRadioSpawnRule : StationEventSystem
-{
- [Dependency] private readonly IEntityManager _entities = default!;
- [Dependency] private readonly IMapManager _mapManager = default!;
- [Dependency] private readonly MapLoaderSystem _map = default!;
- [Dependency] private readonly IRobustRandom _random = default!;
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- [Dependency] private readonly IConfigurationManager _confMan = default!;
-
- protected override void Started(EntityUid uid, PirateRadioSpawnRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
- {
- //Start of Syndicate Listening Outpost spawning system
- base.Started(uid, component, gameRule, args);
- var xformQuery = GetEntityQuery();
- var aabbs = EntityQuery().SelectMany(x =>
- x.Grids.Select(x =>
- xformQuery.GetComponent(x).WorldMatrix.TransformBox(_entities.GetComponent(x).LocalAABB)))
- .ToArray();
- if (aabbs.Length < 1) return;
- var aabb = aabbs[0];
-
- for (var i = 1; i < aabbs.Length; i++)
- {
- aabb.Union(aabbs[i]);
- }
- var distanceFactorCoefficient = component.SOUSK / aabb.Width;
- var distanceModifier = Math.Clamp(component.DistanceModifier, 1, 25);
- var distanceModifierNormalized = distanceModifier * distanceFactorCoefficient;
- var a = MathF.Max(aabb.Height / 2f, aabb.Width / 2f) * distanceModifierNormalized;
- var randomoffset = _random.NextVector2(a, a * 2.5f);
- var outpostOptions = new MapLoadOptions
- {
- Offset = aabb.Center + randomoffset,
- LoadMap = false,
- };
- if (!_map.TryLoad(GameTicker.DefaultMap, component.PirateRadioShuttlePath, out var outpostids, outpostOptions)) return;
- //End of Syndicate Listening Outpost spawning system
-
- //Start of Debris Field Generation
- var debrisSpawner = _confMan.GetCVar(CCVars.WorldgenEnabled);
- if (debrisSpawner == true) return;
- var debrisCount = Math.Clamp(component.DebrisCount, 0, 6);
- if (debrisCount == 0) return;
- var debrisDistanceModifier = Math.Clamp(component.DebrisDistanceModifier, 3, 10);
- foreach (var id in outpostids)
- {
- if (!TryComp(id, out var grid)) return;
- var outpostaabb = _entities.GetComponent(id).WorldMatrix.TransformBox(grid.LocalAABB);
- var b = MathF.Max(outpostaabb.Height / 2f, aabb.Width / 2f) * debrisDistanceModifier;
- var k = 1;
- while (k < debrisCount + 1)
- {
- var debrisRandomOffset = _random.NextVector2(b, b * 2.5f);
- var randomer = _random.NextVector2(b, b * 5f); //Second random vector to ensure the outpost isn't perfectly centered in the debris field
- var debrisOptions = new MapLoadOptions
- {
- Offset = outpostaabb.Center + debrisRandomOffset + randomer,
- LoadMap = false,
- };
-
- var salvageProto = _random.Pick(_prototypeManager.EnumeratePrototypes().ToList());
- _map.TryLoad(GameTicker.DefaultMap, salvageProto.MapPath.ToString(), out _, debrisOptions);
- k++;
- }
- }
- //End of Debris Field generation
- }
-
- protected override void Ended(EntityUid uid, PirateRadioSpawnRuleComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args)
- {
- base.Ended(uid, component, gameRule, args);
-
- if (component.AdditionalRule != null)
- GameTicker.EndGameRule(component.AdditionalRule.Value);
- }
-}
diff --git a/Content.Server/Roles/ListeningPostRoleComponent.cs b/Content.Server/Roles/ListeningPostRoleComponent.cs
new file mode 100644
index 00000000000..7fbc07835a2
--- /dev/null
+++ b/Content.Server/Roles/ListeningPostRoleComponent.cs
@@ -0,0 +1,9 @@
+using Content.Shared.Roles;
+
+namespace Content.Server.Roles;
+
+///
+/// DeltaV - listening post ops have their own role
+///
+[RegisterComponent, ExclusiveAntagonist]
+public sealed partial class ListeningPostRoleComponent : AntagonistRoleComponent;
diff --git a/Content.Server/Roles/RoleSystem.cs b/Content.Server/Roles/RoleSystem.cs
index f7a51773573..ba8889f9205 100644
--- a/Content.Server/Roles/RoleSystem.cs
+++ b/Content.Server/Roles/RoleSystem.cs
@@ -19,6 +19,7 @@ public override void Initialize()
SubscribeAntagEvents();
SubscribeAntagEvents();
SubscribeAntagEvents();
+ SubscribeAntagEvents(); // DeltaV - listening post role
}
public string? MindGetBriefing(EntityUid? mindId)
diff --git a/Resources/Locale/en-US/deltav/game-ticking/game-rules/rule-listening-post.ftl b/Resources/Locale/en-US/deltav/game-ticking/game-rules/rule-listening-post.ftl
new file mode 100644
index 00000000000..77f2dc3f33d
--- /dev/null
+++ b/Resources/Locale/en-US/deltav/game-ticking/game-rules/rule-listening-post.ftl
@@ -0,0 +1 @@
+listening-post-round-end-agent-name = Listening Post Operative
diff --git a/Resources/Maps/Shuttles/DeltaV/DV-pirateradio.yml b/Resources/Maps/Shuttles/DeltaV/listening_post.yml
similarity index 99%
rename from Resources/Maps/Shuttles/DeltaV/DV-pirateradio.yml
rename to Resources/Maps/Shuttles/DeltaV/listening_post.yml
index 9cff388fc88..0e92bd7f06e 100644
--- a/Resources/Maps/Shuttles/DeltaV/DV-pirateradio.yml
+++ b/Resources/Maps/Shuttles/DeltaV/listening_post.yml
@@ -25,7 +25,6 @@ entities:
- type: MetaData
name: unknown
- type: Transform
- parent: invalid
- type: MapGrid
chunks:
0,0:
@@ -5546,7 +5545,7 @@ entities:
- type: Transform
pos: 2.5,2.5
parent: 1
-- proto: SpawnPointGhostSyndicateListener
+- proto: SpawnPointNukies
entities:
- uid: 813
components:
diff --git a/Resources/Prototypes/DeltaV/Entities/Markers/Spawners/ghost_roles.yml b/Resources/Prototypes/DeltaV/Entities/Markers/Spawners/ghost_roles.yml
index 4fa82531ebf..2c2a63bb398 100644
--- a/Resources/Prototypes/DeltaV/Entities/Markers/Spawners/ghost_roles.yml
+++ b/Resources/Prototypes/DeltaV/Entities/Markers/Spawners/ghost_roles.yml
@@ -20,19 +20,15 @@
- sprite: Mobs/Species/Human/parts.rsi
state: full
-- type: entity # Part of PirateRadioSpawn
+- type: entity # Part of ListeningPost
noSpawn: true
+ parent: BaseAntagSpawner
id: SpawnPointGhostSyndicateListener
- name: ghost role spawn point
- suffix: syndicate listener
- parent: MarkerBase
components:
- type: GhostRole
name: ghost-role-information-listeningop-name
description: ghost-role-information-listeningop-description
rules: ghost-role-information-listeningop-rules
- raffle:
- settings: default
requirements: # Worth considering these numbers for the goal of making sure someone willing to MRP takes this.
- !type:OverallPlaytimeRequirement
time: 259200 # 72 hours
@@ -45,17 +41,9 @@
- !type:DepartmentTimeRequirement
department: Command
time: 40000 # 11.1 hours
- - type: GhostRoleMobSpawner
- prototype: MobHumanSyndicateListener
- - type: Sprite
- sprite: Markers/jobs.rsi
- layers:
- - state: green
- - sprite: Structures/Wallmounts/signs.rsi
- state: radiation
- type: entity
- parent: MarkerBase
+ parent: BaseAntagSpawner
id: SpawnPointGhostParadoxAnomaly
name: paradox anomaly spawn point
components:
@@ -63,10 +51,3 @@
name: ghost-role-information-paradox-anomaly-name
description: ghost-role-information-paradox-anomaly-description
rules: ghost-role-information-paradox-anomaly-rules
- raffle:
- settings: default
- - type: GhostRoleAntagSpawner
- - type: Sprite
- sprite: Markers/jobs.rsi
- layers:
- - state: green
diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/Player/human.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/Player/human.yml
deleted file mode 100644
index d539c58496a..00000000000
--- a/Resources/Prototypes/DeltaV/Entities/Mobs/Player/human.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-- type: entity # Delta-V : Part of a mid-round PirateRadioSpawn
- noSpawn: true
- parent: MobHumanSyndicateAgent
- id: MobHumanSyndicateListener
- name: Syndicate Listener
- components:
- - type: Loadout
- prototypes: [SyndicateListenerGear]
- - type: NpcFactionMember
- factions:
- - Syndicate
- - type: AutoTraitor
- giveUplink: false
- giveObjectives: false
- - type: AutoImplant
- implants:
- - DeathAcidifierImplant
diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/Player/humanoid.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/Player/humanoid.yml
index 2c8b01553a7..9441d436a05 100644
--- a/Resources/Prototypes/DeltaV/Entities/Mobs/Player/humanoid.yml
+++ b/Resources/Prototypes/DeltaV/Entities/Mobs/Player/humanoid.yml
@@ -1,19 +1,3 @@
-- type: entity # Delta-V part of PirateRadioSpawn
- id: RandomHumanoidSpawnerListener
- name: Syndicate Listener
- components:
- - type: Sprite
- sprite: Mobs/Species/Human/parts.rsi
- state: full
- - type: RandomHumanoidSpawner
- settings: SyndicateListener
-
-- type: randomHumanoidSettings
- id: SyndicateListener
- components:
- - type: Loadout
- prototypes: [SyndicateListenerGear]
-
# Mobsters
- type: entity
diff --git a/Resources/Prototypes/DeltaV/GameRules/events.yml b/Resources/Prototypes/DeltaV/GameRules/events.yml
index c1faea220b1..0cd0d18db58 100644
--- a/Resources/Prototypes/DeltaV/GameRules/events.yml
+++ b/Resources/Prototypes/DeltaV/GameRules/events.yml
@@ -45,8 +45,8 @@
- id: MobMothroach
prob: 0.05
-- type: entity # Delta-V : Midround Syndie Listening Station Spawn
- id: PirateRadioSpawn
+- type: entity
+ id: ListeningPost
parent: BaseGameRule
noSpawn: true
components:
@@ -55,16 +55,37 @@
weight: 5
minimumPlayers: 25
maxOccurrences: 1
- duration: 1
- # TODO:
- # 1. use the load grid rule to load the post itself
- # 2. rename below to DebrisSpawnRule or something and have it just be debris
- # 3. use AntagSelection to spawn the listeners
- # the map will need spawnpoints like SpawnPointNukies added
- - type: PirateRadioSpawnRule
- debrisCount: 6
+ duration: null
+ - type: RuleGrids
+ - type: LoadFarGridRule
+ path: /Maps/Shuttles/DeltaV/listening_post.yml
distanceModifier: 13
- debrisDistanceModifier: 3
+ - type: DebrisSpawnerRule
+ count: 6
+ distanceModifier: 3
+ # TODO: funny trolling or intel related objectives
+ - type: AntagLoadProfileRule
+ - type: AntagSelection
+ agentName: listening-post-round-end-agent-name
+ definitions:
+ - spawnerPrototype: SpawnPointGhostSyndicateListener
+ min: 2
+ max: 2
+ pickPlayer: false
+ startingGear: SyndicateListenerGear
+ components:
+ - type: RandomMetadata
+ nameSegments:
+ - names_death_commando
+ - type: AutoImplant
+ implants:
+ - DeathAcidifierImplant
+ - type: NpcFactionMember
+ factions:
+ - Syndicate
+ mindComponents:
+ - type: ListeningPostRole
+ prototype: ListeningPost
# Mid round antag spawns
- type: entity
diff --git a/Resources/Prototypes/DeltaV/Roles/Antags/listening_post.yml b/Resources/Prototypes/DeltaV/Roles/Antags/listening_post.yml
new file mode 100644
index 00000000000..d6b8adf1949
--- /dev/null
+++ b/Resources/Prototypes/DeltaV/Roles/Antags/listening_post.yml
@@ -0,0 +1,18 @@
+- type: antag
+ id: ListeningPost
+ name: roles-antag-listening-post-name
+ antagonist: true
+ objective: roles-antag-listening-post-objective
+ # keep these in sync with the spawner
+ requirements: # Worth considering these numbers for the goal of making sure someone willing to MRP takes this.
+ - !type:OverallPlaytimeRequirement
+ time: 259200 # 72 hours
+ - !type:DepartmentTimeRequirement
+ department: Security
+ time: 40000 # 11.1 hours
+ - !type:DepartmentTimeRequirement
+ department: Civilian
+ time: 40000 # 11.1 hours
+ - !type:DepartmentTimeRequirement
+ department: Command
+ time: 40000 # 11.1 hours