Fix firelock danger indicators (#12327)
* Fix firelock danger indicators * remove unused arg
This commit is contained in:
@@ -89,6 +89,28 @@ public partial class AtmosphereSystem
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
}
|
||||
|
||||
public GasMixture?[]? GetTileMixtures(EntityUid? gridUid, EntityUid? mapUid, List<Vector2i> tiles, bool excite = false)
|
||||
{
|
||||
var ev = new GetTileMixturesMethodEvent(gridUid, mapUid, tiles, excite);
|
||||
|
||||
// If we've been passed a grid, try to let it handle it.
|
||||
if (gridUid.HasValue)
|
||||
RaiseLocalEvent(gridUid.Value, ref ev, false);
|
||||
|
||||
if (ev.Handled)
|
||||
return ev.Mixtures;
|
||||
|
||||
// We either don't have a grid, or the event wasn't handled.
|
||||
// Let the map handle it instead, and also broadcast the event.
|
||||
if (mapUid.HasValue)
|
||||
RaiseLocalEvent(mapUid.Value, ref ev, true);
|
||||
else
|
||||
RaiseLocalEvent(ref ev);
|
||||
|
||||
// Default to a space mixture... This is a space game, after all!
|
||||
return ev.Mixtures ?? new GasMixture?[tiles.Count];
|
||||
}
|
||||
|
||||
public GasMixture? GetTileMixture(EntityUid? gridUid, EntityUid? mapUid, Vector2i tile, bool excite = false)
|
||||
{
|
||||
var ev = new GetTileMixtureMethodEvent(gridUid, mapUid, tile, excite);
|
||||
@@ -256,6 +278,9 @@ public partial class AtmosphereSystem
|
||||
[ByRefEvent] private record struct InvalidateTileMethodEvent
|
||||
(EntityUid Grid, Vector2i Tile, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct GetTileMixturesMethodEvent
|
||||
(EntityUid? GridUid, EntityUid? MapUid, List<Vector2i> Tiles, bool Excite = false, GasMixture?[]? Mixtures = null, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct GetTileMixtureMethodEvent
|
||||
(EntityUid? GridUid, EntityUid? MapUid, Vector2i Tile, bool Excite = false, GasMixture? Mixture = null, bool Handled = false);
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ public sealed partial class AtmosphereSystem
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, GetAllMixturesMethodEvent>(GridGetAllMixtures);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, InvalidateTileMethodEvent>(GridInvalidateTile);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, GetTileMixtureMethodEvent>(GridGetTileMixture);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, GetTileMixturesMethodEvent>(GridGetTileMixtures);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, ReactTileMethodEvent>(GridReactTile);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, IsTileAirBlockedMethodEvent>(GridIsTileAirBlocked);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, IsTileSpaceMethodEvent>(GridIsTileSpace);
|
||||
@@ -178,6 +179,28 @@ public sealed partial class AtmosphereSystem
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridGetTileMixtures(EntityUid uid, GridAtmosphereComponent component,
|
||||
ref GetTileMixturesMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
args.Mixtures = new GasMixture?[args.Tiles.Count];
|
||||
|
||||
for (var i = 0; i < args.Tiles.Count; i++)
|
||||
{
|
||||
var tile = args.Tiles[i];
|
||||
if (!component.Tiles.TryGetValue(tile, out var atmosTile))
|
||||
continue;
|
||||
|
||||
if (args.Excite)
|
||||
component.InvalidatedCoords.Add(tile);
|
||||
|
||||
args.Mixtures[i] = atmosTile.Air;
|
||||
}
|
||||
}
|
||||
|
||||
private void GridReactTile(EntityUid uid, GridAtmosphereComponent component, ref ReactTileMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
|
||||
@@ -8,6 +8,7 @@ public partial class AtmosphereSystem
|
||||
{
|
||||
SubscribeLocalEvent<MapAtmosphereComponent, IsTileSpaceMethodEvent>(MapIsTileSpace);
|
||||
SubscribeLocalEvent<MapAtmosphereComponent, GetTileMixtureMethodEvent>(MapGetTileMixture);
|
||||
SubscribeLocalEvent<MapAtmosphereComponent, GetTileMixturesMethodEvent>(MapGetTileMixtures);
|
||||
}
|
||||
|
||||
private void MapIsTileSpace(EntityUid uid, MapAtmosphereComponent component, ref IsTileSpaceMethodEvent args)
|
||||
@@ -28,4 +29,20 @@ public partial class AtmosphereSystem
|
||||
args.Mixture = component.Mixture?.Clone();
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void MapGetTileMixtures(EntityUid uid, MapAtmosphereComponent component, ref GetTileMixturesMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
args.Handled = true;
|
||||
args.Mixtures = new GasMixture?[args.Tiles.Count];
|
||||
|
||||
if (component.Mixture == null)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < args.Tiles.Count; i++)
|
||||
{
|
||||
args.Mixtures[i] = component.Mixture.Clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,14 @@ namespace Content.Server.Doors.Components
|
||||
[DataField("pressureThreshold"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public float PressureThreshold = 20;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum temperature difference before the firelock will refuse to open, in k.
|
||||
/// </summary>
|
||||
[DataField("temperatureThreshold"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public float TemperatureThreshold = 330;
|
||||
// this used to check for hot-spots, but because accessing that data is a a mess this now just checks
|
||||
// temperature. This does mean a cold room will trigger hot-air pop-ups
|
||||
|
||||
/// <summary>
|
||||
/// If true, and if this door has an <see cref="AtmosAlarmableComponent"/>, then it will only auto-close if the
|
||||
/// alarm is set to danger.
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Atmos.Monitor.Systems;
|
||||
using Content.Server.Doors.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Shuttles.Components;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Monitor;
|
||||
using Content.Shared.Doors;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Doors.Systems;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
@@ -19,7 +23,6 @@ namespace Content.Server.Doors.Systems
|
||||
[Dependency] private readonly SharedDoorSystem _doorSystem = default!;
|
||||
[Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosSystem = default!;
|
||||
[Dependency] private readonly TransformSystem _xformSys = default!;
|
||||
[Dependency] private readonly AppearanceSystem _appearance = default!;
|
||||
|
||||
private static float _visualUpdateInterval = 0.5f;
|
||||
@@ -38,10 +41,18 @@ namespace Content.Server.Doors.Systems
|
||||
|
||||
// Visuals
|
||||
SubscribeLocalEvent<FirelockComponent, MapInitEvent>(UpdateVisuals);
|
||||
SubscribeLocalEvent<FirelockComponent, ComponentStartup>(UpdateVisuals);
|
||||
SubscribeLocalEvent<FirelockComponent, PowerChangedEvent>(PowerChanged);
|
||||
}
|
||||
|
||||
private void PowerChanged(EntityUid uid, FirelockComponent component, ref PowerChangedEvent args)
|
||||
{
|
||||
// TODO this should REALLLLY not be door specific appearance thing.
|
||||
_appearance.SetData(uid, DoorVisuals.Powered, args.Powered);
|
||||
}
|
||||
|
||||
#region Visuals
|
||||
private void UpdateVisuals(EntityUid uid, FirelockComponent component, EntityEventArgs args) => UpdateVisuals(uid);
|
||||
private void UpdateVisuals(EntityUid uid, FirelockComponent component, EntityEventArgs args) => UpdateVisuals(uid, component);
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
@@ -51,21 +62,39 @@ namespace Content.Server.Doors.Systems
|
||||
|
||||
_accumulatedFrameTime -= _visualUpdateInterval;
|
||||
|
||||
var powerQuery = GetEntityQuery<ApcPowerReceiverComponent>();
|
||||
var airtightQuery = GetEntityQuery<AirtightComponent>();
|
||||
var appearanceQuery = GetEntityQuery<AppearanceComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var (_, door, appearance, xform) in EntityQuery<FirelockComponent, DoorComponent, AppearanceComponent, TransformComponent>())
|
||||
foreach (var (firelock, door) in EntityQuery<FirelockComponent, DoorComponent>())
|
||||
{
|
||||
UpdateVisuals(door.Owner, door, appearance, xform, powerQuery);
|
||||
// only bother to check pressure on doors that are some variation of closed.
|
||||
if (door.State != DoorState.Closed
|
||||
&& door.State != DoorState.Welded
|
||||
&& door.State != DoorState.Denying)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var uid = door.Owner;
|
||||
if (airtightQuery.TryGetComponent(uid, out var airtight)
|
||||
&& xformQuery.TryGetComponent(uid, out var xform)
|
||||
&& appearanceQuery.TryGetComponent(uid, out var appearance))
|
||||
{
|
||||
var (fire, pressure) = CheckPressureAndFire(uid, firelock, xform, airtight, airtightQuery);
|
||||
_appearance.SetData(uid, DoorVisuals.ClosedLights, fire || pressure, appearance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateVisuals(EntityUid uid,
|
||||
FirelockComponent? firelock = null,
|
||||
DoorComponent? door = null,
|
||||
AirtightComponent? airtight = null,
|
||||
AppearanceComponent? appearance = null,
|
||||
TransformComponent? xform = null,
|
||||
EntityQuery<ApcPowerReceiverComponent>? powerQuery = null)
|
||||
TransformComponent? xform = null)
|
||||
{
|
||||
if (!Resolve(uid, ref door, ref appearance, ref xform, false))
|
||||
if (!Resolve(uid, ref door, ref appearance, false))
|
||||
return;
|
||||
|
||||
// only bother to check pressure on doors that are some variation of closed.
|
||||
@@ -77,14 +106,12 @@ namespace Content.Server.Doors.Systems
|
||||
return;
|
||||
}
|
||||
|
||||
powerQuery ??= EntityManager.GetEntityQuery<ApcPowerReceiverComponent>();
|
||||
if (powerQuery.Value.TryGetComponent(uid, out var receiver) && !receiver.Powered)
|
||||
{
|
||||
_appearance.SetData(uid, DoorVisuals.ClosedLights, false, appearance);
|
||||
var query = GetEntityQuery<AirtightComponent>();
|
||||
if (!Resolve(uid, ref firelock, ref airtight, ref appearance, ref xform, false) || !query.Resolve(uid, ref airtight, false))
|
||||
return;
|
||||
}
|
||||
|
||||
_appearance.SetData(uid, DoorVisuals.ClosedLights, IsHoldingPressureOrFire(uid, xform), appearance);
|
||||
var (fire, pressure) = CheckPressureAndFire(uid, firelock, xform, airtight, query);
|
||||
_appearance.SetData(uid, DoorVisuals.ClosedLights, fire || pressure, appearance);
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -105,13 +132,13 @@ namespace Content.Server.Doors.Systems
|
||||
|
||||
private void OnBeforeDoorOpened(EntityUid uid, FirelockComponent component, BeforeDoorOpenedEvent args)
|
||||
{
|
||||
if (!this.IsPowered(uid, EntityManager) || IsHoldingPressureOrFire(uid))
|
||||
if (!this.IsPowered(uid, EntityManager) || IsHoldingPressureOrFire(uid, component))
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnDoorGetPryTimeModifier(EntityUid uid, FirelockComponent component, DoorGetPryTimeModifierEvent args)
|
||||
{
|
||||
var state = CheckPressureAndFire(uid);
|
||||
var state = CheckPressureAndFire(uid, component);
|
||||
|
||||
if (state.Fire)
|
||||
{
|
||||
@@ -173,55 +200,145 @@ namespace Content.Server.Doors.Systems
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsHoldingPressureOrFire(EntityUid uid, TransformComponent? xform = null)
|
||||
public bool IsHoldingPressureOrFire(EntityUid uid, FirelockComponent firelock)
|
||||
{
|
||||
var result = CheckPressureAndFire(uid, xform);
|
||||
var result = CheckPressureAndFire(uid, firelock);
|
||||
return result.Pressure || result.Fire;
|
||||
}
|
||||
|
||||
public (bool Pressure, bool Fire) CheckPressureAndFire(EntityUid owner, TransformComponent? xform = null)
|
||||
public (bool Pressure, bool Fire) CheckPressureAndFire(EntityUid uid, FirelockComponent firelock)
|
||||
{
|
||||
if (!Resolve(owner, ref xform))
|
||||
var query = GetEntityQuery<AirtightComponent>();
|
||||
if (query.TryGetComponent(uid, out AirtightComponent? airtight))
|
||||
return CheckPressureAndFire(uid, firelock, Transform(uid), airtight, query);
|
||||
return (false, false);
|
||||
}
|
||||
|
||||
public (bool Pressure, bool Fire) CheckPressureAndFire(
|
||||
EntityUid uid,
|
||||
FirelockComponent firelock,
|
||||
TransformComponent xform,
|
||||
AirtightComponent airtight,
|
||||
EntityQuery<AirtightComponent> airtightQuery)
|
||||
{
|
||||
if (!airtight.AirBlocked)
|
||||
return (false, false);
|
||||
|
||||
float threshold = 20;
|
||||
var position = _xformSys.GetGridOrMapTilePosition(owner, xform);
|
||||
if (xform.GridUid is not {} gridUid)
|
||||
return (false, false);
|
||||
var minMoles = float.MaxValue;
|
||||
var maxMoles = 0f;
|
||||
|
||||
return (IsHoldingPressure(), IsHoldingFire());
|
||||
|
||||
bool IsHoldingPressure()
|
||||
if (TryComp(uid, out DockingComponent? dock) && dock.Docked)
|
||||
{
|
||||
foreach (var adjacent in _atmosSystem.GetAdjacentTileMixtures(gridUid, position))
|
||||
{
|
||||
var moles = adjacent.TotalMoles;
|
||||
if (moles < minMoles)
|
||||
minMoles = moles;
|
||||
if (moles > maxMoles)
|
||||
maxMoles = moles;
|
||||
}
|
||||
|
||||
return (maxMoles - minMoles) > threshold;
|
||||
// Currently docking automatically opens the doors. But maybe in future, check the pressure difference before opening doors?
|
||||
return (false, false);
|
||||
}
|
||||
|
||||
bool IsHoldingFire()
|
||||
{
|
||||
if (_atmosSystem.GetTileMixture(gridUid, null, position) == null)
|
||||
return false;
|
||||
if (!TryComp(xform.ParentUid, out GridAtmosphereComponent? gridAtmosphere))
|
||||
return (false, false);
|
||||
|
||||
if (_atmosSystem.IsHotspotActive(gridUid, position))
|
||||
var grid = Comp<IMapGridComponent>(xform.ParentUid).Grid;
|
||||
var pos = grid.CoordinatesToTile(xform.Coordinates);
|
||||
var minPressure = float.MaxValue;
|
||||
var maxPressure = float.MinValue;
|
||||
var minTemperature = float.MaxValue;
|
||||
var maxTemperature = float.MinValue;
|
||||
bool holdingFire = false;
|
||||
bool holdingPressure = false;
|
||||
|
||||
// We cannot simply use `_atmosSystem.GetAdjacentTileMixtures` because of how the `includeBlocked` option
|
||||
// works, we want to ignore the firelock's blocking, while including blockers on other tiles.
|
||||
// GetAdjacentTileMixtures also ignores empty/non-existent tiles, which we don't want. Additionally, for
|
||||
// edge-fire locks, we only want to enumerate over a single directions. So AFAIK there is no nice way of
|
||||
// achieving all this using existing atmos functions, and the functionality is too specialized to bother
|
||||
// adding new public atmos system functions.
|
||||
|
||||
|
||||
// TODO redo this with planet/map atmospheres
|
||||
// there is probably a faster way of doing this. tbh I kinda hate the atmos method events for making
|
||||
// accessing tile data directly such a pain. Dealting with maps will make it even more painful.
|
||||
List<Vector2i> tiles = new(4);
|
||||
List<AtmosDirection> directions = new(4);
|
||||
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
var dir = (AtmosDirection) (1 << i);
|
||||
if (airtight.AirBlockedDirection.HasFlag(dir))
|
||||
{
|
||||
directions.Add(dir);
|
||||
tiles.Add(pos.Offset(dir));
|
||||
}
|
||||
}
|
||||
|
||||
// May also have to consider pressure on the same tile as the firelock.
|
||||
var count = tiles.Count;
|
||||
if (airtight.AirBlockedDirection != AtmosDirection.All)
|
||||
tiles.Add(pos);
|
||||
|
||||
var gasses = _atmosSystem.GetTileMixtures(gridAtmosphere.Owner, null, tiles);
|
||||
if (gasses == null)
|
||||
return (false, false);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var gas = gasses[i];
|
||||
var dir = directions[i];
|
||||
var adjacentPos = tiles[i];
|
||||
|
||||
if (gas != null)
|
||||
{
|
||||
// Is there some airtight entity blocking this direction? If yes, don't include this direction in the
|
||||
// pressure differential
|
||||
if (HasAirtightBlocker(grid.GetAnchoredEntities(adjacentPos), dir.GetOpposite(), airtightQuery))
|
||||
continue;
|
||||
|
||||
var p = gas.Pressure;
|
||||
minPressure = Math.Min(minPressure, p);
|
||||
maxPressure = Math.Max(maxPressure, p);
|
||||
minTemperature = Math.Min(minTemperature, gas.Temperature);
|
||||
maxTemperature = Math.Max(maxTemperature, gas.Temperature);
|
||||
}
|
||||
|
||||
holdingPressure |= maxPressure - minPressure > firelock.PressureThreshold;
|
||||
holdingFire |= maxTemperature - minTemperature > firelock.TemperatureThreshold;
|
||||
|
||||
if (holdingPressure && holdingFire)
|
||||
return (holdingPressure, holdingFire);
|
||||
}
|
||||
|
||||
if (airtight.AirBlockedDirection == AtmosDirection.All)
|
||||
return (holdingPressure, holdingFire);
|
||||
|
||||
var local = gasses[count];
|
||||
if (local != null)
|
||||
{
|
||||
var p = local.Pressure;
|
||||
minPressure = Math.Min(minPressure, p);
|
||||
maxPressure = Math.Max(maxPressure, p);
|
||||
minTemperature = Math.Min(minTemperature, local.Temperature);
|
||||
maxTemperature = Math.Max(maxTemperature, local.Temperature);
|
||||
}
|
||||
else
|
||||
{
|
||||
minPressure = Math.Min(minPressure, 0);
|
||||
maxPressure = Math.Max(maxPressure, 0);
|
||||
minTemperature = Math.Min(minTemperature, 0);
|
||||
maxTemperature = Math.Max(maxTemperature, 0);
|
||||
}
|
||||
|
||||
holdingPressure |= maxPressure - minPressure > firelock.PressureThreshold;
|
||||
holdingFire |= maxTemperature - minTemperature > firelock.TemperatureThreshold;
|
||||
|
||||
return (holdingPressure, holdingFire);
|
||||
}
|
||||
|
||||
private bool HasAirtightBlocker(IEnumerable<EntityUid> enumerable, AtmosDirection dir, EntityQuery<AirtightComponent> airtightQuery)
|
||||
{
|
||||
foreach (var ent in enumerable)
|
||||
{
|
||||
if (!airtightQuery.TryGetComponent(ent, out var airtight) || !airtight.AirBlocked)
|
||||
continue;
|
||||
|
||||
if ((airtight.AirBlockedDirection & dir) == dir)
|
||||
return true;
|
||||
|
||||
foreach (var adjacent in _atmosSystem.GetAdjacentTiles(gridUid, position))
|
||||
{
|
||||
if (_atmosSystem.IsHotspotActive(gridUid, adjacent))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user