Adds Gas Analyzer (#1591)
* -Started Gas Analyzer -TemperatureHelpers * Formatting * Adds PopupTooltip to NotifyManager * Revert Tooltip fuckery * Gas Analyzer gives proper error messages * Localization * Added a very wip gas analyzer ui * UI works, doesn't look good but hey * Safety checks * Fancy WIP gas mix bar * Gas Color * Gas Amount shows only 2 decimal places * -Made bar full width -Moved gas list into a table -Some gas bar things * IDropped something * Description * -Percentage -Padding * ItemStatus * -Proper Danger Warnings -Added Warning danger state * Pressure unit Co-authored-by: Víctor Aguilera Puerto <6766154+Zumorica@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
|||||||
|
using Robust.Client.GameObjects.Components.UserInterface;
|
||||||
|
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using static Content.Shared.GameObjects.Components.SharedGasAnalyzerComponent;
|
||||||
|
|
||||||
|
namespace Content.Client.GameObjects.Components.Atmos
|
||||||
|
{
|
||||||
|
public class GasAnalyzerBoundUserInterface : BoundUserInterface
|
||||||
|
{
|
||||||
|
public GasAnalyzerBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private GasAnalyzerWindow _menu;
|
||||||
|
|
||||||
|
protected override void Open()
|
||||||
|
{
|
||||||
|
base.Open();
|
||||||
|
_menu = new GasAnalyzerWindow(this);
|
||||||
|
|
||||||
|
_menu.OnClose += Close;
|
||||||
|
_menu.OpenCentered();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
|
{
|
||||||
|
base.UpdateState(state);
|
||||||
|
_menu.Populate((GasAnalyzerBoundUserInterfaceState) state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Refresh()
|
||||||
|
{
|
||||||
|
SendMessage(new GasAnalyzerRefreshMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
|
||||||
|
_menu.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
using Content.Client.UserInterface.Stylesheets;
|
||||||
|
using Content.Client.Utility;
|
||||||
|
using Content.Shared.GameObjects;
|
||||||
|
using Content.Shared.GameObjects.Components;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Content.Client.GameObjects.Components.Atmos
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
class GasAnalyzerComponent : SharedGasAnalyzerComponent, IItemStatus
|
||||||
|
{
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded;
|
||||||
|
[ViewVariables] public GasAnalyzerDanger Danger { get; private set; }
|
||||||
|
|
||||||
|
Control IItemStatus.MakeControl()
|
||||||
|
{
|
||||||
|
return new StatusControl(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||||
|
{
|
||||||
|
if (!(curState is GasAnalyzerComponentState state))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Danger = state.Danger;
|
||||||
|
_uiUpdateNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class StatusControl : Control
|
||||||
|
{
|
||||||
|
private readonly GasAnalyzerComponent _parent;
|
||||||
|
private readonly RichTextLabel _label;
|
||||||
|
|
||||||
|
public StatusControl(GasAnalyzerComponent parent)
|
||||||
|
{
|
||||||
|
_parent = parent;
|
||||||
|
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
|
||||||
|
AddChild(_label);
|
||||||
|
|
||||||
|
parent._uiUpdateNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update(FrameEventArgs args)
|
||||||
|
{
|
||||||
|
base.Update(args);
|
||||||
|
|
||||||
|
if (!_parent._uiUpdateNeeded)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_parent._uiUpdateNeeded = false;
|
||||||
|
var color = _parent.Danger switch
|
||||||
|
{
|
||||||
|
GasAnalyzerDanger.Warning => "orange",
|
||||||
|
GasAnalyzerDanger.Hazard => "red",
|
||||||
|
_ => "green",
|
||||||
|
};
|
||||||
|
_label.SetMarkup(Loc.GetString("Pressure: [color={0}]{1}[/color]",
|
||||||
|
color,
|
||||||
|
Enum.GetName(typeof(GasAnalyzerDanger), _parent.Danger)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
284
Content.Client/GameObjects/Components/Atmos/GasAnalyzerMenu.cs
Normal file
284
Content.Client/GameObjects/Components/Atmos/GasAnalyzerMenu.cs
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
using System;
|
||||||
|
using Content.Client.Animations;
|
||||||
|
using Content.Client.GameObjects.EntitySystems;
|
||||||
|
using Content.Client.UserInterface.Stylesheets;
|
||||||
|
using Content.Client.Utility;
|
||||||
|
using Content.Shared.GameObjects.Components;
|
||||||
|
using Content.Shared.Utility;
|
||||||
|
using Robust.Client.Animations;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.Graphics.Drawing;
|
||||||
|
using Robust.Client.Interfaces.ResourceManagement;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Shared.Animations;
|
||||||
|
using Robust.Shared.Input;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using static Content.Shared.GameObjects.Components.SharedGasAnalyzerComponent;
|
||||||
|
|
||||||
|
namespace Content.Client.GameObjects.Components.Atmos
|
||||||
|
{
|
||||||
|
public class GasAnalyzerWindow : BaseWindow
|
||||||
|
{
|
||||||
|
public GasAnalyzerBoundUserInterface Owner { get; }
|
||||||
|
|
||||||
|
private readonly Control _topContainer;
|
||||||
|
private readonly Control _statusContainer;
|
||||||
|
|
||||||
|
private readonly Label _nameLabel;
|
||||||
|
|
||||||
|
public TextureButton CloseButton { get; set; }
|
||||||
|
|
||||||
|
public GasAnalyzerWindow(GasAnalyzerBoundUserInterface owner)
|
||||||
|
{
|
||||||
|
var resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||||
|
|
||||||
|
Owner = owner;
|
||||||
|
var rootContainer = new LayoutContainer { Name = "WireRoot" };
|
||||||
|
AddChild(rootContainer);
|
||||||
|
|
||||||
|
MouseFilter = MouseFilterMode.Stop;
|
||||||
|
|
||||||
|
var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
|
||||||
|
var back = new StyleBoxTexture
|
||||||
|
{
|
||||||
|
Texture = panelTex,
|
||||||
|
Modulate = Color.FromHex("#25252A"),
|
||||||
|
};
|
||||||
|
back.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||||
|
|
||||||
|
var topPanel = new PanelContainer
|
||||||
|
{
|
||||||
|
PanelOverride = back,
|
||||||
|
MouseFilter = MouseFilterMode.Pass
|
||||||
|
};
|
||||||
|
var bottomWrap = new LayoutContainer
|
||||||
|
{
|
||||||
|
Name = "BottomWrap"
|
||||||
|
};
|
||||||
|
|
||||||
|
rootContainer.AddChild(topPanel);
|
||||||
|
rootContainer.AddChild(bottomWrap);
|
||||||
|
|
||||||
|
LayoutContainer.SetAnchorPreset(topPanel, LayoutContainer.LayoutPreset.Wide);
|
||||||
|
LayoutContainer.SetMarginBottom(topPanel, -80);
|
||||||
|
|
||||||
|
LayoutContainer.SetAnchorPreset(bottomWrap, LayoutContainer.LayoutPreset.VerticalCenterWide);
|
||||||
|
LayoutContainer.SetGrowHorizontal(bottomWrap, LayoutContainer.GrowDirection.Both);
|
||||||
|
|
||||||
|
var topContainerWrap = new VBoxContainer
|
||||||
|
{
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
(_topContainer = new VBoxContainer()),
|
||||||
|
new Control {CustomMinimumSize = (0, 110)}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
rootContainer.AddChild(topContainerWrap);
|
||||||
|
|
||||||
|
LayoutContainer.SetAnchorPreset(topContainerWrap, LayoutContainer.LayoutPreset.Wide);
|
||||||
|
|
||||||
|
var font = resourceCache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13);
|
||||||
|
var fontSmall = resourceCache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 10);
|
||||||
|
|
||||||
|
Button refreshButton;
|
||||||
|
var topRow = new MarginContainer
|
||||||
|
{
|
||||||
|
MarginLeftOverride = 4,
|
||||||
|
MarginTopOverride = 2,
|
||||||
|
MarginRightOverride = 12,
|
||||||
|
MarginBottomOverride = 2,
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
new HBoxContainer
|
||||||
|
{
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
(_nameLabel = new Label
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("Gas Analyzer"),
|
||||||
|
FontOverride = font,
|
||||||
|
FontColorOverride = StyleNano.NanoGold,
|
||||||
|
SizeFlagsVertical = SizeFlags.ShrinkCenter
|
||||||
|
}),
|
||||||
|
new Control
|
||||||
|
{
|
||||||
|
CustomMinimumSize = (20, 0),
|
||||||
|
SizeFlagsHorizontal = SizeFlags.Expand
|
||||||
|
},
|
||||||
|
(refreshButton = new Button {Text = "Refresh"}), //TODO: refresh icon?
|
||||||
|
new Control
|
||||||
|
{
|
||||||
|
CustomMinimumSize = (2, 0),
|
||||||
|
},
|
||||||
|
(CloseButton = new TextureButton
|
||||||
|
{
|
||||||
|
StyleClasses = {SS14Window.StyleClassWindowCloseButton},
|
||||||
|
SizeFlagsVertical = SizeFlags.ShrinkCenter
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
refreshButton.OnPressed += a =>
|
||||||
|
{
|
||||||
|
Owner.Refresh();
|
||||||
|
};
|
||||||
|
|
||||||
|
var middle = new PanelContainer
|
||||||
|
{
|
||||||
|
PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#202025") },
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
new MarginContainer
|
||||||
|
{
|
||||||
|
MarginLeftOverride = 8,
|
||||||
|
MarginRightOverride = 8,
|
||||||
|
MarginTopOverride = 4,
|
||||||
|
MarginBottomOverride = 4,
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
(_statusContainer = new VBoxContainer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_topContainer.AddChild(topRow);
|
||||||
|
_topContainer.AddChild(new PanelContainer
|
||||||
|
{
|
||||||
|
CustomMinimumSize = (0, 2),
|
||||||
|
PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#525252ff") }
|
||||||
|
});
|
||||||
|
_topContainer.AddChild(middle);
|
||||||
|
_topContainer.AddChild(new PanelContainer
|
||||||
|
{
|
||||||
|
CustomMinimumSize = (0, 2),
|
||||||
|
PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#525252ff") }
|
||||||
|
});
|
||||||
|
CloseButton.OnPressed += _ => Close();
|
||||||
|
LayoutContainer.SetSize(this, (300, 200));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Populate(GasAnalyzerBoundUserInterfaceState state)
|
||||||
|
{
|
||||||
|
_statusContainer.RemoveAllChildren();
|
||||||
|
if (state.Error != null)
|
||||||
|
{
|
||||||
|
_statusContainer.AddChild(new Label
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("Error: {0}", state.Error),
|
||||||
|
FontColorOverride = Color.Red
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_statusContainer.AddChild(new Label
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("Pressure: {0:0.##} kPa", state.Pressure)
|
||||||
|
});
|
||||||
|
_statusContainer.AddChild(new Label
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("Temperature: {0:0.#}K ({1:0.#}°C)", state.Pressure, TemperatureHelpers.KelvinToCelsius(state.Pressure))
|
||||||
|
});
|
||||||
|
// Return here cause all that stuff down there is gas stuff (so we don't get the seperators)
|
||||||
|
if (state.Gases.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Seperator
|
||||||
|
_statusContainer.AddChild(new Control
|
||||||
|
{
|
||||||
|
CustomMinimumSize = new Vector2(0, 10)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a table with all the gases
|
||||||
|
var tableKey = new VBoxContainer();
|
||||||
|
var tableVal = new VBoxContainer();
|
||||||
|
_statusContainer.AddChild(new HBoxContainer
|
||||||
|
{
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
tableKey,
|
||||||
|
new Control
|
||||||
|
{
|
||||||
|
CustomMinimumSize = new Vector2(20, 0)
|
||||||
|
},
|
||||||
|
tableVal
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// This is the gas bar thingy
|
||||||
|
var height = 30;
|
||||||
|
var minSize = 24; // This basically allows gases which are too small, to be shown properly
|
||||||
|
var gasBar = new HBoxContainer
|
||||||
|
{
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
CustomMinimumSize = new Vector2(0, height)
|
||||||
|
};
|
||||||
|
// Seperator
|
||||||
|
_statusContainer.AddChild(new Control
|
||||||
|
{
|
||||||
|
CustomMinimumSize = new Vector2(0, 10)
|
||||||
|
});
|
||||||
|
|
||||||
|
var totalGasAmount = 0f;
|
||||||
|
foreach (var gas in state.Gases)
|
||||||
|
{
|
||||||
|
totalGasAmount += gas.Amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < state.Gases.Length; i++)
|
||||||
|
{
|
||||||
|
var gas = state.Gases[i];
|
||||||
|
var color = Color.FromHex($"#{gas.Color}", Color.White);
|
||||||
|
// Add to the table
|
||||||
|
tableKey.AddChild(new Label
|
||||||
|
{
|
||||||
|
Text = Loc.GetString(gas.Name)
|
||||||
|
});
|
||||||
|
tableVal.AddChild(new Label
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("{0:0.##} mol", gas.Amount)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add to the gas bar //TODO: highlight the currently hover one
|
||||||
|
var left = (i == 0) ? 0f : 2f;
|
||||||
|
var right = (i == state.Gases.Length - 1) ? 0f : 2f;
|
||||||
|
gasBar.AddChild(new PanelContainer
|
||||||
|
{
|
||||||
|
ToolTip = Loc.GetString("{0}: {1:0.##} mol ({2:0.#}%)", gas.Name, gas.Amount, (gas.Amount / totalGasAmount) * 100),
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
SizeFlagsStretchRatio = gas.Amount,
|
||||||
|
MouseFilter = MouseFilterMode.Pass,
|
||||||
|
PanelOverride = new StyleBoxFlat
|
||||||
|
{
|
||||||
|
BackgroundColor = color,
|
||||||
|
PaddingLeft = left,
|
||||||
|
PaddingRight = right
|
||||||
|
},
|
||||||
|
CustomMinimumSize = new Vector2(minSize, 0)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_statusContainer.AddChild(gasBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
|
||||||
|
{
|
||||||
|
return DragMode.Move;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool HasPoint(Vector2 point)
|
||||||
|
{
|
||||||
|
// This makes it so our base window won't count for hit tests,
|
||||||
|
// but we will still receive mouse events coming in from Pass mouse filter mode.
|
||||||
|
// So basically, it perfectly shells out the hit tests to the panels we have!
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,39 +1,190 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
using Content.Server.GameObjects.EntitySystems;
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
|
using Content.Server.Interfaces;
|
||||||
|
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
|
using Content.Shared.GameObjects.Components;
|
||||||
using Content.Shared.GameObjects.EntitySystems;
|
using Content.Shared.GameObjects.EntitySystems;
|
||||||
|
using Content.Shared.Interfaces;
|
||||||
|
using Content.Shared.Interfaces.GameObjects.Components;
|
||||||
|
using Content.Shared.Utility;
|
||||||
|
using Robust.Server.GameObjects.Components.UserInterface;
|
||||||
|
using Robust.Server.Interfaces.GameObjects;
|
||||||
|
using Robust.Server.Interfaces.Player;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.GameObjects.Systems;
|
using Robust.Shared.GameObjects.Systems;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Atmos
|
namespace Content.Server.GameObjects.Components.Atmos
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class GasAnalyzerComponent : Component, IExamine
|
public class GasAnalyzerComponent : SharedGasAnalyzerComponent, IAfterInteract, IDropped
|
||||||
{
|
{
|
||||||
public override string Name => "GasAnalyzer";
|
#pragma warning disable 649
|
||||||
public void Examine(FormattedMessage message, bool inDetailsRange)
|
[Dependency] private IServerNotifyManager _notifyManager = default!;
|
||||||
{
|
#pragma warning restore 649
|
||||||
if (!inDetailsRange) return;
|
|
||||||
|
|
||||||
|
private BoundUserInterface _userInterface = default!;
|
||||||
|
private GasAnalyzerDanger _pressureDanger;
|
||||||
|
private float _timeSinceSync;
|
||||||
|
private const float TimeBetweenSyncs = 10f;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||||
|
.GetBoundUserInterface(GasAnalyzerUiKey.Key);
|
||||||
|
_userInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ComponentState GetComponentState()
|
||||||
|
{
|
||||||
|
return new GasAnalyzerComponentState(_pressureDanger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Call this from other components to open the gas analyzer UI.
|
||||||
|
/// </summary>
|
||||||
|
public void OpenInterface(IPlayerSession session)
|
||||||
|
{
|
||||||
|
_userInterface.Open(session);
|
||||||
|
UpdateUserInterface();
|
||||||
|
Resync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CloseInterface(IPlayerSession session)
|
||||||
|
{
|
||||||
|
_userInterface.Close(session);
|
||||||
|
Resync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(float frameTime)
|
||||||
|
{
|
||||||
|
_timeSinceSync += frameTime;
|
||||||
|
if (_timeSinceSync > TimeBetweenSyncs)
|
||||||
|
{
|
||||||
|
Resync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Resync()
|
||||||
|
{
|
||||||
|
// Already get the pressure before Dirty(), because we can't get the EntitySystem in that thread or smth
|
||||||
|
var pressure = 0f;
|
||||||
|
var gam = EntitySystem.Get<AtmosphereSystem>().GetGridAtmosphere(Owner.Transform.GridID);
|
||||||
|
var tile = gam?.GetTile(Owner.Transform.GridPosition).Air;
|
||||||
|
if (tile != null)
|
||||||
|
{
|
||||||
|
pressure = tile.Pressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pressure >= Atmospherics.HazardHighPressure || pressure <= Atmospherics.HazardLowPressure)
|
||||||
|
{
|
||||||
|
_pressureDanger = GasAnalyzerDanger.Hazard;
|
||||||
|
}
|
||||||
|
else if (pressure >= Atmospherics.WarningHighPressure || pressure <= Atmospherics.WarningLowPressure)
|
||||||
|
{
|
||||||
|
_pressureDanger = GasAnalyzerDanger.Warning;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_pressureDanger = GasAnalyzerDanger.Nominal;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dirty();
|
||||||
|
_timeSinceSync = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateUserInterface()
|
||||||
|
{
|
||||||
|
string? error = null;
|
||||||
var gam = EntitySystem.Get<AtmosphereSystem>().GetGridAtmosphere(Owner.Transform.GridID);
|
var gam = EntitySystem.Get<AtmosphereSystem>().GetGridAtmosphere(Owner.Transform.GridID);
|
||||||
|
|
||||||
var tile = gam?.GetTile(Owner.Transform.GridPosition).Air;
|
var tile = gam?.GetTile(Owner.Transform.GridPosition).Air;
|
||||||
|
if (tile == null)
|
||||||
|
{
|
||||||
|
error = "No Atmosphere!";
|
||||||
|
_userInterface.SetState(
|
||||||
|
new GasAnalyzerBoundUserInterfaceState(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
null,
|
||||||
|
error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (tile == null) return;
|
var gases = new List<GasEntry>();
|
||||||
|
|
||||||
message.AddText($"Pressure: {tile.Pressure}\n");
|
|
||||||
message.AddText($"Temperature: {tile.Temperature}\n");
|
|
||||||
|
|
||||||
for (int i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
for (int i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||||
{
|
{
|
||||||
var gas = Atmospherics.GetGas(i);
|
var gas = Atmospherics.GetGas(i);
|
||||||
|
|
||||||
if (tile.Gases[i] <= Atmospherics.GasMinMoles) continue;
|
if (tile.Gases[i] <= Atmospherics.GasMinMoles) continue;
|
||||||
|
|
||||||
message.AddText(gas.Name);
|
gases.Add(new GasEntry(gas.Name, tile.Gases[i], gas.Color));
|
||||||
message.AddText($"\n Moles: {tile.Gases[i]}\n");
|
}
|
||||||
|
|
||||||
|
_userInterface.SetState(
|
||||||
|
new GasAnalyzerBoundUserInterfaceState(
|
||||||
|
tile.Pressure,
|
||||||
|
tile.Temperature,
|
||||||
|
gases.ToArray(),
|
||||||
|
error));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UserInterfaceOnReceiveMessage(ServerBoundUserInterfaceMessage serverMsg)
|
||||||
|
{
|
||||||
|
var message = serverMsg.Message;
|
||||||
|
switch (message)
|
||||||
|
{
|
||||||
|
case GasAnalyzerRefreshMessage msg:
|
||||||
|
var player = serverMsg.Session.AttachedEntity;
|
||||||
|
if (player == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!player.TryGetComponent(out IHandsComponent handsComponent))
|
||||||
|
{
|
||||||
|
_notifyManager.PopupMessage(Owner.Transform.GridPosition, player,
|
||||||
|
Loc.GetString("You have no hands."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var activeHandEntity = handsComponent.GetActiveHand?.Owner;
|
||||||
|
if (activeHandEntity == null || !activeHandEntity.TryGetComponent(out GasAnalyzerComponent gasAnalyzer))
|
||||||
|
{
|
||||||
|
_notifyManager.PopupMessage(serverMsg.Session.AttachedEntity,
|
||||||
|
serverMsg.Session.AttachedEntity,
|
||||||
|
Loc.GetString("You need a Gas Analyzer in your hand!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateUserInterface();
|
||||||
|
Resync();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
if (eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||||
|
{
|
||||||
|
OpenInterface(actor.playerSession);
|
||||||
|
//TODO: show other sprite when ui open?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IDropped.Dropped(DroppedEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
if (eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||||
|
{
|
||||||
|
CloseInterface(actor.playerSession);
|
||||||
|
//TODO: if other sprite is shown, change again
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using Content.Server.GameObjects.Components.Atmos;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects.Systems;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.EntitySystems
|
||||||
|
{
|
||||||
|
public class GasAnalyzerSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
foreach (var analyzer in ComponentManager.EntityQuery<GasAnalyzerComponent>())
|
||||||
|
{
|
||||||
|
analyzer.Update(frameTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,6 +69,8 @@ namespace Content.Shared.Atmos
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string OverlayPath { get; private set; }
|
public string OverlayPath { get; private set; }
|
||||||
|
|
||||||
|
public string Color { get; private set; }
|
||||||
|
|
||||||
public void LoadFrom(YamlMappingNode mapping)
|
public void LoadFrom(YamlMappingNode mapping)
|
||||||
{
|
{
|
||||||
var serializer = YamlObjectSerializer.NewReader(mapping);
|
var serializer = YamlObjectSerializer.NewReader(mapping);
|
||||||
@@ -81,6 +83,7 @@ namespace Content.Shared.Atmos
|
|||||||
serializer.DataField(this, x => GasOverlayTexture, "gasOverlayTexture", string.Empty);
|
serializer.DataField(this, x => GasOverlayTexture, "gasOverlayTexture", string.Empty);
|
||||||
serializer.DataField(this, x => GasOverlaySprite, "gasOverlaySprite", string.Empty);
|
serializer.DataField(this, x => GasOverlaySprite, "gasOverlaySprite", string.Empty);
|
||||||
serializer.DataField(this, x => GasOverlayState, "gasOverlayState", string.Empty);
|
serializer.DataField(this, x => GasOverlayState, "gasOverlayState", string.Empty);
|
||||||
|
serializer.DataField(this, x => Color, "color", string.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Content.Shared.GameObjects.Components
|
||||||
|
{
|
||||||
|
public class SharedGasAnalyzerComponent : Component
|
||||||
|
{
|
||||||
|
public override string Name => "GasAnalyzer";
|
||||||
|
public override uint? NetID => ContentNetIDs.GAS_ANALYZER;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum GasAnalyzerUiKey
|
||||||
|
{
|
||||||
|
Key,
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class GasAnalyzerBoundUserInterfaceState : BoundUserInterfaceState
|
||||||
|
{
|
||||||
|
public float Pressure;
|
||||||
|
public float Temperature;
|
||||||
|
public GasEntry[] Gases;
|
||||||
|
public string Error;
|
||||||
|
|
||||||
|
public GasAnalyzerBoundUserInterfaceState(float pressure, float temperature, GasEntry[] gases, string error = null)
|
||||||
|
{
|
||||||
|
Pressure = pressure;
|
||||||
|
Temperature = temperature;
|
||||||
|
Gases = gases;
|
||||||
|
Error = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public struct GasEntry
|
||||||
|
{
|
||||||
|
public readonly string Name;
|
||||||
|
public readonly float Amount;
|
||||||
|
public readonly string Color;
|
||||||
|
|
||||||
|
public GasEntry(string name, float amount, string color)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Amount = amount;
|
||||||
|
Color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Loc.GetString("{0}: {1:0.##} mol", Name, Amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class GasAnalyzerRefreshMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public GasAnalyzerRefreshMessage() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum GasAnalyzerDanger
|
||||||
|
{
|
||||||
|
Nominal,
|
||||||
|
Warning,
|
||||||
|
Hazard
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class GasAnalyzerComponentState : ComponentState
|
||||||
|
{
|
||||||
|
public GasAnalyzerDanger Danger;
|
||||||
|
|
||||||
|
public GasAnalyzerComponentState(GasAnalyzerDanger danger) : base(ContentNetIDs.GAS_ANALYZER)
|
||||||
|
{
|
||||||
|
Danger = danger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,7 +61,8 @@
|
|||||||
public const uint THROWN_ITEM = 1054;
|
public const uint THROWN_ITEM = 1054;
|
||||||
public const uint STRAP = 1055;
|
public const uint STRAP = 1055;
|
||||||
public const uint DISPOSABLE = 1056;
|
public const uint DISPOSABLE = 1056;
|
||||||
public const uint DO_AFTER = 1057;
|
public const uint GAS_ANALYZER = 1057;
|
||||||
|
public const uint DO_AFTER = 1058;
|
||||||
|
|
||||||
// Net IDs for integration tests.
|
// Net IDs for integration tests.
|
||||||
public const uint PREDICTION_TEST = 10001;
|
public const uint PREDICTION_TEST = 10001;
|
||||||
|
|||||||
20
Content.Shared/Utility/TemperatureHelpers.cs
Normal file
20
Content.Shared/Utility/TemperatureHelpers.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using Content.Shared.Maths;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Content.Shared.Utility
|
||||||
|
{
|
||||||
|
public static class TemperatureHelpers
|
||||||
|
{
|
||||||
|
public static float CelsiusToKelvin(float celsius)
|
||||||
|
{
|
||||||
|
return celsius + PhysicalConstants.ZERO_CELCIUS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float KelvinToCelsius(float kelvin)
|
||||||
|
{
|
||||||
|
return kelvin - PhysicalConstants.ZERO_CELCIUS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,16 +2,19 @@
|
|||||||
id: 0
|
id: 0
|
||||||
name: Oxygen
|
name: Oxygen
|
||||||
specificHeat: 20
|
specificHeat: 20
|
||||||
|
color: 2887E8
|
||||||
|
|
||||||
- type: gas
|
- type: gas
|
||||||
id: 1
|
id: 1
|
||||||
name: Nitrogen
|
name: Nitrogen
|
||||||
specificHeat: 30
|
specificHeat: 30
|
||||||
|
color: DA1010
|
||||||
|
|
||||||
- type: gas
|
- type: gas
|
||||||
id: 2
|
id: 2
|
||||||
name: Carbon Dioxide
|
name: Carbon Dioxide
|
||||||
specificHeat: 30
|
specificHeat: 30
|
||||||
|
color: 4e4e4e
|
||||||
|
|
||||||
- type: gas
|
- type: gas
|
||||||
id: 3
|
id: 3
|
||||||
@@ -19,6 +22,7 @@
|
|||||||
specificHeat: 200
|
specificHeat: 200
|
||||||
gasOverlaySprite: /Textures/Effects/atmospherics.rsi
|
gasOverlaySprite: /Textures/Effects/atmospherics.rsi
|
||||||
gasOverlayState: phoron
|
gasOverlayState: phoron
|
||||||
|
color: FF3300
|
||||||
|
|
||||||
- type: gas
|
- type: gas
|
||||||
id: 4
|
id: 4
|
||||||
@@ -26,3 +30,4 @@
|
|||||||
specificHeat: 10
|
specificHeat: 10
|
||||||
gasOverlaySprite: /Textures/Effects/atmospherics.rsi
|
gasOverlaySprite: /Textures/Effects/atmospherics.rsi
|
||||||
gasOverlayState: tritium
|
gasOverlayState: tritium
|
||||||
|
color: 13FF4B
|
||||||
|
|||||||
17
Resources/Prototypes/Entities/Objects/Specific/atmos.yml
Normal file
17
Resources/Prototypes/Entities/Objects/Specific/atmos.yml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
- type: entity
|
||||||
|
name: gas analyzer
|
||||||
|
parent: BaseItem
|
||||||
|
id: GasAnalyzer
|
||||||
|
description: A hand-held environmental scanner which reports current gas levels.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Specific/Atmos/gasanalyzer.rsi
|
||||||
|
state: icon
|
||||||
|
- type: Icon
|
||||||
|
sprite: Objects/Specific/Atmos/gasanalyzer.rsi
|
||||||
|
state: icon
|
||||||
|
- type: GasAnalyzer
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
- key: enum.GasAnalyzerUiKey.Key
|
||||||
|
type: GasAnalyzerBoundUserInterface
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1019 B |
Binary file not shown.
|
After Width: | Height: | Size: 487 B |
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "icon",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
1.0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "atmos2",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.1,
|
||||||
|
0.1,
|
||||||
|
0.1,
|
||||||
|
3.0,
|
||||||
|
0.3,
|
||||||
|
0.2,
|
||||||
|
0.8
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user