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