Files
tbd-station-14/Content.Server/Atmos/Components/GasAnalyzerComponent.cs
Vera Aguilera Puerto a2b737d945 Atmos pipe rework (#3833)
* Initial

* Cleanup a bunch of things

* some changes dunno

* RequireAnchored

* a

* stuff

* more work

* Lots of progress

* delete pipe visualizer

* a

* b

* pipenet and pipenode cleanup

* Fixes

* Adds GasValve

* Adds GasMiner

* Fix stuff, maybe?

* More fixes

* Ignored components on the client

* Adds thermomachine behavior, change a bunch of stuff

* Remove Anchored

* some work, but it's shitcode

* significantly more ECS

* ECS AtmosDevices

* Cleanup

* fix appearance

* when the pipe direction is sus

* Gas tanks and canisters

* pipe anchoring and stuff

* coding is my passion

* Unsafe pipes take longer to unanchor

* turns out we're no longer using eris canisters

* Gas canister inserted tank appearance, improvements

* Work on a bunch of appearances

* Scrubber appearance

* Reorganize AtmosphereSystem.Piping into a bunch of different systems

* Appearance for vent/scrubber/pump turns off when leaving atmosphere

* ThermoMachine appearance

* Cleanup gas tanks

* Remove passive gate unused imports

* remove old canister UI functionality

* PipeNode environment air, make everything use AssumeAir instead of merging manually

* a

* Reorganize atmos to follow new structure

* ?????

* Canister UI, restructure client

* Restructure shared

* Fix build tho

* listen, at least the canister UI works entirely...

* fix build : )

* Atmos device prototypes have names and descriptions

* gas canister ui slider doesn't jitter

* trinary prototypes

* sprite for miners

* ignore components

* fix YAML

* Fix port system doing useless thing

* Fix build

* fix thinking moment

* fix build again because

* canister direction

* pipenode is a word

* GasTank Air will throw on invalid states

* fix build....

* Unhardcode volume pump thresholds

* Volume pump and filter take time into account

* Rename Join/Leave atmosphere events to AtmosDeviceEnabled/Disabled Event

* Gas tank node volume is set by initial mixtuer

* I love node container
2021-06-19 13:25:05 +02:00

288 lines
9.4 KiB
C#

