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
This commit is contained in:
Vera Aguilera Puerto
2021-06-19 13:25:05 +02:00
committed by GitHub
parent cfc3f2e7fc
commit a2b737d945
250 changed files with 3964 additions and 3163 deletions

View File

@@ -1,8 +1,9 @@
#nullable enable #nullable enable
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Client.GameObjects.EntitySystems; using Content.Client.Atmos.EntitySystems;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Prototypes;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.Console; using Robust.Client.Console;

View File

@@ -1,8 +1,9 @@
#nullable enable #nullable enable
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Client.GameObjects.EntitySystems; using Content.Client.Atmos.EntitySystems;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Prototypes;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.Console; using Robust.Client.Console;

View File

@@ -1,7 +1,7 @@
using Content.Client.Items.Components; using Content.Client.Items.Components;
using Content.Client.Message; using Content.Client.Message;
using Content.Client.Stylesheets; using Content.Client.Stylesheets;
using Content.Shared.GameObjects.Components; using Content.Shared.Atmos.Components;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -9,7 +9,7 @@ using Robust.Shared.Localization;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects.Components.Atmos namespace Content.Client.Atmos.Components
{ {
[RegisterComponent] [RegisterComponent]
internal class GasAnalyzerComponent : SharedGasAnalyzerComponent, IItemStatus internal class GasAnalyzerComponent : SharedGasAnalyzerComponent, IItemStatus

View File

@@ -1,9 +1,8 @@
#nullable enable #nullable enable
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Client.Atmos; using Content.Client.Atmos.Overlays;
using Content.Shared.GameObjects.EntitySystems.Atmos;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.Graphics; using Robust.Client.Graphics;
@@ -11,7 +10,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
namespace Content.Client.GameObjects.EntitySystems namespace Content.Client.Atmos.EntitySystems
{ {
[UsedImplicitly] [UsedImplicitly]
internal sealed class AtmosDebugOverlaySystem : SharedAtmosDebugOverlaySystem, IResettingEntitySystem internal sealed class AtmosDebugOverlaySystem : SharedAtmosDebugOverlaySystem, IResettingEntitySystem

View File

@@ -1,7 +1,7 @@
using Content.Shared.GameObjects.EntitySystems.Atmos; using Content.Shared.Atmos.EntitySystems;
using JetBrains.Annotations; using JetBrains.Annotations;
namespace Content.Client.GameObjects.EntitySystems namespace Content.Client.Atmos.EntitySystems
{ {
[UsedImplicitly] [UsedImplicitly]
public class AtmosphereSystem : SharedAtmosphereSystem public class AtmosphereSystem : SharedAtmosphereSystem

View File

@@ -1,20 +1,19 @@
#nullable enable #nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Client.Atmos; using Content.Client.Atmos.Overlays;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.GameObjects.EntitySystems.Atmos; using Content.Shared.Atmos.EntitySystems;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.ResourceManagement; using Robust.Client.ResourceManagement;
using Robust.Client.Utility; using Robust.Client.Utility;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Client.GameObjects.EntitySystems namespace Content.Client.Atmos.EntitySystems
{ {
[UsedImplicitly] [UsedImplicitly]
internal sealed class GasTileOverlaySystem : SharedGasTileOverlaySystem internal sealed class GasTileOverlaySystem : SharedGasTileOverlaySystem

View File

@@ -1,15 +1,14 @@
using Content.Client.GameObjects.EntitySystems; using Content.Client.Atmos.EntitySystems;
using Content.Shared.GameObjects.EntitySystems.Atmos;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.EntitySystems;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Enums;
using System;
namespace Content.Client.Atmos namespace Content.Client.Atmos.Overlays
{ {
public class AtmosDebugOverlay : Overlay public class AtmosDebugOverlay : Overlay
{ {

View File

@@ -1,12 +1,12 @@
using Content.Client.GameObjects.EntitySystems; using Content.Client.Atmos.EntitySystems;
using Robust.Shared.Enums;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
namespace Content.Client.Atmos namespace Content.Client.Atmos.Overlays
{ {
public class GasTileOverlay : Overlay public class GasTileOverlay : Overlay
{ {

View File

@@ -0,0 +1,39 @@
using System;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Client.Atmos.Piping
{
[UsedImplicitly]
public abstract class EnabledAtmosDeviceVisualizer : AppearanceVisualizer
{
[DataField("enabledState")]
private string _enabledState = string.Empty;
protected abstract object LayerMap { get; }
protected abstract Enum DataKey { get; }
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
if (!entity.TryGetComponent(out ISpriteComponent? sprite))
return;
sprite.LayerMapSet(LayerMap, sprite.AddLayerState(_enabledState));
sprite.LayerSetVisible(LayerMap, false);
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
if (!component.Owner.TryGetComponent(out ISpriteComponent? sprite))
return;
if(component.TryGetData(DataKey, out bool enabled))
sprite.LayerSetVisible(LayerMap, enabled);
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using Content.Shared.Atmos.Piping;
using JetBrains.Annotations;
namespace Content.Client.Atmos.Piping
{
[UsedImplicitly]
public class OutletInjectorVisualizer : EnabledAtmosDeviceVisualizer
{
protected override object LayerMap => Layers.Enabled;
protected override Enum DataKey => OutletInjectorVisuals.Enabled;
enum Layers
{
Enabled,
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using Content.Shared.Atmos.Piping;
using JetBrains.Annotations;
namespace Content.Client.Atmos.Piping
{
[UsedImplicitly]
public class PassiveVentVisualizer : EnabledAtmosDeviceVisualizer
{
protected override object LayerMap => Layers.Enabled;
protected override Enum DataKey => PassiveVentVisuals.Enabled;
enum Layers
{
Enabled,
}
}
}

View File

@@ -1,6 +1,6 @@
#nullable enable #nullable enable
using System; using System;
using Content.Shared.GameObjects.Components.Atmos; using Content.Shared.Atmos;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.Graphics; using Robust.Client.Graphics;
@@ -10,9 +10,8 @@ using Robust.Shared.IoC;
using Robust.Shared.Log; using Robust.Shared.Log;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using YamlDotNet.RepresentationModel;
namespace Content.Client.GameObjects.Components.Atmos.Piping namespace Content.Client.Atmos.Piping
{ {
[UsedImplicitly] [UsedImplicitly]
public class PipeConnectorVisualizer : AppearanceVisualizer, ISerializationHooks public class PipeConnectorVisualizer : AppearanceVisualizer, ISerializationHooks
@@ -33,7 +32,7 @@ namespace Content.Client.GameObjects.Components.Atmos.Piping
if (resourceCache.TryGetResource(rsiString, out RSIResource? rsi)) if (resourceCache.TryGetResource(rsiString, out RSIResource? rsi))
_connectorRsi = rsi.RSI; _connectorRsi = rsi.RSI;
else else
Logger.Error($"{nameof(PipeVisualizer)} could not load to load RSI {rsiString}."); Logger.Error($"{nameof(PipeConnectorVisualizer)} could not load to load RSI {rsiString}.");
} }
public override void InitializeEntity(IEntity entity) public override void InitializeEntity(IEntity entity)

View File

@@ -0,0 +1,18 @@
using System;
using Content.Shared.Atmos.Piping;
using JetBrains.Annotations;
namespace Content.Client.Atmos.Piping
{
[UsedImplicitly]
public class PressurePumpVisualizer : EnabledAtmosDeviceVisualizer
{
protected override object LayerMap => Layers.Enabled;
protected override Enum DataKey => PressurePumpVisuals.Enabled;
enum Layers
{
Enabled,
}
}
}

View File

@@ -0,0 +1,52 @@
using Content.Shared.Atmos.Piping.Unary.Visuals;
using Content.Shared.Atmos.Visuals;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
namespace Content.Client.Atmos.Piping
{
[UsedImplicitly]
public class ScrubberVisualizer : AppearanceVisualizer
{
private string _offState = "scrub_off";
private string _scrubState = "scrub_on";
private string _siphonState = "scrub_purge";
private string _weldedState = "scrub_welded";
private string _wideState = "scrub_wide";
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
if (!component.Owner.TryGetComponent(out ISpriteComponent? sprite))
return;
if (!component.TryGetData(ScrubberVisuals.State, out ScrubberState state))
return;
switch (state)
{
case ScrubberState.Off:
sprite.LayerSetState(ScrubberVisualLayers.Scrubber, _offState);
break;
case ScrubberState.Scrub:
sprite.LayerSetState(ScrubberVisualLayers.Scrubber, _scrubState);
break;
case ScrubberState.Siphon:
sprite.LayerSetState(ScrubberVisualLayers.Scrubber, _siphonState);
break;
case ScrubberState.Welded:
sprite.LayerSetState(ScrubberVisualLayers.Scrubber, _weldedState);
break;
case ScrubberState.WideScrub:
sprite.LayerSetState(ScrubberVisualLayers.Scrubber, _wideState);
break;
}
}
}
public enum ScrubberVisualLayers
{
Scrubber,
}
}

View File

@@ -0,0 +1,18 @@
using System;
using Content.Shared.Atmos.Piping;
using JetBrains.Annotations;
namespace Content.Client.Atmos.Piping
{
[UsedImplicitly]
public class ThermoMachineVisualizer : EnabledAtmosDeviceVisualizer
{
protected override object LayerMap => Layers.Enabled;
protected override Enum DataKey => ThermoMachineVisuals.Enabled;
enum Layers
{
Enabled,
}
}
}

View File

@@ -0,0 +1,47 @@
using Content.Shared.Atmos.Visuals;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
namespace Content.Client.Atmos.Piping
{
[UsedImplicitly]
public class VentPumpVisualizer : AppearanceVisualizer
{
private string _offState = "vent_off";
private string _inState = "vent_in";
private string _outState = "vent_out";
private string _weldedState = "vent_welded";
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
if (!component.Owner.TryGetComponent(out ISpriteComponent? sprite))
return;
if (!component.TryGetData(VentPumpVisuals.State, out VentPumpState state))
return;
switch (state)
{
case VentPumpState.Off:
sprite.LayerSetState(VentVisualLayers.Vent, _offState);
break;
case VentPumpState.In:
sprite.LayerSetState(VentVisualLayers.Vent, _inState);
break;
case VentPumpState.Out:
sprite.LayerSetState(VentVisualLayers.Vent, _outState);
break;
case VentPumpState.Welded:
sprite.LayerSetState(VentVisualLayers.Vent, _weldedState);
break;
}
}
}
public enum VentVisualLayers
{
Vent,
}
}

View File

@@ -1,8 +1,8 @@
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using static Content.Shared.GameObjects.Components.SharedGasAnalyzerComponent; using static Content.Shared.Atmos.Components.SharedGasAnalyzerComponent;
namespace Content.Client.GameObjects.Components.Atmos namespace Content.Client.Atmos.UI
{ {
public class GasAnalyzerBoundUserInterface : BoundUserInterface public class GasAnalyzerBoundUserInterface : BoundUserInterface
{ {

View File

@@ -9,9 +9,9 @@ using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using static Content.Shared.GameObjects.Components.SharedGasAnalyzerComponent; using static Content.Shared.Atmos.Components.SharedGasAnalyzerComponent;
namespace Content.Client.GameObjects.Components.Atmos namespace Content.Client.Atmos.UI
{ {
public class GasAnalyzerWindow : BaseWindow public class GasAnalyzerWindow : BaseWindow
{ {

View File

@@ -0,0 +1,86 @@
using Content.Shared.Atmos.Piping.Binary.Components;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
namespace Content.Client.Atmos.UI
{
/// <summary>
/// Initializes a <see cref="GasCanisterWindow"/> and updates it when new server messages are received.
/// </summary>
[UsedImplicitly]
public class GasCanisterBoundUserInterface : BoundUserInterface
{
private GasCanisterWindow? _window;
public GasCanisterBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
}
protected override void Open()
{
base.Open();
_window = new GasCanisterWindow();
if(State != null)
UpdateState(State);
_window.OpenCentered();
_window.OnClose += Close;
_window.ReleaseValveCloseButtonPressed += OnReleaseValveClosePressed;
_window.ReleaseValveOpenButtonPressed += OnReleaseValveOpenPressed;
_window.ReleasePressureSliderChanged += OnReleasePressurePressed;
_window.TankEjectButtonPressed += OnTankEjectPressed;
}
private void OnTankEjectPressed()
{
SendMessage(new GasCanisterHoldingTankEjectMessage());
}
private void OnReleasePressurePressed(float value)
{
SendMessage(new GasCanisterChangeReleasePressureMessage(value));
}
private void OnReleaseValveOpenPressed()
{
SendMessage(new GasCanisterChangeReleaseValveMessage(true));
}
private void OnReleaseValveClosePressed()
{
SendMessage(new GasCanisterChangeReleaseValveMessage(false));
}
/// <summary>
/// Update the UI state based on server-sent info
/// </summary>
/// <param name="state"></param>
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (_window == null || state is not GasCanisterBoundUserInterfaceState cast)
return;
_window.SetCanisterLabel(cast.CanisterLabel);
_window.SetCanisterPressure(cast.CanisterPressure);
_window.SetPortStatus(cast.PortStatus);
_window.SetTankLabel(cast.TankLabel);
_window.SetTankPressure(cast.TankPressure);
_window.SetReleasePressureRange(cast.ReleasePressureMin, cast.ReleasePressureMax);
_window.SetReleasePressure(cast.ReleasePressure);
_window.SetReleaseValve(cast.ReleaseValve);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
_window?.Dispose();
}
}
}

View File

@@ -0,0 +1,52 @@
<SS14Window xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:Content.Client.Stylesheets"
MinSize="480 400" Title="Canister">
<VBoxContainer Margin="5 5 5 5" SeparationOverride="10">
<VBoxContainer VerticalExpand="True">
<Label Text="{Loc comp-gas-canister-ui-canister-status}" FontColorOverride="{x:Static s:StyleNano.NanoGold}" StyleClasses="LabelBig"/>
<HBoxContainer>
<Label Text="{Loc comp-gas-canister-ui-canister-pressure}"/>
<Label Name="CanisterPressureLabel" Align="Center" HorizontalExpand="True"/>
</HBoxContainer>
<HBoxContainer>
<Label Text="{Loc comp-gas-canister-ui-port-status}"/>
<Label Name="PortStatusLabel" Align="Center" HorizontalExpand="True"/>
</HBoxContainer>
</VBoxContainer>
<VBoxContainer VerticalExpand="True">
<Label Text="{Loc comp-gas-canister-ui-holding-tank-status}" FontColorOverride="{x:Static s:StyleNano.NanoGold}" StyleClasses="LabelBig"/>
<HBoxContainer>
<Label Text="{Loc comp-gas-canister-ui-holding-tank-label}"/>
<Label Name="TankLabelLabel" Text="{Loc comp-gas-canister-ui-holding-tank-label-empty}" Align="Center" HorizontalExpand="True"/>
<Button Name="TankEjectButton" Text="{Loc comp-gas-canister-ui-holding-tank-eject}"/>
</HBoxContainer>
<HBoxContainer>
<Label Text="{Loc comp-gas-canister-ui-holding-tank-pressure}"/>
<Label Name="TankPressureLabel" Align="Center" HorizontalExpand="True"/>
</HBoxContainer>
</VBoxContainer>
<VBoxContainer VerticalExpand="True">
<Label Text="{Loc comp-gas-canister-ui-release-valve-status}" FontColorOverride="{x:Static s:StyleNano.NanoGold}" StyleClasses="LabelBig"/>
<HBoxContainer>
<VBoxContainer>
<Label Text="{Loc comp-gas-canister-ui-release-pressure}"/>
<Control VerticalExpand="True"/>
</VBoxContainer>
<VBoxContainer HorizontalExpand="True" Margin="15 0 0 15" SeparationOverride="5">
<Slider Name="ReleasePressureSlider" HorizontalExpand="True"/>
<Label Name="ReleasePressureLabel" Align="Center" HorizontalExpand="True"/>
</VBoxContainer>
</HBoxContainer>
<HBoxContainer>
<Label Text="{Loc comp-gas-canister-ui-release-valve}"/>
<Control HorizontalExpand="True"/>
<Button Name="ReleaseValveOpenButton" Text="{Loc comp-gas-canister-ui-release-valve-open}" ToggleMode="True"/>
<Button Name="ReleaseValveCloseButton" Text="{Loc comp-gas-canister-ui-release-valve-close}" ToggleMode="True"/>
<Control HorizontalExpand="True"/>
</HBoxContainer>
</VBoxContainer>
</VBoxContainer>
</SS14Window>

View File

@@ -0,0 +1,98 @@
using System;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Localization;
namespace Content.Client.Atmos.UI
{
/// <summary>
/// Client-side UI used to control a canister.
/// </summary>
[GenerateTypedNameReferences]
public partial class GasCanisterWindow : SS14Window
{
private readonly ButtonGroup _buttonGroup = new();
public event Action? TankEjectButtonPressed;
public event Action<float>? ReleasePressureSliderChanged;
public event Action? ReleaseValveCloseButtonPressed;
public event Action? ReleaseValveOpenButtonPressed;
public GasCanisterWindow()
{
RobustXamlLoader.Load(this);
ReleaseValveCloseButton.Group = _buttonGroup;
ReleaseValveOpenButton.Group = _buttonGroup;
ReleaseValveCloseButton.OnPressed += _ => ReleaseValveCloseButtonPressed?.Invoke();
ReleaseValveOpenButton.OnPressed += _ => ReleaseValveOpenButtonPressed?.Invoke();
TankEjectButton.OnPressed += _ => TankEjectButtonPressed?.Invoke();
ReleasePressureSlider.OnValueChanged += r => ReleasePressureSliderChanged?.Invoke(r.Value);
}
public void SetCanisterLabel(string label)
{
Title = label;
}
public void SetCanisterPressure(float pressure)
{
CanisterPressureLabel.Text = Loc.GetString("comp-gas-canister-ui-pressure", ("pressure", Math.Round(pressure)));
}
public void SetPortStatus(bool status)
{
if (status)
{
PortStatusLabel.Text = Loc.GetString("comp-gas-canister-ui-port-connected");
}
else
{
PortStatusLabel.Text = Loc.GetString("comp-gas-canister-ui-port-disconnected");
}
}
public void SetTankLabel(string? label)
{
if (label == null)
{
TankEjectButton.Disabled = true;
TankLabelLabel.Text = Loc.GetString("comp-gas-canister-ui-holding-tank-label-empty");
return;
}
TankEjectButton.Disabled = false;
TankLabelLabel.Text = label;
}
public void SetTankPressure(float pressure)
{
TankPressureLabel.Text = Loc.GetString("comp-gas-canister-ui-pressure", ("pressure", Math.Round(pressure)));
}
public void SetReleasePressureRange(float min, float max)
{
ReleasePressureSlider.MinValue = min;
ReleasePressureSlider.MaxValue = max;
}
public void SetReleasePressure(float pressure)
{
if(!ReleasePressureSlider.Grabbed)
ReleasePressureSlider.SetValueWithoutEvent(pressure);
ReleasePressureLabel.Text = Loc.GetString("comp-gas-canister-ui-pressure", ("pressure", Math.Round(pressure)));
}
public void SetReleaseValve(bool valve)
{
if (valve)
ReleaseValveOpenButton.Pressed = true;
else
ReleaseValveCloseButton.Pressed = true;
}
}
}

View File

@@ -1,10 +1,10 @@
using Content.Shared.GameObjects.Components.Atmos; using Content.Shared.Atmos.Visuals;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Client.GameObjects.Components.Atmos namespace Content.Client.Atmos.Visualizers
{ {
[UsedImplicitly] [UsedImplicitly]
public class AtmosPlaqueVisualizer : AppearanceVisualizer public class AtmosPlaqueVisualizer : AppearanceVisualizer

View File

@@ -1,10 +1,10 @@
using Content.Shared.GameObjects.Components.Atmos; using Content.Shared.Atmos.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Client.GameObjects.Components.Atmos namespace Content.Client.Atmos.Visualizers
{ {
[UsedImplicitly] [UsedImplicitly]
public class FireVisualizer : AppearanceVisualizer public class FireVisualizer : AppearanceVisualizer

View File

@@ -1,9 +1,9 @@
using Content.Shared.GameObjects.Components.Atmos; using Content.Shared.Atmos.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Client.GameObjects.Components.Atmos namespace Content.Client.Atmos.Visualizers
{ {
[UsedImplicitly] [UsedImplicitly]
public class GasAnalyzerVisualizer : AppearanceVisualizer public class GasAnalyzerVisualizer : AppearanceVisualizer

View File

@@ -0,0 +1,54 @@
using Content.Shared.Atmos.Piping.Binary.Components;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Client.Atmos.Visualizers
{
[UsedImplicitly]
public class GasCanisterVisualizer : AppearanceVisualizer
{
[DataField("pressureStates")]
private readonly string[] _statePressure = {"", "", "", ""};
[DataField("insertedTankState")]
private readonly string _insertedTankState = string.Empty;
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
var sprite = entity.GetComponent<ISpriteComponent>();
sprite.LayerMapSet(Layers.PressureLight, sprite.AddLayerState(_statePressure[0]));
sprite.LayerSetShader(Layers.PressureLight, "unshaded");
sprite.LayerMapSet(Layers.TankInserted, sprite.AddLayerState(_insertedTankState));
sprite.LayerSetVisible(Layers.TankInserted, false);
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
if (!component.Owner.TryGetComponent(out ISpriteComponent? sprite))
{
return;
}
// Update the canister lights
if (component.TryGetData(GasCanisterVisuals.PressureState, out int pressureState))
if ((pressureState >= 0) && (pressureState < _statePressure.Length))
sprite.LayerSetState(Layers.PressureLight, _statePressure[pressureState]);
if(component.TryGetData(GasCanisterVisuals.TankInserted, out bool inserted))
sprite.LayerSetVisible(Layers.TankInserted, inserted);
}
private enum Layers
{
PressureLight,
TankInserted,
}
}
}

View File

@@ -1,18 +1,17 @@
using Content.Shared.GameObjects.Components.Atmos; using Content.Shared.Atmos.Piping.Unary.Components;
using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Client.GameObjects.Components.Atmos namespace Content.Client.Atmos.Visualizers
{ {
public class GasCanisterVisualizer : AppearanceVisualizer [UsedImplicitly]
public class GasPortableVisualizer : AppearanceVisualizer
{ {
[DataField("stateConnected")] [DataField("stateConnected")]
private string? _stateConnected; private string? _stateConnected;
[DataField("pressureStates")]
private string[] _statePressure = new string[] {"", "", "", ""};
public override void InitializeEntity(IEntity entity) public override void InitializeEntity(IEntity entity)
{ {
base.InitializeEntity(entity); base.InitializeEntity(entity);
@@ -23,9 +22,6 @@ namespace Content.Client.GameObjects.Components.Atmos
{ {
sprite.LayerMapSet(Layers.ConnectedToPort, sprite.AddLayerState(_stateConnected)); sprite.LayerMapSet(Layers.ConnectedToPort, sprite.AddLayerState(_stateConnected));
sprite.LayerSetVisible(Layers.ConnectedToPort, false); sprite.LayerSetVisible(Layers.ConnectedToPort, false);
sprite.LayerMapSet(Layers.PressureLight, sprite.AddLayerState(_stateConnected));
sprite.LayerSetShader(Layers.PressureLight, "unshaded");
} }
} }
@@ -44,21 +40,15 @@ namespace Content.Client.GameObjects.Components.Atmos
} }
// Update the visuals : Is the canister connected to a port or not // Update the visuals : Is the canister connected to a port or not
if (component.TryGetData(GasCanisterVisuals.ConnectedState, out bool isConnected)) if (component.TryGetData(GasPortableVisuals.ConnectedState, out bool isConnected))
{ {
sprite.LayerSetVisible(Layers.ConnectedToPort, isConnected); sprite.LayerSetVisible(Layers.ConnectedToPort, isConnected);
} }
// Update the visuals : Canister lights
if (component.TryGetData(GasCanisterVisuals.PressureState, out int pressureState))
if ((pressureState >= 0) && (pressureState < _statePressure.Length))
sprite.LayerSetState(Layers.PressureLight, _statePressure[pressureState]);
} }
enum Layers private enum Layers
{ {
ConnectedToPort, ConnectedToPort,
PressureLight
} }
} }
} }

View File

@@ -1,5 +1,4 @@
using System; using System;
using Content.Shared.GameObjects.Components;
using Content.Shared.Vapor; using Content.Shared.Vapor;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.Animations; using Robust.Client.Animations;
@@ -8,7 +7,7 @@ using Robust.Shared.Maths;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Client.GameObjects.Components.Atmos namespace Content.Client.Chemistry.Visualizers
{ {
[UsedImplicitly] [UsedImplicitly]
public class VaporVisualizer : AppearanceVisualizer, ISerializationHooks public class VaporVisualizer : AppearanceVisualizer, ISerializationHooks

View File

@@ -1,7 +1,7 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using Content.Client.GameObjects.EntitySystems;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using System; using System;
using Content.Client.Atmos.EntitySystems;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;

View File

@@ -1,12 +1,6 @@
// ReSharper disable once RedundantUsingDirective
// Used to warn the player in big red letters in debug mode
using System; using System;
using Content.Client.GameObjects.Components;
using Content.Client.GameObjects.EntitySystems;
using Content.Client.Markers; using Content.Client.Markers;
using Content.Client.Notifications.Managers; using Content.Client.Notifications.Managers;
using Content.Shared.GameObjects.Components;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.SubFloor; using Content.Shared.SubFloor;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.Console; using Robust.Shared.Console;

View File

@@ -1,5 +1,4 @@
#nullable enable #nullable enable
using Content.Client.GameObjects.EntitySystems;
using Content.Shared; using Content.Shared;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using Content.Client.GameObjects.EntitySystems;
using Content.Shared.Construction; using Content.Shared.Construction;
using Content.Shared.Construction.Prototypes; using Content.Shared.Construction.Prototypes;
using Robust.Client.Graphics; using Robust.Client.Graphics;

View File

@@ -143,6 +143,16 @@ namespace Content.Client.Entry
"Barotrauma", "Barotrauma",
"GasSprayer", "GasSprayer",
"GasVapor", "GasVapor",
"GasVentPump",
"GasPassiveVent",
"GasVentScrubber",
"GasOutletInjector",
"GasMiner",
"GasPressurePump",
"GasVolumePump",
"GasPassiveGate",
"GasValve",
"GasThermoMachine",
"Metabolism", "Metabolism",
"AiFactionTag", "AiFactionTag",
"PressureProtection", "PressureProtection",
@@ -154,7 +164,7 @@ namespace Content.Client.Entry
"VolumePump", "VolumePump",
"PressureSiphon", "PressureSiphon",
"PipeHeater", "PipeHeater",
"PipeNetDevice", "AtmosDevice",
"SignalReceiver", "SignalReceiver",
"SignalSwitch", "SignalSwitch",
"SignalTransmitter", "SignalTransmitter",
@@ -179,6 +189,10 @@ namespace Content.Client.Entry
"ComputerBoard", "ComputerBoard",
"GasCanister", "GasCanister",
"GasCanisterPort", "GasCanisterPort",
"GasPort",
"GasPortable",
"AtmosUnsafeUnanchor",
"GasMixer",
"Cleanable", "Cleanable",
"Configuration", "Configuration",
"PlantHolder", "PlantHolder",

View File

@@ -1,118 +0,0 @@
using Content.Shared.GameObjects.Components.Atmos;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
namespace Content.Client.GameObjects.Components.Atmos
{
/// <summary>
/// Initializes a <see cref="GasCanisterWindow"/> and updates it when new server messages are received.
/// </summary>
[UsedImplicitly]
public class GasCanisterBoundUserInterface : BoundUserInterface
{
private GasCanisterWindow? _window;
public GasCanisterBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
}
/// <summary>
/// When a button is pressed, send a network message to the server
/// </summary>
/// <param name="button">Which button has been pressed, as an enum item</param>
private void ButtonPressed(UiButton button)
{
SendMessage(new UiButtonPressedMessage(button));
}
/// <summary>
/// When the release pressure is changed
/// </summary>
/// <param name="value">The pressure value</param>
private void ReleasePressureButtonPressed(float value)
{
SendMessage(new ReleasePressureButtonPressedMessage(value));
}
protected override void Open()
{
base.Open();
_window = new GasCanisterWindow();
_window.Title = Loc.GetString("Gas Canister");
_window.OpenCentered();
_window.OnClose += Close;
// Bind buttons OnPressed event
foreach (ReleasePressureButton btn in _window.ReleasePressureButtons)
{
btn.OnPressed += _ => ReleasePressureButtonPressed(btn.PressureChange);
}
// Bind events
_window.EditLabelBtn.OnPressed += _ => EditLabel();
_window.ToggleValve.OnPressed += _ => ToggleValve();
}
/// <summary>
/// Called when the edit label button is pressed
/// </summary>
private void EditLabel()
{
// Obligatory check because bool isn't nullable
if (_window == null) return;
if (_window.LabelInputEditable)
{
if (_window.LabelInput.Text != _window.OldLabel)
SendMessage(new CanisterLabelChangedMessage(_window.LabelInput.Text));
_window.LabelInputEditable = false;
}
else
{
_window.LabelInputEditable = true;
_window.LabelInput.HasKeyboardFocus();
}
}
private void ToggleValve()
{
SendMessage(new UiButtonPressedMessage(UiButton.ValveToggle));
}
/// <summary>
/// Update the UI state based on server-sent info
/// </summary>
/// <param name="state"></param>
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (!(state is GasCanisterBoundUserInterfaceState cast))
{
return;
}
_window?.UpdateState(cast);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
_window?.Dispose();
}
}
}

View File

@@ -1,167 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Content.Shared.GameObjects.Components.Atmos;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.Components.Atmos
{
/// <summary>
/// Client-side UI used to control a <see cref="SharedGasCanisterComponent"/>
/// </summary>
public class GasCanisterWindow : SS14Window
{
private readonly Label _pressure;
private readonly Label _releasePressure;
public readonly CheckButton ToggleValve;
public readonly LineEdit LabelInput;
public readonly Button EditLabelBtn;
public string OldLabel { get; set; } = "";
public bool LabelInputEditable {
get => LabelInput.Editable;
set {
LabelInput.Editable = value;
EditLabelBtn.Text = value ? Loc.GetString("OK") : Loc.GetString("Edit");
}
}
public List<ReleasePressureButton> ReleasePressureButtons { get; private set; }
public GasCanisterWindow()
{
SetSize = MinSize = (450, 200);
HBoxContainer releasePressureButtons;
Contents.AddChild(new VBoxContainer
{
Children =
{
new VBoxContainer
{
Children =
{
new HBoxContainer()
{
Children =
{
new Label(){ Text = Loc.GetString("Label: ") },
(LabelInput = new LineEdit() { Text = Name ?? "", Editable = false,
MinSize = new Vector2(200, 30)}),
(EditLabelBtn = new Button()),
}
},
new HBoxContainer
{
Children =
{
new Label {Text = Loc.GetString("Pressure: ")},
(_pressure = new Label())
}
},
new VBoxContainer()
{
Children =
{
new HBoxContainer()
{
Children =
{
new Label() {Text = Loc.GetString("Release pressure: ")},
(_releasePressure = new Label())
}
},
(releasePressureButtons = new HBoxContainer()
{
Children =
{
new ReleasePressureButton() {PressureChange = -50},
new ReleasePressureButton() {PressureChange = -10},
new ReleasePressureButton() {PressureChange = -1},
new ReleasePressureButton() {PressureChange = -0.1f},
new ReleasePressureButton() {PressureChange = 0.1f},
new ReleasePressureButton() {PressureChange = 1},
new ReleasePressureButton() {PressureChange = 10},
new ReleasePressureButton() {PressureChange = 50}
}
})
}
},
new HBoxContainer()
{
Children =
{
new Label { Text = Loc.GetString("Valve: ") },
(ToggleValve = new CheckButton() { Text = Loc.GetString("Closed") })
}
}
},
}
}
});
// Create the release pressure buttons list
ReleasePressureButtons = new List<ReleasePressureButton>();
foreach (var control in releasePressureButtons.Children.ToList())
{
var btn = (ReleasePressureButton) control;
ReleasePressureButtons.Add(btn);
}
// Reset the editable label
LabelInputEditable = false;
}
/// <summary>
/// Update the UI based on <see cref="GasCanisterBoundUserInterfaceState"/>
/// </summary>
/// <param name="state">The state the UI should reflect</param>
public void UpdateState(GasCanisterBoundUserInterfaceState state)
{
_pressure.Text = Loc.GetString("{0}kPa", state.Volume);
_releasePressure.Text = Loc.GetString("{0}kPa", state.ReleasePressure);
// Update the canister label
OldLabel = LabelInput.Text;
LabelInput.Text = state.Label;
Title = state.Label;
// Reset the editable label
LabelInputEditable = false;
ToggleValve.Pressed = state.ValveOpened;
if (ToggleValve.Pressed)
{
ToggleValve.Text = Loc.GetString("Open");
}
else
{
ToggleValve.Text = Loc.GetString("Closed");
}
}
}
/// <summary>
/// Special button class which stores a numerical value and has it as a label
/// </summary>
public class ReleasePressureButton : Button
{
public float PressureChange
{
get { return _pressureChange; }
set
{
_pressureChange = value;
Text = (value >= 0) ? ("+" + value) : value.ToString();
}
}
private float _pressureChange;
public ReleasePressureButton() : base() {}
}
}

View File

@@ -1,42 +0,0 @@
using Content.Shared.GameObjects.Components.Atmos;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Client.GameObjects.Components.Atmos.Piping
{
[UsedImplicitly]
[DataDefinition]
public class GasFilterVisualizer : AppearanceVisualizer
{
[DataField("filterEnabledState")] private string _filterEnabledState = "gasFilterOn";
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
if (!entity.TryGetComponent<ISpriteComponent>(out var sprite)) return;
sprite.LayerMapReserveBlank(Layer.FilterEnabled);
var filterEnabledLayer = sprite.LayerMapGet(Layer.FilterEnabled);
sprite.LayerSetState(filterEnabledLayer, _filterEnabledState);
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
if (!component.Owner.TryGetComponent<ISpriteComponent>(out var sprite)) return;
if (!component.TryGetData(FilterVisuals.VisualState, out FilterVisualState filterVisualState)) return;
var filterEnabledLayer = sprite.LayerMapGet(Layer.FilterEnabled);
sprite.LayerSetVisible(filterEnabledLayer, filterVisualState.Enabled);
}
public enum Layer : byte
{
FilterEnabled,
}
}
}

View File

@@ -1,72 +0,0 @@
#nullable enable
using System;
using Content.Shared.GameObjects.Components.Atmos;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Client.GameObjects.Components.Atmos.Piping
{
/// <summary>
/// Sets the state of the sprite based on what shape of pipe it is.
/// </summary>
[UsedImplicitly]
[DataDefinition]
public class PipeVisualizer : AppearanceVisualizer, ISerializationHooks
{
[DataField("rsi")] private string _rsiString = "Constructible/Atmos/pipe.rsi";
private RSI? _pipeRSI;
void ISerializationHooks.AfterDeserialization()
{
var rsiPath = SharedSpriteComponent.TextureRoot / _rsiString;
var resourceCache = IoCManager.Resolve<IResourceCache>();
if (resourceCache.TryGetResource(rsiPath, out RSIResource? rsi))
{
_pipeRSI = rsi.RSI;
}
}
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
if (!entity.TryGetComponent<ISpriteComponent>(out var sprite)) return;
sprite.LayerMapReserveBlank(Layer.PipeBase);
var pipeBaseLayer = sprite.LayerMapGet(Layer.PipeBase);
if (_pipeRSI != null)
sprite.LayerSetRSI(pipeBaseLayer, _pipeRSI);
sprite.LayerSetVisible(pipeBaseLayer, true);
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
if (!component.Owner.TryGetComponent<ISpriteComponent>(out var sprite)) return;
if (!component.TryGetData(PipeVisuals.VisualState, out PipeVisualState pipeVisualState)) return;
var pipeBase = sprite.LayerMapGet(Layer.PipeBase);
var pipeBaseStateId = GetPipeBaseStateId(pipeVisualState);
sprite.LayerSetState(pipeBase, pipeBaseStateId);
}
private string GetPipeBaseStateId(PipeVisualState pipeVisualState)
{
var stateId = "pipe";
stateId += pipeVisualState.PipeShape.ToString();
return stateId;
}
private enum Layer : byte
{
PipeBase,
}
}
}

View File

@@ -1,42 +0,0 @@
using Content.Shared.GameObjects.Components.Atmos;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Client.GameObjects.Components.Atmos.Piping
{
[UsedImplicitly]
[DataDefinition]
public class PumpVisualizer : AppearanceVisualizer
{
[DataField("pumpEnabledState")] private string _pumpEnabledState = "pumpPressureOn";
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
if (!entity.TryGetComponent(out ISpriteComponent? sprite)) return;
sprite.LayerMapReserveBlank(Layer.PumpEnabled);
var pumpEnabledLayer = sprite.LayerMapGet(Layer.PumpEnabled);
sprite.LayerSetState(pumpEnabledLayer, _pumpEnabledState);
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
if (!component.Owner.TryGetComponent(out ISpriteComponent? sprite)) return;
if (!component.TryGetData(PumpVisuals.VisualState, out PumpVisualState pumpVisualState)) return;
var pumpEnabledLayer = sprite.LayerMapGet(Layer.PumpEnabled);
sprite.LayerSetVisible(pumpEnabledLayer, pumpVisualState.PumpEnabled);
}
public enum Layer : byte
{
PumpEnabled,
}
}
}

View File

@@ -1,42 +0,0 @@
using Content.Shared.GameObjects.Components.Atmos;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Client.GameObjects.Components.Atmos.Piping
{
[UsedImplicitly]
[DataDefinition]
public class SiphonVisualizer : AppearanceVisualizer
{
[DataField("siphonOnState")] private string _siphonOnState = "scrubOn";
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
if (!entity.TryGetComponent(out ISpriteComponent? sprite)) return;
sprite.LayerMapReserveBlank(Layer.SiphonEnabled);
var layer = sprite.LayerMapGet(Layer.SiphonEnabled);
sprite.LayerSetState(layer, _siphonOnState);
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
if (!component.Owner.TryGetComponent(out ISpriteComponent? sprite)) return;
if (!component.TryGetData(SiphonVisuals.VisualState, out SiphonVisualState siphonVisualState)) return;
var layer = sprite.LayerMapGet(Layer.SiphonEnabled);
sprite.LayerSetVisible(layer, siphonVisualState.SiphonEnabled);
}
private enum Layer : byte
{
SiphonEnabled,
}
}
}

View File

@@ -1,42 +0,0 @@
using Content.Shared.GameObjects.Components.Atmos;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Client.GameObjects.Components.Atmos.Piping
{
[UsedImplicitly]
[DataDefinition]
public class VentVisualizer : AppearanceVisualizer
{
[DataField("ventOnState")] private string _ventOnState = "ventOn";
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
if (!entity.TryGetComponent(out ISpriteComponent? sprite)) return;
sprite.LayerMapReserveBlank(Layer.VentEnabled);
var layer = sprite.LayerMapGet(Layer.VentEnabled);
sprite.LayerSetState(layer, _ventOnState);
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
if (!component.Owner.TryGetComponent(out ISpriteComponent? sprite)) return;
if (!component.TryGetData(VentVisuals.VisualState, out VentVisualState ventVisualState)) return;
var layer = sprite.LayerMapGet(Layer.VentEnabled);
sprite.LayerSetVisible(layer, ventVisualState.VentEnabled);
}
private enum Layer : byte
{
VentEnabled,
}
}
}

View File

@@ -1,6 +1,5 @@
#nullable enable #nullable enable
using System.Collections.Generic; using System.Collections.Generic;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Ghost; using Content.Shared.Ghost;
using Robust.Client.Console; using Robust.Client.Console;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;

View File

@@ -1,9 +1,6 @@
using System; using System;
using Content.Client.GameObjects.EntitySystems;
using Content.Client.HUD; using Content.Client.HUD;
using Content.Client.Markers; using Content.Client.Markers;
using Content.Client.UserInterface;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Input; using Content.Shared.Input;
using Content.Shared.Sandbox; using Content.Shared.Sandbox;
using Content.Shared.SubFloor; using Content.Shared.SubFloor;

View File

@@ -1,4 +1,4 @@
using Content.Shared.GameObjects.Components.Atmos.GasTank; using Content.Shared.Atmos.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;

View File

@@ -1,7 +1,7 @@
using Content.Client.Message; using Content.Client.Message;
using Content.Client.Resources; using Content.Client.Resources;
using Content.Client.Stylesheets; using Content.Client.Stylesheets;
using Content.Shared.GameObjects.Components.Atmos.GasTank; using Content.Shared.Atmos.Components;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.ResourceManagement; using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.GameObjects.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using NUnit.Framework; using NUnit.Framework;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;

View File

@@ -1,7 +1,6 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Client.Clickable; using Content.Client.Clickable;
using Content.Client.GameObjects.Components;
using NUnit.Framework; using NUnit.Framework;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;

View File

@@ -1,11 +1,9 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Gravity; using Content.Server.Gravity;
using Content.Server.Gravity.EntitySystems; using Content.Server.Gravity.EntitySystems;
using Content.Shared.Acts; using Content.Shared.Acts;
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.Coordinates; using Content.Shared.Coordinates;
using Content.Shared.GameObjects.EntitySystems;
using NUnit.Framework; using NUnit.Framework;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;

View File

@@ -1,6 +1,5 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Client.Interactable; using Content.Client.Interactable;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Interaction.Helpers; using Content.Shared.Interaction.Helpers;
using NUnit.Framework; using NUnit.Framework;

View File

@@ -3,7 +3,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Shared.GameObjects;
using Content.Shared.NetIDs; using Content.Shared.NetIDs;
using NUnit.Framework; using NUnit.Framework;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;

View File

@@ -2,7 +2,6 @@ using Content.Server.AI.Pathfinding.Accessible;
using Content.Server.AI.WorldState; using Content.Server.AI.WorldState;
using Content.Server.AI.WorldState.States; using Content.Server.AI.WorldState.States;
using Content.Server.Storage.Components; using Content.Server.Storage.Components;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;

View File

@@ -1,4 +1,4 @@
using Content.Server.GameObjects.Components.Atmos; using Content.Server.Atmos.Components;
using Content.Shared.Alert; using Content.Shared.Alert;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;

View File

@@ -1,5 +1,4 @@
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Pulling; using Content.Shared.Pulling;
using Content.Shared.Pulling.Components; using Content.Shared.Pulling.Components;
using JetBrains.Annotations; using JetBrains.Annotations;

View File

@@ -1,6 +1,6 @@
#nullable enable #nullable enable
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Content.Server.GameObjects.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;

View File

@@ -1,16 +1,15 @@
#nullable enable #nullable enable
using Content.Server.GameObjects.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Atmos namespace Content.Server.Atmos.Components
{ {
[RegisterComponent] [RegisterComponent]
public class AirtightComponent : Component, IMapInit public class AirtightComponent : Component, IMapInit
@@ -96,6 +95,7 @@ namespace Content.Server.GameObjects.Components.Atmos
return; return;
_currentAirBlockedDirection = (int) Rotate((AtmosDirection)_initialAirBlockedDirection, ev.NewRotation); _currentAirBlockedDirection = (int) Rotate((AtmosDirection)_initialAirBlockedDirection, ev.NewRotation);
UpdatePosition();
} }
private AtmosDirection Rotate(AtmosDirection myDirection, Angle myAngle) private AtmosDirection Rotate(AtmosDirection myDirection, Angle myAngle)
@@ -118,26 +118,18 @@ namespace Content.Server.GameObjects.Components.Atmos
return newAirBlockedDirs; return newAirBlockedDirs;
} }
/// <inheritdoc />
public void MapInit() public void MapInit()
{ {
if (Owner.Transform.Anchored)
{
var grid = _mapManager.GetGrid(Owner.Transform.GridID);
_lastPosition = (Owner.Transform.GridID, grid.TileIndicesFor(Owner.Transform.Coordinates));
}
UpdatePosition(); UpdatePosition();
} }
/// <inheritdoc />
protected override void Shutdown() protected override void Shutdown()
{ {
base.Shutdown(); base.Shutdown();
_airBlocked = false; _airBlocked = false;
UpdatePosition(_lastPosition.Item1, _lastPosition.Item2); InvalidatePosition(_lastPosition.Item1, _lastPosition.Item2);
if (_fixVacuum) if (_fixVacuum)
{ {
@@ -145,31 +137,31 @@ namespace Content.Server.GameObjects.Components.Atmos
} }
} }
public void OnTransformMove() public void OnSnapGridMove(SnapGridPositionChangedEvent ev)
{ {
UpdatePosition(_lastPosition.Item1, _lastPosition.Item2); // Invalidate old position.
UpdatePosition(); InvalidatePosition(ev.OldGrid, ev.OldPosition);
if (Owner.Transform.Anchored) // Update and invalidate new position.
{ _lastPosition = (ev.NewGrid, ev.Position);
var grid = _mapManager.GetGrid(Owner.Transform.GridID); InvalidatePosition(ev.NewGrid, ev.Position);
_lastPosition = (Owner.Transform.GridID, grid.TileIndicesFor(Owner.Transform.Coordinates));
}
} }
private void UpdatePosition() private void UpdatePosition()
{ {
if (Owner.Transform.Anchored) if (!Owner.Transform.Anchored || !Owner.Transform.GridID.IsValid())
{
if (!Owner.Transform.GridID.IsValid())
return; return;
var grid = _mapManager.GetGrid(Owner.Transform.GridID); var grid = _mapManager.GetGrid(Owner.Transform.GridID);
UpdatePosition(Owner.Transform.GridID, grid.TileIndicesFor(Owner.Transform.Coordinates)); _lastPosition = (Owner.Transform.GridID, grid.TileIndicesFor(Owner.Transform.Coordinates));
} InvalidatePosition(_lastPosition.Item1, _lastPosition.Item2);
} }
private void UpdatePosition(GridId gridId, Vector2i pos) private void InvalidatePosition(GridId gridId, Vector2i pos)
{ {
if (!gridId.IsValid())
return;
var gridAtmos = _atmosphereSystem.GetGridAtmosphere(gridId); var gridAtmos = _atmosphereSystem.GetGridAtmosphere(gridId);
gridAtmos?.UpdateAdjacentBits(pos); gridAtmos?.UpdateAdjacentBits(pos);

View File

@@ -1,10 +1,9 @@
#nullable enable #nullable enable
using Content.Server.Atmos;
using Content.Server.Temperature.Components; using Content.Server.Temperature.Components;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Atmos namespace Content.Server.Atmos.Components
{ {
/// <summary> /// <summary>
/// Represents that entity can be exposed to Atmos /// Represents that entity can be exposed to Atmos

View File

@@ -1,4 +1,4 @@
using Content.Shared.GameObjects.Components.Atmos; using Content.Shared.Atmos.Visuals;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
@@ -6,7 +6,7 @@ using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components namespace Content.Server.Atmos.Components
{ {
[RegisterComponent] [RegisterComponent]
public sealed class AtmosPlaqueComponent : Component, IMapInit public sealed class AtmosPlaqueComponent : Component, IMapInit

View File

@@ -8,7 +8,7 @@ using Content.Shared.Damage;
using Content.Shared.Damage.Components; using Content.Shared.Damage.Components;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
namespace Content.Server.GameObjects.Components.Atmos namespace Content.Server.Atmos.Components
{ {
/// <summary> /// <summary>
/// Barotrauma: injury because of changes in air pressure. /// Barotrauma: injury because of changes in air pressure.

View File

@@ -2,7 +2,6 @@ using Content.Server.Power.Components;
using Content.Server.UserInterface; using Content.Server.UserInterface;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Notification.Managers; using Content.Shared.Notification.Managers;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;

View File

@@ -4,7 +4,7 @@ using Content.Shared.Inventory;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Server.GameObjects.Components.Atmos namespace Content.Server.Atmos.Components
{ {
/// <summary> /// <summary>
/// Used in internals as breath tool. /// Used in internals as breath tool.

View File

@@ -1,16 +1,14 @@
#nullable enable #nullable enable
using Robust.Shared.GameObjects; using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos;
using Content.Server.Doors; using Content.Server.Doors;
using Content.Server.Doors.Components; using Content.Server.Doors.Components;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Doors; using Content.Shared.Doors;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Notification;
using Content.Shared.Notification.Managers; using Content.Shared.Notification.Managers;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization; using Robust.Shared.Localization;
namespace Content.Server.GameObjects.Components.Atmos namespace Content.Server.Atmos.Components
{ {
/// <summary> /// <summary>
/// Companion component to ServerDoorComponent that handles firelock-specific behavior -- primarily prying, and not being openable on open-hand click. /// Companion component to ServerDoorComponent that handles firelock-specific behavior -- primarily prying, and not being openable on open-hand click.

View File

@@ -2,17 +2,15 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.Alert; using Content.Server.Alert;
using Content.Server.Atmos;
using Content.Server.Stunnable.Components; using Content.Server.Stunnable.Components;
using Content.Server.Temperature.Components; using Content.Server.Temperature.Components;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Damage.Components; using Content.Shared.Damage.Components;
using Content.Shared.GameObjects.Components.Atmos;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Notification.Managers; using Content.Shared.Notification.Managers;
using Content.Shared.Temperature; using Content.Shared.Temperature;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
@@ -24,7 +22,7 @@ using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Atmos namespace Content.Server.Atmos.Components
{ {
[RegisterComponent] [RegisterComponent]
public class FlammableComponent : SharedFlammableComponent, IStartCollide, IFireAct, IInteractUsing public class FlammableComponent : SharedFlammableComponent, IStartCollide, IFireAct, IInteractUsing

View File

@@ -1,16 +1,13 @@
#nullable enable #nullable enable
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.GameObjects.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Hands.Components; using Content.Server.Hands.Components;
using Content.Server.UserInterface; using Content.Server.UserInterface;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.DragDrop; using Content.Shared.DragDrop;
using Content.Shared.GameObjects.Components;
using Content.Shared.GameObjects.Components.Atmos;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Notification;
using Content.Shared.Notification.Managers; using Content.Shared.Notification.Managers;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Player; using Robust.Server.Player;
@@ -20,7 +17,7 @@ using Robust.Shared.Map;
using Robust.Shared.Players; using Robust.Shared.Players;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Atmos namespace Content.Server.Atmos.Components
{ {
[RegisterComponent] [RegisterComponent]
public class GasAnalyzerComponent : SharedGasAnalyzerComponent, IAfterInteract, IDropped, IUse public class GasAnalyzerComponent : SharedGasAnalyzerComponent, IAfterInteract, IDropped, IUse

View File

@@ -1,12 +1,9 @@
using Content.Server.Atmos; using Content.Server.Interfaces;
using Content.Server.Interfaces;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Atmos namespace Content.Server.Atmos.Components
{ {
[RegisterComponent] [RegisterComponent]
public class GasMixtureHolderComponent : Component, IGasMixtureHolder public class GasMixtureHolderComponent : Component, IGasMixtureHolder

View File

@@ -1,22 +1,22 @@
#nullable enable #nullable enable
#nullable disable warnings #nullable disable warnings
using System; using System;
using Content.Server.Atmos;
using Content.Server.Body.Respiratory; using Content.Server.Body.Respiratory;
using Content.Server.Explosion; using Content.Server.Explosion;
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Server.NodeContainer;
using Content.Server.UserInterface; using Content.Server.UserInterface;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.Actions; using Content.Shared.Actions;
using Content.Shared.Actions.Behaviors.Item; using Content.Shared.Actions.Behaviors.Item;
using Content.Shared.Actions.Components; using Content.Shared.Actions.Components;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Audio; using Content.Shared.Audio;
using Content.Shared.DragDrop; using Content.Shared.DragDrop;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.GameObjects.Components.Atmos.GasTank;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
@@ -30,12 +30,14 @@ using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Atmos namespace Content.Server.Atmos.Components
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(IActivate))] [ComponentReference(typeof(IActivate))]
public class GasTankComponent : SharedGasTankComponent, IExamine, IGasMixtureHolder, IUse, IDropped, IActivate public class GasTankComponent : Component, IExamine, IGasMixtureHolder, IUse, IDropped, IActivate
{ {
public override string Name => "GasTank";
private const float MaxExplosionRange = 14f; private const float MaxExplosionRange = 14f;
private const float DefaultOutputPressure = Atmospherics.OneAtmosphere; private const float DefaultOutputPressure = Atmospherics.OneAtmosphere;
@@ -45,7 +47,35 @@ namespace Content.Server.GameObjects.Components.Atmos
[ViewVariables] private BoundUserInterface? _userInterface; [ViewVariables] private BoundUserInterface? _userInterface;
[DataField("air")] [ViewVariables] public GasMixture? Air { get; set; } = new(); [ViewVariables]
public GasMixture Air
{
// TODO ATMOS Kill it with fire.
get
{
if (!Owner.TryGetComponent(out NodeContainerComponent nodeContainer))
throw new InvalidOperationException("Can't get tank air without a node container!");
if (!nodeContainer.TryGetNode(TankName, out PipeNode? node))
throw new InvalidOperationException($"Node container doesn't have a pipenode called {TankName}!");
return node.Air;
}
set
{
// This will throw if the node container is not found.
var nodeContainer = Owner.GetComponent<NodeContainerComponent>();
if (!nodeContainer.TryGetNode(TankName, out PipeNode? node))
throw new InvalidOperationException($"Node container doesn't have a pipenode called {TankName}!");
node.Air = value;
}
}
[DataField("air")] [ViewVariables]
public GasMixture InitialMixture { get; set; } = new();
/// <summary> /// <summary>
/// Distributed pressure. /// Distributed pressure.
@@ -88,6 +118,12 @@ namespace Content.Server.GameObjects.Components.Atmos
[DataField("tankFragmentScale")] [DataField("tankFragmentScale")]
public float TankFragmentScale { get; set; } = 10 * Atmospherics.OneAtmosphere; public float TankFragmentScale { get; set; } = 10 * Atmospherics.OneAtmosphere;
/// <summary>
/// NodeContainer node.
/// </summary>
[DataField("tank")]
public string TankName { get; set; } = "tank";
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();

View File

@@ -5,10 +5,8 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Content.Server.Atmos; using Content.Server.Atmos.EntitySystems;
using Content.Server.GameObjects.Components.Atmos.Piping; using Content.Server.Atmos.Piping.Components;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.GameObjects.EntitySystems.Atmos;
using Content.Server.NodeContainer.NodeGroups; using Content.Server.NodeContainer.NodeGroups;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Maps; using Content.Shared.Maps;
@@ -24,7 +22,7 @@ using Robust.Shared.Timing;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
using Dependency = Robust.Shared.IoC.DependencyAttribute; using Dependency = Robust.Shared.IoC.DependencyAttribute;
namespace Content.Server.GameObjects.Components.Atmos namespace Content.Server.Atmos.Components
{ {
/// <summary> /// <summary>
/// This is our SSAir equivalent. /// This is our SSAir equivalent.
@@ -36,6 +34,7 @@ namespace Content.Server.GameObjects.Components.Atmos
[Dependency] private IMapManager _mapManager = default!; [Dependency] private IMapManager _mapManager = default!;
[Dependency] private ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private ITileDefinitionManager _tileDefinitionManager = default!;
[Dependency] private IServerEntityManager _serverEntityManager = default!; [Dependency] private IServerEntityManager _serverEntityManager = default!;
[Dependency] private IGameTiming _gameTiming = default!;
public GridTileLookupSystem GridTileLookupSystem { get; private set; } = default!; public GridTileLookupSystem GridTileLookupSystem { get; private set; } = default!;
internal GasTileOverlaySystem GasTileOverlaySystem { get; private set; } = default!; internal GasTileOverlaySystem GasTileOverlaySystem { get; private set; } = default!;
@@ -55,6 +54,8 @@ namespace Content.Server.GameObjects.Components.Atmos
[ComponentDependency] private IMapGridComponent? _mapGridComponent; [ComponentDependency] private IMapGridComponent? _mapGridComponent;
public virtual bool Simulated => true;
[ViewVariables] [ViewVariables]
public int UpdateCounter { get; private set; } = 0; public int UpdateCounter { get; private set; } = 0;
@@ -128,10 +129,10 @@ namespace Content.Server.GameObjects.Components.Atmos
private double _pipeNetLastProcess; private double _pipeNetLastProcess;
[ViewVariables] [ViewVariables]
private readonly HashSet<PipeNetDeviceComponent> _pipeNetDevices = new(); private readonly HashSet<AtmosDeviceComponent> _atmosDevices = new();
[ViewVariables] [ViewVariables]
private double _pipeNetDevicesLastProcess; private double _atmosDevicesLastProcess;
[ViewVariables] [ViewVariables]
private Queue<TileAtmosphere> _currentRunTiles = new(); private Queue<TileAtmosphere> _currentRunTiles = new();
@@ -143,7 +144,7 @@ namespace Content.Server.GameObjects.Components.Atmos
private Queue<IPipeNet> _currentRunPipeNet = new(); private Queue<IPipeNet> _currentRunPipeNet = new();
[ViewVariables] [ViewVariables]
private Queue<PipeNetDeviceComponent> _currentRunPipeNetDevice = new(); private Queue<AtmosDeviceComponent> _currentRunAtmosDevices = new();
[ViewVariables] [ViewVariables]
private ProcessState _state = ProcessState.TileEqualize; private ProcessState _state = ProcessState.TileEqualize;
@@ -162,7 +163,7 @@ namespace Content.Server.GameObjects.Components.Atmos
Hotspots, Hotspots,
Superconductivity, Superconductivity,
PipeNet, PipeNet,
PipeNetDevices, AtmosDevices,
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -455,14 +456,14 @@ namespace Content.Server.GameObjects.Components.Atmos
_pipeNets.Remove(pipeNet); _pipeNets.Remove(pipeNet);
} }
public virtual void AddPipeNetDevice(PipeNetDeviceComponent pipeNetDevice) public virtual void AddAtmosDevice(AtmosDeviceComponent atmosDevice)
{ {
_pipeNetDevices.Add(pipeNetDevice); _atmosDevices.Add(atmosDevice);
} }
public virtual void RemovePipeNetDevice(PipeNetDeviceComponent pipeNetDevice) public virtual void RemoveAtmosDevice(AtmosDeviceComponent atmosDevice)
{ {
_pipeNetDevices.Remove(pipeNetDevice); _atmosDevices.Remove(atmosDevice);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -634,10 +635,10 @@ namespace Content.Server.GameObjects.Components.Atmos
} }
_paused = false; _paused = false;
_state = ProcessState.PipeNetDevices; _state = ProcessState.AtmosDevices;
break; break;
case ProcessState.PipeNetDevices: case ProcessState.AtmosDevices:
if (!ProcessPipeNetDevices(_paused, maxProcessTime)) if (!ProcessAtmosDevices(_paused, maxProcessTime))
{ {
_paused = true; _paused = true;
return; return;
@@ -857,30 +858,33 @@ namespace Content.Server.GameObjects.Components.Atmos
return true; return true;
} }
protected virtual bool ProcessPipeNetDevices(bool resumed = false, float lagCheck = 5f) protected virtual bool ProcessAtmosDevices(bool resumed = false, float lagCheck = 5f)
{ {
_stopwatch.Restart(); _stopwatch.Restart();
if(!resumed) if(!resumed)
_currentRunPipeNetDevice = new Queue<PipeNetDeviceComponent>(_pipeNetDevices); _currentRunAtmosDevices = new Queue<AtmosDeviceComponent>(_atmosDevices);
var time = _gameTiming.CurTime;
var updateEvent = new AtmosDeviceUpdateEvent(this);
var number = 0; var number = 0;
while (_currentRunPipeNetDevice.Count > 0) while (_currentRunAtmosDevices.Count > 0)
{ {
var device = _currentRunPipeNetDevice.Dequeue(); var device = _currentRunAtmosDevices.Dequeue();
device.Update(); Owner.EntityManager.EventBus.RaiseLocalEvent(device.Owner.Uid, updateEvent, false);
device.LastProcess = time;
if (number++ < LagCheckIterations) continue; if (number++ < LagCheckIterations) continue;
number = 0; number = 0;
// Process the rest next time. // Process the rest next time.
if (_stopwatch.Elapsed.TotalMilliseconds >= lagCheck) if (_stopwatch.Elapsed.TotalMilliseconds >= lagCheck)
{ {
_pipeNetDevicesLastProcess = _stopwatch.Elapsed.TotalMilliseconds; _atmosDevicesLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
return false; return false;
} }
} }
_pipeNetDevicesLastProcess = _stopwatch.Elapsed.TotalMilliseconds; _atmosDevicesLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
return true; return true;
} }

View File

@@ -1,17 +1,21 @@
#nullable enable #nullable enable
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.GameObjects.Components.Atmos; using Content.Server.Atmos.Piping.Components;
using Content.Server.GameObjects.Components.Atmos.Piping;
using Content.Server.NodeContainer.NodeGroups; using Content.Server.NodeContainer.NodeGroups;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
namespace Content.Server.Atmos namespace Content.Server.Atmos.Components
{ {
public interface IGridAtmosphereComponent : IComponent, IEnumerable<TileAtmosphere> public interface IGridAtmosphereComponent : IComponent, IEnumerable<TileAtmosphere>
{ {
/// <summary>
/// Whether this atmosphere is simulated or not.
/// </summary>
bool Simulated { get; }
/// <summary> /// <summary>
/// Number of times <see cref="Update"/> has been called. /// Number of times <see cref="Update"/> has been called.
/// </summary> /// </summary>
@@ -173,8 +177,8 @@ namespace Content.Server.Atmos
void RemovePipeNet(IPipeNet pipeNet); void RemovePipeNet(IPipeNet pipeNet);
void AddPipeNetDevice(PipeNetDeviceComponent pipeNetDevice); void AddAtmosDevice(AtmosDeviceComponent atmosDevice);
void RemovePipeNetDevice(PipeNetDeviceComponent pipeNetDevice); void RemoveAtmosDevice(AtmosDeviceComponent atmosDevice);
} }
} }

View File

@@ -13,7 +13,7 @@ using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Atmos namespace Content.Server.Atmos.Components
{ {
[RegisterComponent] [RegisterComponent]
public class MovedByPressureComponent : Component public class MovedByPressureComponent : Component

View File

@@ -1,11 +1,9 @@
using Content.Server.Pressure; using Content.Server.Pressure;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Atmos namespace Content.Server.Atmos.Components
{ {
[RegisterComponent] [RegisterComponent]
public class PressureProtectionComponent : Component, IPressureProtection public class PressureProtectionComponent : Component, IPressureProtection

View File

@@ -1,12 +1,11 @@
#nullable enable #nullable enable
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Server.Atmos;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
namespace Content.Server.GameObjects.Components.Atmos namespace Content.Server.Atmos.Components
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(IGridAtmosphereComponent))] [ComponentReference(typeof(IGridAtmosphereComponent))]

View File

@@ -1,13 +1,12 @@
#nullable enable #nullable enable
using System; using System;
using Content.Server.Atmos; using Content.Server.Atmos.Piping.Components;
using Content.Server.GameObjects.Components.Atmos.Piping;
using Content.Server.NodeContainer.NodeGroups; using Content.Server.NodeContainer.NodeGroups;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Maths; using Robust.Shared.Maths;
namespace Content.Server.GameObjects.Components.Atmos namespace Content.Server.Atmos.Components
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(IGridAtmosphereComponent))] [ComponentReference(typeof(IGridAtmosphereComponent))]
@@ -17,6 +16,8 @@ namespace Content.Server.GameObjects.Components.Atmos
{ {
public override string Name => "UnsimulatedGridAtmosphere"; public override string Name => "UnsimulatedGridAtmosphere";
public override bool Simulated => false;
public override void PryTile(Vector2i indices) { } public override void PryTile(Vector2i indices) { }
public override void RepopulateTiles() public override void RepopulateTiles()
@@ -63,9 +64,9 @@ namespace Content.Server.GameObjects.Components.Atmos
public override void RemovePipeNet(IPipeNet pipeNet) { } public override void RemovePipeNet(IPipeNet pipeNet) { }
public override void AddPipeNetDevice(PipeNetDeviceComponent pipeNetDevice) { } public override void AddAtmosDevice(AtmosDeviceComponent atmosDevice) { }
public override void RemovePipeNetDevice(PipeNetDeviceComponent pipeNetDevice) { } public override void RemoveAtmosDevice(AtmosDeviceComponent atmosDevice) { }
public override void Update(float frameTime) { } public override void Update(float frameTime) { }
@@ -104,7 +105,7 @@ namespace Content.Server.GameObjects.Components.Atmos
return false; return false;
} }
protected override bool ProcessPipeNetDevices(bool resumed = false, float lagCheck = 5f) protected override bool ProcessAtmosDevices(bool resumed = false, float lagCheck = 5f)
{ {
return false; return false;
} }

View File

@@ -0,0 +1,26 @@
using Content.Server.Atmos.Components;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
namespace Content.Server.Atmos.EntitySystems
{
[UsedImplicitly]
public class AirtightSystem : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<AirtightComponent, SnapGridPositionChangedEvent>(OnAirtightPositionChanged);
SubscribeLocalEvent<AirtightComponent, RotateEvent>(OnAirtightRotated);
}
private void OnAirtightPositionChanged(EntityUid uid, AirtightComponent component, SnapGridPositionChangedEvent args)
{
component.OnSnapGridMove(args);
}
private void OnAirtightRotated(EntityUid uid, AirtightComponent airtight, RotateEvent ev)
{
airtight.RotateEvent(ev);
}
}
}

View File

@@ -1,11 +1,9 @@
#nullable enable #nullable enable
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.GameObjects.Components.Atmos; using Content.Server.Atmos.Components;
using Content.Server.Atmos;
using Content.Shared;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.GameObjects.EntitySystems.Atmos;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
@@ -15,7 +13,7 @@ using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Timing; using Robust.Shared.Timing;
namespace Content.Server.GameObjects.EntitySystems.Atmos namespace Content.Server.Atmos.EntitySystems
{ {
[UsedImplicitly] [UsedImplicitly]
public sealed class AtmosDebugOverlaySystem : SharedAtmosDebugOverlaySystem public sealed class AtmosDebugOverlaySystem : SharedAtmosDebugOverlaySystem

View File

@@ -0,0 +1,24 @@
using Content.Shared.CCVar;
namespace Content.Server.Atmos.EntitySystems
{
public partial class AtmosphereSystem
{
public bool SpaceWind { get; private set; }
public bool MonstermosEqualization { get; private set; }
public bool Superconduction { get; private set; }
public bool ExcitedGroupsSpaceIsAllConsuming { get; private set; }
public float AtmosMaxProcessTime { get; private set; }
public float AtmosTickRate { get; private set; }
private void InitializeCVars()
{
_cfg.OnValueChanged(CCVars.SpaceWind, value => SpaceWind = value, true);
_cfg.OnValueChanged(CCVars.MonstermosEqualization, value => MonstermosEqualization = value, true);
_cfg.OnValueChanged(CCVars.Superconduction, value => Superconduction = value, true);
_cfg.OnValueChanged(CCVars.AtmosMaxProcessTime, value => AtmosMaxProcessTime = value, true);
_cfg.OnValueChanged(CCVars.AtmosTickRate, value => AtmosTickRate = value, true);
_cfg.OnValueChanged(CCVars.ExcitedGroupsSpaceIsAllConsuming, value => ExcitedGroupsSpaceIsAllConsuming = value, true);
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.Atmos.Reactions;
using Content.Shared.Atmos;
using Robust.Shared.Maths;
namespace Content.Server.Atmos.EntitySystems
{
public partial class AtmosphereSystem
{
private GasReactionPrototype[] _gasReactions = Array.Empty<GasReactionPrototype>();
private float[] _gasSpecificHeats = new float[Atmospherics.TotalNumberOfGases];
/// <summary>
/// List of gas reactions ordered by priority.
/// </summary>
public IEnumerable<GasReactionPrototype> GasReactions => _gasReactions!;
public float[] GasSpecificHeats => _gasSpecificHeats;
private void InitializeGases()
{
_gasReactions = _protoMan.EnumeratePrototypes<GasReactionPrototype>().ToArray();
Array.Sort(_gasReactions, (a, b) => b.Priority.CompareTo(a.Priority));
Array.Resize(ref _gasSpecificHeats, MathHelper.NextMultipleOf(Atmospherics.TotalNumberOfGases, 4));
for (var i = 0; i < GasPrototypes.Length; i++)
{
_gasSpecificHeats[i] = GasPrototypes[i].SpecificHeat;
}
}
}
}

View File

@@ -1,14 +1,7 @@
#nullable enable
using System; using System;
using System.Collections.Generic; using System.Diagnostics.CodeAnalysis;
using System.Linq; using Content.Server.Atmos.Components;
using Content.Server.Atmos; using Content.Shared.Atmos.EntitySystems;
using Content.Server.Atmos.Reactions;
using Content.Server.GameObjects.Components.Atmos;
using Content.Shared;
using Content.Shared.Atmos;
using Content.Shared.CCVar;
using Content.Shared.GameObjects.EntitySystems.Atmos;
using Content.Shared.Maps; using Content.Shared.Maps;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
@@ -16,31 +9,23 @@ using Robust.Shared.Configuration;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Timing; using Robust.Shared.Timing;
namespace Content.Server.GameObjects.EntitySystems namespace Content.Server.Atmos.EntitySystems
{ {
[UsedImplicitly] [UsedImplicitly]
public class AtmosphereSystem : SharedAtmosphereSystem public partial class AtmosphereSystem : SharedAtmosphereSystem
{ {
[Dependency] private readonly IPrototypeManager _protoMan = default!; [Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPauseManager _pauseManager = default!; [Dependency] private readonly IPauseManager _pauseManager = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IConfigurationManager _cfg = default!;
private GasReactionPrototype[] _gasReactions = Array.Empty<GasReactionPrototype>();
private GridTileLookupSystem? _gridTileLookup = null; private GridTileLookupSystem? _gridTileLookup = null;
/// <summary> private const float ExposedUpdateDelay = 1f;
/// List of gas reactions ordered by priority. private float _exposedTimer = 0f;
/// </summary>
public IEnumerable<GasReactionPrototype> GasReactions => _gasReactions!;
private float[] _gasSpecificHeats = new float[Atmospherics.TotalNumberOfGases];
public float[] GasSpecificHeats => _gasSpecificHeats;
public GridTileLookupSystem GridTileLookupSystem => _gridTileLookup ??= Get<GridTileLookupSystem>(); public GridTileLookupSystem GridTileLookupSystem => _gridTileLookup ??= Get<GridTileLookupSystem>();
@@ -48,71 +33,16 @@ namespace Content.Server.GameObjects.EntitySystems
{ {
base.Initialize(); base.Initialize();
_gasReactions = _protoMan.EnumeratePrototypes<GasReactionPrototype>().ToArray(); InitializeGases();
Array.Sort(_gasReactions, (a, b) => b.Priority.CompareTo(a.Priority)); InitializeCVars();
#region Events
// Map events.
_mapManager.MapCreated += OnMapCreated; _mapManager.MapCreated += OnMapCreated;
_mapManager.TileChanged += OnTileChanged; _mapManager.TileChanged += OnTileChanged;
Array.Resize(ref _gasSpecificHeats, MathHelper.NextMultipleOf(Atmospherics.TotalNumberOfGases, 4)); #endregion
for (var i = 0; i < GasPrototypes.Length; i++)
{
_gasSpecificHeats[i] = GasPrototypes[i].SpecificHeat;
}
// Required for airtight components.
SubscribeLocalEvent<RotateEvent>(RotateEvent);
SubscribeLocalEvent<AirtightComponent, SnapGridPositionChangedEvent>(HandleSnapGridMove);
_cfg.OnValueChanged(CCVars.SpaceWind, OnSpaceWindChanged, true);
_cfg.OnValueChanged(CCVars.MonstermosEqualization, OnMonstermosEqualizationChanged, true);
_cfg.OnValueChanged(CCVars.Superconduction, OnSuperconductionChanged, true);
_cfg.OnValueChanged(CCVars.AtmosMaxProcessTime, OnAtmosMaxProcessTimeChanged, true);
_cfg.OnValueChanged(CCVars.AtmosTickRate, OnAtmosTickRateChanged, true);
_cfg.OnValueChanged(CCVars.ExcitedGroupsSpaceIsAllConsuming, OnExcitedGroupsSpaceIsAllConsumingChanged, true);
}
private static void HandleSnapGridMove(EntityUid uid, AirtightComponent component, SnapGridPositionChangedEvent args)
{
component.OnTransformMove();
}
public bool SpaceWind { get; private set; }
public bool MonstermosEqualization { get; private set; }
public bool Superconduction { get; private set; }
public bool ExcitedGroupsSpaceIsAllConsuming { get; private set; }
public float AtmosMaxProcessTime { get; private set; }
public float AtmosTickRate { get; private set; }
private void OnExcitedGroupsSpaceIsAllConsumingChanged(bool obj)
{
ExcitedGroupsSpaceIsAllConsuming = obj;
}
private void OnAtmosTickRateChanged(float obj)
{
AtmosTickRate = obj;
}
private void OnAtmosMaxProcessTimeChanged(float obj)
{
AtmosMaxProcessTime = obj;
}
private void OnMonstermosEqualizationChanged(bool obj)
{
MonstermosEqualization = obj;
}
private void OnSuperconductionChanged(bool obj)
{
Superconduction = obj;
}
private void OnSpaceWindChanged(bool obj)
{
SpaceWind = obj;
} }
public override void Shutdown() public override void Shutdown()
@@ -120,16 +50,35 @@ namespace Content.Server.GameObjects.EntitySystems
base.Shutdown(); base.Shutdown();
_mapManager.MapCreated -= OnMapCreated; _mapManager.MapCreated -= OnMapCreated;
_mapManager.TileChanged -= OnTileChanged;
} }
private void RotateEvent(RotateEvent ev) private void OnTileChanged(object? sender, TileChangedEventArgs eventArgs)
{ {
if (ev.Sender.TryGetComponent(out AirtightComponent? airtight)) // When a tile changes, we want to update it only if it's gone from
// space -> not space or vice versa. So if the old tile is the
// same as the new tile in terms of space-ness, ignore the change
if (eventArgs.NewTile.IsSpace() == eventArgs.OldTile.IsSpace())
{ {
airtight.RotateEvent(ev); return;
}
} }
GetGridAtmosphere(eventArgs.NewTile.GridIndex)?.Invalidate(eventArgs.NewTile.GridIndices);
}
private void OnMapCreated(object? sender, MapEventArgs e)
{
if (e.Map == MapId.Nullspace)
return;
var map = _mapManager.GetMapEntity(e.Map);
if (!map.HasComponent<IGridAtmosphereComponent>())
map.AddComponent<SpaceGridAtmosphereComponent>();
}
#region Helper Methods
public IGridAtmosphereComponent? GetGridAtmosphere(GridId gridId) public IGridAtmosphereComponent? GetGridAtmosphere(GridId gridId)
{ {
if (!gridId.IsValid()) if (!gridId.IsValid())
@@ -165,41 +114,61 @@ namespace Content.Server.GameObjects.EntitySystems
return _mapManager.GetMapEntity(coordinates.MapId).GetComponent<IGridAtmosphereComponent>(); return _mapManager.GetMapEntity(coordinates.MapId).GetComponent<IGridAtmosphereComponent>();
} }
/// <summary>
/// Unlike <see cref="GetGridAtmosphere"/>, this doesn't return space grid when not found.
/// </summary>
public bool TryGetSimulatedGridAtmosphere(MapCoordinates coordinates, [NotNullWhen(true)] out IGridAtmosphereComponent? atmosphere)
{
if (coordinates.MapId == MapId.Nullspace)
{
atmosphere = null;
return false;
}
if (_mapManager.TryFindGridAt(coordinates, out var mapGrid)
&& ComponentManager.TryGetComponent(mapGrid.GridEntityId, out IGridAtmosphereComponent? atmosGrid)
&& atmosGrid.Simulated)
{
atmosphere = atmosGrid;
return true;
}
if (_mapManager.GetMapEntity(coordinates.MapId).TryGetComponent(out IGridAtmosphereComponent? atmosMap)
&& atmosMap.Simulated)
{
atmosphere = atmosMap;
return true;
}
atmosphere = null;
return false;
}
#endregion
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
base.Update(frameTime); base.Update(frameTime);
_exposedTimer += frameTime;
foreach (var (mapGridComponent, gridAtmosphereComponent) in EntityManager.ComponentManager.EntityQuery<IMapGridComponent, IGridAtmosphereComponent>(true)) foreach (var (mapGridComponent, gridAtmosphereComponent) in EntityManager.ComponentManager.EntityQuery<IMapGridComponent, IGridAtmosphereComponent>(true))
{ {
if (_pauseManager.IsGridPaused(mapGridComponent.GridIndex)) continue; if (_pauseManager.IsGridPaused(mapGridComponent.GridIndex)) continue;
gridAtmosphereComponent.Update(frameTime); gridAtmosphereComponent.Update(frameTime);
} }
}
private void OnTileChanged(object? sender, TileChangedEventArgs eventArgs) if (_exposedTimer >= ExposedUpdateDelay)
{ {
// When a tile changes, we want to update it only if it's gone from foreach (var exposed in EntityManager.ComponentManager.EntityQuery<AtmosExposedComponent>(true))
// space -> not space or vice versa. So if the old tile is the
// same as the new tile in terms of space-ness, ignore the change
if (eventArgs.NewTile.IsSpace() == eventArgs.OldTile.IsSpace())
{ {
return; var tile = exposed.Owner.Transform.Coordinates.GetTileAtmosphere();
if (tile == null) continue;
exposed.Update(tile, _exposedTimer);
} }
GetGridAtmosphere(eventArgs.NewTile.GridPosition(_mapManager))?.Invalidate(eventArgs.NewTile.GridIndices); _exposedTimer = 0;
} }
private void OnMapCreated(object? sender, MapEventArgs e)
{
if (e.Map == MapId.Nullspace)
return;
var map = _mapManager.GetMapEntity(e.Map);
if (!map.HasComponent<IGridAtmosphereComponent>())
map.AddComponent<SpaceGridAtmosphereComponent>();
} }
} }
} }

View File

@@ -1,11 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Server.GameObjects.Components; using Content.Server.GameObjects.Components;
using Content.Shared.GameTicking;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using JetBrains.Annotations; using JetBrains.Annotations;
namespace Content.Server.GameObjects.EntitySystems namespace Content.Server.GameObjects.EntitySystems

View File

@@ -1,8 +1,8 @@
using Content.Server.GameObjects.Components.Atmos; using Content.Server.Atmos.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
namespace Content.Server.GameObjects.EntitySystems namespace Content.Server.Atmos.EntitySystems
{ {
[UsedImplicitly] [UsedImplicitly]
public class GasAnalyzerSystem : EntitySystem public class GasAnalyzerSystem : EntitySystem

View File

@@ -1,8 +1,8 @@
using Content.Server.GameObjects.Components.Atmos; using Content.Server.Atmos.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
namespace Content.Server.GameObjects.EntitySystems namespace Content.Server.Atmos.EntitySystems
{ {
[UsedImplicitly] [UsedImplicitly]
public class GasTankSystem : EntitySystem public class GasTankSystem : EntitySystem

View File

@@ -3,11 +3,10 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Content.Server.GameObjects.Components.Atmos; using Content.Server.Atmos.Components;
using Content.Shared;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.GameObjects.EntitySystems.Atmos;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.Player; using Robust.Server.Player;
@@ -15,14 +14,14 @@ using Robust.Shared;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
// ReSharper disable once RedundantUsingDirective
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Timing; using Robust.Shared.Timing;
// ReSharper disable once RedundantUsingDirective
using Dependency = Robust.Shared.IoC.DependencyAttribute; using Dependency = Robust.Shared.IoC.DependencyAttribute;
namespace Content.Server.GameObjects.EntitySystems.Atmos namespace Content.Server.Atmos.EntitySystems
{ {
[UsedImplicitly] [UsedImplicitly]
internal sealed class GasTileOverlaySystem : SharedGasTileOverlaySystem, IResettingEntitySystem internal sealed class GasTileOverlaySystem : SharedGasTileOverlaySystem, IResettingEntitySystem

View File

@@ -1,6 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.GameObjects.Components.Atmos; using Content.Server.Atmos.Components;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;

View File

@@ -3,8 +3,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Reactions; using Content.Server.Atmos.Reactions;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -154,8 +154,7 @@ namespace Content.Server.Atmos
var combinedHeatCapacity = HeatCapacity + giver.HeatCapacity; var combinedHeatCapacity = HeatCapacity + giver.HeatCapacity;
if (combinedHeatCapacity > 0f) if (combinedHeatCapacity > 0f)
{ {
Temperature = Temperature = (giver.Temperature * giver.HeatCapacity + Temperature * HeatCapacity) / combinedHeatCapacity;
(giver.Temperature * giver.HeatCapacity + Temperature * HeatCapacity) / combinedHeatCapacity;
} }
} }
@@ -600,5 +599,18 @@ namespace Content.Server.Atmos
}; };
return newMixture; return newMixture;
} }
public void ScrubInto(GasMixture destination, IReadOnlyCollection<Gas> filterGases)
{
var buffer = new GasMixture(Volume){Temperature = Temperature};
foreach (var gas in filterGases)
{
buffer.AdjustMoles(gas, GetMoles(gas));
SetMoles(gas, 0f);
}
destination.Merge(buffer);
}
} }
} }

View File

@@ -0,0 +1,52 @@
using Content.Shared.Atmos;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Atmos.Piping.Binary.Components
{
[RegisterComponent]
public class GasCanisterComponent : Component
{
public override string Name => "GasCanister";
[ViewVariables(VVAccess.ReadWrite)]
[DataField("port")]
public string PortName { get; set; } = "port";
[ViewVariables(VVAccess.ReadWrite)]
[DataField("tank")]
public string TankName { get; set; } = "tank";
/// <summary>
/// Container name for the gas tank holder.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("container")]
public string ContainerName { get; set; } = "GasCanisterTankHolder";
[ViewVariables(VVAccess.ReadWrite)]
[DataField("gasMixture")]
public GasMixture InitialMixture { get; } = new();
/// <summary>
/// Stores the last pressure the tank had, for appearance-updating purposes.
/// </summary>
[ViewVariables]
public float LastPressure { get; set; } = 0f;
/// <summary>
/// Minimum release pressure possible for the release valve.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("minReleasePressure")]
public float MinReleasePressure { get; set; } = Atmospherics.OneAtmosphere / 10;
/// <summary>
/// Maximum release pressure possible for the release valve.
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("maxReleasePressure")]
public float MaxReleasePressure { get; set; } = Atmospherics.OneAtmosphere * 10;
}
}

View File

@@ -0,0 +1,35 @@
using Content.Shared.Atmos;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Atmos.Piping.Binary.Components
{
[RegisterComponent]
public class GasPassiveGateComponent : Component
{
public override string Name => "GasPassiveGate";
[DataField("enabled")]
[ViewVariables(VVAccess.ReadWrite)]
public bool Enabled { get; set; } = true;
/// <summary>
/// This is the minimum difference needed to overcome the friction in the mechanism.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("frictionDifference")]
public float FrictionPressureDifference { get; set; } = 10f;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("inlet")]
public string InletName { get; set; } = "inlet";
[ViewVariables(VVAccess.ReadWrite)]
[DataField("outlet")]
public string OutletName { get; set; } = "outlet";
[ViewVariables(VVAccess.ReadWrite)]
public float TargetPressure { get; set; } = Atmospherics.OneAtmosphere;
}
}

View File

@@ -0,0 +1,19 @@
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Atmos.Piping.Binary.Components
{
[RegisterComponent]
public class GasPortComponent : Component
{
public override string Name => "GasPort";
[ViewVariables(VVAccess.ReadWrite)]
[DataField("pipe")]
public string PipeName { get; set; } = "connected";
[ViewVariables(VVAccess.ReadOnly)]
public GasMixture Buffer { get; } = new();
}
}

View File

@@ -0,0 +1,27 @@
using Content.Shared.Atmos;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Atmos.Piping.Binary.Components
{
[RegisterComponent]
public class GasPressurePumpComponent : Component
{
public override string Name => "GasPressurePump";
[ViewVariables(VVAccess.ReadWrite)]
public bool Enabled { get; set; } = true;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("inlet")]
public string InletName { get; set; } = "inlet";
[ViewVariables(VVAccess.ReadWrite)]
[DataField("outlet")]
public string OutletName { get; set; } = "outlet";
[ViewVariables(VVAccess.ReadWrite)]
public float TargetPressure { get; set; } = Atmospherics.OneAtmosphere;
}
}

View File

@@ -0,0 +1,55 @@
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
using Content.Server.NodeContainer;
using Content.Shared.ActionBlocker;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Helpers;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Atmos.Piping.Binary.Components
{
// TODO ATMOS: Make ECS.
[ComponentReference(typeof(IActivate))]
[RegisterComponent]
public class GasValveComponent : Component, IActivate
{
public override string Name => "GasValve";
[ViewVariables]
[DataField("open")]
private bool _open = true;
[DataField("pipe")]
[ViewVariables(VVAccess.ReadWrite)]
private string _pipeName = "pipe";
protected override void Startup()
{
base.Startup();
Set();
}
private void Set()
{
if (Owner.TryGetComponent(out NodeContainerComponent? nodeContainer)
&& nodeContainer.TryGetNode(_pipeName, out PipeNode? pipe))
{
pipe.ConnectionsEnabled = _open;
}
}
private void Toggle()
{
_open = !_open;
Set();
}
void IActivate.Activate(ActivateEventArgs eventArgs)
{
if(eventArgs.InRangeUnobstructed() && EntitySystem.Get<ActionBlockerSystem>().CanInteract(eventArgs.User))
Toggle();
}
}
}

View File

@@ -0,0 +1,42 @@
using Content.Shared.Atmos;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Atmos.Piping.Binary.Components
{
[RegisterComponent]
public class GasVolumePumpComponent : Component
{
public override string Name => "GasVolumePump";
[ViewVariables(VVAccess.ReadWrite)]
public bool Enabled { get; set; } = true;
[ViewVariables(VVAccess.ReadWrite)]
public bool Overclocked { get; set; } = false;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("inlet")]
public string InletName { get; set; } = "inlet";
[ViewVariables(VVAccess.ReadWrite)]
[DataField("outlet")]
public string OutletName { get; set; } = "outlet";
[ViewVariables(VVAccess.ReadWrite)]
public float TransferRate { get; set; } = Atmospherics.MaxTransferRate;
[DataField("leakRatio")]
public float LeakRatio { get; set; } = 0.1f;
[DataField("lowerThreshold")]
public float LowerThreshold { get; set; } = 0.01f;
[DataField("higherThreshold")]
public float HigherThreshold { get; set; } = 9000f;
[DataField("overclockThreshold")]
public float OverclockThreshold { get; set; } = 1000f;
}
}

View File

@@ -0,0 +1,247 @@
using System;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.Piping.Binary.Components;
using Content.Server.Atmos.Piping.Components;
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
using Content.Server.Hands.Components;
using Content.Server.NodeContainer;
using Content.Server.UserInterface;
using Content.Shared.ActionBlocker;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping.Binary.Components;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Helpers;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
namespace Content.Server.Atmos.Piping.Binary.EntitySystems
{
[UsedImplicitly]
public class GasCanisterSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<GasCanisterComponent, ComponentStartup>(OnCanisterStartup);
SubscribeLocalEvent<GasCanisterComponent, AtmosDeviceUpdateEvent>(OnCanisterUpdated);
SubscribeLocalEvent<GasCanisterComponent, ActivateInWorldEvent>(OnCanisterActivate);
SubscribeLocalEvent<GasCanisterComponent, InteractHandEvent>(OnCanisterInteractHand);
SubscribeLocalEvent<GasCanisterComponent, InteractUsingEvent>(OnCanisterInteractUsing);
SubscribeLocalEvent<GasCanisterComponent, EntInsertedIntoContainerMessage>(OnCanisterContainerInserted);
SubscribeLocalEvent<GasCanisterComponent, EntRemovedFromContainerMessage>(OnCanisterContainerRemoved);
}
private void OnCanisterStartup(EntityUid uid, GasCanisterComponent canister, ComponentStartup args)
{
// TODO ATMOS: Don't use Owner to get the UI.
if(canister.Owner.GetUIOrNull(GasCanisterUiKey.Key) is {} ui)
ui.OnReceiveMessage += msg => OnCanisterUIMessage(uid, canister, msg);
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
return;
if (!nodeContainer.TryGetNode(canister.PortName, out PipeNode? portNode))
return;
// Create a pipenet if we don't have one already.
portNode.TryAssignGroupIfNeeded();
portNode.Air.Merge(canister.InitialMixture);
portNode.Air.Temperature = canister.InitialMixture.Temperature;
portNode.Volume = canister.InitialMixture.Volume;
}
private void DirtyUI(EntityUid uid)
{
if (!ComponentManager.TryGetComponent(uid, out IMetaDataComponent? metadata)
|| !ComponentManager.TryGetComponent(uid, out GasCanisterComponent? canister)
|| !ComponentManager.TryGetComponent(uid, out GasPassiveGateComponent? passiveGate)
|| !ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)
|| !nodeContainer.TryGetNode(canister.PortName, out PipeNode? portNode)
|| !nodeContainer.TryGetNode(canister.TankName, out PipeNode? tankNode)
|| !ComponentManager.TryGetComponent(uid, out ServerUserInterfaceComponent? userInterfaceComponent)
|| !userInterfaceComponent.TryGetBoundUserInterface(GasCanisterUiKey.Key, out var ui))
return;
string? tankLabel = null;
var tankPressure = 0f;
if (ComponentManager.TryGetComponent(uid, out ContainerManagerComponent? containerManager) && containerManager.TryGetContainer(canister.ContainerName, out var tankContainer) && tankContainer.ContainedEntities.Count > 0)
{
var tank = tankContainer.ContainedEntities[0];
tankLabel = tank.Name;
tankPressure = tankNode.Air.Pressure;
}
ui.SetState(new GasCanisterBoundUserInterfaceState(metadata.EntityName, portNode.Air.Pressure,
portNode.NodeGroup.Nodes.Count > 1, tankLabel, tankPressure,
passiveGate.TargetPressure, passiveGate.Enabled,
canister.MinReleasePressure, canister.MaxReleasePressure));
}
private void OnCanisterUIMessage(EntityUid uid, GasCanisterComponent canister, ServerBoundUserInterfaceMessage msg)
{
if (msg.Session.AttachedEntity is not {} entity
|| !Get<ActionBlockerSystem>().CanInteract(entity)
|| !Get<ActionBlockerSystem>().CanUse(entity))
return;
if (!ComponentManager.TryGetComponent(uid, out GasPassiveGateComponent? passiveGate)
|| !ComponentManager.TryGetComponent(uid, out ContainerManagerComponent? containerManager)
|| !containerManager.TryGetContainer(canister.ContainerName, out var container))
return;
switch (msg.Message)
{
case GasCanisterHoldingTankEjectMessage:
if (container.ContainedEntities.Count == 0)
break;
container.Remove(container.ContainedEntities[0]);
break;
case GasCanisterChangeReleasePressureMessage changeReleasePressure:
var pressure = Math.Clamp(changeReleasePressure.Pressure, canister.MinReleasePressure, canister.MaxReleasePressure);
passiveGate.TargetPressure = pressure;
DirtyUI(uid);
break;
case GasCanisterChangeReleaseValveMessage changeReleaseValve:
passiveGate.Enabled = changeReleaseValve.Valve;
DirtyUI(uid);
break;
}
}
private void OnCanisterUpdated(EntityUid uid, GasCanisterComponent canister, AtmosDeviceUpdateEvent args)
{
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)
|| !ComponentManager.TryGetComponent(uid, out AppearanceComponent? appearance))
return;
if (!nodeContainer.TryGetNode(canister.PortName, out PipeNode? portNode))
return;
DirtyUI(uid);
// Nothing to do here.
if (MathHelper.CloseTo(portNode.Air.Pressure, canister.LastPressure))
return;
canister.LastPressure = portNode.Air.Pressure;
if (portNode.Air.Pressure < 10)
{
appearance.SetData(GasCanisterVisuals.PressureState, 0);
}
else if (portNode.Air.Pressure < Atmospherics.OneAtmosphere)
{
appearance.SetData(GasCanisterVisuals.PressureState, 1);
}
else if (portNode.Air.Pressure < (15 * Atmospherics.OneAtmosphere))
{
appearance.SetData(GasCanisterVisuals.PressureState, 2);
}
else
{
appearance.SetData(GasCanisterVisuals.PressureState, 3);
}
}
private void OnCanisterActivate(EntityUid uid, GasCanisterComponent component, ActivateInWorldEvent args)
{
if (!args.User.TryGetComponent(out ActorComponent? actor))
return;
component.Owner.GetUIOrNull(GasCanisterUiKey.Key)?.Open(actor.PlayerSession);
args.Handled = true;
}
private void OnCanisterInteractHand(EntityUid uid, GasCanisterComponent component, InteractHandEvent args)
{
if (!args.User.TryGetComponent(out ActorComponent? actor))
return;
component.Owner.GetUIOrNull(GasCanisterUiKey.Key)?.Open(actor.PlayerSession);
args.Handled = true;
}
private void OnCanisterInteractUsing(EntityUid uid, GasCanisterComponent component, InteractUsingEvent args)
{
var canister = EntityManager.GetEntity(uid);
var container = canister.EnsureContainer<ContainerSlot>(component.ContainerName);
// Container full.
if (container.ContainedEntity != null)
return;
// Check the used item is valid...
if (!args.Used.TryGetComponent(out GasTankComponent? _)
|| !args.Used.TryGetComponent(out NodeContainerComponent? _))
return;
// Check the user has hands.
if (!args.User.TryGetComponent(out HandsComponent? hands))
return;
if (!args.User.InRangeUnobstructed(canister, SharedInteractionSystem.InteractionRange, popup: true))
return;
if (!hands.Drop(args.Used, canister.Transform.Coordinates))
return;
if (!container.Insert(args.Used))
return;
args.Handled = true;
}
private void OnCanisterContainerInserted(EntityUid uid, GasCanisterComponent component, EntInsertedIntoContainerMessage args)
{
if (args.Container.ID != component.ContainerName)
return;
DirtyUI(uid);
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)
|| !nodeContainer.TryGetNode(component.TankName, out PipeNode? tankNode))
return;
tankNode.EnvironmentalAir = false;
tankNode.ConnectToContainedEntities = true;
tankNode.NodeGroup.RemakeGroup();
if (!ComponentManager.TryGetComponent(uid, out AppearanceComponent? appearance))
return;
appearance.SetData(GasCanisterVisuals.TankInserted, true);
}
private void OnCanisterContainerRemoved(EntityUid uid, GasCanisterComponent component, EntRemovedFromContainerMessage args)
{
if (args.Container.ID != component.ContainerName)
return;
DirtyUI(uid);
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)
|| !nodeContainer.TryGetNode(component.TankName, out PipeNode? tankNode))
return;
tankNode.NodeGroup.RemakeGroup();
tankNode.ConnectToContainedEntities = false;
tankNode.EnvironmentalAir = true;
if (!ComponentManager.TryGetComponent(uid, out AppearanceComponent? appearance))
return;
appearance.SetData(GasCanisterVisuals.TankInserted, false);
}
}
}

View File

@@ -0,0 +1,53 @@
using System;
using Content.Server.Atmos.Piping.Binary.Components;
using Content.Server.Atmos.Piping.Components;
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
using Content.Server.NodeContainer;
using Content.Shared.Atmos;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
namespace Content.Server.Atmos.Piping.Binary.EntitySystems
{
[UsedImplicitly]
public class GasPassiveGateSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<GasPassiveGateComponent, AtmosDeviceUpdateEvent>(OnPassiveGateUpdated);
}
private void OnPassiveGateUpdated(EntityUid uid, GasPassiveGateComponent gate, AtmosDeviceUpdateEvent args)
{
if (!gate.Enabled)
return;
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
return;
if (!nodeContainer.TryGetNode(gate.InletName, out PipeNode? inlet)
|| !nodeContainer.TryGetNode(gate.OutletName, out PipeNode? outlet))
return;
var outputStartingPressure = outlet.Air.Pressure;
var inputStartingPressure = inlet.Air.Pressure;
if (outputStartingPressure >= MathF.Min(gate.TargetPressure, inputStartingPressure - gate.FrictionPressureDifference))
return; // No need to pump gas, target reached or input pressure too low.
if (inlet.Air.TotalMoles > 0 && inlet.Air.Temperature > 0)
{
// We calculate the necessary moles to transfer using our good ol' friend PV=nRT.
var pressureDelta = MathF.Min(gate.TargetPressure - outputStartingPressure, (inputStartingPressure - outputStartingPressure)/2);
// We can't have a pressure delta that would cause outlet pressure > inlet pressure.
var transferMoles = pressureDelta * outlet.Air.Volume / (inlet.Air.Temperature * Atmospherics.R);
// Actually transfer the gas.
outlet.AssumeAir(inlet.Air.Remove(transferMoles));
}
}
}
}

View File

@@ -0,0 +1,67 @@
using Content.Server.Atmos.Piping.Binary.Components;
using Content.Server.Atmos.Piping.Components;
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
using Content.Server.NodeContainer;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
namespace Content.Server.Atmos.Piping.Binary.EntitySystems
{
[UsedImplicitly]
public class GasPressurePumpSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<GasPressurePumpComponent, AtmosDeviceUpdateEvent>(OnPumpUpdated);
SubscribeLocalEvent<GasPressurePumpComponent, AtmosDeviceDisabledEvent>(OnPumpLeaveAtmosphere);
}
private void OnPumpUpdated(EntityUid uid, GasPressurePumpComponent pump, AtmosDeviceUpdateEvent args)
{
var appearance = pump.Owner.GetComponentOrNull<AppearanceComponent>();
appearance?.SetData(PressurePumpVisuals.Enabled, false);
if (!pump.Enabled)
return;
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
return;
if (!nodeContainer.TryGetNode(pump.InletName, out PipeNode? inlet)
|| !nodeContainer.TryGetNode(pump.OutletName, out PipeNode? outlet))
return;
var outputStartingPressure = outlet.Air.Pressure;
if (MathHelper.CloseTo(pump.TargetPressure, outputStartingPressure))
return; // No need to pump gas if target has been reached.
if (inlet.Air.TotalMoles > 0 && inlet.Air.Temperature > 0)
{
appearance?.SetData(PressurePumpVisuals.Enabled, true);
// We calculate the necessary moles to transfer using our good ol' friend PV=nRT.
var pressureDelta = pump.TargetPressure - outputStartingPressure;
var transferMoles = pressureDelta * outlet.Air.Volume / inlet.Air.Temperature * Atmospherics.R;
var removed = inlet.Air.Remove(transferMoles);
outlet.AssumeAir(removed);
}
}
private void OnPumpLeaveAtmosphere(EntityUid uid, GasPressurePumpComponent component, AtmosDeviceDisabledEvent args)
{
if (ComponentManager.TryGetComponent(uid, out AppearanceComponent? appearance))
{
appearance.SetData(PressurePumpVisuals.Enabled, false);
}
}
}
}

View File

@@ -0,0 +1,70 @@
using Content.Server.Atmos.Piping.Binary.Components;
using Content.Server.Atmos.Piping.Components;
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
using Content.Server.NodeContainer;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Timing;
namespace Content.Server.Atmos.Piping.Binary.EntitySystems
{
[UsedImplicitly]
public class GasVolumePumpSystem : EntitySystem
{
[Dependency] private IGameTiming _gameTiming = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<GasVolumePumpComponent, AtmosDeviceUpdateEvent>(OnVolumePumpUpdated);
}
private void OnVolumePumpUpdated(EntityUid uid, GasVolumePumpComponent pump, AtmosDeviceUpdateEvent args)
{
if (!pump.Enabled)
return;
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
return;
if (!ComponentManager.TryGetComponent(uid, out AtmosDeviceComponent? device))
return;
if (!nodeContainer.TryGetNode(pump.InletName, out PipeNode? inlet)
|| !nodeContainer.TryGetNode(pump.OutletName, out PipeNode? outlet))
return;
var inputStartingPressure = inlet.Air.Pressure;
var outputStartingPressure = outlet.Air.Pressure;
// Pump mechanism won't do anything if the pressure is too high/too low unless you overclock it.
if ((inputStartingPressure < pump.LowerThreshold) || (outputStartingPressure > pump.HigherThreshold) && !pump.Overclocked)
return;
// Overclocked pumps can only force gas a certain amount.
if ((outputStartingPressure - inputStartingPressure > pump.OverclockThreshold) && pump.Overclocked)
return;
// We multiply the transfer rate in L/s by the seconds passed since the last process to get the liters.
var transferRatio = (float)(pump.TransferRate * (_gameTiming.CurTime - device.LastProcess).TotalSeconds) / inlet.Air.Volume;
var removed = inlet.Air.RemoveRatio(transferRatio);
// Some of the gas from the mixture leaks when overclocked.
if (pump.Overclocked)
{
var tile = args.Atmosphere.GetTile(pump.Owner.Transform.Coordinates);
if (tile != null)
{
var leaked = removed.RemoveRatio(pump.LeakRatio);
tile.AssumeAir(leaked);
}
}
outlet.AssumeAir(removed);
}
}
}

View File

@@ -0,0 +1,61 @@
#nullable enable
using System;
using Content.Server.Atmos.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Atmos.Piping.Components
{
/// <summary>
/// Adds itself to a <see cref="IGridAtmosphereComponent"/> to be updated by.
/// </summary>
[RegisterComponent]
public class AtmosDeviceComponent : Component
{
public override string Name => "AtmosDevice";
/// <summary>
/// Whether this device requires being anchored to join an atmosphere.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("requireAnchored")]
public bool RequireAnchored { get; private set; } = true;
public IGridAtmosphereComponent? Atmosphere { get; set; }
[ViewVariables]
public TimeSpan LastProcess { get; set; } = TimeSpan.Zero;
}
public abstract class BaseAtmosDeviceEvent : EntityEventArgs
{
public IGridAtmosphereComponent Atmosphere { get; }
public BaseAtmosDeviceEvent(IGridAtmosphereComponent atmosphere)
{
Atmosphere = atmosphere;
}
}
public sealed class AtmosDeviceUpdateEvent : BaseAtmosDeviceEvent
{
public AtmosDeviceUpdateEvent(IGridAtmosphereComponent atmosphere) : base(atmosphere)
{
}
}
public sealed class AtmosDeviceEnabledEvent : BaseAtmosDeviceEvent
{
public AtmosDeviceEnabledEvent(IGridAtmosphereComponent atmosphere) : base(atmosphere)
{
}
}
public sealed class AtmosDeviceDisabledEvent : BaseAtmosDeviceEvent
{
public AtmosDeviceDisabledEvent(IGridAtmosphereComponent atmosphere) : base(atmosphere)
{
}
}
}

View File

@@ -0,0 +1,16 @@
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Atmos.Piping.Components
{
[RegisterComponent]
public class AtmosUnsafeUnanchorComponent : Component
{
public override string Name => "AtmosUnsafeUnanchor";
[ViewVariables(VVAccess.ReadWrite)]
[DataField("enabled")]
public bool Enabled { get; set; } = true;
}
}

View File

@@ -0,0 +1,92 @@
using System;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Piping.Components;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Physics;
using Robust.Shared.Timing;
namespace Content.Server.Atmos.Piping.EntitySystems
{
[UsedImplicitly]
public class AtmosDeviceSystem : EntitySystem
{
[Dependency] private IGameTiming _gameTiming = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AtmosDeviceComponent, ComponentInit>(OnDeviceInitialize);
SubscribeLocalEvent<AtmosDeviceComponent, ComponentShutdown>(OnDeviceShutdown);
SubscribeLocalEvent<AtmosDeviceComponent, PhysicsBodyTypeChangedEvent>(OnDeviceBodyTypeChanged);
SubscribeLocalEvent<AtmosDeviceComponent, EntParentChangedMessage>(OnDeviceParentChanged);
}
private bool CanJoinAtmosphere(AtmosDeviceComponent component)
{
return !component.RequireAnchored || !component.Owner.TryGetComponent(out PhysicsComponent? physics) || physics.BodyType == BodyType.Static;
}
public void JoinAtmosphere(AtmosDeviceComponent component)
{
if (!CanJoinAtmosphere(component))
return;
// We try to get a valid, simulated atmosphere.
if (!Get<AtmosphereSystem>().TryGetSimulatedGridAtmosphere(component.Owner.Transform.MapPosition, out var atmosphere))
return;
component.LastProcess = _gameTiming.CurTime;
component.Atmosphere = atmosphere;
atmosphere.AddAtmosDevice(component);
RaiseLocalEvent(component.Owner.Uid, new AtmosDeviceEnabledEvent(atmosphere), false);
}
public void LeaveAtmosphere(AtmosDeviceComponent component)
{
var atmosphere = component.Atmosphere;
atmosphere?.RemoveAtmosDevice(component);
component.Atmosphere = null;
component.LastProcess = TimeSpan.Zero;
if(atmosphere != null)
RaiseLocalEvent(component.Owner.Uid, new AtmosDeviceDisabledEvent(atmosphere), false);
}
public void RejoinAtmosphere(AtmosDeviceComponent component)
{
LeaveAtmosphere(component);
JoinAtmosphere(component);
}
private void OnDeviceInitialize(EntityUid uid, AtmosDeviceComponent component, ComponentInit args)
{
JoinAtmosphere(component);
}
private void OnDeviceShutdown(EntityUid uid, AtmosDeviceComponent component, ComponentShutdown args)
{
LeaveAtmosphere(component);
}
private void OnDeviceBodyTypeChanged(EntityUid uid, AtmosDeviceComponent component, PhysicsBodyTypeChangedEvent args)
{
// Do nothing if the component doesn't require being anchored to function.
if (!component.RequireAnchored)
return;
if (args.New == BodyType.Static)
JoinAtmosphere(component);
else
LeaveAtmosphere(component);
}
private void OnDeviceParentChanged(EntityUid uid, AtmosDeviceComponent component, EntParentChangedMessage args)
{
RejoinAtmosphere(component);
}
}
}

View File

@@ -0,0 +1,80 @@
using Content.Server.Anchor;
using Content.Server.Atmos.Piping.Components;
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
using Content.Server.NodeContainer;
using Content.Shared.Atmos;
using Content.Shared.Notification.Managers;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
namespace Content.Server.Atmos.Piping.EntitySystems
{
[UsedImplicitly]
public class AtmosUnsafeUnanchorSystem : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<AtmosUnsafeUnanchorComponent, BeforeUnanchoredEvent>(OnBeforeUnanchored);
SubscribeLocalEvent<AtmosUnsafeUnanchorComponent, UnanchorAttemptEvent>(OnUnanchorAttempt);
}
private void OnUnanchorAttempt(EntityUid uid, AtmosUnsafeUnanchorComponent component, UnanchorAttemptEvent args)
{
if (!component.Enabled || !ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodes))
return;
if (!component.Owner.Transform.Coordinates.TryGetTileAir(out var environment, EntityManager))
return;
foreach (var node in nodes.Nodes.Values)
{
if (node is not PipeNode pipe) continue;
if ((pipe.Air.Pressure - environment.Pressure) > 2 * Atmospherics.OneAtmosphere)
{
args.Delay += 1.5f;
args.User?.PopupMessageCursor(Loc.GetString("comp-atmos-unsafe-unanchor-warning"));
return; // Show the warning only once.
}
}
}
private void OnBeforeUnanchored(EntityUid uid, AtmosUnsafeUnanchorComponent component, BeforeUnanchoredEvent args)
{
if (!component.Enabled || !ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodes))
return;
if (!component.Owner.Transform.Coordinates.TryGetTileAtmosphere(out var environment))
environment = null;
var environmentPressure = environment?.Air?.Pressure ?? 0f;
var environmentVolume = environment?.Air?.Volume ?? Atmospherics.CellVolume;
var environmentTemperature = environment?.Air?.Volume ?? Atmospherics.TCMB;
var lost = 0f;
var timesLost = 0;
foreach (var node in nodes.Nodes.Values)
{
if (node is not PipeNode pipe) continue;
var difference = pipe.Air.Pressure - environmentPressure;
lost += difference * environmentVolume / (environmentTemperature * Atmospherics.R);
timesLost++;
}
var sharedLoss = lost / timesLost;
var buffer = new GasMixture();
foreach (var node in nodes.Nodes.Values)
{
if (node is not PipeNode pipe) continue;
buffer.Merge(pipe.Air.Remove(sharedLoss));
}
environment?.AssumeAir(buffer);
}
}
}

View File

@@ -0,0 +1,37 @@
using Content.Shared.Atmos;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Atmos.Piping.Other.Components
{
[RegisterComponent]
public class GasMinerComponent : Component
{
public override string Name => "GasMiner";
public bool Enabled { get; set; } = true;
public bool Broken { get; set; } = false;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("maxExternalAmount")]
public float MaxExternalAmount { get; set; } = float.PositiveInfinity;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("maxExternalPressure")]
public float MaxExternalPressure { get; set; } = 6500f;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("spawnGas")]
public Gas SpawnGas { get; set; } = Gas.Invalid;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("spawnTemperature")]
public float SpawnTemperature { get; set; } = Atmospherics.T20C;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("spawnAmount")]
public float SpawnAmount { get; set; } = Atmospherics.MolesCellStandard * 20f;
}
}

View File

@@ -0,0 +1,71 @@
using System.Diagnostics.CodeAnalysis;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.Piping.Components;
using Content.Server.Atmos.Piping.Other.Components;
using Content.Shared.Atmos;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
namespace Content.Server.Atmos.Piping.Other.EntitySystems
{
[UsedImplicitly]
public class GasMinerSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<GasMinerComponent, AtmosDeviceUpdateEvent>(OnMinerUpdated);
}
private void OnMinerUpdated(EntityUid uid, GasMinerComponent miner, AtmosDeviceUpdateEvent args)
{
if (!CheckMinerOperation(args.Atmosphere, miner, out var tile) || !miner.Enabled || miner.SpawnGas <= Gas.Invalid || miner.SpawnAmount <= 0f)
return;
// Time to mine some gas.
var merger = new GasMixture(1) { Temperature = miner.SpawnTemperature };
merger.SetMoles(miner.SpawnGas, miner.SpawnAmount);
tile.AssumeAir(merger);
}
private bool CheckMinerOperation(IGridAtmosphereComponent atmosphere, GasMinerComponent miner, [NotNullWhen(true)] out TileAtmosphere? tile)
{
tile = atmosphere.GetTile(miner.Owner.Transform.Coordinates)!;
// Space.
if (atmosphere.IsSpace(tile.GridIndices))
{
miner.Broken = true;
return false;
}
// Airblocked location.
if (tile.Air == null)
{
miner.Broken = true;
return false;
}
// External pressure above threshold.
if (!float.IsInfinity(miner.MaxExternalPressure) &&
tile.Air.Pressure > miner.MaxExternalPressure - miner.SpawnAmount * miner.SpawnTemperature * Atmospherics.R / tile.Air.Volume)
{
miner.Broken = true;
return false;
}
// External gas amount above threshold.
if (!float.IsInfinity(miner.MaxExternalAmount) && tile.Air.TotalMoles > miner.MaxExternalAmount)
{
miner.Broken = true;
return false;
}
miner.Broken = false;
return true;
}
}
}

View File

@@ -0,0 +1,35 @@
#nullable enable
using Content.Shared.Atmos;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Atmos.Piping.Trinary.Components
{
[RegisterComponent]
public class GasFilterComponent : Component
{
public override string Name => "GasFilter";
[ViewVariables(VVAccess.ReadWrite)]
public bool Enabled { get; set; } = true;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("inlet")]
public string InletName { get; set; } = "inlet";
[ViewVariables(VVAccess.ReadWrite)]
[DataField("filter")]
public string FilterName { get; set; } = "filter";
[ViewVariables(VVAccess.ReadWrite)]
[DataField("outlet")]
public string OutletName { get; set; } = "outlet";
[ViewVariables(VVAccess.ReadWrite)]
public float TransferRate { get; set; } = Atmospherics.MaxTransferRate;
[ViewVariables(VVAccess.ReadWrite)]
public Gas? FilteredGas { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show More