CatStation/Content.Server/Atmos/Consoles/AtmosAlertsComputerSystem.cs
Dmitry 25c5e59248
Upstream (#3172)
Signed-off-by: Prole <172158352+Prole0@users.noreply.github.com>
Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com>
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
Co-authored-by: Samuka-C <47865393+Samuka-C@users.noreply.github.com>
Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>
Co-authored-by: Partmedia <kevinz5000@gmail.com>
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
Co-authored-by: themias <89101928+themias@users.noreply.github.com>
Co-authored-by: Victor Shen <71985089+Vexerot@users.noreply.github.com>
Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com>
Co-authored-by: Milon <milonpl.git@proton.me>
Co-authored-by: Kirus59 <145689588+Kirus59@users.noreply.github.com>
Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
Co-authored-by: Stomf <5dorkydorks@gmail.com>
Co-authored-by: drakewill-CRL <46307022+drakewill-CRL@users.noreply.github.com>
Co-authored-by: PraxisMapper <praxismapper@gmail.com>
Co-authored-by: EmoGarbage404 <retron404@gmail.com>
Co-authored-by: lzk <124214523+lzk228@users.noreply.github.com>
Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
Co-authored-by: IProduceWidgets <107586145+IProduceWidgets@users.noreply.github.com>
Co-authored-by: TytosB <54259736+TytosB@users.noreply.github.com>
Co-authored-by: abadaba695 <spacestation13thingy@gmail.com>
Co-authored-by: kosticia <kosticia46@gmail.com>
Co-authored-by: Thinbug <101073555+Thinbug0@users.noreply.github.com>
Co-authored-by: pathetic meowmeow <uhhadd@gmail.com>
Co-authored-by: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com>
Co-authored-by: Boaz1111 <149967078+Boaz1111@users.noreply.github.com>
Co-authored-by: ActiveMammmoth <140334666+ActiveMammmoth@users.noreply.github.com>
Co-authored-by: Myra <vasilis@pikachu.systems>
Co-authored-by: Whatstone <166147148+whatston3@users.noreply.github.com>
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
Co-authored-by: K-Dynamic <20566341+K-Dynamic@users.noreply.github.com>
Co-authored-by: Gentleman-Bird <dcgreen406@gmail.com>
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Co-authored-by: BIGZi0348 <svalker0348@gmail.com>
Co-authored-by: LaCumbiaDelCoronavirus <90893484+LaCumbiaDelCoronavirus@users.noreply.github.com>
Co-authored-by: imatsoup <93290208+imatsoup@users.noreply.github.com>
Co-authored-by: Matthew Herber <32679887+happyrobot33@users.noreply.github.com>
Co-authored-by: Ertanic <36124833+Ertanic@users.noreply.github.com>
Co-authored-by: MissKay1994 <15877268+MissKay1994@users.noreply.github.com>
Co-authored-by: Errant <35878406+Errant-4@users.noreply.github.com>
Co-authored-by: eoineoineoin <helloworld@eoinrul.es>
Co-authored-by: Tiniest Shark <head.rebel@yahoo.com>
Co-authored-by: nikitosych <boriszyn@gmail.com>
Co-authored-by: Tayrtahn <tayrtahn@gmail.com>
Co-authored-by: Perry Fraser <perryprog@users.noreply.github.com>
Co-authored-by: YoungThug <ramialanbagy@gmail.com>
Co-authored-by: beck-thompson <107373427+beck-thompson@users.noreply.github.com>
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
Co-authored-by: Southbridge <7013162+southbridge-fur@users.noreply.github.com>
Co-authored-by: Vladislav Suchkov <20380250+murolem@users.noreply.github.com>
Co-authored-by: Prole <172158352+Prole0@users.noreply.github.com>
Co-authored-by: Unkn0wn_Gh0st <shadowstalkermll@gmail.com>
Co-authored-by: 3nderall <101940324+3nderall@users.noreply.github.com>
Co-authored-by: Radezolid <snappednexus@gmail.com>
Co-authored-by: J <billsmith116@gmail.com>
Co-authored-by: Ghagliiarghii <68826635+Ghagliiarghii@users.noreply.github.com>
Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com>
Co-authored-by: youtissoum <51883137+youtissoum@users.noreply.github.com>
Co-authored-by: Minemoder5000 <minemoder50000@gmail.com>
Co-authored-by: Spanky <scott@wearejacob.com>
Co-authored-by: Spessmann <156740760+Spessmann@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: brainfood1183 <113240905+brainfood1183@users.noreply.github.com>
Co-authored-by: Deerstop <edainturner@gmail.com>
Co-authored-by: B_Kirill <153602297+B-Kirill@users.noreply.github.com>
Co-authored-by: archee1 <archee3@hotmail.co.uk>
Co-authored-by: Cojoke <83733158+Cojoke-dot@users.noreply.github.com>
Co-authored-by: Quantum-cross <7065792+Quantum-cross@users.noreply.github.com>
Co-authored-by: poklj <compgeek223@gmail.com>
Co-authored-by: Krunklehorn <42424291+Krunklehorn@users.noreply.github.com>
Co-authored-by: OnyxTheBrave <131422822+OnyxTheBrave@users.noreply.github.com>
Co-authored-by: UpAndLeaves <92269094+Alpha-Two@users.noreply.github.com>
Co-authored-by: Flareguy <78941145+Flareguy@users.noreply.github.com>
Co-authored-by: Zalycon <84675130+Zalycon@users.noreply.github.com>
Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com>
Co-authored-by: Verm <32827189+Vermidia@users.noreply.github.com>
Co-authored-by: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com>
Co-authored-by: ScarKy0 <scarky0@onet.eu>
Co-authored-by: Dmitry <57028746+dimm00n@users.noreply.github.com>
2025-05-24 11:00:17 +03:00

412 lines
16 KiB
C#

using Content.Server.Atmos.Monitor.Components;
using Content.Server.DeviceNetwork.Systems;
using Content.Server.Pinpointer;
using Content.Server.Power.Components;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.Consoles;
using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Monitor.Components;
using Content.Shared.DeviceNetwork.Components;
using Content.Shared.Pinpointer;
using Robust.Server.GameObjects;
using Robust.Shared.Map.Components;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Content.Server.Atmos.Monitor.Systems;
public sealed class AtmosAlertsComputerSystem : SharedAtmosAlertsComputerSystem
{
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly AirAlarmSystem _airAlarmSystem = default!;
[Dependency] private readonly AtmosDeviceNetworkSystem _atmosDevNet = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly MapSystem _mapSystem = default!;
[Dependency] private readonly TransformSystem _transformSystem = default!;
[Dependency] private readonly NavMapSystem _navMapSystem = default!;
[Dependency] private readonly DeviceListSystem _deviceListSystem = default!;
private const float UpdateTime = 1.0f;
// Note: this data does not need to be saved
private float _updateTimer = 1.0f;
public override void Initialize()
{
base.Initialize();
// Console events
SubscribeLocalEvent<AtmosAlertsComputerComponent, ComponentInit>(OnConsoleInit);
SubscribeLocalEvent<AtmosAlertsComputerComponent, EntParentChangedMessage>(OnConsoleParentChanged);
SubscribeLocalEvent<AtmosAlertsComputerComponent, AtmosAlertsComputerFocusChangeMessage>(OnFocusChangedMessage);
// Grid events
SubscribeLocalEvent<GridSplitEvent>(OnGridSplit);
// Alarm events
SubscribeLocalEvent<AtmosAlertsDeviceComponent, EntityTerminatingEvent>(OnDeviceTerminatingEvent);
SubscribeLocalEvent<AtmosAlertsDeviceComponent, AnchorStateChangedEvent>(OnDeviceAnchorChanged);
}
#region Event handling
private void OnConsoleInit(EntityUid uid, AtmosAlertsComputerComponent component, ComponentInit args)
{
InitalizeConsole(uid, component);
}
private void OnConsoleParentChanged(EntityUid uid, AtmosAlertsComputerComponent component, EntParentChangedMessage args)
{
InitalizeConsole(uid, component);
}
private void OnFocusChangedMessage(EntityUid uid, AtmosAlertsComputerComponent component, AtmosAlertsComputerFocusChangeMessage args)
{
component.FocusDevice = args.FocusDevice;
}
private void OnGridSplit(ref GridSplitEvent args)
{
// Collect grids
var allGrids = args.NewGrids.ToList();
if (!allGrids.Contains(args.Grid))
allGrids.Add(args.Grid);
// Update atmos monitoring consoles that stand upon an updated grid
var query = AllEntityQuery<AtmosAlertsComputerComponent, TransformComponent>();
while (query.MoveNext(out var ent, out var entConsole, out var entXform))
{
if (entXform.GridUid == null)
continue;
if (!allGrids.Contains(entXform.GridUid.Value))
continue;
InitalizeConsole(ent, entConsole);
}
}
private void OnDeviceAnchorChanged(EntityUid uid, AtmosAlertsDeviceComponent component, AnchorStateChangedEvent args)
{
OnDeviceAdditionOrRemoval(uid, component, args.Anchored);
}
private void OnDeviceTerminatingEvent(EntityUid uid, AtmosAlertsDeviceComponent component, ref EntityTerminatingEvent args)
{
OnDeviceAdditionOrRemoval(uid, component, false);
}
private void OnDeviceAdditionOrRemoval(EntityUid uid, AtmosAlertsDeviceComponent component, bool isAdding)
{
var xform = Transform(uid);
var gridUid = xform.GridUid;
if (gridUid == null)
return;
if (!TryComp<NavMapComponent>(xform.GridUid, out var navMap))
return;
if (!TryGetAtmosDeviceNavMapData(uid, component, xform, out var data))
return;
var netEntity = GetNetEntity(uid);
var query = AllEntityQuery<AtmosAlertsComputerComponent, TransformComponent>();
while (query.MoveNext(out var ent, out var entConsole, out var entXform))
{
if (gridUid != entXform.GridUid)
continue;
if (isAdding)
{
entConsole.AtmosDevices.Add(data.Value);
}
else
{
entConsole.AtmosDevices.RemoveWhere(x => x.NetEntity == netEntity);
_navMapSystem.RemoveNavMapRegion(gridUid.Value, navMap, netEntity);
}
Dirty(ent, entConsole);
}
}
#endregion
public override void Update(float frameTime)
{
base.Update(frameTime);
_updateTimer += frameTime;
if (_updateTimer >= UpdateTime)
{
_updateTimer -= UpdateTime;
// Keep a list of UI entries for each gridUid, in case multiple consoles stand on the same grid
var airAlarmEntriesForEachGrid = new Dictionary<EntityUid, AtmosAlertsComputerEntry[]>();
var fireAlarmEntriesForEachGrid = new Dictionary<EntityUid, AtmosAlertsComputerEntry[]>();
var query = AllEntityQuery<AtmosAlertsComputerComponent, TransformComponent>();
while (query.MoveNext(out var ent, out var entConsole, out var entXform))
{
if (entXform?.GridUid == null)
continue;
// Make a list of alarm state data for all the air and fire alarms on the grid
if (!airAlarmEntriesForEachGrid.TryGetValue(entXform.GridUid.Value, out var airAlarmEntries))
{
airAlarmEntries = GetAlarmStateData(entXform.GridUid.Value, AtmosAlertsComputerGroup.AirAlarm).ToArray();
airAlarmEntriesForEachGrid[entXform.GridUid.Value] = airAlarmEntries;
}
if (!fireAlarmEntriesForEachGrid.TryGetValue(entXform.GridUid.Value, out var fireAlarmEntries))
{
fireAlarmEntries = GetAlarmStateData(entXform.GridUid.Value, AtmosAlertsComputerGroup.FireAlarm).ToArray();
fireAlarmEntriesForEachGrid[entXform.GridUid.Value] = fireAlarmEntries;
}
// Determine the highest level of alert for the console (based on non-silenced alarms)
var highestAlert = AtmosAlarmType.Invalid;
foreach (var entry in airAlarmEntries)
{
if (entry.AlarmState > highestAlert && !entConsole.SilencedDevices.Contains(entry.NetEntity))
highestAlert = entry.AlarmState;
}
foreach (var entry in fireAlarmEntries)
{
if (entry.AlarmState > highestAlert && !entConsole.SilencedDevices.Contains(entry.NetEntity))
highestAlert = entry.AlarmState;
}
// Update the appearance of the console based on the highest recorded level of alert
if (TryComp<AppearanceComponent>(ent, out var entAppearance))
_appearance.SetData(ent, AtmosAlertsComputerVisuals.ComputerLayerScreen, (int) highestAlert, entAppearance);
// If the console UI is open, send UI data to each subscribed session
UpdateUIState(ent, airAlarmEntries, fireAlarmEntries, entConsole, entXform);
}
}
}
public void UpdateUIState
(EntityUid uid,
AtmosAlertsComputerEntry[] airAlarmStateData,
AtmosAlertsComputerEntry[] fireAlarmStateData,
AtmosAlertsComputerComponent component,
TransformComponent xform)
{
if (!_userInterfaceSystem.IsUiOpen(uid, AtmosAlertsComputerUiKey.Key))
return;
var gridUid = xform.GridUid!.Value;
if (!HasComp<MapGridComponent>(gridUid))
return;
// The grid must have a NavMapComponent to visualize the map in the UI
EnsureComp<NavMapComponent>(gridUid);
// Gathering remaining data to be send to the client
var focusAlarmData = GetFocusAlarmData(uid, GetEntity(component.FocusDevice), gridUid);
// Set the UI state
_userInterfaceSystem.SetUiState(uid, AtmosAlertsComputerUiKey.Key,
new AtmosAlertsComputerBoundInterfaceState(airAlarmStateData, fireAlarmStateData, focusAlarmData));
}
private List<AtmosAlertsComputerEntry> GetAlarmStateData(EntityUid gridUid, AtmosAlertsComputerGroup group)
{
var alarmStateData = new List<AtmosAlertsComputerEntry>();
var queryAlarms = AllEntityQuery<AtmosAlertsDeviceComponent, AtmosAlarmableComponent, DeviceNetworkComponent, TransformComponent>();
while (queryAlarms.MoveNext(out var ent, out var entDevice, out var entAtmosAlarmable, out var entDeviceNetwork, out var entXform))
{
if (entXform.GridUid != gridUid)
continue;
if (!entXform.Anchored)
continue;
if (entDevice.Group != group)
continue;
if (!TryComp<MapGridComponent>(entXform.GridUid, out var mapGrid))
continue;
if (!TryComp<NavMapComponent>(entXform.GridUid, out var navMap))
continue;
// If emagged, change the alarm type to normal
var alarmState = (entAtmosAlarmable.LastAlarmState == AtmosAlarmType.Emagged) ? AtmosAlarmType.Normal : entAtmosAlarmable.LastAlarmState;
// Unpowered alarms can't sound
if (TryComp<ApcPowerReceiverComponent>(ent, out var entAPCPower) && !entAPCPower.Powered)
alarmState = AtmosAlarmType.Invalid;
// Create entry
var netEnt = GetNetEntity(ent);
var entry = new AtmosAlertsComputerEntry
(netEnt,
GetNetCoordinates(entXform.Coordinates),
entDevice.Group,
alarmState,
MetaData(ent).EntityName,
entDeviceNetwork.Address);
// Get the list of sensors attached to the alarm
var sensorList = TryComp<DeviceListComponent>(ent, out var entDeviceList) ? _deviceListSystem.GetDeviceList(ent, entDeviceList) : null;
if (sensorList?.Any() == true)
{
var alarmRegionSeeds = new HashSet<Vector2i>();
// If valid and anchored, use the position of sensors as seeds for the region
foreach (var (address, sensorEnt) in sensorList)
{
if (!sensorEnt.IsValid() || !HasComp<AtmosMonitorComponent>(sensorEnt))
continue;
var sensorXform = Transform(sensorEnt);
if (sensorXform.Anchored && sensorXform.GridUid == entXform.GridUid)
alarmRegionSeeds.Add(_mapSystem.CoordinatesToTile(entXform.GridUid.Value, mapGrid, _transformSystem.GetMapCoordinates(sensorEnt, sensorXform)));
}
var regionProperties = new SharedNavMapSystem.NavMapRegionProperties(netEnt, AtmosAlertsComputerUiKey.Key, alarmRegionSeeds);
_navMapSystem.AddOrUpdateNavMapRegion(gridUid, navMap, netEnt, regionProperties);
}
else
{
_navMapSystem.RemoveNavMapRegion(entXform.GridUid.Value, navMap, netEnt);
}
alarmStateData.Add(entry);
}
return alarmStateData;
}
private AtmosAlertsFocusDeviceData? GetFocusAlarmData(EntityUid uid, EntityUid? focusDevice, EntityUid gridUid)
{
if (focusDevice == null)
return null;
var focusDeviceXform = Transform(focusDevice.Value);
if (!focusDeviceXform.Anchored ||
focusDeviceXform.GridUid != gridUid ||
!TryComp<AirAlarmComponent>(focusDevice.Value, out var focusDeviceAirAlarm))
{
return null;
}
// Force update the sensors attached to the alarm
if (!_userInterfaceSystem.IsUiOpen(focusDevice.Value, SharedAirAlarmInterfaceKey.Key))
{
_atmosDevNet.Register(focusDevice.Value, null);
_atmosDevNet.Sync(focusDevice.Value, null);
foreach ((var address, var _) in focusDeviceAirAlarm.SensorData)
_atmosDevNet.Register(uid, null);
}
// Get the sensor data
var temperatureData = (_airAlarmSystem.CalculateTemperatureAverage(focusDeviceAirAlarm), AtmosAlarmType.Normal);
var pressureData = (_airAlarmSystem.CalculatePressureAverage(focusDeviceAirAlarm), AtmosAlarmType.Normal);
var gasData = new Dictionary<Gas, (float, float, AtmosAlarmType)>();
foreach ((var address, var sensorData) in focusDeviceAirAlarm.SensorData)
{
if (sensorData.TemperatureThreshold.CheckThreshold(sensorData.Temperature, out var temperatureState) &&
(int) temperatureState > (int) temperatureData.Item2)
{
temperatureData = (temperatureData.Item1, temperatureState);
}
if (sensorData.PressureThreshold.CheckThreshold(sensorData.Pressure, out var pressureState) &&
(int) pressureState > (int) pressureData.Item2)
{
pressureData = (pressureData.Item1, pressureState);
}
if (focusDeviceAirAlarm.SensorData.Sum(g => g.Value.TotalMoles) > 1e-8)
{
foreach ((var gas, var threshold) in sensorData.GasThresholds)
{
if (!gasData.ContainsKey(gas))
{
float mol = _airAlarmSystem.CalculateGasMolarConcentrationAverage(focusDeviceAirAlarm, gas, out var percentage);
if (mol < 1e-8)
continue;
gasData[gas] = (mol, percentage, AtmosAlarmType.Normal);
}
if (threshold.CheckThreshold(gasData[gas].Item2, out var gasState) &&
(int) gasState > (int) gasData[gas].Item3)
{
gasData[gas] = (gasData[gas].Item1, gasData[gas].Item2, gasState);
}
}
}
}
return new AtmosAlertsFocusDeviceData(GetNetEntity(focusDevice.Value), temperatureData, pressureData, gasData);
}
private HashSet<AtmosAlertsDeviceNavMapData> GetAllAtmosDeviceNavMapData(EntityUid gridUid)
{
var atmosDeviceNavMapData = new HashSet<AtmosAlertsDeviceNavMapData>();
var query = AllEntityQuery<AtmosAlertsDeviceComponent, TransformComponent>();
while (query.MoveNext(out var ent, out var entComponent, out var entXform))
{
if (entXform.GridUid != gridUid)
continue;
if (TryGetAtmosDeviceNavMapData(ent, entComponent, entXform, out var data))
atmosDeviceNavMapData.Add(data.Value);
}
return atmosDeviceNavMapData;
}
private bool TryGetAtmosDeviceNavMapData
(EntityUid uid,
AtmosAlertsDeviceComponent component,
TransformComponent xform,
[NotNullWhen(true)] out AtmosAlertsDeviceNavMapData? output)
{
output = null;
if (!xform.Anchored)
return false;
output = new AtmosAlertsDeviceNavMapData(GetNetEntity(uid), GetNetCoordinates(xform.Coordinates), component.Group);
return true;
}
private void InitalizeConsole(EntityUid uid, AtmosAlertsComputerComponent component)
{
var xform = Transform(uid);
if (xform.GridUid == null)
return;
var grid = xform.GridUid.Value;
component.AtmosDevices = GetAllAtmosDeviceNavMapData(grid);
Dirty(uid, component);
}
}