diff --git a/Content.Client/GameObjects/Components/Atmos/GasAnalyzerBoundUserInterface.cs b/Content.Client/GameObjects/Components/Atmos/GasAnalyzerBoundUserInterface.cs new file mode 100644 index 0000000000..c4907bc31d --- /dev/null +++ b/Content.Client/GameObjects/Components/Atmos/GasAnalyzerBoundUserInterface.cs @@ -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(); + } + } +} diff --git a/Content.Client/GameObjects/Components/Atmos/GasAnalyzerComponent.cs b/Content.Client/GameObjects/Components/Atmos/GasAnalyzerComponent.cs new file mode 100644 index 0000000000..e8b9a29700 --- /dev/null +++ b/Content.Client/GameObjects/Components/Atmos/GasAnalyzerComponent.cs @@ -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))); + } + } + } +} diff --git a/Content.Client/GameObjects/Components/Atmos/GasAnalyzerMenu.cs b/Content.Client/GameObjects/Components/Atmos/GasAnalyzerMenu.cs new file mode 100644 index 0000000000..35e6997381 --- /dev/null +++ b/Content.Client/GameObjects/Components/Atmos/GasAnalyzerMenu.cs @@ -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(); + + 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; + } + } +} diff --git a/Content.Server/GameObjects/Components/Atmos/GasAnalyzerComponent.cs b/Content.Server/GameObjects/Components/Atmos/GasAnalyzerComponent.cs index 8d3970797a..0e5bd87125 100644 --- a/Content.Server/GameObjects/Components/Atmos/GasAnalyzerComponent.cs +++ b/Content.Server/GameObjects/Components/Atmos/GasAnalyzerComponent.cs @@ -1,39 +1,190 @@ #nullable enable using Content.Server.GameObjects.EntitySystems; +using Content.Server.Interfaces; +using Content.Server.Interfaces.GameObjects.Components.Items; using Content.Shared.Atmos; +using Content.Shared.GameObjects.Components; 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.Systems; +using Robust.Shared.IoC; +using Robust.Shared.Localization; using Robust.Shared.Map; using Robust.Shared.Utility; +using System; +using System.Collections.Generic; namespace Content.Server.GameObjects.Components.Atmos { [RegisterComponent] - public class GasAnalyzerComponent : Component, IExamine + public class GasAnalyzerComponent : SharedGasAnalyzerComponent, IAfterInteract, IDropped { - public override string Name => "GasAnalyzer"; - public void Examine(FormattedMessage message, bool inDetailsRange) - { - if (!inDetailsRange) return; +#pragma warning disable 649 + [Dependency] private IServerNotifyManager _notifyManager = default!; +#pragma warning restore 649 + private BoundUserInterface _userInterface = default!; + private GasAnalyzerDanger _pressureDanger; + private float _timeSinceSync; + private const float TimeBetweenSyncs = 10f; + + public override void Initialize() + { + base.Initialize(); + _userInterface = Owner.GetComponent() + .GetBoundUserInterface(GasAnalyzerUiKey.Key); + _userInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage; + } + + public override ComponentState GetComponentState() + { + return new GasAnalyzerComponentState(_pressureDanger); + } + + /// + /// Call this from other components to open the gas analyzer UI. + /// + 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().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().GetGridAtmosphere(Owner.Transform.GridID); 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; - - message.AddText($"Pressure: {tile.Pressure}\n"); - message.AddText($"Temperature: {tile.Temperature}\n"); - + var gases = new List(); for (int i = 0; i < Atmospherics.TotalNumberOfGases; i++) { var gas = Atmospherics.GetGas(i); if (tile.Gases[i] <= Atmospherics.GasMinMoles) continue; - message.AddText(gas.Name); - message.AddText($"\n Moles: {tile.Gases[i]}\n"); + gases.Add(new GasEntry(gas.Name, tile.Gases[i], gas.Color)); + } + + _userInterface.SetState( + new GasAnalyzerBoundUserInterfaceState( + tile.Pressure, + tile.Temperature, + gases.ToArray(), + error)); + } + + private void UserInterfaceOnReceiveMessage(ServerBoundUserInterfaceMessage serverMsg) + { + var message = serverMsg.Message; + switch (message) + { + case GasAnalyzerRefreshMessage msg: + var player = serverMsg.Session.AttachedEntity; + if (player == null) + { + return; + } + + if (!player.TryGetComponent(out IHandsComponent handsComponent)) + { + _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 } } } diff --git a/Content.Server/GameObjects/EntitySystems/GasAnalyzerSystem.cs b/Content.Server/GameObjects/EntitySystems/GasAnalyzerSystem.cs new file mode 100644 index 0000000000..79e50ced63 --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/GasAnalyzerSystem.cs @@ -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()) + { + analyzer.Update(frameTime); + } + } + } +} diff --git a/Content.Shared/Atmos/GasPrototype.cs b/Content.Shared/Atmos/GasPrototype.cs index 95aed54219..8237d54b4c 100644 --- a/Content.Shared/Atmos/GasPrototype.cs +++ b/Content.Shared/Atmos/GasPrototype.cs @@ -69,6 +69,8 @@ namespace Content.Shared.Atmos /// public string OverlayPath { get; private set; } + public string Color { get; private set; } + public void LoadFrom(YamlMappingNode 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 => GasOverlaySprite, "gasOverlaySprite", string.Empty); serializer.DataField(this, x => GasOverlayState, "gasOverlayState", string.Empty); + serializer.DataField(this, x => Color, "color", string.Empty); } } } diff --git a/Content.Shared/GameObjects/Components/SharedGasAnalyzerComponent.cs b/Content.Shared/GameObjects/Components/SharedGasAnalyzerComponent.cs new file mode 100644 index 0000000000..d5ab8162ad --- /dev/null +++ b/Content.Shared/GameObjects/Components/SharedGasAnalyzerComponent.cs @@ -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; + } + } + } +} diff --git a/Content.Shared/GameObjects/ContentNetIDs.cs b/Content.Shared/GameObjects/ContentNetIDs.cs index 0a2854e429..6a38d3e669 100644 --- a/Content.Shared/GameObjects/ContentNetIDs.cs +++ b/Content.Shared/GameObjects/ContentNetIDs.cs @@ -61,7 +61,8 @@ public const uint THROWN_ITEM = 1054; public const uint STRAP = 1055; 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. public const uint PREDICTION_TEST = 10001; diff --git a/Content.Shared/Utility/TemperatureHelpers.cs b/Content.Shared/Utility/TemperatureHelpers.cs new file mode 100644 index 0000000000..529c8d6746 --- /dev/null +++ b/Content.Shared/Utility/TemperatureHelpers.cs @@ -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; + } + } +} diff --git a/Resources/Prototypes/Atmospherics/gases.yml b/Resources/Prototypes/Atmospherics/gases.yml index b6ec483c1b..7862919b50 100644 --- a/Resources/Prototypes/Atmospherics/gases.yml +++ b/Resources/Prototypes/Atmospherics/gases.yml @@ -2,16 +2,19 @@ id: 0 name: Oxygen specificHeat: 20 + color: 2887E8 - type: gas id: 1 name: Nitrogen specificHeat: 30 + color: DA1010 - type: gas id: 2 name: Carbon Dioxide specificHeat: 30 + color: 4e4e4e - type: gas id: 3 @@ -19,6 +22,7 @@ specificHeat: 200 gasOverlaySprite: /Textures/Effects/atmospherics.rsi gasOverlayState: phoron + color: FF3300 - type: gas id: 4 @@ -26,3 +30,4 @@ specificHeat: 10 gasOverlaySprite: /Textures/Effects/atmospherics.rsi gasOverlayState: tritium + color: 13FF4B diff --git a/Resources/Prototypes/Entities/Objects/Specific/atmos.yml b/Resources/Prototypes/Entities/Objects/Specific/atmos.yml new file mode 100644 index 0000000000..7dce8b5aa0 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Specific/atmos.yml @@ -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 diff --git a/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/atmos2.png b/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/atmos2.png new file mode 100644 index 0000000000..12af2b52d1 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/atmos2.png differ diff --git a/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/icon.png b/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/icon.png new file mode 100644 index 0000000000..ffd06e791b Binary files /dev/null and b/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/meta.json b/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/meta.json new file mode 100644 index 0000000000..97925d64e5 --- /dev/null +++ b/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/meta.json @@ -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 + ] + ] + } + ] +} \ No newline at end of file