#nullable enable
using System.Collections.Generic;
using System.Threading.Tasks;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Hands.Components;
using Content.Server.UserInterface;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.DragDrop;
using Content.Shared.Interaction;
using Content.Shared.Notification.Managers;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Players;
using Robust.Shared.ViewVariables;
namespace Content.Server.Atmos.Components
{
[RegisterComponent]
public class GasAnalyzerComponent : SharedGasAnalyzerComponent, IAfterInteract, IDropped, IUse
{
private GasAnalyzerDanger _pressureDanger;
private float _timeSinceSync;
private const float TimeBetweenSyncs = 2f;
private bool _checkPlayer = false; // Check at the player pos or at some other tile?
private EntityCoordinates? _position; // The tile that we scanned
private AppearanceComponent? _appearance;
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(GasAnalyzerUiKey.Key);
public override void Initialize()
{
base.Initialize();
if (UserInterface != null)
{
UserInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage;
UserInterface.OnClosed += UserInterfaceOnClose;
}
Owner.TryGetComponent(out _appearance);
}
public override ComponentState GetComponentState(ICommonSession player)
{
return new GasAnalyzerComponentState(_pressureDanger);
}
/// <summary>
/// Call this from other components to open the gas analyzer UI.
/// Uses the player position.
/// </summary>
/// <param name="session">The session to open the ui for</param>
public void OpenInterface(IPlayerSession session)
{
_checkPlayer = true;
_position = null;
UserInterface?.Open(session);
UpdateUserInterface();
UpdateAppearance(true);
Resync();
}
/// <summary>
/// Call this from other components to open the gas analyzer UI.
/// Uses a given position.
/// </summary>
/// <param name="session">The session to open the ui for</param>
/// <param name="pos">The position to analyze the gas</param>
public void OpenInterface(IPlayerSession session, EntityCoordinates pos)
{
_checkPlayer = false;
_position = pos;
UserInterface?.Open(session);
UpdateUserInterface();
UpdateAppearance(true);
Resync();
}
public void ToggleInterface(IPlayerSession session)
{
if (UserInterface == null)
return;
if (UserInterface.SessionHasOpen(session))
CloseInterface(session);
else
OpenInterface(session);
}
public void CloseInterface(IPlayerSession session)
{
_position = null;
UserInterface?.Close(session);
// Our OnClose will do the appearance stuff
Resync();
}
private void UserInterfaceOnClose(IPlayerSession obj)
{
UpdateAppearance(false);
}
private void UpdateAppearance(bool open)
{
_appearance?.SetData(GasAnalyzerVisuals.VisualState,
open ? GasAnalyzerVisualState.Working : GasAnalyzerVisualState.Off);
}
public void Update(float frameTime)
{
_timeSinceSync += frameTime;
if (_timeSinceSync > TimeBetweenSyncs)
{
Resync();
UpdateUserInterface();
}
}
private void Resync()
{
// Already get the pressure before Dirty(), because we can't get the EntitySystem in that thread or smth
var pressure = 0f;
var gam = EntitySystem.Get<AtmosphereSystem>().GetGridAtmosphere(Owner.Transform.Coordinates);
var tile = gam?.GetTile(Owner.Transform.Coordinates)?.Air;
if (tile != null)
{
pressure = tile.Pressure;
}
if (pressure >= Atmospherics.HazardHighPressure || pressure <= Atmospherics.HazardLowPressure)
{
_pressureDanger = GasAnalyzerDanger.Hazard;
}
else if (pressure >= Atmospherics.WarningHighPressure || pressure <= Atmospherics.WarningLowPressure)
{
_pressureDanger = GasAnalyzerDanger.Warning;
}
else
{
_pressureDanger = GasAnalyzerDanger.Nominal;
}
Dirty();
_timeSinceSync = 0f;
}
private void UpdateUserInterface()
{
if (UserInterface == null)
{
return;
}
string? error = null;
// Check if the player is still holding the gas analyzer => if not, don't update
foreach (var session in UserInterface.SubscribedSessions)
{
if (session.AttachedEntity == null)
return;
if (!session.AttachedEntity.TryGetComponent(out IHandsComponent? handsComponent))
return;
var activeHandEntity = handsComponent?.GetActiveHand?.Owner;
if (activeHandEntity == null || !activeHandEntity.TryGetComponent(out GasAnalyzerComponent? gasAnalyzer))
{
return;
}
}
var pos = Owner.Transform.Coordinates;
if (!_checkPlayer && _position.HasValue)
{
// Check if position is out of range => don't update
if (!_position.Value.InRange(Owner.EntityManager, pos, SharedInteractionSystem.InteractionRange))
return;
pos = _position.Value;
}
var atmosSystem = EntitySystem.Get<AtmosphereSystem>();
var gam = atmosSystem.GetGridAtmosphere(pos);
var tile = gam.GetTile(pos)?.Air;
if (tile == null)
{
error = "No Atmosphere!";
UserInterface.SetState(
new GasAnalyzerBoundUserInterfaceState(
0,
0,
null,
error));
return;
}
var gases = new List<GasEntry>();
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
{
var gas = atmosSystem.GetGas(i);
if (tile.Gases[i] <= Atmospherics.GasMinMoles) continue;
gases.Add(new GasEntry(gas.Name, tile.Gases[i], gas.Color));
}
UserInterface.SetState(
new GasAnalyzerBoundUserInterfaceState(
tile.Pressure,
tile.Temperature,
gases.ToArray(),
error));
}
private void UserInterfaceOnReceiveMessage(ServerBoundUserInterfaceMessage serverMsg)
{
var message = serverMsg.Message;
switch (message)
{
case GasAnalyzerRefreshMessage msg:
var player = serverMsg.Session.AttachedEntity;
if (player == null)
{
return;
}
if (!player.TryGetComponent(out IHandsComponent? handsComponent))
{
Owner.PopupMessage(player, Loc.GetString("You have no hands."));
return;
}
var activeHandEntity = handsComponent.GetActiveHand?.Owner;
if (activeHandEntity == null || !activeHandEntity.TryGetComponent(out GasAnalyzerComponent? gasAnalyzer))
{
serverMsg.Session.AttachedEntity?.PopupMessage(Loc.GetString("You need a Gas Analyzer in your hand!"));
return;
}
UpdateUserInterface();
Resync();
break;
}
}
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
{
if (!eventArgs.CanReach)
{
eventArgs.User.PopupMessage(Loc.GetString("You can't reach there!"));
return true;
}
if (eventArgs.User.TryGetComponent(out ActorComponent? actor))
{
OpenInterface(actor.PlayerSession, eventArgs.ClickLocation);
}
return true;
}
void IDropped.Dropped(DroppedEventArgs eventArgs)
{
if (eventArgs.User.TryGetComponent(out ActorComponent? actor))
{
CloseInterface(actor.PlayerSession);
}
}
bool IUse.UseEntity(UseEntityEventArgs eventArgs)
{
if (eventArgs.User.TryGetComponent(out ActorComponent? actor))
{
ToggleInterface(actor.PlayerSession);
return true;
}
return false;
}
}
}