mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 10:21:18 +03:00
fix some station events targeting off-station grids (#45813)
* fix vent critters spawning off station * I polish the code * add utility to get entities ONLY on the station. use in vent critters rule. * fix station events affecting outside grids * better argument names for GetEntitiesWithComponentOnStation * BreakerFlipRule * \n * fix doc in GetEntitiesWithComponentOnStation * keep the comments * remove BreakerFlipRuleComponent Blacklist * minor cleanup * review * format --------- Co-authored-by: Pok245 <113675512+Pok27@users.noreply.github.com>
This commit is contained in:
parent
29668eb5a3
commit
e39cd127d9
13 changed files with 319 additions and 424 deletions
|
|
@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get entities with <see cref="TComponent"/> on a random event-eligible station's largest grid.
|
||||
/// </summary>
|
||||
/// <param name="onlyAnchored">Whether to only get anchored entities.
|
||||
/// Good check for air vents, bad for containers like crates.</param>
|
||||
/// <returns>All matching entities.</returns>
|
||||
public HashSet<Entity<TComponent>> GetEntitiesWithComponentOnStation<TComponent>(bool onlyAnchored)
|
||||
where TComponent : IComponent
|
||||
{
|
||||
return GetEntitiesWithComponentOnStation<TComponent>(onlyAnchored, out _);
|
||||
}
|
||||
|
||||
/// <param name="station">The station picked to search for the entities.</param>
|
||||
/// <inheritdoc cref="GetEntitiesWithComponentOnStation{TComponent}(bool)" />
|
||||
public HashSet<Entity<TComponent>> GetEntitiesWithComponentOnStation<TComponent>(bool onlyAnchored,
|
||||
out Entity<StationDataComponent>? station)
|
||||
where TComponent : IComponent
|
||||
{
|
||||
HashSet<Entity<TComponent>> entities = [];
|
||||
|
||||
if (!TryGetRandomStation<StationEventEligibleComponent>(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<TComponent, TransformComponent>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool TryFindRandomTileOnStation(Entity<StationDataComponent> 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<MapGridComponent> Entity, int Count, List<TileRef> 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<MapGridComponent>(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.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public sealed class StationInitializedEvent : EntityEventArgs
|
||||
public sealed class StationInitializedEvent(EntityUid station) : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Station this event is for.
|
||||
/// </summary>
|
||||
public EntityUid Station;
|
||||
|
||||
public StationInitializedEvent(EntityUid station)
|
||||
{
|
||||
Station = station;
|
||||
}
|
||||
public EntityUid Station = station;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Directed event fired on a station when a grid becomes a member of the station.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public sealed class StationGridAddedEvent : EntityEventArgs
|
||||
public sealed class StationGridAddedEvent(EntityUid gridId, EntityUid station, bool isSetup) : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// ID of the grid added to the station.
|
||||
/// </summary>
|
||||
public EntityUid GridId;
|
||||
public EntityUid GridId = gridId;
|
||||
|
||||
/// <summary>
|
||||
/// EntityUid of the station this grid was added to.
|
||||
/// </summary>
|
||||
public EntityUid Station;
|
||||
public EntityUid Station = station;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the event was fired during station setup,
|
||||
/// so that it can be ignored if StationInitializedEvent was already handled.
|
||||
/// </summary>
|
||||
public bool IsSetup;
|
||||
|
||||
public StationGridAddedEvent(EntityUid gridId, EntityUid station, bool isSetup)
|
||||
{
|
||||
GridId = gridId;
|
||||
Station = station;
|
||||
IsSetup = isSetup;
|
||||
}
|
||||
public bool IsSetup = isSetup;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Directed event fired on a station when a grid is no longer a member of the station.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public sealed class StationGridRemovedEvent : EntityEventArgs
|
||||
public sealed class StationGridRemovedEvent(EntityUid gridId, EntityUid station) : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// ID of the grid removed from the station.
|
||||
/// </summary>
|
||||
public EntityUid GridId;
|
||||
public EntityUid GridId = gridId;
|
||||
|
||||
/// <summary>
|
||||
/// EntityUid of the station this grid was added to.
|
||||
/// </summary>
|
||||
public EntityUid Station;
|
||||
|
||||
public StationGridRemovedEvent(EntityUid gridId, EntityUid station)
|
||||
{
|
||||
GridId = gridId;
|
||||
Station = station;
|
||||
}
|
||||
public EntityUid Station = station;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Directed event fired on a station when it is renamed.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public sealed class StationRenamedEvent : EntityEventArgs
|
||||
public sealed class StationRenamedEvent(string oldName, string newName) : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Prior name of the station.
|
||||
/// </summary>
|
||||
public string OldName;
|
||||
public string OldName = oldName;
|
||||
|
||||
/// <summary>
|
||||
/// New name of the station.
|
||||
/// </summary>
|
||||
public string NewName;
|
||||
|
||||
public StationRenamedEvent(string oldName, string newName)
|
||||
{
|
||||
OldName = oldName;
|
||||
NewName = newName;
|
||||
}
|
||||
public string NewName = newName;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,4 @@ using Content.Shared.Whitelist;
|
|||
namespace Content.Server.StationEvents.Components;
|
||||
|
||||
[RegisterComponent, Access(typeof(BreakerFlipRule))]
|
||||
public sealed partial class BreakerFlipRuleComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Blacklist of structures not eligible to trigger this game rule.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityWhitelist? Blacklist;
|
||||
}
|
||||
public sealed partial class BreakerFlipRuleComponent : Component;
|
||||
|
|
|
|||
|
|
@ -32,24 +32,12 @@ public sealed partial class GasLeakRuleComponent : Component
|
|||
[DataField]
|
||||
public float LeakCooldown = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The station where the leak is located.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid TargetStation;
|
||||
|
||||
/// <summary>
|
||||
/// The specific grid where the leak is located.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid TargetGrid;
|
||||
|
||||
/// <summary>
|
||||
/// The tile coordinates where the leak is located.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Vector2i TargetTile;
|
||||
|
||||
/// <summary>
|
||||
/// The world coordinates of the leak location.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// The collection of lights that will be affected by the solar flare event.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public HashSet<(EntityUid, PoweredLightComponent)> AffectedLights = [];
|
||||
|
||||
/// <summary>
|
||||
/// The collection of airlocks that will be affected by the solar flare event.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public HashSet<(EntityUid, AirlockComponent)> AffectedAirlocks = [];
|
||||
|
||||
/// <summary>
|
||||
/// Chance light bulb breaks per second during event
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -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<BreakerFlipRuleComponent>
|
||||
{
|
||||
[Dependency] private ApcSystem _apcSystem = default!;
|
||||
[Dependency] private EntityWhitelistSystem _whitelist = default!;
|
||||
|
||||
protected override void Added(Entity<BreakerFlipRuleComponent, GameRuleComponent> ent, ref GameRuleAddedEvent args)
|
||||
{
|
||||
|
|
@ -34,20 +32,7 @@ public sealed partial class BreakerFlipRule : StationEventSystem<BreakerFlipRule
|
|||
{
|
||||
base.Started(ent, ref args);
|
||||
|
||||
if (!Station.TryGetRandomStation<StationEventEligibleComponent>(
|
||||
out var chosenStation,
|
||||
uid => _whitelist.IsWhitelistFailOrNull(ent.Comp1.Blacklist, uid)))
|
||||
return;
|
||||
|
||||
var stationApcs = new List<Entity<ApcComponent>>();
|
||||
var query = EntityQueryEnumerator<ApcComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var apcUid, out var apc, out var xform))
|
||||
{
|
||||
if (apc.MainBreakerEnabled && CompOrNull<StationMemberComponent>(xform.GridUid)?.Station == chosenStation.Value.Owner)
|
||||
{
|
||||
stationApcs.Add((apcUid, apc));
|
||||
}
|
||||
}
|
||||
var stationApcs = Station.GetEntitiesWithComponentOnStation<ApcComponent>(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<BreakerFlipRule
|
|||
_apcSystem.ApcToggleBreaker(apc, apc);
|
||||
|
||||
var stateString = apc.Comp.MainBreakerEnabled ? "Enabled" : "Disabled";
|
||||
AdminLogManager.Add(LogType.ItemConfigure, LogImpact.Medium,
|
||||
AdminLogManager.Add(LogType.ItemConfigure,
|
||||
LogImpact.Medium,
|
||||
$"Station event {ToPrettyString(ent):user} set the main breaker state of {ToPrettyString(apc):entity} to {stateString:state}");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Atmos.Piping.Unary.Components;
|
||||
using Content.Server.StationEvents.Components;
|
||||
using Content.Shared.GameTicking.Components;
|
||||
using Robust.Shared.Audio;
|
||||
|
|
@ -26,20 +27,30 @@ public sealed partial class GasLeakRule : StationEventSystem<GasLeakRuleComponen
|
|||
if (!TryComp<StationEventComponent>(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<GasVentScrubberComponent>(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<GasLeakRuleComponen
|
|||
return;
|
||||
}
|
||||
|
||||
var environment = _atmosphere.GetTileMixture(component.TargetGrid, null, component.TargetTile, true);
|
||||
var environment = _atmosphere.GetTileMixture(component.TargetGrid, null, (Vector2i)component.TargetCoords.Position, true);
|
||||
|
||||
environment?.AdjustMoles(component.LeakGas, component.LeakCooldown * component.MolesPerSecond);
|
||||
}
|
||||
|
|
@ -77,20 +88,20 @@ public sealed partial class GasLeakRule : StationEventSystem<GasLeakRuleComponen
|
|||
|
||||
private void Spark(Entity<GasLeakRuleComponent> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,174 +9,160 @@ using Robust.Shared.Player;
|
|||
using Robust.Shared.Utility;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
|
||||
namespace Content.Server.StationEvents.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Handler for events that force a number of APCs off on a station for a period of time.
|
||||
/// </summary>
|
||||
/// <seealso cref="PowerGridCheckRuleComponent"/>
|
||||
[UsedImplicitly]
|
||||
public sealed partial class PowerGridCheckRule : StationEventSystem<PowerGridCheckRuleComponent>
|
||||
{
|
||||
[Dependency] private ApcSystem _apcSystem = default!;
|
||||
namespace Content.Server.StationEvents.Events;
|
||||
|
||||
public override void Initialize()
|
||||
/// <summary>
|
||||
/// Handler for events that force a number of APCs off on a station for a period of time.
|
||||
/// </summary>
|
||||
/// <seealso cref="PowerGridCheckRuleComponent"/>
|
||||
[UsedImplicitly]
|
||||
public sealed partial class PowerGridCheckRule : StationEventSystem<PowerGridCheckRuleComponent>
|
||||
{
|
||||
[Dependency] private ApcSystem _apc = default!;
|
||||
|
||||
protected override void Started(Entity<PowerGridCheckRuleComponent, GameRuleComponent> ent, ref GameRuleStartedEvent args)
|
||||
{
|
||||
base.Started(ent, ref args);
|
||||
|
||||
var apcs = Station.GetEntitiesWithComponentOnStation<ApcComponent>(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<PowerGridCheckNotifyComponent, ComponentStartup>(OnApcStartup);
|
||||
SubscribeLocalEvent<PowerGridCheckNotifyComponent, ApcToggleMainBreakerAttemptEvent>(OnApcToggleMainBreaker);
|
||||
if (!apcUid.Comp.MainBreakerEnabled)
|
||||
continue;
|
||||
|
||||
var apcTransform = Transform(apcUid);
|
||||
if (apcTransform.GridUid != largestGrid)
|
||||
continue;
|
||||
|
||||
powerGridCheck.Powered.Add(apcUid);
|
||||
}
|
||||
|
||||
protected override void Started(Entity<PowerGridCheckRuleComponent, GameRuleComponent> 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.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the entity should be affected by an existing
|
||||
/// PowerGridCheckRuleComponent and if so, turns off the APC.
|
||||
/// </summary>
|
||||
[SubscribeLocalEvent]
|
||||
private void OnApcStartup(EntityUid apcUid, PowerGridCheckNotifyComponent comp, ComponentStartup args)
|
||||
{
|
||||
if (!TryComp<ApcComponent>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the PowerGridCheckRuleComponent affecting the uid, or null if none
|
||||
/// </summary>
|
||||
private PowerGridCheckRuleComponent? GetRuleAffectingEntity(EntityUid uid)
|
||||
{
|
||||
if (!TryComp(uid, out TransformComponent? xform))
|
||||
return null;
|
||||
|
||||
if (!TryComp<StationMemberComponent>(xform.GridUid, out var stationMemberComp))
|
||||
return null;
|
||||
|
||||
var activeRules = AllEntityQuery<PowerGridCheckRuleComponent, ActiveGameRuleComponent>();
|
||||
while (activeRules.MoveNext(out _, out var powerGridRule, out _))
|
||||
{
|
||||
base.Started(ent, ref args);
|
||||
if (stationMemberComp.Station != powerGridRule.AffectedStation)
|
||||
continue;
|
||||
|
||||
if (!Station.TryGetRandomStation<StationEventEligibleComponent>(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<ApcComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var apcUid, out var apc, out var transform))
|
||||
{
|
||||
if (!apc.MainBreakerEnabled)
|
||||
continue;
|
||||
if (xform.GridUid != largestGrid.Value)
|
||||
continue;
|
||||
|
||||
if (CompOrNull<StationMemberComponent>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the entity should be affected by an existing
|
||||
/// PowerGridCheckRuleComponent and if so, turns off the APC.
|
||||
/// </summary>
|
||||
private void OnApcStartup(EntityUid apcUid, PowerGridCheckNotifyComponent comp, ComponentStartup args)
|
||||
{
|
||||
if (!TryComp<ApcComponent>(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<PowerGridCheckRuleComponent> rule, ref GameRuleEndedEvent args)
|
||||
{
|
||||
base.Ended(rule, ref args);
|
||||
|
||||
foreach (var entity in rule.Comp.Unpowered)
|
||||
{
|
||||
if (Deleted(entity))
|
||||
continue;
|
||||
|
||||
if (!TryComp<ApcComponent>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the PowerGridCheckRuleComponent affecting the uid, or null if none
|
||||
/// </summary>
|
||||
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<ApcComponent>(selected, out var apcComponent))
|
||||
{
|
||||
return null;
|
||||
if (apcComponent.MainBreakerEnabled)
|
||||
_apc.ApcToggleBreaker(selected, apcComponent);
|
||||
}
|
||||
|
||||
if (!TryComp<StationMemberComponent>(xform.GridUid, out var stationMemberComp))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var activeRules = AllEntityQuery<PowerGridCheckRuleComponent, ActiveGameRuleComponent>();
|
||||
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<PowerGridCheckRuleComponent> 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<ApcComponent>(selected, out var apcComponent))
|
||||
{
|
||||
if (apcComponent.MainBreakerEnabled)
|
||||
_apcSystem.ApcToggleBreaker(selected, apcComponent);
|
||||
}
|
||||
component.Unpowered.Add(selected);
|
||||
}
|
||||
component.Unpowered.Add(selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Ra
|
|||
{
|
||||
base.Started(ent, ref args);
|
||||
|
||||
if (!Station.TryGetRandomStation<StationEventEligibleComponent>(out var station))
|
||||
return;
|
||||
|
||||
var spawnRule = ent.Comp1;
|
||||
|
||||
var validLockers = new List<(EntityUid, EntityStorageComponent)>();
|
||||
var validLockers = new List<Entity<EntityStorageComponent>>();
|
||||
var spawn = Spawn(spawnRule.Prototype, MapCoordinates.Nullspace);
|
||||
|
||||
var query = EntityQueryEnumerator<EntityStorageComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var storageUid, out var storage, out var xform))
|
||||
foreach (var storageEnt in Station.GetEntitiesWithComponentOnStation<EntityStorageComponent>(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<Ra
|
|||
return;
|
||||
}
|
||||
|
||||
var (locker, storageComp) = RobustRandom.Pick(validLockers);
|
||||
if (!_entityStorage.Insert(spawn, locker, storageComp))
|
||||
{
|
||||
var locker = RobustRandom.Pick(validLockers);
|
||||
if (!_entityStorage.Insert(spawn, locker, locker.Comp))
|
||||
Del(spawn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Light.EntitySystems;
|
||||
using Content.Server.StationEvents.Components;
|
||||
using Content.Shared.Doors.Components;
|
||||
|
|
@ -25,7 +26,7 @@ public sealed partial class SolarFlareRule : StationEventSystem<SolarFlareRuleCo
|
|||
|
||||
[Dependency] private EntityQuery<HeadsetComponent> _headsetQuery;
|
||||
|
||||
private float _effectTimer = 0;
|
||||
private float _effectTimer;
|
||||
|
||||
protected override void Started(Entity<SolarFlareRuleComponent, GameRuleComponent> ent, ref GameRuleStartedEvent args)
|
||||
{
|
||||
|
|
@ -36,6 +37,13 @@ public sealed partial class SolarFlareRule : StationEventSystem<SolarFlareRuleCo
|
|||
var channel = RobustRandom.Pick(ent.Comp1.ExtraChannels);
|
||||
ent.Comp1.AffectedChannels.Add(channel);
|
||||
}
|
||||
|
||||
ent.Comp1.AffectedLights = Station.GetEntitiesWithComponentOnStation<PoweredLightComponent>(true)
|
||||
.Select(e => (e.Owner, e.Comp))
|
||||
.ToHashSet();
|
||||
ent.Comp1.AffectedAirlocks = Station.GetEntitiesWithComponentOnStation<AirlockComponent>(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<SolarFlareRuleCo
|
|||
base.ActiveTick(uid, component, gameRule, frameTime);
|
||||
|
||||
_effectTimer -= frameTime;
|
||||
if (_effectTimer < 0)
|
||||
if (!(_effectTimer < 0))
|
||||
return;
|
||||
|
||||
_effectTimer += 1;
|
||||
foreach (var light in component.AffectedLights)
|
||||
{
|
||||
_effectTimer += 1;
|
||||
var lightQuery = EntityQueryEnumerator<PoweredLightComponent>();
|
||||
while (lightQuery.MoveNext(out var lightEnt, out var light))
|
||||
{
|
||||
if (RobustRandom.Prob(component.LightBreakChancePerSecond))
|
||||
_poweredLight.TryDestroyBulb(lightEnt, light);
|
||||
}
|
||||
var airlockQuery = EntityQueryEnumerator<AirlockComponent, DoorComponent>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<VentClogRuleCompon
|
|||
{
|
||||
base.Started(ent, ref args);
|
||||
|
||||
if (!Station.TryGetRandomStation<StationEventEligibleComponent>(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<VentClogRuleCompon
|
|||
.Where(x => !x.Abstract)
|
||||
.Select(x => new ProtoId<ReagentPrototype>(x.ID)).ToList();
|
||||
|
||||
foreach (var (_, transform) in EntityQuery<GasVentPumpComponent, TransformComponent>())
|
||||
foreach (var ventPump in Station.GetEntitiesWithComponentOnStation<GasVentPumpComponent>(true))
|
||||
{
|
||||
if (CompOrNull<StationMemberComponent>(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<VentClogRuleCompon
|
|||
var quantity = weak ? ventClog.WeakReagentQuantity : ventClog.ReagentQuantity;
|
||||
solution.AddReagent(reagent, quantity);
|
||||
|
||||
var foamEnt = Spawn(ChemicalReactionSystem.FoamReaction, transform.Coordinates);
|
||||
var foamEnt = Spawn(ChemicalReactionSystem.FoamReaction, targetCoords);
|
||||
var spreadAmount = weak ? ventClog.WeakSpread : ventClog.Spread;
|
||||
_smoke.StartSmoke(foamEnt, solution, ventClog.Time, spreadAmount);
|
||||
Audio.PlayPvs(ventClog.Sound, transform.Coordinates);
|
||||
Audio.PlayPvs(ventClog.Sound, targetCoords);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
using Content.Server.StationEvents.Components;
|
||||
using Content.Shared.GameTicking.Components;
|
||||
using Content.Shared.Station.Components;
|
||||
using Content.Shared.Storage;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.StationEvents.Events;
|
||||
|
|
@ -19,45 +17,31 @@ public sealed partial class VentCrittersRule : StationEventSystem<VentCrittersRu
|
|||
{
|
||||
base.Started(ent, ref args);
|
||||
|
||||
if (!Station.TryGetRandomStation<StationEventEligibleComponent>(out var station))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var critterRule = ent.Comp1;
|
||||
|
||||
var locations = EntityQueryEnumerator<VentCritterSpawnLocationComponent, TransformComponent>();
|
||||
var validLocations = new List<EntityCoordinates>();
|
||||
while (locations.MoveNext(out _, out _, out var transform))
|
||||
{
|
||||
if (!transform.Anchored)
|
||||
continue;
|
||||
|
||||
if (CompOrNull<StationMemberComponent>(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<VentCritterSpawnLocationComponent>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<VentHordeRuleComp
|
|||
[Dependency] private VentHordeSystem _horde = default!;
|
||||
[Dependency] private IGameTiming _timing = default!;
|
||||
|
||||
[Dependency] private EntityQuery<VentHordeSpawnerComponent> _hordeSpawnerQuery;
|
||||
|
||||
protected override void Added(Entity<VentHordeRuleComponent, GameRuleComponent> ent, ref GameRuleAddedEvent args)
|
||||
{
|
||||
var ventHorde = ent.Comp1;
|
||||
|
|
@ -76,7 +73,7 @@ public sealed partial class VentHordeRule : StationEventSystem<VentHordeRuleComp
|
|||
var ventHorde = ent.Comp1;
|
||||
|
||||
// We grab when the gamerule is expected to end and subtract the current time from it to get the duration.
|
||||
var duration = (stationEventComp.EndTime - _timing.CurTime) ?? TimeSpan.Zero;
|
||||
var duration = stationEventComp.EndTime - _timing.CurTime ?? TimeSpan.Zero;
|
||||
|
||||
var spawns = _table.GetSpawns(ventHorde.Table);
|
||||
|
||||
|
|
@ -91,30 +88,8 @@ public sealed partial class VentHordeRule : StationEventSystem<VentHordeRuleComp
|
|||
|
||||
private EntityUid? ChooseVent()
|
||||
{
|
||||
// Get a station
|
||||
if (!Station.TryGetRandomStation<StationEventEligibleComponent>(out var station))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Query the possible locations
|
||||
var locations = EntityQueryEnumerator<VentCritterSpawnLocationComponent, TransformComponent>();
|
||||
var validLocations = new List<EntityUid>();
|
||||
|
||||
// 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<StationMemberComponent>(transform.GridUid)?.Station == station.Value.Owner)
|
||||
{
|
||||
validLocations.Add(uid);
|
||||
}
|
||||
}
|
||||
var validLocations = Station.GetEntitiesWithComponentOnStation<VentCritterSpawnLocationComponent>(true);
|
||||
validLocations.RemoveWhere(uid => HasComp<VentHordeSpawnerComponent>(uid));
|
||||
|
||||
// Pick one at random
|
||||
if (validLocations.Count != 0)
|
||||
|
|
|
|||
|
|
@ -158,9 +158,6 @@
|
|||
duration: 1
|
||||
minimumPlayers: 15
|
||||
- type: BreakerFlipRule
|
||||
blacklist:
|
||||
components:
|
||||
- ProtectedGrid
|
||||
- type: DynamicRuleCost
|
||||
cost: 10
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue