diff --git a/Content.Server/Station/Systems/ServerStationSystem.cs b/Content.Server/Station/Systems/ServerStationSystem.cs
index bdd2768ddb..f1d20624d3 100644
--- a/Content.Server/Station/Systems/ServerStationSystem.cs
+++ b/Content.Server/Station/Systems/ServerStationSystem.cs
@@ -1,4 +1,3 @@
-using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Atmos.EntitySystems;
@@ -311,6 +310,53 @@ public sealed partial class ServerStationSystem : Shared.Station.Systems.Station
QueueDel(station);
}
+ ///
+ /// Get entities with on a random event-eligible station's largest grid.
+ ///
+ /// Whether to only get anchored entities.
+ /// Good check for air vents, bad for containers like crates.
+ /// All matching entities.
+ public HashSet> GetEntitiesWithComponentOnStation(bool onlyAnchored)
+ where TComponent : IComponent
+ {
+ return GetEntitiesWithComponentOnStation(onlyAnchored, out _);
+ }
+
+ /// The station picked to search for the entities.
+ ///
+ public HashSet> GetEntitiesWithComponentOnStation(bool onlyAnchored,
+ out Entity? station)
+ where TComponent : IComponent
+ {
+ HashSet> entities = [];
+
+ if (!TryGetRandomStation(out var eligibleStation))
+ {
+ station = null;
+ return entities;
+ }
+
+ station = (eligibleStation.Value.Owner, eligibleStation.Value.Comp1);
+
+ var grid = GetLargestGrid(station.Value.Owner);
+ if (grid is null)
+ return entities;
+
+ var locations = EntityQueryEnumerator();
+ while (locations.MoveNext(out var uid, out var component, out var transform))
+ {
+ if (onlyAnchored && !transform.Anchored)
+ continue;
+
+ if (transform.GridUid != grid)
+ continue;
+
+ entities.Add((uid, component));
+ }
+
+ return entities;
+ }
+
///
public override bool TryFindRandomTileOnStation(Entity station,
out Vector2i tile,
@@ -319,89 +365,40 @@ public sealed partial class ServerStationSystem : Shared.Station.Systems.Station
int numAttempts = 10)
{
tile = default;
- targetCoords = EntityCoordinates.Invalid;
targetGrid = null;
+ targetCoords = EntityCoordinates.Invalid;
- // Weight grid choice by tilecount
- var totalTiles = 0;
- var grids = new List<(Entity Entity, int Count, List Tiles)>();
- foreach (var possibleTarget in station.Comp.Grids)
- {
- if (!GridQuery.TryComp(possibleTarget, out var comp))
- continue;
-
- // Get the tile count for the given grid.
- var tileCount = Map.GetFilledTileCount((possibleTarget, comp));
-
- // Just to be sure, no empty elements.
- if (tileCount > 0)
- {
- grids.Add(((possibleTarget, comp), tileCount, new()));
- totalTiles += tileCount;
- }
- }
-
- if (grids.Count == 0)
+ var targetGridMaybe = GetLargestGrid(station.Owner);
+ if (targetGridMaybe is null)
return false;
- for (var i = 0; i < numAttempts; i++)
+ if (!TryComp(targetGridMaybe.Value, out var comp))
+ return false;
+
+ targetGrid = (targetGridMaybe.Value, comp);
+ var grid = targetGrid.Value;
+
+ var gridTiles = Map.GetAllTiles(grid, grid.Comp).ToList();
+ var totalTiles = gridTiles.Count;
+ if (totalTiles == 0)
+ return false;
+
+ for (var i = 0; i < numAttempts && totalTiles > 0; i++)
{
// Find random tile within list.
var nextTileIndex = Random.Next(totalTiles);
- TileRef? randomTileRef = null;
- var startIndex = 0;
- for (int j = 0; j < grids.Count; j++)
- {
- var grid = grids[j];
- // If the index is in this particular grid, find it and remove the tile to prevent selecting it twice.
- if (nextTileIndex >= startIndex + grid.Count)
- {
- startIndex += grid.Count;
- continue;
- }
-
- targetGrid = grid.Entity;
-
- // Empty list: hasn't been queried yet - get our tiles.
- if (grid.Tiles.Count <= 0)
- {
- grid.Tiles = Map.GetAllTiles(targetGrid.Value, targetGrid.Value.Comp).ToList();
-
- // Actual list count doesn't match expected count (a bug - return failure).
- Debug.Assert(grid.Tiles.Count == grid.Count);
- if (grid.Tiles.Count != grid.Count)
- return false;
- }
-
- var ourTileIndex = nextTileIndex - startIndex;
- randomTileRef = grid.Tiles[ourTileIndex];
- grid.Tiles.RemoveSwap(ourTileIndex);
- grid.Count--;
- totalTiles--;
-
- // Empty list, remove element
- if (grid.Tiles.Count <= 0)
- grids.RemoveSwap(j);
-
- break;
- }
-
- // Out of valid tiles, return early.
- if (randomTileRef is not { } tileRef)
- return false;
-
- // Could not find a grid...
- if (targetGrid == null)
- continue;
+ var tileRef = gridTiles[nextTileIndex];
+ gridTiles.RemoveSwap(nextTileIndex);
+ totalTiles--;
// Invalid tile, try again.
- if (_atmos.IsTileSpace(targetGrid.Value.Owner, Transform(targetGrid.Value).MapUid, tileRef.GridIndices)
- || _atmos.IsTileAirBlockedCached(targetGrid.Value.Owner, tileRef.GridIndices))
+ if (_atmos.IsTileSpace(grid.Owner, Transform(grid).MapUid, tileRef.GridIndices)
+ || _atmos.IsTileAirBlockedCached(grid.Owner, tileRef.GridIndices))
{
continue;
}
- targetCoords = Map.GridTileToLocal(targetGrid.Value, targetGrid.Value, tileRef.GridIndices);
+ targetCoords = Map.GridTileToLocal(grid, grid.Comp, tileRef.GridIndices);
tile = tileRef.GridIndices;
return true;
}
@@ -415,92 +412,67 @@ public sealed partial class ServerStationSystem : Shared.Station.Systems.Station
/// This is the ideal point to add components to it.
///
[PublicAPI]
-public sealed class StationInitializedEvent : EntityEventArgs
+public sealed class StationInitializedEvent(EntityUid station) : EntityEventArgs
{
///
/// Station this event is for.
///
- public EntityUid Station;
-
- public StationInitializedEvent(EntityUid station)
- {
- Station = station;
- }
+ public EntityUid Station = station;
}
///
/// Directed event fired on a station when a grid becomes a member of the station.
///
[PublicAPI]
-public sealed class StationGridAddedEvent : EntityEventArgs
+public sealed class StationGridAddedEvent(EntityUid gridId, EntityUid station, bool isSetup) : EntityEventArgs
{
///
/// ID of the grid added to the station.
///
- public EntityUid GridId;
+ public EntityUid GridId = gridId;
///
/// EntityUid of the station this grid was added to.
///
- public EntityUid Station;
+ public EntityUid Station = station;
///
/// Indicates that the event was fired during station setup,
/// so that it can be ignored if StationInitializedEvent was already handled.
///
- public bool IsSetup;
-
- public StationGridAddedEvent(EntityUid gridId, EntityUid station, bool isSetup)
- {
- GridId = gridId;
- Station = station;
- IsSetup = isSetup;
- }
+ public bool IsSetup = isSetup;
}
///
/// Directed event fired on a station when a grid is no longer a member of the station.
///
[PublicAPI]
-public sealed class StationGridRemovedEvent : EntityEventArgs
+public sealed class StationGridRemovedEvent(EntityUid gridId, EntityUid station) : EntityEventArgs
{
///
/// ID of the grid removed from the station.
///
- public EntityUid GridId;
+ public EntityUid GridId = gridId;
///
/// EntityUid of the station this grid was added to.
///
- public EntityUid Station;
-
- public StationGridRemovedEvent(EntityUid gridId, EntityUid station)
- {
- GridId = gridId;
- Station = station;
- }
+ public EntityUid Station = station;
}
///
/// Directed event fired on a station when it is renamed.
///
[PublicAPI]
-public sealed class StationRenamedEvent : EntityEventArgs
+public sealed class StationRenamedEvent(string oldName, string newName) : EntityEventArgs
{
///
/// Prior name of the station.
///
- public string OldName;
+ public string OldName = oldName;
///
/// New name of the station.
///
- public string NewName;
-
- public StationRenamedEvent(string oldName, string newName)
- {
- OldName = oldName;
- NewName = newName;
- }
+ public string NewName = newName;
}
-
diff --git a/Content.Server/StationEvents/Components/BreakerFlipRuleComponent.cs b/Content.Server/StationEvents/Components/BreakerFlipRuleComponent.cs
index b0dac4984e..d0105deead 100644
--- a/Content.Server/StationEvents/Components/BreakerFlipRuleComponent.cs
+++ b/Content.Server/StationEvents/Components/BreakerFlipRuleComponent.cs
@@ -4,11 +4,4 @@ using Content.Shared.Whitelist;
namespace Content.Server.StationEvents.Components;
[RegisterComponent, Access(typeof(BreakerFlipRule))]
-public sealed partial class BreakerFlipRuleComponent : Component
-{
- ///
- /// Blacklist of structures not eligible to trigger this game rule.
- ///
- [DataField]
- public EntityWhitelist? Blacklist;
-}
+public sealed partial class BreakerFlipRuleComponent : Component;
diff --git a/Content.Server/StationEvents/Components/GasLeakRuleComponent.cs b/Content.Server/StationEvents/Components/GasLeakRuleComponent.cs
index 61ced84812..9dc083fed2 100644
--- a/Content.Server/StationEvents/Components/GasLeakRuleComponent.cs
+++ b/Content.Server/StationEvents/Components/GasLeakRuleComponent.cs
@@ -32,24 +32,12 @@ public sealed partial class GasLeakRuleComponent : Component
[DataField]
public float LeakCooldown = 1.0f;
- ///
- /// The station where the leak is located.
- ///
- [DataField]
- public EntityUid TargetStation;
-
///
/// The specific grid where the leak is located.
///
[DataField]
public EntityUid TargetGrid;
- ///
- /// The tile coordinates where the leak is located.
- ///
- [DataField]
- public Vector2i TargetTile;
-
///
/// The world coordinates of the leak location.
///
diff --git a/Content.Server/StationEvents/Components/SolarFlareRuleComponent.cs b/Content.Server/StationEvents/Components/SolarFlareRuleComponent.cs
index c8f65aa5e2..fb2785277d 100644
--- a/Content.Server/StationEvents/Components/SolarFlareRuleComponent.cs
+++ b/Content.Server/StationEvents/Components/SolarFlareRuleComponent.cs
@@ -1,4 +1,6 @@
using Content.Server.StationEvents.Events;
+using Content.Shared.Doors.Components;
+using Content.Shared.Light.Components;
using Content.Shared.Radio;
using Robust.Shared.Prototypes;
@@ -40,6 +42,18 @@ public sealed partial class SolarFlareRuleComponent : Component
[DataField("extraCount")]
public uint ExtraCount;
+ ///
+ /// The collection of lights that will be affected by the solar flare event.
+ ///
+ [DataField]
+ public HashSet<(EntityUid, PoweredLightComponent)> AffectedLights = [];
+
+ ///
+ /// The collection of airlocks that will be affected by the solar flare event.
+ ///
+ [DataField]
+ public HashSet<(EntityUid, AirlockComponent)> AffectedAirlocks = [];
+
///
/// Chance light bulb breaks per second during event
///
diff --git a/Content.Server/StationEvents/Events/BreakerFlipRule.cs b/Content.Server/StationEvents/Events/BreakerFlipRule.cs
index bde62c5971..6a51ee7ac7 100644
--- a/Content.Server/StationEvents/Events/BreakerFlipRule.cs
+++ b/Content.Server/StationEvents/Events/BreakerFlipRule.cs
@@ -1,10 +1,9 @@
+using System.Linq;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.StationEvents.Components;
using Content.Shared.Database;
using Content.Shared.GameTicking.Components;
-using Content.Shared.Station.Components;
-using Content.Shared.Whitelist;
using JetBrains.Annotations;
namespace Content.Server.StationEvents.Events;
@@ -17,7 +16,6 @@ namespace Content.Server.StationEvents.Events;
public sealed partial class BreakerFlipRule : StationEventSystem
{
[Dependency] private ApcSystem _apcSystem = default!;
- [Dependency] private EntityWhitelistSystem _whitelist = default!;
protected override void Added(Entity ent, ref GameRuleAddedEvent args)
{
@@ -34,20 +32,7 @@ public sealed partial class BreakerFlipRule : StationEventSystem(
- out var chosenStation,
- uid => _whitelist.IsWhitelistFailOrNull(ent.Comp1.Blacklist, uid)))
- return;
-
- var stationApcs = new List>();
- var query = EntityQueryEnumerator();
- while (query.MoveNext(out var apcUid, out var apc, out var xform))
- {
- if (apc.MainBreakerEnabled && CompOrNull(xform.GridUid)?.Station == chosenStation.Value.Owner)
- {
- stationApcs.Add((apcUid, apc));
- }
- }
+ var stationApcs = Station.GetEntitiesWithComponentOnStation(true).ToList();
var toDisable = Math.Min(RobustRandom.Next(3, 7), stationApcs.Count);
if (toDisable == 0)
@@ -61,7 +46,8 @@ public sealed partial class BreakerFlipRule : StationEventSystem(ent, out var stationEvent))
return;
- // Essentially we'll pick out a target amount of gas to leak, then a rate to leak it at, then work out the duration from there.
- if (Station.TryFindRandomTile(out gasLeak.TargetTile, out var target, out var grid, out gasLeak.TargetCoords))
+ var stationVents = Station.GetEntitiesWithComponentOnStation(true);
+ if (stationVents.Count == 0)
{
- gasLeak.TargetGrid = grid.Value;
- gasLeak.TargetStation = target.Value;
- gasLeak.FoundTile = true;
+ ForceEndSelf((ent, gameRule));
+ return;
+ }
- gasLeak.LeakGas = RobustRandom.Pick(gasLeak.LeakableGases);
- // Was 50-50 on using normal distribution.
- var totalGas = RobustRandom.Next(gasLeak.MinimumGas, gasLeak.MaximumGas);
- gasLeak.MolesPerSecond = RobustRandom.Next(gasLeak.MinimumMolesPerSecond, gasLeak.MaximumMolesPerSecond);
+ var targetVent = RobustRandom.Pick(stationVents);
- if (gameRule.Delay is {} startAfter)
- stationEvent.EndTime = _timing.CurTime + TimeSpan.FromSeconds(totalGas / gasLeak.MolesPerSecond + startAfter.Next(RobustRandom));
+ gasLeak.FoundTile = true;
+ gasLeak.TargetCoords = Transform(targetVent).Coordinates;
+ gasLeak.TargetGrid = Transform(targetVent).GridUid!.Value;
+
+ // Essentially we'll pick out a target amount of gas to leak, then a rate to leak it at, then work out the duration from there.
+ gasLeak.LeakGas = RobustRandom.Pick(gasLeak.LeakableGases);
+ // Was 50-50 on using normal distribution.
+ var totalGas = RobustRandom.Next(gasLeak.MinimumGas, gasLeak.MaximumGas);
+ gasLeak.MolesPerSecond = RobustRandom.Next(gasLeak.MinimumMolesPerSecond, gasLeak.MaximumMolesPerSecond);
+
+ if (gameRule.Delay is { } startAfter)
+ {
+ stationEvent.EndTime = _timing.CurTime +
+ TimeSpan.FromSeconds(totalGas / gasLeak.MolesPerSecond +
+ startAfter.Next(RobustRandom));
}
// Look technically if you wanted to guarantee a leak you'd do this in announcement but having the announcement
@@ -64,7 +75,7 @@ public sealed partial class GasLeakRule : StationEventSystem rule)
{
- if (RobustRandom.NextFloat() <= rule.Comp.SparkChance)
- {
- if (!rule.Comp.FoundTile ||
- rule.Comp.TargetGrid == default ||
- (!Exists(rule.Comp.TargetGrid) ? EntityLifeStage.Deleted : MetaData(rule.Comp.TargetGrid).EntityLifeStage) >= EntityLifeStage.Deleted ||
- !_atmosphere.IsSimulatedGrid(rule.Comp.TargetGrid))
- {
- return;
- }
+ if (!(RobustRandom.NextFloat() <= rule.Comp.SparkChance))
+ return;
- // Don't want it to be so obnoxious as to instantly murder anyone in the area but enough that
- // it COULD start potentially start a bigger fire.
- _atmosphere.HotspotExpose(rule.Comp.TargetGrid, rule.Comp.TargetTile, 700f, 50f, null, true);
- Audio.PlayPvs(new SoundPathSpecifier("/Audio/Effects/sparks4.ogg"), rule.Comp.TargetCoords);
+ if (!rule.Comp.FoundTile ||
+ rule.Comp.TargetGrid == default ||
+ (!Exists(rule.Comp.TargetGrid) ? EntityLifeStage.Deleted : MetaData(rule.Comp.TargetGrid).EntityLifeStage) >= EntityLifeStage.Deleted ||
+ !_atmosphere.IsSimulatedGrid(rule.Comp.TargetGrid))
+ {
+ return;
}
+
+ // Don't want it to be so obnoxious as to instantly murder anyone in the area but enough that
+ // it COULD start potentially start a bigger fire.
+ _atmosphere.HotspotExpose(rule.Comp.TargetGrid, (Vector2i)rule.Comp.TargetCoords.Position, 700f, 50f, null, true);
+ Audio.PlayPvs(new SoundPathSpecifier("/Audio/Effects/sparks4.ogg"), rule.Comp.TargetCoords);
}
}
diff --git a/Content.Server/StationEvents/Events/PowerGridCheckRule.cs b/Content.Server/StationEvents/Events/PowerGridCheckRule.cs
index 32120202c4..2178b18d36 100644
--- a/Content.Server/StationEvents/Events/PowerGridCheckRule.cs
+++ b/Content.Server/StationEvents/Events/PowerGridCheckRule.cs
@@ -9,174 +9,160 @@ using Robust.Shared.Player;
using Robust.Shared.Utility;
using Timer = Robust.Shared.Timing.Timer;
-namespace Content.Server.StationEvents.Events
-{
- ///
- /// Handler for events that force a number of APCs off on a station for a period of time.
- ///
- ///
- [UsedImplicitly]
- public sealed partial class PowerGridCheckRule : StationEventSystem
- {
- [Dependency] private ApcSystem _apcSystem = default!;
+namespace Content.Server.StationEvents.Events;
- public override void Initialize()
+///
+/// Handler for events that force a number of APCs off on a station for a period of time.
+///
+///
+[UsedImplicitly]
+public sealed partial class PowerGridCheckRule : StationEventSystem
+{
+ [Dependency] private ApcSystem _apc = default!;
+
+ protected override void Started(Entity ent, ref GameRuleStartedEvent args)
+ {
+ base.Started(ent, ref args);
+
+ var apcs = Station.GetEntitiesWithComponentOnStation(true, out var chosenStation);
+ if (chosenStation is null)
+ return;
+
+ var powerGridCheck = ent.Comp1;
+
+ powerGridCheck.AffectedStation = chosenStation.Value;
+ var largestGrid = Station.GetLargestGrid(chosenStation.Value.AsNullable());
+ if (largestGrid is null)
+ return;
+
+ foreach (var apcUid in apcs)
{
- base.Initialize();
- SubscribeLocalEvent(OnApcStartup);
- SubscribeLocalEvent(OnApcToggleMainBreaker);
+ if (!apcUid.Comp.MainBreakerEnabled)
+ continue;
+
+ var apcTransform = Transform(apcUid);
+ if (apcTransform.GridUid != largestGrid)
+ continue;
+
+ powerGridCheck.Powered.Add(apcUid);
}
- protected override void Started(Entity ent, ref GameRuleStartedEvent args)
+ RobustRandom.Shuffle(powerGridCheck.Powered);
+
+ powerGridCheck.NumberPerSecond = Math.Max(1, (int)(powerGridCheck.Powered.Count / powerGridCheck.SecondsUntilOff)); // Number of APCs to turn off every second. At least one.
+ }
+
+ ///
+ /// Check if the entity should be affected by an existing
+ /// PowerGridCheckRuleComponent and if so, turns off the APC.
+ ///
+ [SubscribeLocalEvent]
+ private void OnApcStartup(EntityUid apcUid, PowerGridCheckNotifyComponent comp, ComponentStartup args)
+ {
+ if (!TryComp(apcUid, out var apcComp))
+ return;
+
+ var rule = GetRuleAffectingEntity(apcUid);
+ if (rule == null || !apcComp.MainBreakerEnabled)
+ return;
+
+ _apc.ApcToggleBreaker(apcUid, apcComp);
+ rule.Unpowered.Add(apcUid);
+ }
+
+ [SubscribeLocalEvent]
+ private void OnApcToggleMainBreaker(EntityUid uid, PowerGridCheckNotifyComponent component, ref ApcToggleMainBreakerAttemptEvent args)
+ {
+ args.Cancelled |= GetRuleAffectingEntity(uid) != null;
+ }
+
+ ///
+ /// Returns the PowerGridCheckRuleComponent affecting the uid, or null if none
+ ///
+ private PowerGridCheckRuleComponent? GetRuleAffectingEntity(EntityUid uid)
+ {
+ if (!TryComp(uid, out TransformComponent? xform))
+ return null;
+
+ if (!TryComp(xform.GridUid, out var stationMemberComp))
+ return null;
+
+ var activeRules = AllEntityQuery();
+ while (activeRules.MoveNext(out _, out var powerGridRule, out _))
{
- base.Started(ent, ref args);
+ if (stationMemberComp.Station != powerGridRule.AffectedStation)
+ continue;
- if (!Station.TryGetRandomStation(out var chosenStation))
- return;
-
- var powerGridCheck = ent.Comp1;
-
- powerGridCheck.AffectedStation = chosenStation.Value;
-
- var largestGrid = Station.GetLargestGrid(chosenStation.Value.AsNullable());
+ var largestGrid = Station.GetLargestGrid(powerGridRule.AffectedStation);
if (largestGrid == null)
- return;
+ continue;
- var query = AllEntityQuery();
- while (query.MoveNext(out var apcUid, out var apc, out var transform))
- {
- if (!apc.MainBreakerEnabled)
- continue;
+ if (xform.GridUid != largestGrid.Value)
+ continue;
- if (CompOrNull(transform.GridUid)?.Station != chosenStation.Value.Owner)
- continue;
-
- if (transform.GridUid != largestGrid.Value)
- continue;
-
- powerGridCheck.Powered.Add(apcUid);
- }
-
- RobustRandom.Shuffle(powerGridCheck.Powered);
-
- powerGridCheck.NumberPerSecond = Math.Max(1, (int)(powerGridCheck.Powered.Count / powerGridCheck.SecondsUntilOff)); // Number of APCs to turn off every second. At least one.
+ return powerGridRule;
}
- ///
- /// Check if the entity should be affected by an existing
- /// PowerGridCheckRuleComponent and if so, turns off the APC.
- ///
- private void OnApcStartup(EntityUid apcUid, PowerGridCheckNotifyComponent comp, ComponentStartup args)
- {
- if (!TryComp(apcUid, out var apcComp))
- {
- return;
- }
+ return null;
+ }
- PowerGridCheckRuleComponent? rule = GetRuleAffectingEntity(apcUid);
- if (rule != null && apcComp.MainBreakerEnabled)
- {
- _apcSystem.ApcToggleBreaker(apcUid, apcComp);
- rule.Unpowered.Add(apcUid);
- }
+ protected override void Ended(Entity rule, ref GameRuleEndedEvent args)
+ {
+ base.Ended(rule, ref args);
+
+ foreach (var entity in rule.Comp.Unpowered)
+ {
+ if (Deleted(entity))
+ continue;
+
+ if (!TryComp(entity, out var apcComponent))
+ continue;
+
+ if (!apcComponent.MainBreakerEnabled)
+ _apc.ApcToggleBreaker(entity, apcComponent);
}
- private void OnApcToggleMainBreaker(EntityUid uid, PowerGridCheckNotifyComponent component, ref ApcToggleMainBreakerAttemptEvent args)
+ // Can't use the default EndAudio
+ rule.Comp.AnnounceCancelToken?.Cancel();
+ rule.Comp.AnnounceCancelToken = new CancellationTokenSource();
+ Timer.Spawn(3000,
+ () =>
{
- args.Cancelled |= GetRuleAffectingEntity(uid) != null;
+ Audio.PlayGlobal(rule.Comp.PowerOnSound, Filter.Broadcast(), true);
+ },
+ rule.Comp.AnnounceCancelToken.Token);
+ rule.Comp.Unpowered.Clear();
+ }
+
+ protected override void ActiveTick(EntityUid uid, PowerGridCheckRuleComponent component, GameRuleComponent gameRule, float frameTime)
+ {
+ base.ActiveTick(uid, component, gameRule, frameTime);
+
+ var updates = 0;
+ component.FrameTimeAccumulator += frameTime;
+ if (component.FrameTimeAccumulator > component.UpdateRate)
+ {
+ updates = (int)(component.FrameTimeAccumulator / component.UpdateRate);
+ component.FrameTimeAccumulator -= component.UpdateRate * updates;
}
- ///
- /// Returns the PowerGridCheckRuleComponent affecting the uid, or null if none
- ///
- private PowerGridCheckRuleComponent? GetRuleAffectingEntity(EntityUid uid)
+ for (var i = 0; i < updates; i++)
{
- if (!TryComp(uid, out TransformComponent? xform))
+ if (component.Powered.Count == 0)
+ break;
+
+ var selected = component.Powered.Pop();
+ if (Deleted(selected))
+ continue;
+
+ if (TryComp(selected, out var apcComponent))
{
- return null;
+ if (apcComponent.MainBreakerEnabled)
+ _apc.ApcToggleBreaker(selected, apcComponent);
}
- if (!TryComp(xform.GridUid, out var stationMemberComp))
- {
- return null;
- }
-
- var activeRules = AllEntityQuery();
- while (activeRules.MoveNext(out _, out var powerGridRule, out _))
- {
- if (stationMemberComp.Station != powerGridRule.AffectedStation)
- continue;
-
- var largestGrid = Station.GetLargestGrid(powerGridRule.AffectedStation);
-
- if (largestGrid == null)
- continue;
-
- if (xform.GridUid != largestGrid.Value)
- continue;
-
- return powerGridRule;
- }
-
- return null;
- }
-
- protected override void Ended(Entity rule, ref GameRuleEndedEvent args)
- {
- base.Ended(rule, ref args);
-
- foreach (var entity in rule.Comp.Unpowered)
- {
- if (Deleted(entity))
- continue;
-
- if (TryComp(entity, out ApcComponent? apcComponent))
- {
- if (!apcComponent.MainBreakerEnabled)
- _apcSystem.ApcToggleBreaker(entity, apcComponent);
- }
- }
-
- // Can't use the default EndAudio
- rule.Comp.AnnounceCancelToken?.Cancel();
- rule.Comp.AnnounceCancelToken = new CancellationTokenSource();
- Timer.Spawn(3000,
- () =>
- {
- Audio.PlayGlobal(rule.Comp.PowerOnSound, Filter.Broadcast(), true);
- },
- rule.Comp.AnnounceCancelToken.Token);
- rule.Comp.Unpowered.Clear();
- }
-
- protected override void ActiveTick(EntityUid uid, PowerGridCheckRuleComponent component, GameRuleComponent gameRule, float frameTime)
- {
- base.ActiveTick(uid, component, gameRule, frameTime);
-
- var updates = 0;
- component.FrameTimeAccumulator += frameTime;
- if (component.FrameTimeAccumulator > component.UpdateRate)
- {
- updates = (int)(component.FrameTimeAccumulator / component.UpdateRate);
- component.FrameTimeAccumulator -= component.UpdateRate * updates;
- }
-
- for (var i = 0; i < updates; i++)
- {
- if (component.Powered.Count == 0)
- break;
-
- var selected = component.Powered.Pop();
- if (Deleted(selected))
- continue;
- if (TryComp(selected, out var apcComponent))
- {
- if (apcComponent.MainBreakerEnabled)
- _apcSystem.ApcToggleBreaker(selected, apcComponent);
- }
- component.Unpowered.Add(selected);
- }
+ component.Unpowered.Add(selected);
}
}
}
diff --git a/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs b/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs
index 16eb40fda4..c2d92e1e6a 100644
--- a/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs
+++ b/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs
@@ -1,7 +1,6 @@
using Content.Server.StationEvents.Components;
using Content.Server.Storage.EntitySystems;
using Content.Shared.GameTicking.Components;
-using Content.Shared.Station.Components;
using Content.Shared.Storage.Components;
using Robust.Shared.Map;
using Robust.Shared.Random;
@@ -20,24 +19,17 @@ public sealed partial class RandomEntityStorageSpawnRule : StationEventSystem(out var station))
- return;
-
var spawnRule = ent.Comp1;
- var validLockers = new List<(EntityUid, EntityStorageComponent)>();
+ var validLockers = new List>();
var spawn = Spawn(spawnRule.Prototype, MapCoordinates.Nullspace);
- var query = EntityQueryEnumerator();
- while (query.MoveNext(out var storageUid, out var storage, out var xform))
+ foreach (var storageEnt in Station.GetEntitiesWithComponentOnStation(false))
{
- if (Station.GetOwningStation(storageUid, xform) != station.Value.Owner)
+ if (!_entityStorage.CanInsert(spawn, storageEnt, storageEnt.Comp))
continue;
- if (!_entityStorage.CanInsert(spawn, storageUid, storage))
- continue;
-
- validLockers.Add((storageUid, storage));
+ validLockers.Add(storageEnt);
}
if (validLockers.Count == 0)
@@ -46,10 +38,8 @@ public sealed partial class RandomEntityStorageSpawnRule : StationEventSystem _headsetQuery;
- private float _effectTimer = 0;
+ private float _effectTimer;
protected override void Started(Entity ent, ref GameRuleStartedEvent args)
{
@@ -36,6 +37,13 @@ public sealed partial class SolarFlareRule : StationEventSystem(true)
+ .Select(e => (e.Owner, e.Comp))
+ .ToHashSet();
+ ent.Comp1.AffectedAirlocks = Station.GetEntitiesWithComponentOnStation(true)
+ .Select(e => (e.Owner, e.Comp))
+ .ToHashSet();
}
protected override void ActiveTick(EntityUid uid, SolarFlareRuleComponent component, GameRuleComponent gameRule, float frameTime)
@@ -43,21 +51,20 @@ public sealed partial class SolarFlareRule : StationEventSystem();
- while (lightQuery.MoveNext(out var lightEnt, out var light))
- {
- if (RobustRandom.Prob(component.LightBreakChancePerSecond))
- _poweredLight.TryDestroyBulb(lightEnt, light);
- }
- var airlockQuery = EntityQueryEnumerator();
- while (airlockQuery.MoveNext(out var airlockEnt, out var airlock, out var door))
- {
- if (airlock.AutoClose && RobustRandom.Prob(component.DoorToggleChancePerSecond))
- _door.TryToggleDoor(airlockEnt, door);
- }
+ if (RobustRandom.Prob(component.LightBreakChancePerSecond))
+ _poweredLight.TryDestroyBulb(light.Item1, light.Item2);
+ }
+
+ foreach (var airlockEnt in component.AffectedAirlocks)
+ {
+ if (airlockEnt.Item2.AutoClose && RobustRandom.Prob(component.DoorToggleChancePerSecond))
+ _door.TryToggleDoor(airlockEnt.Item1);
}
}
diff --git a/Content.Server/StationEvents/Events/VentClogRule.cs b/Content.Server/StationEvents/Events/VentClogRule.cs
index 7c469fcdae..cec30901de 100644
--- a/Content.Server/StationEvents/Events/VentClogRule.cs
+++ b/Content.Server/StationEvents/Events/VentClogRule.cs
@@ -6,7 +6,6 @@ using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reaction;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.GameTicking.Components;
-using Content.Shared.Station.Components;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
@@ -25,9 +24,6 @@ public sealed partial class VentClogRule : StationEventSystem(out var chosenStation))
- return;
-
var ventClog = ent.Comp1;
// TODO: "safe random" for chems. Right now this includes admin chemicals.
@@ -35,13 +31,9 @@ public sealed partial class VentClogRule : StationEventSystem !x.Abstract)
.Select(x => new ProtoId(x.ID)).ToList();
- foreach (var (_, transform) in EntityQuery())
+ foreach (var ventPump in Station.GetEntitiesWithComponentOnStation(true))
{
- if (CompOrNull(transform.GridUid)?.Station != chosenStation.Value.Owner)
- {
- continue;
- }
-
+ var targetCoords = Transform(ventPump).Coordinates;
var solution = new Solution();
if (!RobustRandom.Prob(0.33f))
@@ -54,10 +46,10 @@ public sealed partial class VentClogRule : StationEventSystem(out var station))
- {
- return;
- }
-
var critterRule = ent.Comp1;
-
- var locations = EntityQueryEnumerator();
- var validLocations = new List();
- while (locations.MoveNext(out _, out _, out var transform))
- {
- if (!transform.Anchored)
- continue;
-
- if (CompOrNull(transform.GridUid)?.Station == station.Value.Owner)
- {
- validLocations.Add(transform.Coordinates);
- foreach (var spawn in EntitySpawnCollection.GetSpawns(critterRule.Entries, RobustRandom))
- {
- Spawn(spawn, transform.Coordinates);
- }
- }
- }
-
- if (critterRule.SpecialEntries.Count == 0 || validLocations.Count == 0)
- {
+ var validLocations = Station.GetEntitiesWithComponentOnStation(true);
+ if (validLocations.Count == 0)
return;
- }
// guaranteed spawn
- var specialEntry = RobustRandom.Pick(critterRule.SpecialEntries);
- var specialSpawn = RobustRandom.Pick(validLocations);
- Spawn(specialEntry.PrototypeId, specialSpawn);
+ if (critterRule.SpecialEntries.Count > 0)
+ {
+ var specialEntry = RobustRandom.Pick(critterRule.SpecialEntries);
+ var specialSpawn = Transform(RobustRandom.Pick(validLocations)).Coordinates;
+ Spawn(specialEntry.PrototypeId, specialSpawn);
+ }
foreach (var location in validLocations)
{
- foreach (var spawn in EntitySpawnCollection.GetSpawns(critterRule.SpecialEntries, RobustRandom))
+ var spawns = EntitySpawnCollection.GetSpawns(critterRule.Entries, RobustRandom);
+ foreach (var spawn in spawns)
{
- Spawn(spawn, location);
+ Spawn(spawn, Transform(location).Coordinates);
+ }
+
+ var specialSpawns = EntitySpawnCollection.GetSpawns(critterRule.SpecialEntries, RobustRandom);
+ foreach (var spawn in specialSpawns)
+ {
+ Spawn(spawn, Transform(location).Coordinates);
}
}
}
diff --git a/Content.Server/StationEvents/Events/VentHordeRule.cs b/Content.Server/StationEvents/Events/VentHordeRule.cs
index 9048b19cfd..a2fdcf2187 100644
--- a/Content.Server/StationEvents/Events/VentHordeRule.cs
+++ b/Content.Server/StationEvents/Events/VentHordeRule.cs
@@ -5,7 +5,6 @@ using Content.Server.VentHorde.Components;
using Content.Server.VentHorde.Systems;
using Content.Shared.EntityTable;
using Content.Shared.GameTicking.Components;
-using Content.Shared.Station.Components;
using Robust.Shared.Random;
using Robust.Shared.Timing;
@@ -26,8 +25,6 @@ public sealed partial class VentHordeRule : StationEventSystem _hordeSpawnerQuery;
-
protected override void Added(Entity ent, ref GameRuleAddedEvent args)
{
var ventHorde = ent.Comp1;
@@ -76,7 +73,7 @@ public sealed partial class VentHordeRule : StationEventSystem(out var station))
- {
- return null;
- }
-
- // Query the possible locations
- var locations = EntityQueryEnumerator();
- var validLocations = new List();
-
- // Filter to things on the same station
- while (locations.MoveNext(out var uid, out _, out var transform))
- {
- if (!transform.Anchored)
- continue;
-
- if (_hordeSpawnerQuery.HasComp(uid))
- continue;
-
- if (CompOrNull(transform.GridUid)?.Station == station.Value.Owner)
- {
- validLocations.Add(uid);
- }
- }
+ var validLocations = Station.GetEntitiesWithComponentOnStation(true);
+ validLocations.RemoveWhere(uid => HasComp(uid));
// Pick one at random
if (validLocations.Count != 0)
diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml
index 65799a5b3d..e00de31ae9 100644
--- a/Resources/Prototypes/GameRules/events.yml
+++ b/Resources/Prototypes/GameRules/events.yml
@@ -158,9 +158,6 @@
duration: 1
minimumPlayers: 15
- type: BreakerFlipRule
- blacklist:
- components:
- - ProtectedGrid
- type: DynamicRuleCost
cost: 10