Files
tbd-station-14/Content.Server/GameObjects/Components/Atmos/GasCanisterComponent.cs
Acruid 00e01d51fd SnapGridComponent Removal (#3884)
* Removed SnapGridOffset, there is only center now.

* SnapGridComponent methods are now static.

* Removed SnapGridComponent.OnPositionChanged.

* Refactored static functions off SnapGridComponent to MapGrid.
Refactored away usages of SnapGridComponent.Position.

* Added Transform.Anchored for checking if an entity is a tile entity.
More refactoring for static MapGrid functions.

* Static snapgrid methods on MapGrid are no longer static.

* Add setter to ITransformComponent.Anchored.
Removed direct references to SnapGridComponent from content.

* Grid functions now deal with EntityUids instead of SnapGridComponents.
Began renaming public API functions from SnapGrid to Anchor.

* Remove the SnapGridComponent 'Offset' field from all yaml files. This was removed in code previously, so the yaml linter was upset.

* Update engine submodule to v0.4.46.
2021-04-28 10:49:37 -07:00

330 lines
10 KiB
C#

#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.Atmos;
using Content.Server.GameObjects.Components.Atmos.Piping;
using Content.Server.Interfaces;
using Content.Server.Utility;
using Content.Shared.Atmos;
using Content.Shared.GameObjects.Components.Atmos;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
using Robust.Shared.Physics;
namespace Content.Server.GameObjects.Components.Atmos
{
/// <summary>
/// Component that manages gas mixtures temperature, pressure and output.
/// </summary>
[RegisterComponent]
[ComponentReference(typeof(IActivate))]
public class GasCanisterComponent : Component, IGasMixtureHolder, IActivate
{
[Dependency] private readonly IMapManager _mapManager = default!;
public override string Name => "GasCanister";
private const int MaxLabelLength = 32;
[ViewVariables(VVAccess.ReadWrite)]
public string Label { get; set; } = "Gas Canister";
[ViewVariables(VVAccess.ReadWrite)]
public bool ValveOpened { get; set; } = false;
/// <summary>
/// What <see cref="GasMixture"/> the canister contains.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("gasMixture")]
public GasMixture Air { get; set; } = new (DefaultVolume);
[ViewVariables]
public bool Anchored => !Owner.TryGetComponent<IPhysBody>(out var physics) || physics.BodyType == BodyType.Static;
/// <summary>
/// The floor connector port that the canister is attached to.
/// </summary>
[ViewVariables]
public GasCanisterPortComponent? ConnectedPort { get; private set; }
[ViewVariables]
public bool ConnectedToPort => ConnectedPort != null;
public const float DefaultVolume = 10;
[ViewVariables(VVAccess.ReadWrite)] public float ReleasePressure { get; set; }
/// <summary>
/// The user interface bound to the canister.
/// </summary>
private BoundUserInterface? UserInterface => Owner.GetUIOrNull(SharedGasCanisterComponent.GasCanisterUiKey.Key);
/// <summary>
/// Stores the last ui state after it's been casted into <see cref="GasCanisterBoundUserInterface"/>
/// </summary>
private GasCanisterBoundUserInterfaceState? _lastUiState;
private AppearanceComponent? _appearance;
public override void Initialize()
{
base.Initialize();
if (Owner.TryGetComponent<IPhysBody>(out var physics))
{
AnchorUpdate();
}
if (UserInterface != null)
{
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
}
// Init some variables
Label = Owner.Name;
Owner.TryGetComponent(out _appearance);
UpdateUserInterface();
UpdateAppearance();
}
public override void HandleMessage(ComponentMessage message, IComponent? component)
{
base.HandleMessage(message, component);
switch (message)
{
case AnchoredChangedMessage:
AnchorUpdate();
break;
}
}
#region Connector port methods
public override void OnRemove()
{
base.OnRemove();
if (UserInterface != null)
{
UserInterface.OnReceiveMessage -= OnUiReceiveMessage;
}
DisconnectFromPort();
}
public void TryConnectToPort()
{
if (!Owner.Transform.Anchored) return;
var grid = _mapManager.GetGrid(Owner.Transform.GridID);
var coords = Owner.Transform.Coordinates;
var port = grid.GetLocal(coords)
.Select(entity => Owner.EntityManager.ComponentManager.TryGetComponent<GasCanisterPortComponent>(entity, out var port) ? port : null)
.Where(port => port != null)
.Where(port => !port!.ConnectedToCanister)
.FirstOrDefault();
if (port == null) return;
ConnectedPort = port;
ConnectedPort.ConnectGasCanister(this);
}
public void DisconnectFromPort()
{
ConnectedPort?.DisconnectGasCanister();
ConnectedPort = null;
}
private void AnchorUpdate()
{
if (Anchored)
{
TryConnectToPort();
}
else
{
DisconnectFromPort();
}
UpdateAppearance();
}
#endregion
void IActivate.Activate(ActivateEventArgs eventArgs)
{
if (!eventArgs.User.TryGetComponent(out IActorComponent? actor))
return;
UserInterface?.Open(actor.playerSession);
}
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
{
if (obj.Session.AttachedEntity == null)
{
return;
}
if (!PlayerCanUse(obj.Session.AttachedEntity))
{
return;
}
// If the label has been changed by a client
if (obj.Message is CanisterLabelChangedMessage canLabelMessage)
{
var newLabel = canLabelMessage.NewLabel;
if (newLabel.Length > MaxLabelLength)
newLabel = newLabel.Substring(0, MaxLabelLength);
Label = newLabel;
Owner.Name = Label;
UpdateUserInterface();
return;
}
// If the release pressure has been adjusted by the client on the gas canister
if (obj.Message is ReleasePressureButtonPressedMessage rPMessage)
{
ReleasePressure += rPMessage.ReleasePressure;
ReleasePressure = Math.Clamp(ReleasePressure, 0, 1000);
ReleasePressure = MathF.Round(ReleasePressure, 2);
UpdateUserInterface();
return;
}
if (obj.Message is UiButtonPressedMessage btnPressedMessage)
{
switch (btnPressedMessage.Button)
{
case UiButton.ValveToggle:
ToggleValve();
break;
}
}
}
/// <summary>
/// Update the user interface if relevant
/// </summary>
private void UpdateUserInterface()
{
var state = GetUserInterfaceState();
if (_lastUiState != null && _lastUiState.Equals(state))
{
return;
}
_lastUiState = state;
UserInterface?.SetState(state);
}
/// <summary>
/// Update the canister's sprite
/// </summary>
private void UpdateAppearance()
{
_appearance?.SetData(GasCanisterVisuals.ConnectedState, ConnectedToPort);
// The Eris canisters are being used, so best to use the Eris light logic unless someone else has a better idea.
// https://github.com/discordia-space/CEV-Eris/blob/fdd6ee7012f46838a6711adb1737cd90c48ae448/code/game/machinery/atmoalter/canister.dm#L129
if (Air.Pressure < 10)
{
_appearance?.SetData(GasCanisterVisuals.PressureState, 0);
}
else if (Air.Pressure < Atmospherics.OneAtmosphere)
{
_appearance?.SetData(GasCanisterVisuals.PressureState, 1);
}
else if (Air.Pressure < (15 * Atmospherics.OneAtmosphere))
{
_appearance?.SetData(GasCanisterVisuals.PressureState, 2);
}
else
{
_appearance?.SetData(GasCanisterVisuals.PressureState, 3);
}
}
/// <summary>
/// Get the current interface state from server data
/// </summary>
/// <returns>The state</returns>
private GasCanisterBoundUserInterfaceState GetUserInterfaceState()
{
// We round the pressure for ease of reading
return new GasCanisterBoundUserInterfaceState(Label,
MathF.Round(Air.Pressure, 2),
ReleasePressure,
ValveOpened);
}
public void AirWasUpdated()
{
UpdateUserInterface();
UpdateAppearance();
}
#region Check methods
private bool PlayerCanUse(IEntity? player)
{
if (player == null)
{
return false;
}
if (!ActionBlockerSystem.CanInteract(player) ||
!ActionBlockerSystem.CanUse(player))
{
return false;
}
return true;
}
#endregion
/// <summary>
/// Called when the canister's valve is toggled
/// </summary>
private void ToggleValve()
{
ValveOpened = !ValveOpened;
UpdateUserInterface();
}
/// <summary>
/// Called every frame
/// </summary>
/// <param name="frameTime"></param>
public void Update(in float frameTime)
{
if (ValveOpened)
{
var tileAtmosphere = Owner.Transform.Coordinates.GetTileAtmosphere();
if (tileAtmosphere != null)
{
// If tileAtmosphere.Air is null, then we're airblocked, so DON'T release
if (tileAtmosphere.Air != null)
{
Air.ReleaseGasTo(tileAtmosphere.Air, ReleasePressure);
tileAtmosphere.Invalidate();
}
}
else
{
Air.ReleaseGasTo(null, ReleasePressure);
}
AirWasUpdated();
}
}
}
}