Fix firelock danger indicators (#12327)

* Fix firelock danger indicators

* remove unused arg
This commit is contained in:
Leon Friedrich
2022-11-09 08:55:45 +13:00
committed by GitHub
parent 5f71fc1ea1
commit 8620899a4a
5 changed files with 243 additions and 53 deletions

View File

@@ -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;
}
}
}