From 870d05235446e301b6eab4622213e4d288c3efad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Aguilera=20Puerto?= <6766154+Zumorica@users.noreply.github.com> Date: Tue, 27 Oct 2020 20:53:44 +0100 Subject: [PATCH] Gas tanks and masks (#2409) Co-authored-by: a.rudenko Co-authored-by: creadth Co-authored-by: DrSmugleaf --- Content.Client/IgnoredComponents.cs | 5 +- .../GasTank/GasTankBoundUserInterface.cs | 50 +++ .../Atmos/GasTank/GasTankWindow.cs | 235 ++++++++++++ .../Tests/Body/LungTest.cs | 7 +- Content.Server/Atmos/GasMixture.cs | 37 +- Content.Server/Atmos/TileAtmosphere.cs | 4 + .../Components/Atmos/BreathToolComponent.cs | 70 ++++ .../Atmos/GasMixtureHolderComponent.cs | 13 +- .../Components/Atmos/GasTankComponent.cs | 352 +++++++++++++++++- .../Body/Behavior/LungBehaviorComponent.cs | 16 +- .../Body/Respiratory/InternalsComponent.cs | 63 ++++ .../Components/GUI/HandsComponent.cs | 24 +- .../Components/GUI/InventoryComponent.cs | 9 +- .../Components/Items/Storage/ItemComponent.cs | 4 +- .../EntitySystems/GasTankSystem.cs | 32 ++ .../Components/Items/IHandsComponent.cs | 18 +- .../Interfaces/IGasMixtureHolder.cs | 15 + Content.Shared/Atmos/GasPrototype.cs | 14 + .../GasTank/GasTankBoundUserInterfaceState.cs | 16 + .../GasTank/GasTankSetPressureMessage.cs | 12 + .../GasTank/GasTankToggleInternalsMessage.cs | 11 + .../Atmos/GasTank/SharedGasTankComponent.cs | 10 + .../Atmos/GasTank/SharedGasTankUiKey.cs | 11 + Content.Shared/GameObjects/ContentNetIDs.cs | 2 + Resources/Prototypes/Atmospherics/gases.yml | 12 + .../Prototypes/Catalog/Fills/lockers.yml | 20 +- .../Entities/Clothing/Back/gas_tanks.yml | 314 ++++++++++++++++ .../Entities/Clothing/Masks/masks.yml | 14 +- .../Entities/Clothing/OuterClothing/suits.yml | 4 +- .../Entities/Mobs/Species/human.yml | 1 + .../Entities/Objects/Boxes/boxes_general.yml | 2 +- .../anesthetic.rsi/equipped-BACKPACK.png | Bin 0 -> 858 bytes .../Objects/Tanks/anesthetic.rsi/icon.png | Bin 0 -> 632 bytes .../Tanks/anesthetic.rsi/inhand-left.png | Bin 0 -> 661 bytes .../Tanks/anesthetic.rsi/inhand-right.png | Bin 0 -> 659 bytes .../Objects/Tanks/anesthetic.rsi/meta.json | 27 ++ .../Objects/Tanks/emergency.rsi/icon.png | Bin 0 -> 459 bytes .../Tanks/emergency.rsi/inhand-left.png | Bin 0 -> 316 bytes .../Tanks/emergency.rsi/inhand-right.png | Bin 0 -> 358 bytes .../Objects/Tanks/emergency.rsi/meta.json | 38 ++ .../Tanks/emergency_double.rsi/icon.png | Bin 0 -> 664 bytes .../emergency_double.rsi/inhand-left.png | Bin 0 -> 340 bytes .../emergency_double.rsi/inhand-right.png | Bin 0 -> 341 bytes .../Tanks/emergency_double.rsi/meta.json | 38 ++ .../Tanks/emergency_yellow.rsi/icon.png | Bin 0 -> 463 bytes .../emergency_yellow.rsi/inhand-left.png | Bin 0 -> 340 bytes .../emergency_yellow.rsi/inhand-right.png | Bin 0 -> 341 bytes .../Tanks/emergency_yellow.rsi/meta.json | 38 ++ .../Tanks/generic.rsi/equipped-BACKPACK.png | Bin 0 -> 825 bytes .../Objects/Tanks/generic.rsi/icon.png | Bin 0 -> 623 bytes .../Objects/Tanks/generic.rsi/inhand-left.png | Bin 0 -> 677 bytes .../Tanks/generic.rsi/inhand-right.png | Bin 0 -> 671 bytes .../Objects/Tanks/generic.rsi/meta.json | 27 ++ .../Tanks/oxygen.rsi/equipped-BACKPACK.png | Bin 0 -> 834 bytes .../Objects/Tanks/oxygen.rsi/icon.png | Bin 0 -> 651 bytes .../Objects/Tanks/oxygen.rsi/inhand-left.png | Bin 0 -> 733 bytes .../Objects/Tanks/oxygen.rsi/inhand-right.png | Bin 0 -> 738 bytes .../Objects/Tanks/oxygen.rsi/meta.json | 27 ++ .../Objects/Tanks/phoron.rsi/icon.png | Bin 0 -> 482 bytes .../Objects/Tanks/phoron.rsi/inhand-left.png | Bin 0 -> 537 bytes .../Objects/Tanks/phoron.rsi/inhand-right.png | Bin 0 -> 522 bytes .../Objects/Tanks/phoron.rsi/meta.json | 38 ++ .../Tanks/plasmaman.rsi/equipped-BACKPACK.png | Bin 0 -> 725 bytes .../Objects/Tanks/plasmaman.rsi/icon.png | Bin 0 -> 590 bytes .../Tanks/plasmaman.rsi/inhand-left.png | Bin 0 -> 635 bytes .../Tanks/plasmaman.rsi/inhand-right.png | Bin 0 -> 626 bytes .../Objects/Tanks/plasmaman.rsi/meta.json | 27 ++ .../Tanks/red.rsi/equipped-BACKPACK.png | Bin 0 -> 851 bytes .../Textures/Objects/Tanks/red.rsi/icon.png | Bin 0 -> 619 bytes .../Objects/Tanks/red.rsi/inhand-left.png | Bin 0 -> 736 bytes .../Objects/Tanks/red.rsi/inhand-right.png | Bin 0 -> 725 bytes .../Textures/Objects/Tanks/red.rsi/meta.json | 27 ++ .../Tanks/yellow.rsi/equipped-BACKPACK.png | Bin 0 -> 839 bytes .../Objects/Tanks/yellow.rsi/icon.png | Bin 0 -> 661 bytes .../Objects/Tanks/yellow.rsi/inhand-left.png | Bin 0 -> 645 bytes .../Objects/Tanks/yellow.rsi/inhand-right.png | Bin 0 -> 652 bytes .../Objects/Tanks/yellow.rsi/meta.json | 27 ++ 77 files changed, 1653 insertions(+), 58 deletions(-) create mode 100644 Content.Client/UserInterface/Atmos/GasTank/GasTankBoundUserInterface.cs create mode 100644 Content.Client/UserInterface/Atmos/GasTank/GasTankWindow.cs create mode 100644 Content.Server/GameObjects/Components/Atmos/BreathToolComponent.cs create mode 100644 Content.Server/GameObjects/Components/Body/Respiratory/InternalsComponent.cs create mode 100644 Content.Server/GameObjects/EntitySystems/GasTankSystem.cs create mode 100644 Content.Shared/GameObjects/Components/Atmos/GasTank/GasTankBoundUserInterfaceState.cs create mode 100644 Content.Shared/GameObjects/Components/Atmos/GasTank/GasTankSetPressureMessage.cs create mode 100644 Content.Shared/GameObjects/Components/Atmos/GasTank/GasTankToggleInternalsMessage.cs create mode 100644 Content.Shared/GameObjects/Components/Atmos/GasTank/SharedGasTankComponent.cs create mode 100644 Content.Shared/GameObjects/Components/Atmos/GasTank/SharedGasTankUiKey.cs create mode 100644 Resources/Prototypes/Entities/Clothing/Back/gas_tanks.yml create mode 100644 Resources/Textures/Objects/Tanks/anesthetic.rsi/equipped-BACKPACK.png create mode 100644 Resources/Textures/Objects/Tanks/anesthetic.rsi/icon.png create mode 100644 Resources/Textures/Objects/Tanks/anesthetic.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Tanks/anesthetic.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Tanks/anesthetic.rsi/meta.json create mode 100644 Resources/Textures/Objects/Tanks/emergency.rsi/icon.png create mode 100644 Resources/Textures/Objects/Tanks/emergency.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Tanks/emergency.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Tanks/emergency.rsi/meta.json create mode 100644 Resources/Textures/Objects/Tanks/emergency_double.rsi/icon.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_double.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_double.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_double.rsi/meta.json create mode 100644 Resources/Textures/Objects/Tanks/emergency_yellow.rsi/icon.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_yellow.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_yellow.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_yellow.rsi/meta.json create mode 100644 Resources/Textures/Objects/Tanks/generic.rsi/equipped-BACKPACK.png create mode 100644 Resources/Textures/Objects/Tanks/generic.rsi/icon.png create mode 100644 Resources/Textures/Objects/Tanks/generic.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Tanks/generic.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Tanks/generic.rsi/meta.json create mode 100644 Resources/Textures/Objects/Tanks/oxygen.rsi/equipped-BACKPACK.png create mode 100644 Resources/Textures/Objects/Tanks/oxygen.rsi/icon.png create mode 100644 Resources/Textures/Objects/Tanks/oxygen.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Tanks/oxygen.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Tanks/oxygen.rsi/meta.json create mode 100644 Resources/Textures/Objects/Tanks/phoron.rsi/icon.png create mode 100644 Resources/Textures/Objects/Tanks/phoron.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Tanks/phoron.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Tanks/phoron.rsi/meta.json create mode 100644 Resources/Textures/Objects/Tanks/plasmaman.rsi/equipped-BACKPACK.png create mode 100644 Resources/Textures/Objects/Tanks/plasmaman.rsi/icon.png create mode 100644 Resources/Textures/Objects/Tanks/plasmaman.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Tanks/plasmaman.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Tanks/plasmaman.rsi/meta.json create mode 100644 Resources/Textures/Objects/Tanks/red.rsi/equipped-BACKPACK.png create mode 100644 Resources/Textures/Objects/Tanks/red.rsi/icon.png create mode 100644 Resources/Textures/Objects/Tanks/red.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Tanks/red.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Tanks/red.rsi/meta.json create mode 100644 Resources/Textures/Objects/Tanks/yellow.rsi/equipped-BACKPACK.png create mode 100644 Resources/Textures/Objects/Tanks/yellow.rsi/icon.png create mode 100644 Resources/Textures/Objects/Tanks/yellow.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Tanks/yellow.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Tanks/yellow.rsi/meta.json diff --git a/Content.Client/IgnoredComponents.cs b/Content.Client/IgnoredComponents.cs index ffaa51467d..9f74eb6aec 100644 --- a/Content.Client/IgnoredComponents.cs +++ b/Content.Client/IgnoredComponents.cs @@ -190,7 +190,10 @@ "Hoe", "Seed", "BotanySharp", - "PlantSampleTaker" + "PlantSampleTaker", + "Internals", + "GasTank", + "BreathMask", }; } } diff --git a/Content.Client/UserInterface/Atmos/GasTank/GasTankBoundUserInterface.cs b/Content.Client/UserInterface/Atmos/GasTank/GasTankBoundUserInterface.cs new file mode 100644 index 0000000000..868aad1443 --- /dev/null +++ b/Content.Client/UserInterface/Atmos/GasTank/GasTankBoundUserInterface.cs @@ -0,0 +1,50 @@ +using Content.Shared.GameObjects.Components.Atmos.GasTank; +using JetBrains.Annotations; +using Robust.Client.GameObjects.Components.UserInterface; +using Robust.Shared.GameObjects.Components.UserInterface; + +namespace Content.Client.UserInterface.Atmos.GasTank +{ + [UsedImplicitly] + public class GasTankBoundUserInterface + : BoundUserInterface + { + public GasTankBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : + base(owner, uiKey) + { + } + + private GasTankWindow _window; + + public void SetOutputPressure(in float value) + { + SendMessage(new GasTankSetPressureMessage {Pressure = value}); + } + + public void ToggleInternals() + { + SendMessage(new GasTankToggleInternalsMessage()); + } + + protected override void Open() + { + base.Open(); + _window = new GasTankWindow(this); + _window.OnClose += Close; + _window.OpenCentered(); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + _window.UpdateState((GasTankBoundUserInterfaceState) state); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + _window.Close(); + } + } +} diff --git a/Content.Client/UserInterface/Atmos/GasTank/GasTankWindow.cs b/Content.Client/UserInterface/Atmos/GasTank/GasTankWindow.cs new file mode 100644 index 0000000000..49210c9464 --- /dev/null +++ b/Content.Client/UserInterface/Atmos/GasTank/GasTankWindow.cs @@ -0,0 +1,235 @@ +using Content.Client.UserInterface.Stylesheets; +using Content.Client.Utility; +using Content.Shared.GameObjects.Components.Atmos.GasTank; +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.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; + +namespace Content.Client.UserInterface.Atmos.GasTank +{ + public class GasTankWindow + : BaseWindow + { + private GasTankBoundUserInterface _owner; + private readonly Label _lblName; + private readonly VBoxContainer _topContainer; + private readonly Control _contentContainer; + + + private readonly IResourceCache _resourceCache = default!; + private readonly RichTextLabel _lblPressure; + private readonly FloatSpinBox _spbPressure; + private readonly RichTextLabel _lblInternals; + private readonly Button _btnInternals; + + public GasTankWindow(GasTankBoundUserInterface owner) + { + TextureButton btnClose; + _resourceCache = IoCManager.Resolve(); + _owner = owner; + var rootContainer = new LayoutContainer {Name = "GasTankRoot"}; + 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, -85); + + 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 topRow = new MarginContainer + { + MarginLeftOverride = 4, + MarginTopOverride = 2, + MarginRightOverride = 12, + MarginBottomOverride = 2, + Children = + { + new HBoxContainer + { + Children = + { + (_lblName = new Label + { + Text = Loc.GetString("Gas Tank"), + FontOverride = font, + FontColorOverride = StyleNano.NanoGold, + SizeFlagsVertical = SizeFlags.ShrinkCenter + }), + new Control + { + CustomMinimumSize = (20, 0), + SizeFlagsHorizontal = SizeFlags.Expand + }, + (btnClose = new TextureButton + { + StyleClasses = {SS14Window.StyleClassWindowCloseButton}, + SizeFlagsVertical = SizeFlags.ShrinkCenter + }) + } + } + } + }; + + var middle = new PanelContainer + { + PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#202025")}, + Children = + { + new MarginContainer + { + MarginLeftOverride = 8, + MarginRightOverride = 8, + MarginTopOverride = 4, + MarginBottomOverride = 4, + Children = + { + (_contentContainer = 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")} + }); + + + _lblPressure = new RichTextLabel(); + _contentContainer.AddChild(_lblPressure); + + //internals + _lblInternals = new RichTextLabel + {CustomMinimumSize = (200, 0), SizeFlagsVertical = SizeFlags.ShrinkCenter}; + _btnInternals = new Button {Text = Loc.GetString("Toggle")}; + + _contentContainer.AddChild( + new MarginContainer + { + MarginTopOverride = 7, + Children = + { + new HBoxContainer + { + Children = {_lblInternals, _btnInternals} + } + } + }); + + // Separator + _contentContainer.AddChild(new Control + { + CustomMinimumSize = new Vector2(0, 10) + }); + + _contentContainer.AddChild(new Label + { + Text = Loc.GetString("Output Pressure"), + Align = Label.AlignMode.Center + }); + _spbPressure = new FloatSpinBox {IsValid = f => f >= 0 || f <= 3000}; + _contentContainer.AddChild( + new MarginContainer + { + MarginRightOverride = 25, + MarginLeftOverride = 25, + MarginBottomOverride = 7, + Children = + { + _spbPressure + } + } + ); + + // Handlers + _spbPressure.OnValueChanged += args => + { + _owner.SetOutputPressure(args.Value); + }; + + _btnInternals.OnPressed += args => + { + _owner.ToggleInternals(); + }; + + btnClose.OnPressed += _ => Close(); + } + + public void UpdateState(GasTankBoundUserInterfaceState state) + { + _lblPressure.SetMarkup(Loc.GetString("Pressure: {0:0.##} kPa", state.TankPressure)); + _btnInternals.Disabled = !state.CanConnectInternals; + _lblInternals.SetMarkup(Loc.GetString("Internals: [color={0}]{1}[/color]", + state.InternalsConnected ? "green" : "red", + state.InternalsConnected ? "Connected" : "Disconnected")); + if (state.OutputPressure.HasValue) + { + _spbPressure.Value = state.OutputPressure.Value; + } + } + + protected override DragMode GetDragModeFor(Vector2 relativeMousePos) + { + return DragMode.Move; + } + + protected override bool HasPoint(Vector2 point) + { + return false; + } + } +} diff --git a/Content.IntegrationTests/Tests/Body/LungTest.cs b/Content.IntegrationTests/Tests/Body/LungTest.cs index 58becef26d..30c181e189 100644 --- a/Content.IntegrationTests/Tests/Body/LungTest.cs +++ b/Content.IntegrationTests/Tests/Body/LungTest.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Content.Server.Atmos; @@ -46,7 +47,7 @@ namespace Content.IntegrationTests.Tests.Body var originalOxygen = 2; var originalNitrogen = 8; - var breathedPercentage = Atmospherics.BreathPercentage; + var breathedPercentage = Atmospherics.BreathVolume / gas.Volume; gas.AdjustMoles(Gas.Oxygen, originalOxygen); gas.AdjustMoles(Gas.Nitrogen, originalNitrogen); @@ -76,7 +77,7 @@ namespace Content.IntegrationTests.Tests.Body lung.Exhale(1, gas); var lungOxygenAfterExhale = lung.Air.GetMoles(Gas.Oxygen); - var exhaledOxygen = lungOxygenBeforeExhale - lungOxygenAfterExhale; + var exhaledOxygen = Math.Abs(lungOxygenBeforeExhale - lungOxygenAfterExhale); // Not completely empty Assert.Positive(lung.Air.Gases.Sum()); diff --git a/Content.Server/Atmos/GasMixture.cs b/Content.Server/Atmos/GasMixture.cs index 425c5da204..e9dca425ed 100644 --- a/Content.Server/Atmos/GasMixture.cs +++ b/Content.Server/Atmos/GasMixture.cs @@ -65,6 +65,38 @@ namespace Content.Server.Atmos } } + /// + /// Heat capacity ratio of gas mixture + /// + [ViewVariables] + public float HeatCapacityRatio + { + get + { + var delimiterSum = 0f; + for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) + { + delimiterSum += _moles[i] / (_atmosphereSystem.GetGas(i).HeatCapacityRatio - 1); + } + return 1 + TotalMoles / delimiterSum; + } + } + + public float MolarMass + { + get + { + var molarMass = 0f; + var totalMoles = TotalMoles; + for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) + { + molarMass += _atmosphereSystem.GetGas(i).MolarMass * (_moles[i] / totalMoles); + } + + return molarMass; + } + } + [ViewVariables] public float HeatCapacityArchived { @@ -136,14 +168,15 @@ namespace Content.Server.Atmos public GasMixture(AtmosphereSystem? atmosphereSystem) { _atmosphereSystem = atmosphereSystem ?? EntitySystem.Get(); + _moles = new float[_atmosphereSystem.Gases.Count()]; + _molesArchived = new float[_moles.Length]; } - public GasMixture(float volume, AtmosphereSystem? atmosphereSystem = null) + public GasMixture(float volume, AtmosphereSystem? atmosphereSystem = null): this(atmosphereSystem) { if (volume < 0) volume = 0; Volume = volume; - _atmosphereSystem = atmosphereSystem ?? EntitySystem.Get(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Content.Server/Atmos/TileAtmosphere.cs b/Content.Server/Atmos/TileAtmosphere.cs index d885790d26..6e5d91e5ec 100644 --- a/Content.Server/Atmos/TileAtmosphere.cs +++ b/Content.Server/Atmos/TileAtmosphere.cs @@ -1127,6 +1127,10 @@ namespace Content.Server.Atmos UpdateVisuals(); + if (!Excited) + { + _gridAtmosphereComponent.AddActiveTile(this); + } return true; } diff --git a/Content.Server/GameObjects/Components/Atmos/BreathToolComponent.cs b/Content.Server/GameObjects/Components/Atmos/BreathToolComponent.cs new file mode 100644 index 0000000000..b5228797ea --- /dev/null +++ b/Content.Server/GameObjects/Components/Atmos/BreathToolComponent.cs @@ -0,0 +1,70 @@ +#nullable enable +using Content.Server.GameObjects.Components.Body.Respiratory; +using Content.Shared.GameObjects.Components.Inventory; +using Content.Shared.Interfaces.GameObjects.Components; +using Npgsql.TypeHandlers; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Serialization; + +namespace Content.Server.GameObjects.Components.Atmos +{ + /// + /// Used in internals as breath tool. + /// + [RegisterComponent] + public class BreathToolComponent : Component, IEquipped, IUnequipped + { + /// + /// Tool is functional only in allowed slots + /// + private EquipmentSlotDefines.SlotFlags _allowedSlots; + + public override string Name => "BreathMask"; + public bool IsFunctional { get; private set; } + public IEntity? ConnectedInternalsEntity { get; private set; } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + serializer.DataField(ref _allowedSlots, "allowedSlots", EquipmentSlotDefines.SlotFlags.MASK); + } + + protected override void Shutdown() + { + base.Shutdown(); + DisconnectInternals(); + } + + public void Equipped(EquippedEventArgs eventArgs) + { + if ((EquipmentSlotDefines.SlotMasks[eventArgs.Slot] & _allowedSlots) != _allowedSlots) return; + IsFunctional = true; + + if (eventArgs.User.TryGetComponent(out InternalsComponent? internals)) + { + ConnectedInternalsEntity = eventArgs.User; + internals.ConnectBreathTool(Owner); + } + } + + public void Unequipped(UnequippedEventArgs eventArgs) + { + DisconnectInternals(); + + } + + public void DisconnectInternals() + { + var old = ConnectedInternalsEntity; + ConnectedInternalsEntity = null; + + if (old != null && old.TryGetComponent(out var internalsComponent)) + { + internalsComponent.DisconnectBreathTool(); + } + + IsFunctional = false; + } + } +} diff --git a/Content.Server/GameObjects/Components/Atmos/GasMixtureHolderComponent.cs b/Content.Server/GameObjects/Components/Atmos/GasMixtureHolderComponent.cs index 5f2efb662a..3ef464f36c 100644 --- a/Content.Server/GameObjects/Components/Atmos/GasMixtureHolderComponent.cs +++ b/Content.Server/GameObjects/Components/Atmos/GasMixtureHolderComponent.cs @@ -1,4 +1,5 @@ using Content.Server.Atmos; +using Content.Server.Interfaces; using Robust.Shared.GameObjects; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; @@ -6,23 +7,19 @@ using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Atmos { [RegisterComponent] - public class GasMixtureHolderComponent : Component + public class GasMixtureHolderComponent : Component, IGasMixtureHolder { public override string Name => "GasMixtureHolder"; - [ViewVariables] public GasMixture GasMixture { get; set; } + [ViewVariables] public GasMixture Air { get; set; } public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); - GasMixture = new GasMixture(); + Air = new GasMixture(); - serializer.DataReadWriteFunction( - "volume", - 0f, - vol => GasMixture.Volume = vol, - () => GasMixture.Volume); + serializer.DataField(this, x => x.Air, "air", new GasMixture()); } } } diff --git a/Content.Server/GameObjects/Components/Atmos/GasTankComponent.cs b/Content.Server/GameObjects/Components/Atmos/GasTankComponent.cs index 47f96463e0..7962b56eab 100644 --- a/Content.Server/GameObjects/Components/Atmos/GasTankComponent.cs +++ b/Content.Server/GameObjects/Components/Atmos/GasTankComponent.cs @@ -1,10 +1,356 @@ -using Robust.Shared.GameObjects; +#nullable enable +using System; +using Content.Server.Atmos; +using Content.Server.Explosions; +using Content.Server.GameObjects.Components.Body.Respiratory; +using Content.Server.GameObjects.Components.GUI; +using Content.Server.Interfaces; +using Content.Server.Utility; +using Content.Shared.Atmos; +using Content.Shared.Audio; +using Content.Shared.GameObjects.Components.Atmos.GasTank; +using Content.Shared.GameObjects.Components.Inventory; +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.GameObjects.Verbs; +using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; +using Robust.Server.GameObjects.Components.UserInterface; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Server.Interfaces.GameObjects; +using Robust.Server.Interfaces.Player; +using Robust.Shared.Containers; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.EntitySystemMessages; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Map; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; +using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Atmos { [RegisterComponent] - public class GasTankComponent : Component + [ComponentReference(typeof(IActivate))] + public class GasTankComponent : SharedGasTankComponent, IExamine, IGasMixtureHolder, IUse, IDropped, IActivate { - public override string Name => "GasTank"; + private const float MaxExplosionRange = 14f; + private const float DefaultOutputPressure = Atmospherics.OneAtmosphere; + + private float _pressureResistance; + + private int _integrity = 3; + + [Dependency] private readonly IEntityManager _entityManager = default!; + [ViewVariables] private BoundUserInterface? _userInterface; + + [ViewVariables] public GasMixture? Air { get; set; } + + /// + /// Distributed pressure. + /// + [ViewVariables] public float OutputPressure { get; private set; } + + /// + /// Tank is connected to internals. + /// + [ViewVariables] public bool IsConnected { get; set; } + + /// + /// Represents that tank is functional and can be connected to internals. + /// + public bool IsFunctional => GetInternalsComponent() != null; + + /// + /// Pressure at which tanks start leaking. + /// + public float TankLeakPressure { get; set; } = 30 * Atmospherics.OneAtmosphere; + + /// + /// Pressure at which tank spills all contents into atmosphere. + /// + public float TankRupturePressure { get; set; } = 40 * Atmospherics.OneAtmosphere; + + /// + /// Base 3x3 explosion. + /// + public float TankFragmentPressure { get; set; } = 50 * Atmospherics.OneAtmosphere; + + /// + /// Increases explosion for each scale kPa above threshold. + /// + public float TankFragmentScale { get; set; } = 10 * Atmospherics.OneAtmosphere; + + public override void Initialize() + { + base.Initialize(); + _userInterface = Owner.GetUIOrNull(SharedGasTankUiKey.Key); + if (_userInterface != null) + { + _userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage; + } + } + + public void OpenInterface(IPlayerSession session) + { + _userInterface?.Open(session); + UpdateUserInterface(true); + } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(this, x => x.Air, "air", new GasMixture()); + serializer.DataField(this, x => x.OutputPressure, "outputPressure", DefaultOutputPressure); + serializer.DataField(this, x => x.TankLeakPressure, "tankLeakPressure", 30 * Atmospherics.OneAtmosphere); + serializer.DataField(this, x => x.TankRupturePressure, "tankRupturePressure", 40 * Atmospherics.OneAtmosphere); + serializer.DataField(this, x => x.TankFragmentPressure, "tankFragmentPressure", 50 * Atmospherics.OneAtmosphere); + serializer.DataField(this, x => x.TankFragmentScale, "tankFragmentScale", 10 * Atmospherics.OneAtmosphere); + serializer.DataField(ref _pressureResistance, "pressureResistance", Atmospherics.OneAtmosphere * 5f); + } + + public void Examine(FormattedMessage message, bool inDetailsRange) + { + message.AddMarkup(Loc.GetString("Pressure: [color=orange]{0}[/color] kPa.\n", + Math.Round(Air?.Pressure ?? 0))); + if (IsConnected) + { + message.AddMarkup(Loc.GetString("Connected to external component")); + } + } + + protected override void Shutdown() + { + base.Shutdown(); + DisconnectFromInternals(); + } + + public void Update() + { + Air?.React(this); + CheckStatus(); + UpdateUserInterface(); + } + + public GasMixture? RemoveAir(float amount) + { + var gas = Air?.Remove(amount); + CheckStatus(); + return gas; + } + + public GasMixture RemoveAirVolume(float volume) + { + if (Air == null) + return new GasMixture(volume); + + var tankPressure = Air.Pressure; + if (tankPressure < OutputPressure) + { + OutputPressure = tankPressure; + UpdateUserInterface(); + } + + var molesNeeded = OutputPressure * volume / (Atmospherics.R * Air.Temperature); + + var air = RemoveAir(molesNeeded); + + if (air != null) + air.Volume = volume; + else + return new GasMixture(volume); + + return air; + } + + public bool UseEntity(UseEntityEventArgs eventArgs) + { + if (!eventArgs.User.TryGetComponent(out IActorComponent? actor)) return false; + OpenInterface(actor.playerSession); + return true; + } + + public void Activate(ActivateEventArgs eventArgs) + { + if (!eventArgs.User.TryGetComponent(out IActorComponent? actor)) return; + OpenInterface(actor.playerSession); + } + + public void ConnectToInternals() + { + if (IsConnected || !IsFunctional) return; + var internals = GetInternalsComponent(); + if (internals == null) return; + IsConnected = internals.TryConnectTank(Owner); + UpdateUserInterface(); + } + + public void DisconnectFromInternals(IEntity? owner = null) + { + if (!IsConnected) return; + IsConnected = false; + GetInternalsComponent(owner)?.DisconnectTank(); + UpdateUserInterface(); + } + + private void UpdateUserInterface(bool initialUpdate = false) + { + _userInterface?.SetState( + new GasTankBoundUserInterfaceState + { + TankPressure = Air?.Pressure ?? 0, + OutputPressure = initialUpdate ? OutputPressure : (float?) null, + InternalsConnected = IsConnected, + CanConnectInternals = IsFunctional && GetInternalsComponent() != null + }); + } + + private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage message) + { + switch (message.Message) + { + case GasTankSetPressureMessage msg: + OutputPressure = msg.Pressure; + break; + case GasTankToggleInternalsMessage _: + ToggleInternals(); + break; + } + } + + private void ToggleInternals() + { + if (IsConnected) + { + DisconnectFromInternals(); + return; + } + + ConnectToInternals(); + } + + private InternalsComponent? GetInternalsComponent(IEntity? owner = null) + { + if (owner != null) return owner.GetComponentOrNull(); + return ContainerHelpers.TryGetContainer(Owner, out var container) + ? container.Owner.GetComponentOrNull() + : null; + } + + public void AssumeAir(GasMixture giver) + { + Air?.Merge(giver); + CheckStatus(); + } + + private void CheckStatus() + { + if (Air == null) + return; + + var pressure = Air.Pressure; + + if (pressure > TankFragmentPressure) + { + // Give the gas a chance to build up more pressure. + for (var i = 0; i < 3; i++) + { + Air.React(this); + } + + pressure = Air.Pressure; + var range = (pressure - TankFragmentPressure) / TankFragmentScale; + + // Let's cap the explosion, yeah? + if (range > MaxExplosionRange) + { + range = MaxExplosionRange; + } + + Owner.SpawnExplosion((int) (range * 0.25f), (int) (range * 0.5f), (int) (range * 1.5f), 1); + + Owner.Delete(); + return; + } + + if (pressure > TankRupturePressure) + { + if (_integrity <= 0) + { + var tileAtmos = Owner.Transform.Coordinates.GetTileAtmosphere(); + tileAtmos?.AssumeAir(Air); + + EntitySystem.Get().PlayAtCoords("Audio/Effects/spray.ogg", Owner.Transform.Coordinates, + AudioHelpers.WithVariation(0.125f)); + + Owner.Delete(); + return; + } + + _integrity--; + return; + } + + if (pressure > TankLeakPressure) + { + if (_integrity <= 0) + { + var tileAtmos = Owner.Transform.Coordinates.GetTileAtmosphere(); + if (tileAtmos == null) + return; + + var leakedGas = Air.RemoveRatio(0.25f); + tileAtmos.AssumeAir(leakedGas); + } + else + { + _integrity--; + } + + return; + } + + if (_integrity < 3) + _integrity++; + } + + /// + /// Open interaction window + /// + [Verb] + private sealed class ControlVerb : Verb + { + public override bool RequireInteractionRange => true; + + protected override void GetData(IEntity user, GasTankComponent component, VerbData data) + { + data.Visibility = VerbVisibility.Invisible; + if (!user.HasComponent()) + { + return; + } + + data.Visibility = VerbVisibility.Visible; + data.Text = "Open Control Panel"; + } + + protected override void Activate(IEntity user, GasTankComponent component) + { + if (!user.TryGetComponent(out var actor)) + { + return; + } + + component.OpenInterface(actor.playerSession); + } + } + + public void Dropped(DroppedEventArgs eventArgs) + { + DisconnectFromInternals(eventArgs.User); + } } } diff --git a/Content.Server/GameObjects/Components/Body/Behavior/LungBehaviorComponent.cs b/Content.Server/GameObjects/Components/Body/Behavior/LungBehaviorComponent.cs index 78375835a4..195733fe71 100644 --- a/Content.Server/GameObjects/Components/Body/Behavior/LungBehaviorComponent.cs +++ b/Content.Server/GameObjects/Components/Body/Behavior/LungBehaviorComponent.cs @@ -2,7 +2,9 @@ using System; using System.Linq; using Content.Server.Atmos; +using Content.Server.GameObjects.Components.Atmos; using Content.Server.GameObjects.Components.Body.Circulatory; +using Content.Server.GameObjects.Components.Body.Respiratory; using Content.Server.Utility; using Content.Shared.Atmos; using Content.Shared.GameObjects.Components.Body.Behavior; @@ -10,6 +12,7 @@ using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.Timing; using Robust.Shared.IoC; using Robust.Shared.Localization; +using Robust.Shared.Log; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; @@ -147,6 +150,16 @@ namespace Content.Server.GameObjects.Components.Body.Behavior public override void Inhale(float frameTime) { + if (Body != null && Body.Owner.TryGetComponent(out InternalsComponent? internals) + && internals.BreathToolEntity != null && internals.GasTankEntity != null + && internals.BreathToolEntity.TryGetComponent(out BreathToolComponent? breathTool) + && breathTool.IsFunctional && internals.GasTankEntity.TryGetComponent(out GasTankComponent? gasTank) + && gasTank.Air != null) + { + Inhale(frameTime, gasTank.RemoveAirVolume(Atmospherics.BreathVolume)); + return; + } + if (!Owner.Transform.Coordinates.TryGetTileAir(out var tileAir)) { return; @@ -157,8 +170,7 @@ namespace Content.Server.GameObjects.Components.Body.Behavior public void Inhale(float frameTime, GasMixture from) { - var ratio = Atmospherics.BreathPercentage * frameTime; - + var ratio = (Atmospherics.BreathVolume / from.Volume) * frameTime; Transfer(from, Air, ratio); ToBloodstream(Air); diff --git a/Content.Server/GameObjects/Components/Body/Respiratory/InternalsComponent.cs b/Content.Server/GameObjects/Components/Body/Respiratory/InternalsComponent.cs new file mode 100644 index 0000000000..6c3380da4e --- /dev/null +++ b/Content.Server/GameObjects/Components/Body/Respiratory/InternalsComponent.cs @@ -0,0 +1,63 @@ +#nullable enable +using Content.Server.GameObjects.Components.Atmos; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.Body.Respiratory +{ + [RegisterComponent] + public class InternalsComponent : Component + { + public override string Name => "Internals"; + [ViewVariables] public IEntity? GasTankEntity { get; set; } + [ViewVariables] public IEntity? BreathToolEntity { get; set; } + + public void DisconnectBreathTool() + { + var old = BreathToolEntity; + BreathToolEntity = null; + + if (old != null && old.TryGetComponent(out BreathToolComponent? breathTool) ) + { + breathTool.DisconnectInternals(); + DisconnectTank(); + } + } + + public void ConnectBreathTool(IEntity toolEntity) + { + if (BreathToolEntity != null && BreathToolEntity.TryGetComponent(out BreathToolComponent? tool)) + { + tool.DisconnectInternals(); + } + + BreathToolEntity = toolEntity; + } + + public void DisconnectTank() + { + if (GasTankEntity != null && GasTankEntity.TryGetComponent(out GasTankComponent? tank)) + { + tank.DisconnectFromInternals(Owner); + } + + GasTankEntity = null; + } + + public bool TryConnectTank(IEntity tankEntity) + { + if (BreathToolEntity == null) + return false; + + if (GasTankEntity != null && GasTankEntity.TryGetComponent(out GasTankComponent? tank)) + { + tank.DisconnectFromInternals(Owner); + } + + GasTankEntity = tankEntity; + return true; + } + + } +} diff --git a/Content.Server/GameObjects/Components/GUI/HandsComponent.cs b/Content.Server/GameObjects/Components/GUI/HandsComponent.cs index f105d0278a..092076d26d 100644 --- a/Content.Server/GameObjects/Components/GUI/HandsComponent.cs +++ b/Content.Server/GameObjects/Components/GUI/HandsComponent.cs @@ -245,7 +245,7 @@ namespace Content.Server.GameObjects.Components.GUI return false; } - public bool Drop(string slot, EntityCoordinates coords, bool doMobChecks = true) + public bool Drop(string slot, EntityCoordinates coords, bool doMobChecks = true, bool doDropInteraction = true) { var hand = GetHand(slot); if (!CanDrop(slot, doMobChecks) || hand?.Entity == null) @@ -260,7 +260,7 @@ namespace Content.Server.GameObjects.Components.GUI return false; } - if (!DroppedInteraction(item, false)) + if (doDropInteraction && !DroppedInteraction(item, false)) return false; item.RemovedFromSlot(); @@ -282,7 +282,7 @@ namespace Content.Server.GameObjects.Components.GUI return true; } - public bool Drop(IEntity entity, EntityCoordinates coords, bool doMobChecks = true) + public bool Drop(IEntity entity, EntityCoordinates coords, bool doMobChecks = true, bool doDropInteraction = true) { if (entity == null) { @@ -294,15 +294,15 @@ namespace Content.Server.GameObjects.Components.GUI throw new ArgumentException("Entity must be held in one of our hands.", nameof(entity)); } - return Drop(slot, coords, doMobChecks); + return Drop(slot, coords, doMobChecks, doDropInteraction); } - public bool Drop(string slot, bool mobChecks = true) + public bool Drop(string slot, bool mobChecks = true, bool doDropInteraction = true) { - return Drop(slot, Owner.Transform.Coordinates, mobChecks); + return Drop(slot, Owner.Transform.Coordinates, mobChecks, doDropInteraction); } - public bool Drop(IEntity entity, bool mobChecks = true) + public bool Drop(IEntity entity, bool mobChecks = true, bool doDropInteraction = true) { if (entity == null) { @@ -314,10 +314,10 @@ namespace Content.Server.GameObjects.Components.GUI throw new ArgumentException("Entity must be held in one of our hands.", nameof(entity)); } - return Drop(slot, Owner.Transform.Coordinates, mobChecks); + return Drop(slot, Owner.Transform.Coordinates, mobChecks, doDropInteraction); } - public bool Drop(string slot, BaseContainer targetContainer, bool doMobChecks = true) + public bool Drop(string slot, BaseContainer targetContainer, bool doMobChecks = true, bool doDropInteraction = true) { if (slot == null) { @@ -352,7 +352,7 @@ namespace Content.Server.GameObjects.Components.GUI throw new InvalidOperationException(); } - if (!DroppedInteraction(item, doMobChecks)) + if (doDropInteraction && !DroppedInteraction(item, doMobChecks)) return false; item.RemovedFromSlot(); @@ -368,7 +368,7 @@ namespace Content.Server.GameObjects.Components.GUI return true; } - public bool Drop(IEntity entity, BaseContainer targetContainer, bool doMobChecks = true) + public bool Drop(IEntity entity, BaseContainer targetContainer, bool doMobChecks = true, bool doDropInteraction = true) { if (entity == null) { @@ -380,7 +380,7 @@ namespace Content.Server.GameObjects.Components.GUI throw new ArgumentException("Entity must be held in one of our hands.", nameof(entity)); } - return Drop(slot, targetContainer, doMobChecks); + return Drop(slot, targetContainer, doMobChecks, doDropInteraction); } /// diff --git a/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs b/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs index 8c8edaa9cc..27102390cf 100644 --- a/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs +++ b/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs @@ -156,6 +156,13 @@ namespace Content.Server.GameObjects.Components.GUI { return GetSlotItem(slot); } + + public IEnumerable LookupItems() where T: Component + { + return _slotContainers.Values.SelectMany(x => x.ContainedEntities.Select(e => e.GetComponentOrNull())) + .Where(x => x != null); + } + public T GetSlotItem(Slots slot) where T : ItemComponent { if (!_slotContainers.ContainsKey(slot)) @@ -435,7 +442,7 @@ namespace Content.Server.GameObjects.Components.GUI var activeHand = hands.GetActiveHand; if (activeHand != null && activeHand.Owner.TryGetComponent(out ItemComponent clothing)) { - hands.Drop(hands.ActiveHand); + hands.Drop(hands.ActiveHand, doDropInteraction:false); if (!Equip(msg.Inventoryslot, clothing, true, out var reason)) { hands.PutInHand(clothing); diff --git a/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs index b1c5648e07..9c88a9769a 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs @@ -58,12 +58,12 @@ namespace Content.Server.GameObjects.Components.Items.Storage } } - public void Equipped(EquippedEventArgs eventArgs) + public virtual void Equipped(EquippedEventArgs eventArgs) { EquippedToSlot(); } - public void Unequipped(UnequippedEventArgs eventArgs) + public virtual void Unequipped(UnequippedEventArgs eventArgs) { RemovedFromSlot(); } diff --git a/Content.Server/GameObjects/EntitySystems/GasTankSystem.cs b/Content.Server/GameObjects/EntitySystems/GasTankSystem.cs new file mode 100644 index 0000000000..fe88bc52f3 --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/GasTankSystem.cs @@ -0,0 +1,32 @@ +using System; +using Content.Server.GameObjects.Components.Atmos; +using JetBrains.Annotations; +using Robust.Shared.GameObjects.EntitySystemMessages; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.Timing; +using Robust.Shared.IoC; + +namespace Content.Server.GameObjects.EntitySystems +{ + [UsedImplicitly] + public class GasTankSystem : EntitySystem + { + private float _timer = 0f; + private const float Interval = 0.5f; + + public override void Update(float frameTime) + { + base.Update(frameTime); + + _timer += frameTime; + + if (_timer < Interval) return; + _timer = 0f; + + foreach (var gasTank in EntityManager.ComponentManager.EntityQuery()) + { + gasTank.Update(); + } + } + } +} diff --git a/Content.Server/Interfaces/GameObjects/Components/Items/IHandsComponent.cs b/Content.Server/Interfaces/GameObjects/Components/Items/IHandsComponent.cs index a840d589fb..d2da0870d0 100644 --- a/Content.Server/Interfaces/GameObjects/Components/Items/IHandsComponent.cs +++ b/Content.Server/Interfaces/GameObjects/Components/Items/IHandsComponent.cs @@ -109,14 +109,16 @@ namespace Content.Server.Interfaces.GameObjects.Components.Items /// /// The slot of which to drop to drop the item. /// Whether to check the for the mob or not. + /// Whether to perform Dropped interactions. /// True on success, false if something blocked the drop. - bool Drop(string slot, bool mobChecks = true); + bool Drop(string slot, bool mobChecks = true, bool doDropInteraction = true); /// /// Drops an item held by one of our hand slots to the same position as our owning entity. /// /// The item to drop. /// Whether to check the for the mob or not. + /// Whether to perform Dropped interactions. /// True on success, false if something blocked the drop. /// /// Thrown if is null. @@ -124,7 +126,7 @@ namespace Content.Server.Interfaces.GameObjects.Components.Items /// /// Thrown if is not actually held in any hand. /// - bool Drop(IEntity entity, bool mobChecks = true); + bool Drop(IEntity entity, bool mobChecks = true, bool doDropInteraction = true); /// /// Drops the item in a slot. @@ -132,8 +134,9 @@ namespace Content.Server.Interfaces.GameObjects.Components.Items /// The slot to drop the item from. /// /// Whether to check the for the mob or not. + /// Whether to perform Dropped interactions. /// True if an item was dropped, false otherwise. - bool Drop(string slot, EntityCoordinates coords, bool doMobChecks = true); + bool Drop(string slot, EntityCoordinates coords, bool doMobChecks = true, bool doDropInteraction = true); /// /// Drop the specified entity in our hands to a certain position. @@ -145,6 +148,7 @@ namespace Content.Server.Interfaces.GameObjects.Components.Items /// The entity to drop, must be held in one of the hands. /// The coordinates to drop the entity at. /// Whether to check the for the mob or not. + /// Whether to perform Dropped interactions. /// /// True if the drop succeeded, /// false if it failed (due to failing to eject from our hand slot, etc...) @@ -155,7 +159,7 @@ namespace Content.Server.Interfaces.GameObjects.Components.Items /// /// Thrown if is not actually held in any hand. /// - bool Drop(IEntity entity, EntityCoordinates coords, bool doMobChecks = true); + bool Drop(IEntity entity, EntityCoordinates coords, bool doMobChecks = true, bool doDropInteraction = true); /// /// Drop the item contained in a slot into another container. @@ -163,13 +167,14 @@ namespace Content.Server.Interfaces.GameObjects.Components.Items /// The slot of which to drop the entity. /// The container to drop into. /// Whether to check the for the mob or not. + /// Whether to perform Dropped interactions. /// True on success, false if something was blocked (insertion or removal). /// /// Thrown if dry-run checks reported OK to remove and insert, /// but practical remove or insert returned false anyways. /// This is an edge-case that is currently unhandled. /// - bool Drop(string slot, BaseContainer targetContainer, bool doMobChecks = true); + bool Drop(string slot, BaseContainer targetContainer, bool doMobChecks = true, bool doDropInteraction = true); /// /// Drops an item in one of the hands into a container. @@ -177,6 +182,7 @@ namespace Content.Server.Interfaces.GameObjects.Components.Items /// The item to drop. /// The container to drop into. /// Whether to check the for the mob or not. + /// Whether to perform Dropped interactions. /// True on success, false if something was blocked (insertion or removal). /// /// Thrown if dry-run checks reported OK to remove and insert, @@ -189,7 +195,7 @@ namespace Content.Server.Interfaces.GameObjects.Components.Items /// /// Thrown if is not actually held in any hand. /// - bool Drop(IEntity entity, BaseContainer targetContainer, bool doMobChecks = true); + bool Drop(IEntity entity, BaseContainer targetContainer, bool doMobChecks = true, bool doDropInteraction = true); /// /// Checks whether the item in the specified hand can be dropped. diff --git a/Content.Server/Interfaces/IGasMixtureHolder.cs b/Content.Server/Interfaces/IGasMixtureHolder.cs index ad7d035f88..d155f02bea 100644 --- a/Content.Server/Interfaces/IGasMixtureHolder.cs +++ b/Content.Server/Interfaces/IGasMixtureHolder.cs @@ -5,5 +5,20 @@ namespace Content.Server.Interfaces public interface IGasMixtureHolder { public GasMixture Air { get; set; } + + public void AssumeAir(GasMixture giver) + { + Air.Merge(giver); + } + + public GasMixture RemoveAir(float amount) + { + return Air.Remove(amount); + } + + public GasMixture RemoveAirVolume(float ratio) + { + return Air.RemoveRatio(ratio); + } } } diff --git a/Content.Shared/Atmos/GasPrototype.cs b/Content.Shared/Atmos/GasPrototype.cs index 064ec0139d..3186b09847 100644 --- a/Content.Shared/Atmos/GasPrototype.cs +++ b/Content.Shared/Atmos/GasPrototype.cs @@ -19,6 +19,17 @@ namespace Content.Shared.Atmos /// public float SpecificHeat { get; private set; } + /// + /// Heat capacity ratio for gas + /// + public float HeatCapacityRatio { get; private set; } + + /// + /// Molar mass of gas + /// + public float MolarMass { get; set; } + + /// /// Minimum amount of moles for this gas to be visible. /// @@ -49,6 +60,7 @@ namespace Content.Shared.Atmos /// public string OverlayPath { get; private set; } + public string Color { get; private set; } public void LoadFrom(YamlMappingNode mapping) @@ -59,6 +71,8 @@ namespace Content.Shared.Atmos serializer.DataField(this, x => Name, "name", string.Empty); serializer.DataField(this, x => OverlayPath, "overlayPath", string.Empty); serializer.DataField(this, x => SpecificHeat, "specificHeat", 0f); + serializer.DataField(this, x => HeatCapacityRatio, "heatCapacityRatio", 1.4f); + serializer.DataField(this, x => MolarMass, "molarMass", 1f); serializer.DataField(this, x => GasMolesVisible, "gasMolesVisible", 0.25f); serializer.DataField(this, x => GasOverlayTexture, "gasOverlayTexture", string.Empty); serializer.DataField(this, x => GasOverlaySprite, "gasOverlaySprite", string.Empty); diff --git a/Content.Shared/GameObjects/Components/Atmos/GasTank/GasTankBoundUserInterfaceState.cs b/Content.Shared/GameObjects/Components/Atmos/GasTank/GasTankBoundUserInterfaceState.cs new file mode 100644 index 0000000000..4c5bf4b02d --- /dev/null +++ b/Content.Shared/GameObjects/Components/Atmos/GasTank/GasTankBoundUserInterfaceState.cs @@ -0,0 +1,16 @@ +using System; +using Robust.Shared.GameObjects.Components.UserInterface; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Atmos.GasTank +{ + [Serializable, NetSerializable] + public class GasTankBoundUserInterfaceState : BoundUserInterfaceState + { + public float TankPressure { get; set; } + public float? OutputPressure { get; set; } + public bool InternalsConnected { get; set; } + public bool CanConnectInternals { get; set; } + + } +} diff --git a/Content.Shared/GameObjects/Components/Atmos/GasTank/GasTankSetPressureMessage.cs b/Content.Shared/GameObjects/Components/Atmos/GasTank/GasTankSetPressureMessage.cs new file mode 100644 index 0000000000..4fbf2bfc84 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Atmos/GasTank/GasTankSetPressureMessage.cs @@ -0,0 +1,12 @@ +using System; +using Robust.Shared.GameObjects.Components.UserInterface; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Atmos.GasTank +{ + [Serializable, NetSerializable] + public class GasTankSetPressureMessage : BoundUserInterfaceMessage + { + public float Pressure { get; set; } + } +} diff --git a/Content.Shared/GameObjects/Components/Atmos/GasTank/GasTankToggleInternalsMessage.cs b/Content.Shared/GameObjects/Components/Atmos/GasTank/GasTankToggleInternalsMessage.cs new file mode 100644 index 0000000000..d009450482 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Atmos/GasTank/GasTankToggleInternalsMessage.cs @@ -0,0 +1,11 @@ +using System; +using Robust.Shared.GameObjects.Components.UserInterface; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Atmos.GasTank +{ + [Serializable, NetSerializable] + public class GasTankToggleInternalsMessage : BoundUserInterfaceMessage + { + } +} diff --git a/Content.Shared/GameObjects/Components/Atmos/GasTank/SharedGasTankComponent.cs b/Content.Shared/GameObjects/Components/Atmos/GasTank/SharedGasTankComponent.cs new file mode 100644 index 0000000000..541d1ecbc2 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Atmos/GasTank/SharedGasTankComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameObjects; + +namespace Content.Shared.GameObjects.Components.Atmos.GasTank +{ + public class SharedGasTankComponent : Component + { + public override string Name => "GasTank"; + public override uint? NetID => ContentNetIDs.GAS_TANK; + } +} diff --git a/Content.Shared/GameObjects/Components/Atmos/GasTank/SharedGasTankUiKey.cs b/Content.Shared/GameObjects/Components/Atmos/GasTank/SharedGasTankUiKey.cs new file mode 100644 index 0000000000..246aad545d --- /dev/null +++ b/Content.Shared/GameObjects/Components/Atmos/GasTank/SharedGasTankUiKey.cs @@ -0,0 +1,11 @@ +using System; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Atmos.GasTank +{ + [Serializable, NetSerializable] + public enum SharedGasTankUiKey + { + Key + } +} diff --git a/Content.Shared/GameObjects/ContentNetIDs.cs b/Content.Shared/GameObjects/ContentNetIDs.cs index d6dc3fcafc..32ebc3ed06 100644 --- a/Content.Shared/GameObjects/ContentNetIDs.cs +++ b/Content.Shared/GameObjects/ContentNetIDs.cs @@ -82,6 +82,8 @@ public const uint PLACEABLE_SURFACE = 1076; public const uint STORABLE = 1077; public const uint PULLABLE = 1078; + public const uint GAS_TANK = 1079; + // Net IDs for integration tests. public const uint PREDICTION_TEST = 10001; diff --git a/Resources/Prototypes/Atmospherics/gases.yml b/Resources/Prototypes/Atmospherics/gases.yml index 0e414ec304..a337e11985 100644 --- a/Resources/Prototypes/Atmospherics/gases.yml +++ b/Resources/Prototypes/Atmospherics/gases.yml @@ -2,24 +2,32 @@ id: 0 name: Oxygen specificHeat: 20 + heatCapacityRatio: 1.4 + molarMass: 32 color: 2887E8 - type: gas id: 1 name: Nitrogen specificHeat: 30 + heatCapacityRatio: 1.4 + molarMass: 28 color: DA1010 - type: gas id: 2 name: Carbon Dioxide specificHeat: 30 + heatCapacityRatio: 1.3 + molarMass: 44 color: 4e4e4e - type: gas id: 3 name: Phoron specificHeat: 200 + heatCapacityRatio: 1.7 + molarMass: 120 #creadth: making it very heavy (x4 of oxygen), idk what the proper value should be gasOverlaySprite: /Textures/Effects/atmospherics.rsi gasOverlayState: phoron color: FF3300 @@ -28,6 +36,8 @@ id: 4 name: Tritium specificHeat: 10 + heatCapacityRatio: 1.3 + molarMass: 6 gasOverlaySprite: /Textures/Effects/atmospherics.rsi gasOverlayState: tritium color: 13FF4B @@ -36,6 +46,8 @@ id: 5 name: Water Vapor specificHeat: 40 + heatCapacityRatio: 1.33 + molarMass: 18 gasOverlaySprite: /Textures/Effects/atmospherics.rsi gasOverlayState: water_vapor color: bffffd diff --git a/Resources/Prototypes/Catalog/Fills/lockers.yml b/Resources/Prototypes/Catalog/Fills/lockers.yml index 1262d869a8..8001709552 100644 --- a/Resources/Prototypes/Catalog/Fills/lockers.yml +++ b/Resources/Prototypes/Catalog/Fills/lockers.yml @@ -62,15 +62,10 @@ prob: 0.4 - name: BreathMaskClothing prob: 0.25 - # TODO: uncomment when we actually have these items. - #- name: TankOxygenSmallFilled - # prob: 0.4s - #- name: TankOxygenSmallFilled - # prob: 0.25 - #- name: MedkitOxygenFilled - # prob: 0.25 - #- name: TankOxygenFilled - # prob: 0.2 + - name: EmergencyOxygenTankFilled + prob: 0.4 + - name: OxygenTankFilled + prob: 0.2 - type: entity id: LockerBoozeFilled @@ -291,6 +286,13 @@ id: LockerFireFilled parent: LockerFire suffix: Filled + components: + - type: StorageFill + contents: + - name: RedOxygenTankFilled + prob: 0.6 + - name: OuterclothingFiresuit + prob: 0.75 - type: entity id: WardrobePajama diff --git a/Resources/Prototypes/Entities/Clothing/Back/gas_tanks.yml b/Resources/Prototypes/Entities/Clothing/Back/gas_tanks.yml new file mode 100644 index 0000000000..489c820463 --- /dev/null +++ b/Resources/Prototypes/Entities/Clothing/Back/gas_tanks.yml @@ -0,0 +1,314 @@ +- type: entity + parent: BaseItem + abstract: true + id: GasTankBase + name: Gas Tank + description: It's a gas tank. It contains gas. + components: + - type: Sprite + sprite: Objects/Tanks/generic.rsi + state: icon + - type: UserInterface + interfaces: + - key: enum.SharedGasTankUiKey.Key + type: GasTankBoundUserInterface + - type: Clothing + sprite: Objects/Tanks/generic.rsi + QuickEquip: false + Slots: + - Back + - Belt + - type: GasTank + +- type: entity + id: OxygenTank + parent: GasTankBase + name: oxygen tank + description: A tank of oxygen. + suffix: Empty + components: + - type: Sprite + sprite: Objects/Tanks/oxygen.rsi + state: icon + - type: GasTank + outputPressure: 21.27825 + air: + volume: 70 + temperature: 293.15 + - type: Clothing + sprite: Objects/Tanks/oxygen.rsi + Slots: + - Back + - Belt + +- type: entity + id: OxygenTankFilled + parent: OxygenTank + name: oxygen tank + description: A tank of oxygen. + suffix: Filled + components: + - type: GasTank + outputPressure: 21.27825 + air: + volume: 70 + moles: + - 22.6293856 # oxygen + temperature: 293.15 + +- type: entity + id: YellowOxygenTank + parent: OxygenTank + name: oxygen tank + description: A tank of oxygen. This one is in yellow. + suffix: Empty + components: + - type: Sprite + sprite: Objects/Tanks/yellow.rsi + state: icon + - type: Clothing + sprite: Objects/Tanks/yellow.rsi + Slots: + - Back + - Belt + +- type: entity + id: YellowOxygenTankFilled + parent: OxygenTankFilled + name: oxygen tank + description: A tank of oxygen. This one is in yellow. + suffix: Filled + components: + - type: Sprite + sprite: Objects/Tanks/yellow.rsi + state: icon + - type: Clothing + sprite: Objects/Tanks/yellow.rsi + Slots: + - Back + - Belt + +- type: entity + id: RedOxygenTank + parent: OxygenTank + name: oxygen tank + description: A tank of oxygen. This one is in yellow. + suffix: Empty + components: + - type: Sprite + sprite: Objects/Tanks/red.rsi + state: icon + - type: Clothing + sprite: Objects/Tanks/red.rsi + Slots: + - Back + - Belt + +- type: entity + id: RedOxygenTankFilled + parent: OxygenTankFilled + name: oxygen tank + description: A tank of oxygen. This one is in yellow. + suffix: Filled + components: + - type: Sprite + sprite: Objects/Tanks/red.rsi + state: icon + - type: Clothing + sprite: Objects/Tanks/red.rsi + Slots: + - Back + - Belt + +- type: entity + id: EmergencyOxygenTank + parent: OxygenTank + name: emergency oxygen tank + description: Used for emergencies. Contains very little oxygen, so try to conserve it until you actually need it. + suffix: Empty + components: + - type: Sprite + sprite: Objects/Tanks/emergency.rsi + state: icon + - type: GasTank + outputPressure: 21.27825 + air: + volume: 2 + temperature: 293.15 + - type: Clothing + sprite: Objects/Tanks/emergency.rsi + Slots: + - Back + - Pocket + - Belt + +- type: entity + id: EmergencyOxygenTankFilled + parent: EmergencyOxygenTank + name: emergency oxygen tank + description: Used for emergencies. Contains very little oxygen, so try to conserve it until you actually need it. + suffix: Filled + components: + - type: GasTank + outputPressure: 21.27825 + air: + volume: 2 + moles: + - 0.323460326 # oxygen + temperature: 293.15 + +- type: entity + id: ExtendedEmergencyOxygenTank + parent: EmergencyOxygenTank + name: extended-capacity emergency oxygen tank + description: Used for emergencies. Contains very little oxygen, so try to conserve it until you actually need it. + suffix: Empty + components: + - type: Sprite + sprite: Objects/Tanks/emergency_yellow.rsi + state: icon + - type: GasTank + outputPressure: 21.27825 + air: + volume: 6 + temperature: 293.15 + - type: Clothing + sprite: Objects/Tanks/emergency_yellow.rsi + Slots: + - Back + - Pocket + - Belt + +- type: entity + id: ExtendedEmergencyOxygenTankFilled + parent: ExtendedEmergencyOxygenTank + name: extended-capacity emergency oxygen tank + description: Used for emergencies. Contains very little oxygen, so try to conserve it until you actually need it. + suffix: Filled + components: + - type: GasTank + outputPressure: 21.27825 + air: + volume: 6 + moles: + - 0.969830813 # oxygen + temperature: 293.15 + +- type: entity + id: DoubleEmergencyOxygenTank + parent: ExtendedEmergencyOxygenTank + name: double emergency oxygen tank + description: Used for emergencies. Contains very little oxygen, so try to conserve it until you actually need it. + suffix: Empty + components: + - type: Sprite + sprite: Objects/Tanks/emergency_double.rsi + state: icon + - type: GasTank + outputPressure: 21.27825 + air: + volume: 10 + temperature: 293.15 + - type: Clothing + sprite: Objects/Tanks/emergency_double.rsi + Slots: + - Back + - Pocket + - Belt + +- type: entity + id: DoubleEmergencyOxygenTankFilled + parent: DoubleEmergencyOxygenTank + name: double emergency oxygen tank + description: Used for emergencies. Contains very little oxygen, so try to conserve it until you actually need it. + suffix: Filled + components: + - type: GasTank + outputPressure: 21.27825 + air: + volume: 10 + moles: + - 1.61721219 # oxygen + temperature: 293.15 + +- type: entity + id: AirTank + parent: GasTankBase + name: air tank + description: Mixed anyone? + suffix: Empty + components: + - type: Sprite + sprite: Objects/Tanks/generic.rsi + state: icon + - type: GasTank + outputPressure: 101.325 + air: + volume: 70 + temperature: 293.15 + - type: Clothing + sprite: Objects/Tanks/generic.rsi + Slots: + - Back + - Belt + +- type: entity + id: AirTankFilled + parent: GasTankBase + name: air tank + description: Mixed anyone? + suffix: Filled + components: + - type: Sprite + sprite: Objects/Tanks/generic.rsi + state: icon + - type: GasTank + outputPressure: 101.325 + air: + volume: 70 + moles: + - 4.75217098 # oxygen + - 17.8772147 # nitrogen + temperature: 293.15 + - type: Clothing + sprite: Objects/Tanks/generic.rsi + Slots: + - Back + - Belt + +- type: entity + id: PhoronTank + parent: GasTankBase + name: phoron tank + suffix: Empty + description: "Contains dangerous phoron. Do not inhale. Warning: extremely flammable." + components: + - type: Sprite + sprite: Objects/Tanks/phoron.rsi + state: icon + - type: GasTank + outputPressure: 101.325 + air: + volume: 70 + temperature: 293.15 + - type: Clothing + sprite: Objects/Tanks/phoron.rsi + Slots: [] # no straps + +- type: entity + id: PhoronTankFilled + parent: PhoronTank + name: phoron tank + suffix: Filled + description: "Contains dangerous phoron. Do not inhale. Warning: extremely flammable." + components: + - type: GasTank + outputPressure: 101.325 + air: + volume: 70 + moles: + - 0 + - 0 + - 0 + - 11.3146928 # phoron + temperature: 293.15 diff --git a/Resources/Prototypes/Entities/Clothing/Masks/masks.yml b/Resources/Prototypes/Entities/Clothing/Masks/masks.yml index 01166060d8..9767140b7c 100644 --- a/Resources/Prototypes/Entities/Clothing/Masks/masks.yml +++ b/Resources/Prototypes/Entities/Clothing/Masks/masks.yml @@ -15,7 +15,7 @@ - type: Sprite sprite: Clothing/Masks/mask_gasalt.rsi state: icon - + - type: BreathMask - type: Clothing sprite: Clothing/Masks/mask_gasalt.rsi @@ -28,7 +28,7 @@ - type: Sprite sprite: Clothing/Masks/mask_gas.rsi state: icon - + - type: BreathMask - type: Clothing sprite: Clothing/Masks/mask_gas.rsi @@ -41,7 +41,7 @@ - type: Sprite sprite: Clothing/Masks/mask_breath.rsi state: icon - + - type: BreathMask - type: Clothing sprite: Clothing/Masks/mask_breath.rsi @@ -54,7 +54,7 @@ - type: Sprite sprite: Clothing/Masks/mask_clown.rsi state: icon - + - type: BreathMask - type: Clothing sprite: Clothing/Masks/mask_clown.rsi @@ -67,7 +67,7 @@ - type: Sprite sprite: Clothing/Masks/mask_joy.rsi state: icon - + - type: BreathMask - type: Clothing sprite: Clothing/Masks/mask_joy.rsi @@ -80,7 +80,7 @@ - type: Sprite sprite: Clothing/Masks/mask_mime.rsi state: icon - + - type: BreathMask - type: Clothing sprite: Clothing/Masks/mask_mime.rsi @@ -93,6 +93,6 @@ - type: Sprite sprite: Clothing/Masks/mask_sterile.rsi state: icon - + - type: BreathMask - type: Clothing sprite: Clothing/Masks/mask_sterile.rsi diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml index 660f344337..4d310059a1 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml @@ -150,7 +150,9 @@ components: - type: Sprite sprite: Clothing/OuterClothing/firesuit.rsi - + - type: PressureProtection + highPressureMultiplier: 0.85 + lowPressureMultiplier: 25 - type: Clothing sprite: Clothing/OuterClothing/firesuit.rsi diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index 26741a8d06..c72316ba55 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -160,6 +160,7 @@ producesGases: Oxygen: 0.00045572916 CarbonDioxide: 0.00015190972 + - type: Internals - type: MobStateManager - type: HeatResistance - type: Appearance diff --git a/Resources/Prototypes/Entities/Objects/Boxes/boxes_general.yml b/Resources/Prototypes/Entities/Objects/Boxes/boxes_general.yml index 824c5f345a..7d30fd762f 100644 --- a/Resources/Prototypes/Entities/Objects/Boxes/boxes_general.yml +++ b/Resources/Prototypes/Entities/Objects/Boxes/boxes_general.yml @@ -91,7 +91,7 @@ - type: StorageFill contents: - name: BreathMaskClothing - #- name: O2 Canister + - name: EmergencyOxygenTankFilled #- name: Injector - type: Sprite layers: diff --git a/Resources/Textures/Objects/Tanks/anesthetic.rsi/equipped-BACKPACK.png b/Resources/Textures/Objects/Tanks/anesthetic.rsi/equipped-BACKPACK.png new file mode 100644 index 0000000000000000000000000000000000000000..57675fec2201ecd146d3d4dd3aa3ad49596096c0 GIT binary patch literal 858 zcmV-g1Eu_lP)&b6b$jwi#4?e39B|;%!bllEPWozTVu)Y>wBASvvz*RO)_t0GV|WPnE?obAP9mu zuO!Y|0Ts+#mH|N1ZQ!bQ&3j%dSETKUr2_z9ba;^6xOocz@a^jttXH>`Z0V8x0r&n% zpLZHFmt~{FgA6z_+;!?$uWlvsmZh|Qva{+%!#(ppK38j)o1cYk+W^2a{e4QZq$5Dn z(^AhnO{rW-e0+Kr0FX+hU>F88J&lE>K;Hp+*6hn%S{jf9%xC7 z9R3Eh3)uLjE=MW_T&EuD^KL}S0ivBgs*~VM@yqk5@}I9&C=}TC_BJaN3M|apQhNYD z19yFS0#V@Cjp9v{lgtkUIzZodaq;;xRNVt?`1IgaM^ie1 z-%P@%cW^}P+MEWm~CU_!3^j$3VfRFn0AKR0>MQLO@GUtT4zZUQ9#l5DQ~B$J{{}3<^DY9V2Xq4R;cU zksf63B&@Vh7!$e*g|+h_$XJ}L$#zus@6?dmHfhr6sqZoQljr%~{2>AQ>F0_f@Ej3% zjySEOuL^4Db%e(!;3VOjfWUJkv6zIR*CFs62{1tv^EoiLZre)uA|UV_nT^ijwW45T zGz98q5VO%a0KoD0Z?+P?HIUue#>i+0#@}A3RSz}>lYZKhj5^7f@N7(aG=WuVxmaiY;53N;sI8MNmKoe ztPB&}`=Psn16?%GfY)j@L^5f-{6iu9@4N{fWd=OK0fz>xaBJQ^U&9zZWWrZZetCl1 z3Uq=ucKd<4356N-Jb5E+AAu7b0M_4Udxm!uXb1HAo;$_yTVMqP@bs$LrELSddwbNw zR0@(LwZNY}ec~J3F)>jjnx+v=(@3FE=;|laZ5$_wi6YU@P66QhChw=8>&$=79vnEQ SBv~N<0000BjU5C`zTy{3Z;#w1wVYqu8f?<=&Ew~#%<8xs&- z(#Ubaq?Eo8h!g1A>k#BPsSCBQqvSb{iO@jD}T(2s|OnCRs~G!$HPJxRB_h({Ksd>E-|mb z_vlS=4e{uQc6Kz?pFc<&;)|$Kc^6Mu)efw%M)Cr>r03MT|l%nLum{t@;Xti26`>vkcbaTP6 zEs|1_QcCM9khdRJPdK(U_sJ&$-^=&OGsYNWj4{R-V~jEO0%g@MH@U9yzr}%v0H@~$ zOs;Ku9>I759NVJJjdk3OZ!x;OK&f0YmV_+k5sW9GcyfsI_zL?69RR@i<{G7PMZ0Ws zGmo$pgp*kTAp}j&5$LR&)jWdn3i$BxJ%ER{mg~Bc_jOxaf15{0j<+(p^^k-Rq?F36 zvy;_4f)U+%NbOoR^R|{_TQi3yOz%2#J8f<+tfx2YIBA-(#w_B>KlgkdVcw3k(VO5h v`uHa25wtA3m8~<6@ZZpxM_`OG#$KS`{W)JC2yS2I00000NkvXXu0mjf9w{_4 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/anesthetic.rsi/inhand-right.png b/Resources/Textures/Objects/Tanks/anesthetic.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..f6e2438c9a425057cc23bb869a4a829be02adbff GIT binary patch literal 659 zcmV;E0&M+>P)BjE6bJBsO}BJ#!I&hgKy(rsga(6*33!O82^gHrUNQ!}WbM|H{k}pwc?;Py+?asq zlEzL8CZ+U3AWk4_Uk6u4B(^Krx=1$f2gV?zyVJX;Tiyd0V~jDz7-KA^hVN6>Ye-8W z$;}PlC(m^V0K)%e7drr!Y0&1zI_`&e7+hVVSSqhX5Ml?QaC(S~-Zl0QIsky-?G1{h z@=ARUGgZAhNhwK6NkRy+OoM6}uM(_J zyWPDVynpi+?OG-MjS)hSQi}4n>rw4mh0dDW^w4QjoFGiifJtd|-u;5QSIOP3PtqIw z#=FAWd7GB&F2MGvQeVc&(l${60>008z6I=JXvqi}k-$gt_t z)iV$sM+h;Vqw3Z3l>r|=eE^Vt&p`Bf+qTCgu5i9w(cLp(nFcAPNC-hkTj^&&B9jid zcCDhlgl3rrwQH4Z0}vKWpI(2d`k`eSbk^L?CIHcD@zb7xmt04f32Bb`I_U#p&p;Nf tGXLhwJ`1ugn`buvD)bC6#u#I4>@Q0-T+5IQJi`D0002ovPDHLkV1kh3GKc^G literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/anesthetic.rsi/meta.json b/Resources/Textures/Objects/Tanks/anesthetic.rsi/meta.json new file mode 100644 index 0000000000..5fdc78ab33 --- /dev/null +++ b/Resources/Textures/Objects/Tanks/anesthetic.rsi/meta.json @@ -0,0 +1,27 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/tgstation/tgstation at commit e1142f20f5e4661cb6845cfcf2dd69f864d67432", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "directions": 1 + }, + { + "name": "equipped-BACKPACK", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Objects/Tanks/emergency.rsi/icon.png b/Resources/Textures/Objects/Tanks/emergency.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a1f124628beb182d888626876b797089d89ede87 GIT binary patch literal 459 zcmV;+0W|)JP)ZYxTxTsW$#plu(=nx7uTNcrwsNhsb>EfSok`6^dCntptE~0}V(m_xQ z(ibQ?h>NH+35tUqKBpEGTap^LoGIjWPPmtVi;IhkgDA2j6j>5>o@sMZY8pCYlT+A< z_)~x)OJZSZ5!297WJz@U{G4ac@b>j%YbRktGqEQQ1nT848B*bozG9k7+LyN?w;Z5q2{AUm%JGNSQ8`!`fvP3M`(X56%-2E z3}Q#PCj;SDZhm!uSzkf5aY~{T!ph-Zm zV1IJpiz~K&vJyizZs(E^Z=KeZ?RE;k7ipsd)*O zX+^WuHon`ur~FRdq111|N3Tu3-y&f2`((R-Q<}UJKLf*`M*h;%`P28dU;hiT%hT1* JWt~$(696^8hid=; literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/emergency.rsi/inhand-right.png b/Resources/Textures/Objects/Tanks/emergency.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..6703263be1f8ac28f7d26f687aa6620d6d7fbb82 GIT binary patch literal 358 zcmV-s0h#`ZP)p_5XXw^GL>&Z02cN(eii^YP zP%O3(NZ=+nSN^{>#3G+XVP zd0SprjQ7gB)Fk7J>;1d}MzW~(iS-Ny9NW|Y-btz6?aj1wIN<5D}y z*LzuShT3YUUs~bw=XbSM1lzL&*5?_lKfj7(006)t-+_K|GnDA=3d9_m~a^m?6lZ}$iH;c)Kx{{QoR-#G^`F)=v@ zLIFR80)Eo-?*$_SIgg7jTWtV<{LZfBSZQD^G!6ii-`TZjtsOE#{K3Kk0KjT@fb+P> z>GqbbF!HYnYe}L&~jzjjeVE4iEO#akosIIRk9xO1*|Z8*`9y zYhYt0UanLvhX?zbnK+XHQQ&Di=R-D~!Ng=3&Fe4VbbFCaXAq6ppww${y1fVm{KQO* zjB*`K^ld;CczXNnB2H>Q5RKT{b{>t`aP8_Z)x@#V005LLRZAhi*)d*Ur$7{Vdimiw zPKtL>EDa;QHH7V)4^y)lRlFPJN_DUvVB*adah8eon1b}yzfi@}a8J>`5e1%PSte27 z=~Mg$i2_eYN{N1db+>(NUhgFY=s1R=z*9V#!piazu5dmSzI_JEL@R9Ps~;O(k6b4- zOCSn7B^IlAJbzaek0(=jc<-qp;+h6Tfv1^Z696z1Y^vfb%S(tSQ+V{+HL$oQqQKMN zz1wQdS&d~`ruo_XBuNs50{*^ht_${>9VsQXv058Vk|eq{Jxy<3B?cDnT7r>L4#Lej z06>txq^3HtSVbbXiYY#fY&tV=+Pg01JT4jwjpOHD85>{T;rsh1ZSzLeTr)tciS?Lr yE)CZ+Nb8f!FtXcq1>!s|wb2Z_UQJ9){&NcY##;FL(&wW90000F&@37m6L8I*dn0+px0(X?{W{-8xhfO*e>l~%XTO%FkL47=l`nvdn%2??sovq z0)m8{*^#T>%RT$GAynhH75nx|!HNFTu4hj6JLfNs*>OpwsEqrT8voPD&10=vc4>X7-2d{*(Zt+)2F^2fReTe8wd%sk?yb2@)<5^3 z5qOrD^s;>SmS^G{?s7xyWMGI$?>1-DyT0L*-GQgK8G>(Lm0Hbiz_(YXPhEX^iLSS; zjK2}zUYojoxzo2NcmFw;(z@~CS?Wq+r)h5Pmswupa*Lw0zt?Kr;{6ld@)%ngh kg45owWjCs3IPm`yH)CthTI)A_bs*b3UHx3vIVCg!0HCIj_W%F@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/emergency_double.rsi/inhand-right.png b/Resources/Textures/Objects/Tanks/emergency_double.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..e72d222b9440a093354e60704723c6614af914d7 GIT binary patch literal 341 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEV3hH6aSW-L^Y)e@Uz3AC>%(64 z4F+)xPM)c6HYls8xal3}wGiN4zQJhY#$^G*zcw0V>zEX#CPsd$x7}^L_`;q*kl{dZ z|Ly0PnCl6*4}ED&**){BKo47i<@_r}TjxEsf2dw4;-c{6FtjqJYttV^8Oqp!+ObVVaf64Kc+34PAtiF(w}|(k;a5wUQ2VGJ}bUx zug#5nebs&a_jIRcm)Ie;GB5<2xi#fH=6n3NA^wGIlVUk%&Rp9Q4DYtTUa*_xx6SmY zS!?y4Pky`jt%P>?oS3pmv6*is6*^!2zI)E|hIK-zbD93{PdcLRIu?QV_{+x{q-0-Q7P2L4pMNZ_zbX=$a~Izi;*%>N++( zy?w|^coxt#Rg7dtaUC07Q$;AOaB_4=xV=M4!jpimsbXL-#mdqWU6CjN1A{35inn~^S=_P6yC}BHkq`-`f)R{emKAA{s{im z_8iMJMA^9zmT8E+`62O`jflr=q}s3N4FIUi%rXtJxHiYz`Ebp6W#_XSHbf0ol6T;B<>%|0KUS51PSsd@($Cs%{B$pc-;U1002ovPDHLk FV1h>X(f|Me literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/inhand-left.png b/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..55ff15618f74f122d98a3e40fca80a873058c438 GIT binary patch literal 340 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEV3hWBaSW-L^Y)fuSF?l2v5&_$ zv3m#>F&@37m6L8I*dn0+px0(X?{W{-8xhfO*e>l~%XTO%FkL47=l`nvdn%2??sovq z0)m8{*^#T>%RT$GAynhH75nx|!HNFTu4hj6JLfNs*>OpwsEqrT8voPD&10=vc4>X7-2d{*(Zt+)2F^2fReTe8wd%sk?yb2@)<5^3 z5qOrD^s;>SmS^G{?s7xyWMGI$?>1-DyT0L*-GQgK8G>(Lm0Hbiz_(YXPhEX^iLSS; zjK2}zUYojoxzo2NcmFw;(z@~CS?Wq+r)h5Pmswupa*Lw0zt?Kr;{6ld@)%ngh kg45owWjCs3IPm`yH)CthTI)A_bs*b3UHx3vIVCg!0HCIj_W%F@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/inhand-right.png b/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..e72d222b9440a093354e60704723c6614af914d7 GIT binary patch literal 341 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEV3hH6aSW-L^Y)e@Uz3AC>%(64 z4F+)xPM)c6HYls8xal3}wGiN4zQJhY#$^G*zcw0V>zEX#CPsd$x7}^L_`;q*kl{dZ z|Ly0PnCl6*4}ED&**){BKo47i<@_r}TjxEsf2dw4;-c{6FtjqJYttV^8Oqp!+ObVVaf64Kc+34PAtiF(w}|(k;a5wUQ2VGJ}bUx zug#5nebs&a_jIRcm)Ie;GB5<2xi#fH=6n3NA^wGIlVUk%&Rp9Q4DYtTUa*_xx6SmY zS!?y4Pky`jt%P>?oS3pmv6*is6*^!2zI)E|hIK-zbD93{yjTOP4uEDJ*;{u1;Nlu=p{M!FX>-VO8Yl7287a!)KkS= zHiS@s|{ z0h2<_Y6wo7xtsz{V#)zpw`1%2EMh7RidK5JG6C>61K7n>1RNC#6F)d|*(^IM6kJ;e z27>{-UM~iNfh{?vB7kQBIT2?*{|Z)v0bHqVXx)yU1@6)beBA!c4VIRcaCmqKNs@Hk zv~I`r|G-?X+;Sq$QZHX2lgXf1EaKbOFP46UfH`#l@bABeRpSYQAP9mW2!bF8f*|fC z!b-|KR$fN?2SnTE!m6;(zk+M6KR3n$!xwd}^}+iGj1X3Z`DO>N$Xcnqy2a?52ZsFv zA`A`L-Q8t7J3CNS6##H?dV*59qVEmNf;0Gw0fze)aw5*2N1`~ZHjzpvp(qOS`TRs3 zg%QB(?y9Ql#*Wth-QExxW!%`Ma>euO^IPV9(}W~RP!z>bhHwqo*x0bCru*>zflw$U zyjSRe^YinG*NplF++b2q!uoausdN&JMnfpW_V)NR`vRO8oB9LnJUk2i0Csf{W&yqk z@>Dp=v{Rsw4w57}vj}c&ZNcyNBNmHUP;WZKVlgZ)FC!X_3hxz0Kt7*GtyY7os*n?L zcH85(dj;Z#PEYjXEUZ%mK@bE%5HrbdhIc`PdJ@r000000NkvXXu0mjf DKZAb8 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/generic.rsi/icon.png b/Resources/Textures/Objects/Tanks/generic.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0202db50aad41fa2ef23b84e3f35f753014a65b4 GIT binary patch literal 623 zcmV-#0+9WQP)1t|gX1MZTyE%6C>*4>gg}R*;o9BhkD(2SlS2k$A{48GlRBw%2snyUB`F!* z4nqO~aY+gX77Hq5P(qT!(cyP72T9E3l3Y9Go$fyF?s>lNKmI#dWtB^ckxWPlaUnz$Q>VEwjF{iSzppw==h>o|f4*c2FWwPl-SToI#&$V~0Wa7lmRD zfP41~*k^6D_?9d2zjtD30VA1^Yab7>&)O7^o&cb#Dgc=~cWKsZID;Wg43Wa}gfKI359xIV3^-rHe0PXXO(D*YJo`4WSK7aX|lgbBo?Bj4hnK1GT z&*$@WyWNrcIQ;#)w{G{-GWP_85Hgp`dBQbK^97$ z>pG7g7JS2f3kV@(sZ^rXYN04fU~pdoLpVS>oyN9p6h#RHK2u=$!j;Qqx0X()X*3${ zxvHuQ!#xFr5Hg$1;y4bPrj1CLrb(~YLseB?y?C}T9H#68LI{~krQ9tQKv5JLjRvM^ zy0xR?gQei$F#%1}M%LN3ZDO$)mSqJ72aj(-I2;Bhcei*v9vB>^OrTP!gpQAo(RF>K z9>ki@6(NMQEK6FJC9Bn{ub<3$GqV#Rg#7vA8^G0dzRD_BnZH8}4{{7_k5V literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/generic.rsi/inhand-left.png b/Resources/Textures/Objects/Tanks/generic.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..c590366755a301fbf898a7da543c8c9f52c8a077 GIT binary patch literal 677 zcmV;W0$TlvP)4}06bJCXegX@TB$f+XEsMn?o$1mD;kvu>j3pr z;^gxX3z4Y+^;6mzYfNP&8N8 zCiku-Z`yYqR!Xtfnzhz^RzJ7C0*Hu-D3_I$dIgN8vqJLS^E`xM2mtu}<&%YJJtHFS zcDracn^wn7WZncv(^;X@`i@$yhA<4#Znq0>U$q$`;$E+ZIF4-%xgv7`fTtq-`1Tc^ z=dJvfcIhT>em})M0KzatwOX|^(Y!4mgHaT5 z6h#~a!N!{y5wS6blWUU=Hm~E=+6qXFrn7?YIvhn21HfP~z;HO+YK%CJF_}yN0M%+0 zzkmIFvFRQJ-*s4P%}S}Qk1!%)tu-5Ca(nYT;k!=i^P7kc`{9KULI@#*5JCtcg!q8+ zYM0v_*ZSY$z|#bma|1TVww*_?UI5>9_~iHqkBb<$H`geYE7ppTM;^g?0gC1dlj$8= zol5|~;^7{pawYS!J(xVgS^fNPRzNAm%Q=E9)i50Z`$R< zC)VW=j4>M*!RjWsk3RmoJVKHz*{OZEva{q7{u^2H2to)U#0T^T)X!$x_{OvW00000 LNkvXXu0mjfC&4&> literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/generic.rsi/inhand-right.png b/Resources/Textures/Objects/Tanks/generic.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..e8680785b671bdf461c89b0c227084af91c9ca6d GIT binary patch literal 671 zcmV;Q0$}}#P)BjE6bJCX{Q-&xW0GhTXcC%^Y|fTNU^=-OYg+J-!QfKT!9%}*2M_ufg&PW)ZxH$b zK@e=5!2}J8197HoeVrtfTA@;$v*qx9i;b{!ckkZG!+QW@j4{R-V~iElab4Q=9NtR3 z%#S;+OYLJp0FZp{yTSo*ER&875Ae8pz+yf{quF{FK`0!6y1Kyi>=ymO5CE{czeA(h z+WX^BH~=5ceqs3S9EM?_KN#Za5Agf)vbLB{b3j5Q37I z$-eCTzp|O|s(%C_#NIAG;aKMD7S9-Cj4{R-V~jDz82c|>MN-|x*HZlfd35#+WS^@m z5E}(RXU{TI@{5s}$Wm1CC{q@B8F= z-m_xuPUMxDlv3JDXpUu)QfjvW5JFHK$7>VVR{bz3c5Y*=HA&AvQg6>rYMb-W5ro1o z!7vOF1OWgbIrH1@8AxjXJ5i;cft`*29(o2CV~jDD`U|VWVo}PC=C1$%002ovPDHLk FV1l-YIo1FG literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/generic.rsi/meta.json b/Resources/Textures/Objects/Tanks/generic.rsi/meta.json new file mode 100644 index 0000000000..19e8d88681 --- /dev/null +++ b/Resources/Textures/Objects/Tanks/generic.rsi/meta.json @@ -0,0 +1,27 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "directions": 1 + }, + { + "name": "equipped-BACKPACK", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Objects/Tanks/oxygen.rsi/equipped-BACKPACK.png b/Resources/Textures/Objects/Tanks/oxygen.rsi/equipped-BACKPACK.png new file mode 100644 index 0000000000000000000000000000000000000000..d890d08c1af1fb34351a5fd916f25ef67eea0ea6 GIT binary patch literal 834 zcmV-I1HJr-P)AxyBv-O236O}4(j<0L!#&a?CXeE@@a}`1r_iUD7?ktgG4Wg3ZR0L!gmJ31Acpj(%@Nqy!Mn^lq&LKy?L(M*ETI zs5q)eN`SRg%5=jqolLT|RLZq^V7%)H&xXqw?{YQ~N(IbUf&EW=@CQSP^oEf;&BG`b zFgy}R?lh0<6hYTi<`0JO(-*4z>3?VKk#G+zmc_b^Xe9tzdTU&4~VdA$i&nfJDFTWAe#Yz+U+aP z4YYz7{KEj%yoIi*YC^QlTc9>>h=#fPLDU! z7tn}zlRv=8!>!;4a2i8hlb~4+zS&ElRSeueAVMer&%w{eN;bl44zsaR%|9SQxD$N2 zo=3Vogh2KObWLUdD$X||+$(V1CJDasa9_ez@x0G=;v7RrtmlRGSqkv`YzOIbTWu$J zRd;iH7>ESk+}ieyDPVzY2I-iBp0gdx`u^+FAmE*t34$O9f*@Sy5Aec43xb4aCIA2c M07*qoM6N<$f`~?nYybcN literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/oxygen.rsi/icon.png b/Resources/Textures/Objects/Tanks/oxygen.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..95c148e604f7bd5119d0a010c6d9ac09e278bfc0 GIT binary patch literal 651 zcmV;60(AX}P)Ah{^+POVPBKf8wr|t%1Mb$3dd1VT>lDZm@ z6;)Gi-sj=o=d$&`;nj9umw>FOTJPm+tQ|Qk!bQKAw+mOQ!lA|^(06VI508Z`!)p?- zQhRtT#Ea>Rmf@BKWJT2;Kbqy~vlt_;!m)7C@2wdQ7G^+)>lvL{NA!D1Zl%iLwbWi3 z(+2c`8=YA<)~1JBNk<w zo^2RzD$vz+9w<(FsmOkC$~JgKftT?`NBr&$!egPb^@Bd2Ewz`%^h5ez0L3}58(vjF lm-zL42f%iCGtK-L`2*RN3UI@X4}Aat002ovPDHLkV1iuFIzj*d literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/oxygen.rsi/inhand-left.png b/Resources/Textures/Objects/Tanks/oxygen.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..33f244834ba42e547d22c40330d95148f75dc2a9 GIT binary patch literal 733 zcmV<30wVp1P)~O&mSSSs@(ca)pT2lTPy#n+IF1Z#Qg0EV49{Fw+O1Hi%zE_ z;_1S0^<|^>|Ly>bbyKw78-^hOV0mM6(?}12Y6%ySDCTyOl&KDp767;| zf@zup0Jcw;aClT1E5_kb1>2`f0D$c$^O)O7-ni*fawRA*xEy|F-XTyeVgBnUe0cwQ z$j9&K=t!5qmV2G>;Zpe=y{D z+qyN6V0%8`U+$;g;#=w^03d)YdLOemIQ-&qx>9b#_UY!xSu?H P00000NkvXXu0mjfte{lI literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/oxygen.rsi/inhand-right.png b/Resources/Textures/Objects/Tanks/oxygen.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..c6368e4f3974ce63fa8e033af011eb227e329a58 GIT binary patch literal 738 zcmV<80v-K{P)xR?%JI^5xuAxp`0P#j{nxcVPR!$Fsl4z7h8 zO1BnM3K^0*C}>cU!AhVBAyMp5vb+vKvB@QzyC)=1HG+jFv=0oTi$ z0DwTXgjiJW|6XaFObs(Zw_Pu9VrhK~!vw*$c)CFOVwXm3*9XPZ1rkP$^2M&LOt?1# ztOm!TGTyy>I&!;i<Rr#y&K}H!I~ky8IxVel0RRlc z=ns^IMB}jY(>p0#h9d}6OL!6PV1IxA!A!9H1P%@>$eu0&08G=QaalZYb0|;mq>z0+ z3joNTF5=*@a_{|?i&8Y5em;4I%WwqKJE^hw=5LdQM87g05U7?g`~3?(eSACI8Wc?* z>C@g0Sx7_uCt!>*#u#IaF~%5UjM?W_k<{(t>rnjxR^03v7<{f{{VSdh)%9Nu3xYaX|QdRN?Lug-|0{~`{IUF@w@a=6od90;JP6j{Tc#j_& zuK@r7|R;*8l(j literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/oxygen.rsi/meta.json b/Resources/Textures/Objects/Tanks/oxygen.rsi/meta.json new file mode 100644 index 0000000000..19e8d88681 --- /dev/null +++ b/Resources/Textures/Objects/Tanks/oxygen.rsi/meta.json @@ -0,0 +1,27 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "directions": 1 + }, + { + "name": "equipped-BACKPACK", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Objects/Tanks/phoron.rsi/icon.png b/Resources/Textures/Objects/Tanks/phoron.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a7fdcf8f676acd409535449890051e28d97434da GIT binary patch literal 482 zcmV<80UiE{P)1wvv+)FN5J4z*VNmN4RQwr8DMByMF6u%&fCumZrHi`IBa~91h+vOUwM4`s z3PsZB0o)WF7bQhwn@rM;i@d84X7W7qecuG4M2RwC3?ZZ;gbcfXW>Wax&K7U>JAk}s zG-5%&5qQH-`1&d|^nzfFygmSNjaPx8S zLQjz?&EbYSF`r`SNy)*(p65&`35Kiw|;41MHFof_uxfk5;^Z-*{a4iuZOtzjHTXqMYX{H0i!D9vtA>`rwJ=M=m4red1 zzGi)Ue%xK-WZ_mB?pcA0Mk7{!U1op!nsR>^ZK^+V-OsWIg?kE&k}zu>^z70Q8m8y& zC@pliL(eV^|9X?al?6shB;~LIVc~%U#z_F7;eiF*5?0R!2n`P<;7B;nlfa`yiSjS< Y4ap24mIN^B=Kufz07*qoM6N<$g1_9)%K!iX literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/phoron.rsi/inhand-left.png b/Resources/Textures/Objects/Tanks/phoron.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..a8654c7d9cf6829c5a23dd427117a10321d06f20 GIT binary patch literal 537 zcmV+!0_OdRP)Y5C-5m%V1%lLNt&@NMVW;%XTZN7tYe35G*8>l~@JKRN}9&5J{C-CS9-)vK9#e zK@>qPtkRB!tX%vc-Z__3c%SaBn4O*5+h&Fc000000000003e~zTFyF$%!Jl5x2tTl z;^@2Ra~wu1jutd&oVhSsF}JJC&HPjm{QI~k<6|*>oHTM@5R2r7t>spILga>1aoqVxEY&EH0`&Tq!PezT*cXvKSlYuY}}5tU1Hadty~^9Q#TiBvxcB!d~PxVv*tZX8f~gQqrsbhSdg z{^iW|j8=SDd7_iMP4Xwf#|!o!MDtCB(dzv!2(9H#tr+_vG+J?|R%Cw{a5JCyGMy!r ziW;qWX?Zo)m;Wk;KM60-kBMSr%4qfLuO<-#00000000000002eHLp2=KRJ((v?U&w z^gM!y@8$&NwN8@ai_Hf_`}l4g5V@gL=&uRQBZ%R4l?S_PZyntfySbk4LWkD!V0VqV zUFA-#7`rPi)^X)#o_!yG>vVlC-CT)z1Tks%@xLQ6k02t;8`w{jWwv>rL?#PI*3UWz bfPd%}wWe7SpJve%00000NkvXXu0mjfqjB$n literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/phoron.rsi/inhand-right.png b/Resources/Textures/Objects/Tanks/phoron.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..7dd141ed66d944f4183d489704fc8aa00e4f5aaf GIT binary patch literal 522 zcmV+l0`>igP)YbrT1{bZ99Q ziXy0klk0QPgiB1NqRn09|Jxd9-+OnL>+l{C00000000000Kf!+)@(b6+$bs7ELcgw z-1gb^MNm@2`2||D`FU!A*34}`{&G}2H;RepF3QULyG>h2KM1tui-R|!uut9kA}J}j zTVJHGHK1_l5rzF(=KpJ9YmmsB!aiLbyiI(46APfE;QfsrRZj~<jRhu&Ni zPejCxB1id}#h=`+OrN`wf;+VmM|S}?icY`xCM89DR|Q&gr&jtl_lYZFo`lzzC+BzG zq@>`b<<+0>eAW-m%g(3GbM62D00000000000ItfbUG79%-qy@Ox?OLDt(k#Y$N4fJ zkj47Uz-Ncr?ETL+pdsBU?$k=mZJ+P<);^x)&^eAezJbDp8|9QquBoHlvts6Q0Ph=$l+kJQ2@d5skcT90x5fNJPO8@`> M07*qoM6N<$g3?F$?*IS* literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/phoron.rsi/meta.json b/Resources/Textures/Objects/Tanks/phoron.rsi/meta.json new file mode 100644 index 0000000000..5123c111a0 --- /dev/null +++ b/Resources/Textures/Objects/Tanks/phoron.rsi/meta.json @@ -0,0 +1,38 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/tgstation/tgstation at commit e1142f20f5e4661cb6845cfcf2dd69f864d67432", + "states": [ + { + "name": "icon", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "inhand-left", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "inhand-right", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Objects/Tanks/plasmaman.rsi/equipped-BACKPACK.png b/Resources/Textures/Objects/Tanks/plasmaman.rsi/equipped-BACKPACK.png new file mode 100644 index 0000000000000000000000000000000000000000..2aee6424a870dddfa5b9744dea367d005fa55742 GIT binary patch literal 725 zcmV;`0xJE9P)5h92AcPP?2q7er<2WMC z@w6dhZ0XbX6E`B=KG z;%?f?Uvz*c`u&N6L0M~U( zQR9THjHkd+6N$zjN&#?Px5POYx~>cUZ@>|3Tb3nO1!r9>&V_B;B6#Y5{NcYd?e@58 znVD8NE6e`vqywQs1^~Dn3V*#ycHx8&LI@#*5JCtcgb=xK8rn3?OxFP<5w6qxx&fJ3 zlZoL8$oKc2U^1E992*XYxlTxiFT7f<;?eiFP?;>1p3pTQ!kO1)2}sj4MP5Nz_1ocT z^Qh@^X<}qN^$R#YXv3(btL!GJtQ&~T42`FL4!*U!c+50S!5B+g&(s)WOP@z7>jtC& zI5|g&X?_lVm0qux+cQA++i}+oM7Dr5x%a%qse9zV*EJyO_ut^^_cfjl4hxZjdc|Lh z%GM6NZ=J}VAlCYTrq%(pwbX4R+SNk<@zxDUZ-JxcY&Tdman=n)J_CZ>dytDSg86j= zGSXWh=tp0=>!{Y|oL%}aAUX)jL*f5D7hj~h5<&rhnM>-nKE>5KI<(w@nj{#m_vvl3f}oazs{m*0g%6A((QM7JolW2tAi+t`HcX< zgbUvJ;pP6&9KieKVcmYkw}E^g#e5<_Fk~98o_3HsfAOV0H1G9$G`}`uNBY$QFzy+|HUns7|P9w@Yv@-%RQ~4xpq~ z>iFVs2vwh@##*S>ZfjZJ_xij}xTt#`86XIuwjgfE#0CgLfS7Jq!pG}O02BleJ7iL& cN)<+b07PrzOkS&j4*&oF07*qoM6N<$f@BmCjsO4v literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/plasmaman.rsi/inhand-left.png b/Resources/Textures/Objects/Tanks/plasmaman.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..495c4f398d2f02beb1686940d5652cc64dfadbec GIT binary patch literal 635 zcmV->0)+jEP)`~?{@y~V%4-J$w~}!=naX+#lPRrQ%u%e{QPE3OrrwzI}P4`edRO6D~TL| z-jJv_B&y!+(%x!MCOer#Y+uh?FKc4b8tkmW<9qu6yuav54D~M}4RAVdI#KI>i>zr^ zJ*~mU>hze*!+qNCtl7U$3%LDrpTmM^(#1p9gsY0Xajw17_+@bNo5 ztQ<%W6%hC$G)vaR)MLByY0XYxk*7nR+M_c#vpZ*8*kcxAtiisuxhBB%9*yoOgq*xm zN-3q3Qc5YMlv1iZ%6JtoMlz33x?fwR^9bS>BcVy>5lYtp={!Q|8UUB`2v^-q&%-+( zuxx+k5qxirg6x1$c^;6@BZxP^WdcGsK`M_R`WXB@6X343L&xU2PrByIw+>)$qBmCI z^#|L8dH%Q94#?B#+b2uMoo6GgROPaeYzt56z+~eNwm*2Y~zaI!8gb+dqA%wV|>$)oI zJ!HmpT{RpI)on^b$>!#PhXVNd$l=|mZN}rV)%rZTV|wGjL-_&J%kbiN!1E`^H@UCW z^p9l9H~|2i28>SO@Ej^%0;`QJ%hQK;Nia@8cSPyq8NA#MlaN;%PMC!DR@*2ERu$j} z@ah<9PWU~K4$s3Rv^HBtl@ObNUK5qH*biX;$nFKYOdo!|rgpzhy&T4pbnjFGdQDW# zVQO)EtF0=lefEzidtEsTWDZW0a?!s z*v|f#GD<+!GXuI=d)=@@8b+JeZ~IOIeq2Q7y|UWB@tFY~{Q64t>_sNq_z3(zGvHOj zGr!D%i47+j+F0aqFb*KgnF0N5NbdEgk!n&ji?_Xgw)Fai5JD`@U+7cZ)@&~2&j0`b M07*qoM6N<$f~5~GjsO4v literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/plasmaman.rsi/meta.json b/Resources/Textures/Objects/Tanks/plasmaman.rsi/meta.json new file mode 100644 index 0000000000..19e8d88681 --- /dev/null +++ b/Resources/Textures/Objects/Tanks/plasmaman.rsi/meta.json @@ -0,0 +1,27 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "directions": 1 + }, + { + "name": "equipped-BACKPACK", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Objects/Tanks/red.rsi/equipped-BACKPACK.png b/Resources/Textures/Objects/Tanks/red.rsi/equipped-BACKPACK.png new file mode 100644 index 0000000000000000000000000000000000000000..6c36a11d4de1ca12b164ea0a74d4ce91f42430fe GIT binary patch literal 851 zcmV-Z1FZasP)81CYcQe)DIt=A5KqgN3QMJ-7g74-l0#1(N)P!5dheyD+;S+TE&T^- zONvlRNXCz+t1V4oZ<0|r2_yUw7kSF2d)4Bz8AkDU)VJ@mIpt0!hyDdWwh)1Jcr<%{} zf!TFvl)%&^Hs)=xeJl0dmf;!DZK|1_g2QGkm%tz~Ff7V#E=7ck>0AE0BG$1 zjYU6#&AMfM0zlV5XH@_MfJ7wBlF1}1m&+`fOtM5IY`SklflJ#3c0T7Jy4>)2J=i}u zgj%g2xDvwt!6CL%sdEd4iYzhF<%a7-#rm8B!Dbyh6LxGK)li)hp;jw~?*n5QxfNMr zu^A^;#nwsi?ayOV7a?Fw9RU1&_b@wnf*=TjAP9mW2!bGp3kf%qGLD&-PW=F{Y|c%K zi!L{6s`;GTFi2-}LQOq}rXSFQTOQYn9a=`#Oy<=pI=?*7(hu;^9Y`V)X6ti~Rt+9# zXJ54%z$iFF`!E2ol}ZVUEV1B9sI4E^JgVWv$4plnxicWxtOKGQ+iIgd*v{O4JQ`)^ zSqsqPtiIFPobcoIeRgTB)Kk@n8v_PWgSQ$Z`T?#3^lI>WdI2lwrb&p+ID6I)^j#hw zrY>L*eUm<*pO;5L574g-Jq5vT(RA2bpq>qyet-ve8~pw7F*2D9ruX)M5ARKwXg$++ zZ{gj-0>a@iuy#lHzg!2*KDY<}gdGL70Y#SBzaG~PBAQm19l{rSK!!<I@#)Ybs%jKuxbQt51YcRq^}0Fm?9ou4YIrc^~n0a z@NT<@J{r(sipa@lP2q9bX$ju5fU)zEi7`Wxce0yyovp`yJ*a8QW2DF$W z(moFWp~EKGx`T<#0+mVyfX)00X`iR3fwL^2#S}3qjkJ$cLn5s=xOO`UfZy+D>B0Sh z;9&9!GH!8X$g*_@lhO#A`4!vX0M1Td99+5xTjgV?TiWMg(Iz-_XyDn}d&k{RJKVVv zII`aBA!8+`KVFvI*8`2$Ki4>0FXWB&jE002ovPDHLk FV1m?0D%1b~ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/red.rsi/inhand-left.png b/Resources/Textures/Objects/Tanks/red.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..fe2a53b1ba275f22ed117d29dd0834e46755185d GIT binary patch literal 736 zcmV<60w4W}P)9C0>0kn0m79PX_Fg-zyY|8I$m#Yhgx0}`9u;~ErQjF2%<|{GUo>XuuOy9 z%-mLTvH}P2(fJ(eeqZ){0KnZ#f-~x2nFd?a#-xwSMMMYq6$E>K?J|maz0(t3(*T-Qw1vwrvNlM+zI{ z;K!r(S8JJlTGU>Hq1J{NI$}VL!=*p5JCvKfPMmWD?zHr SOx1<}0000jc42LlAFnSWPGpI%CVU$8sVLQp*4kgmnv^NAili3_w~hq?*|Y<2qActeikO562pVXG~W~O;fr}|2txrF zn#y*>IRHSRP#9dO$Px?s~W(9uPyHR9`eSRdPWcTs&FaK+Cz|d3{ zw0Hcd6j@@tuR}rzA%qY@2qA905^Wt3|#HkuOK`H;AhRi`sxZ3 zV{vqv4P4mgSXx|wAOBK&3c#DHB}72sFR!1e2${+;`clcxaut{JFWEuq+LBNt^u zA&<*Q0ZgN2plY>*wfZq~(JAk68JfyW(;OPbr1lRLSz?Eq&!>}up{eZ8tEbE~O?JIz zV3OnTBLwb8AP6-B&ffnxaZeyfH3L&C*tiQd1B4Jl2pRYX4FEr&+_NrW00000NkvXX Hu0mjf3L8az literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/red.rsi/meta.json b/Resources/Textures/Objects/Tanks/red.rsi/meta.json new file mode 100644 index 0000000000..5fdc78ab33 --- /dev/null +++ b/Resources/Textures/Objects/Tanks/red.rsi/meta.json @@ -0,0 +1,27 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/tgstation/tgstation at commit e1142f20f5e4661cb6845cfcf2dd69f864d67432", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "directions": 1 + }, + { + "name": "equipped-BACKPACK", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Objects/Tanks/yellow.rsi/equipped-BACKPACK.png b/Resources/Textures/Objects/Tanks/yellow.rsi/equipped-BACKPACK.png new file mode 100644 index 0000000000000000000000000000000000000000..0ae5d0fe8bc82dfb58f5e1b124c8f3908d7ae680 GIT binary patch literal 839 zcmV-N1GxN&P)F|9LK*~Q7(~eU9_z&F_8R-+8|;KoA5$5X40# zQ11Yopld1vfCFH-7>#3EraD99!002aK!t74>JpjP>>^H1!<{Z`KlIRPT`qf^q zHM*vf8EPxb} zg|63{)Ji(=`S}9?Kv3?(;6NUoN-O4Gzwu@WGy*r`uK)n0j)d#7gajCl#VU3+rM(SK z$Dt^lN-O3Qd0g90xGqb$2UO~6PP<^e@#PSxB|-w2MZ?woMnp&e-c0Y+N&u8sfKo>S zLAlRWIYI&~B$E|49K*2~TSz87n+KM%<2WuUSjvvODn}?_YZdsiz6L2ILuqeAVRsK^ z(Lis11clu_oTmu7rZOocL%RA0GZQ%kVMh)BXWo+MIhTEQMqonESq| zJWy_^19Kmif546_ya%jghjk*`$S1F-TOKI;2YA?4WGp_!W+rmTtabqaUc4*!1S>bt z2x3?b1C;X?x~8(;{s_KqZeu=?$K-4nZ{L4(7UNiah#jtvoE$%&^^8{;VUcg&FU+wg zdXycmkFc@$kZpY2IiMCfc&`!h4{!yr%fZ|E1-zh=Ct+%|rE33x>-2a%eF3%jHu(eG zyxa(W0JkwzH3?SA!Pk2Uw2Q&=5Afh_gpNxJK4r(T)3gI%NjSiIkZl^lQT8?-$qM&b zt^n7KXL#JaflRIox~8&!6}L7bmRI1sO%m41WBC%!il_V62Mjjn5j6Pj5nBPa3O{jV zm?t~Iv$~tt!+DHwgGAW`ZCHf*=Ub`3n&sL~Qck RQHuZo002ovPDHLkV1medaz;}$yNmW_po+2b?2>}KJEK-_v7w9-~ac%{O_Qust!n! z(;+0ML+p25%|SuasU*#A$q-iq*F;Gr$f}4%q$0&Fdet3n$X37Up{?QHKAJqFa0m5Z)_s{^AE%B{CT_7L7mC0YC;zS=~R+- zse{z_P5dKq0LHJJVm%t6xwTC(u~#Q5HXu12qUP-o>(K~vV<`aaw!Dd3=kh&Br;I#?vh(QTQuJZAAO!wRH5>4GWO47|Gu}TQqSn%~C){p}mkkFE9VoyH3Qzar zl8uA{nId>Ov2T}Z0)@hbr~8#zwqby4w}0sZ2bBbT9$8Eb#sH|bv=9np$Tkd+NF)GQ zSzKUZFjf&9N+jU($inN&Dd*YEHC(chxu;Xgtmnejis2;G_SO9Wy|N0#a7fUwxVwF v?MtTfAZTD`FpY7EfY<@4TIp1dqZQ=3zz29q6`W_&J5JCtcgb)dB*J?EHH6)eO z4N%=!Cw0wYBD4}v-B_nWv4rd11s>vitSm3VGL1|nA#nn>Yc(nqONj4o0RV?PYdG)T zAa}CQwzxMJoK)fh8WnKXy<8Z7-D?b(2*Z#7pt87!)8ogfVw@g7qO!OL0QhBm!`3dnxaq!m zmT5efZ<4QoD*LScu=UCtSo{cmyLX@TQ& z1DvtB=MmTwV3`K3EHB|9zQ=X%0)=9UEeUDNBd{kRce0Q3?hOui)&KzU-7N~ml6Ki< zW*%X)I+_)T0-MI?2z276HIKkv0jiI$<^>ep*4C`%5wt~tO;KReu;ZrI*-2|2fkoPz zS5y)v-%r|#V>8Q!Cf7Uyn|X9|C+88uFr;C}eRUF8Z-Vpa<8#a-u$Xr%TW22Ozo9da fAcPP?yhDEgvU^oC`ClL300000NkvXXu0mjf{(dL_ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/yellow.rsi/inhand-right.png b/Resources/Textures/Objects/Tanks/yellow.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..b79125ee7c6fe7e8eaef1c3dbb933f83bf834108 GIT binary patch literal 652 zcmV;70(1R|P)qVA8FpgOO2V)sbUgOE%|O1VZ=g+@rhP12D!IV~jDzSWFeeprq$8 zFPUjRt{4WXyE_B`!E@Qg4uB#{w7Id4NB;pg-7Dnsg_Q_G>;PoWk8s(!#p(Vw0KmV$ zLoQ!fso!CuvZ_(PUZj4#NS>pSB1;4eGb8D_g_Tu}Y)g+lr?+uGJT`~VW^3!8_uo6$ zZ{qf`2*)N+(z5_``ueRl-?@2?MuWCVY1{QM&lzUwFTWKl6aIS!L^Uqy@9^={kJRmY zlt0bSI6KKkuH!|vrIYKrV-3^1^MT-u3ZA`QZ(=5lWq@tzbas*j0932hQA0(RXc=~X z?||^({ZBMoTj*v@G#ZVGnGpF4T-2UWS~~;)xUNgfvUp&4DDNE*eoJ2f0Hw7%_M7Q5r2n+DB1jY4jVs`W#r+#Rn8wdfnm~V~jDz7-Nhv#u#HS5_L%``to(E`hX}R zYX-voiVDO`0T5X;pziKaE?>Y+_X>~x12#9-h1^Wd6aY~*0~N!dT)qJR{tf_ey1$Lf z&Mh+MN3(Q0f1+vz!uWQhiClRQTw&zXM) zOeN}?X%Phz;UI_{XQHHQ27=4ZMT|AZwsab_O>$kAp4SX4avmar5c?2JL(M>N_n(L~ m)eIyy{wmZAFvb{TY~(NYd{zE~1p00O0000