]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
THE RETURN OF ITEM STATUS (#22986)
authorPieter-Jan Briers <pieterjan.briers+git@gmail.com>
Sun, 21 Apr 2024 13:16:23 +0000 (15:16 +0200)
committerGitHub <noreply@github.com>
Sun, 21 Apr 2024 13:16:23 +0000 (23:16 +1000)
* THE RETURN OF ITEM STATUS

Item status is now inline with the hands again. You can now see item status for both hands at once.

If you have more than 2 hands, the last active hand of that side is displayed in the respective item status.

The item status for the active hand is also highlighted.

Item status has been given a new look so it looks unique and matches every UI theme.

* Shrink item status to 125px

This is going to require fixing the existing controls. Do that later.

* New bullet item status rendering

sex

* Make gun item status look just a little bit nicer.

Avoid only one or two bullets ending up on a single row of an item status.

* Delete Eris theme files

* More improvements

Fixed the fact that left/right were flipped around when assigning status panel locations. Involved renaming all the UI textures.

Redid how content margins are set from the theme. Less complex and cleaner now.

Made the item name always left-aligned, just looks better since other UI elements don't adapt anyways.

* Compact down item status text

Now it fits 3 lines of text on one line. Yay.

This is achieved by compacting RichTextLabels by reducing their line height and giving them a negative bottom margin.

* Add item status sprites for Ashen theme.

* Add status control to show beaker/bucket/jug solution/transfer volumes

Also PollingItemStatusControl I'll be using that more.

* Fix welder item status, clean up welder code

The item status control implementation was ancient and bad. That's why it was buggy.

Removed all the complex dated networking stuff for welders, we just sync the solution contents now anyways so none of that is needed anymore. This moves a buncha stuff to shared and just removes code.

Cleanup. The code was doing some really dumb stuff.

* Spray bottles show contents in item status.

* cowtools

* Fix plasmafire and clockwork themes.

Actual git gaslighting wtf.

---------

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
62 files changed:
Content.Client/Chemistry/Components/SolutionItemStatusComponent.cs [new file with mode: 0644]
Content.Client/Chemistry/EntitySystems/SolutionItemStatusSystem.cs [new file with mode: 0644]
Content.Client/Chemistry/UI/SolutionStatusControl.cs [new file with mode: 0644]
Content.Client/Items/UI/PollingItemStatusControl.cs [new file with mode: 0644]
Content.Client/Stylesheets/StyleNano.cs
Content.Client/Tools/ToolSystem.cs
Content.Client/Tools/UI/WelderStatusControl.cs
Content.Client/UserInterface/Systems/Hands/HandsUIController.cs
Content.Client/UserInterface/Systems/Hotbar/HotbarUIController.cs
Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml
Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml.cs
Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml
Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml.cs
Content.Client/Weapons/Ranged/ItemStatus/BulletRender.cs [new file with mode: 0644]
Content.Client/Weapons/Ranged/Systems/GunSystem.AmmoCounter.cs
Content.Server/Content.Server.csproj
Content.Server/Entry/IgnoredComponents.cs
Content.Server/Tools/ToolSystem.cs
Content.Shared/Tools/Components/WelderComponent.cs
Content.Shared/Tools/Systems/SharedToolSystem.Welder.cs
Content.Shared/Tools/Systems/SharedToolSystem.cs
Resources/Locale/en-US/chemistry/components/solution-status.ftl [new file with mode: 0644]
Resources/Locale/en-US/tools/components/welder-component.ftl
Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml
Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml
Resources/Prototypes/Entities/Objects/Specific/chemistry.yml
Resources/Prototypes/Entities/Objects/Tools/bucket.yml
Resources/Prototypes/themes.yml
Resources/Textures/Interface/Ashen/item_status_left.png [new file with mode: 0644]
Resources/Textures/Interface/Ashen/item_status_left_highlight.png [new file with mode: 0644]
Resources/Textures/Interface/Ashen/item_status_right.png [new file with mode: 0644]
Resources/Textures/Interface/Ashen/item_status_right_highlight.png [new file with mode: 0644]
Resources/Textures/Interface/Clockwork/item_status_left.png [new file with mode: 0644]
Resources/Textures/Interface/Clockwork/item_status_left_highlight.png [new file with mode: 0644]
Resources/Textures/Interface/Clockwork/item_status_right.png [new file with mode: 0644]
Resources/Textures/Interface/Clockwork/item_status_right_highlight.png [new file with mode: 0644]
Resources/Textures/Interface/Default/item_status_left.png [new file with mode: 0644]
Resources/Textures/Interface/Default/item_status_left_highlight.png [new file with mode: 0644]
Resources/Textures/Interface/Default/item_status_right.png [new file with mode: 0644]
Resources/Textures/Interface/Default/item_status_right_highlight.png [new file with mode: 0644]
Resources/Textures/Interface/Minimalist/item_status_left.png [new file with mode: 0644]
Resources/Textures/Interface/Minimalist/item_status_left_highlight.png [new file with mode: 0644]
Resources/Textures/Interface/Minimalist/item_status_right.png [new file with mode: 0644]
Resources/Textures/Interface/Minimalist/item_status_right_highlight.png [new file with mode: 0644]
Resources/Textures/Interface/Nano/item_status_left.svg [deleted file]
Resources/Textures/Interface/Nano/item_status_left.svg.96dpi.png [deleted file]
Resources/Textures/Interface/Nano/item_status_middle.svg [deleted file]
Resources/Textures/Interface/Nano/item_status_middle.svg.96dpi.png [deleted file]
Resources/Textures/Interface/Nano/item_status_right.svg [deleted file]
Resources/Textures/Interface/Nano/item_status_right.svg.96dpi.png [deleted file]
Resources/Textures/Interface/Plasmafire/item_status_left.png [new file with mode: 0644]
Resources/Textures/Interface/Plasmafire/item_status_left_highlight.png [new file with mode: 0644]
Resources/Textures/Interface/Plasmafire/item_status_right.png [new file with mode: 0644]
Resources/Textures/Interface/Plasmafire/item_status_right_highlight.png [new file with mode: 0644]
Resources/Textures/Interface/Retro/item_status_left.png [new file with mode: 0644]
Resources/Textures/Interface/Retro/item_status_left_highlight.png [new file with mode: 0644]
Resources/Textures/Interface/Retro/item_status_right.png [new file with mode: 0644]
Resources/Textures/Interface/Retro/item_status_right_highlight.png [new file with mode: 0644]
Resources/Textures/Interface/Slimecore/item_status_left.png [new file with mode: 0644]
Resources/Textures/Interface/Slimecore/item_status_left_highlight.png [new file with mode: 0644]
Resources/Textures/Interface/Slimecore/item_status_right.png [new file with mode: 0644]
Resources/Textures/Interface/Slimecore/item_status_right_highlight.png [new file with mode: 0644]

diff --git a/Content.Client/Chemistry/Components/SolutionItemStatusComponent.cs b/Content.Client/Chemistry/Components/SolutionItemStatusComponent.cs
new file mode 100644 (file)
index 0000000..58c5a05
--- /dev/null
@@ -0,0 +1,22 @@
+using Content.Client.Chemistry.EntitySystems;
+using Content.Client.Chemistry.UI;
+
+namespace Content.Client.Chemistry.Components;
+
+/// <summary>
+/// Exposes a solution container's contents via a basic item status control.
+/// </summary>
+/// <remarks>
+/// Shows the solution volume, max volume, and transfer amount.
+/// </remarks>
+/// <seealso cref="SolutionItemStatusSystem"/>
+/// <seealso cref="SolutionStatusControl"/>
+[RegisterComponent]
+public sealed partial class SolutionItemStatusComponent : Component
+{
+    /// <summary>
+    /// The ID of the solution that will be shown on the item status control.
+    /// </summary>
+    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    public string Solution = "default";
+}
diff --git a/Content.Client/Chemistry/EntitySystems/SolutionItemStatusSystem.cs b/Content.Client/Chemistry/EntitySystems/SolutionItemStatusSystem.cs
new file mode 100644 (file)
index 0000000..76aab51
--- /dev/null
@@ -0,0 +1,22 @@
+using Content.Client.Chemistry.Components;
+using Content.Client.Chemistry.UI;
+using Content.Client.Items;
+using Content.Shared.Chemistry.EntitySystems;
+
+namespace Content.Client.Chemistry.EntitySystems;
+
+/// <summary>
+/// Wires up item status logic for <see cref="SolutionItemStatusComponent"/>.
+/// </summary>
+/// <seealso cref="SolutionStatusControl"/>
+public sealed class SolutionItemStatusSystem : EntitySystem
+{
+    [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+        Subs.ItemStatus<SolutionItemStatusComponent>(
+            entity => new SolutionStatusControl(entity, EntityManager, _solutionContainerSystem));
+    }
+}
diff --git a/Content.Client/Chemistry/UI/SolutionStatusControl.cs b/Content.Client/Chemistry/UI/SolutionStatusControl.cs
new file mode 100644 (file)
index 0000000..1a33ffb
--- /dev/null
@@ -0,0 +1,59 @@
+using Content.Client.Chemistry.Components;
+using Content.Client.Chemistry.EntitySystems;
+using Content.Client.Items.UI;
+using Content.Client.Message;
+using Content.Client.Stylesheets;
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.FixedPoint;
+using Robust.Client.UserInterface.Controls;
+
+namespace Content.Client.Chemistry.UI;
+
+/// <summary>
+/// Displays basic solution information for <see cref="SolutionItemStatusComponent"/>.
+/// </summary>
+/// <seealso cref="SolutionItemStatusSystem"/>
+public sealed class SolutionStatusControl : PollingItemStatusControl<SolutionStatusControl.Data>
+{
+    private readonly Entity<SolutionItemStatusComponent> _parent;
+    private readonly IEntityManager _entityManager;
+    private readonly SharedSolutionContainerSystem _solutionContainers;
+    private readonly RichTextLabel _label;
+
+    public SolutionStatusControl(
+        Entity<SolutionItemStatusComponent> parent,
+        IEntityManager entityManager,
+        SharedSolutionContainerSystem solutionContainers)
+    {
+        _parent = parent;
+        _entityManager = entityManager;
+        _solutionContainers = solutionContainers;
+        _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
+        AddChild(_label);
+    }
+
+    protected override Data PollData()
+    {
+        if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.Solution, out _, out var solution))
+            return default;
+
+        FixedPoint2? transferAmount = null;
+        if (_entityManager.TryGetComponent(_parent.Owner, out SolutionTransferComponent? transfer))
+            transferAmount = transfer.TransferAmount;
+
+        return new Data(solution.Volume, solution.MaxVolume, transferAmount);
+    }
+
+    protected override void Update(in Data data)
+    {
+        var markup = Loc.GetString("solution-status-volume",
+            ("currentVolume", data.Volume),
+            ("maxVolume", data.MaxVolume));
+        if (data.TransferVolume is { } transferVolume)
+            markup += "\n" + Loc.GetString("solution-status-transfer", ("volume", transferVolume));
+        _label.SetMarkup(markup);
+    }
+
+    public readonly record struct Data(FixedPoint2 Volume, FixedPoint2 MaxVolume, FixedPoint2? TransferVolume);
+}
diff --git a/Content.Client/Items/UI/PollingItemStatusControl.cs b/Content.Client/Items/UI/PollingItemStatusControl.cs
new file mode 100644 (file)
index 0000000..39cffb0
--- /dev/null
@@ -0,0 +1,28 @@
+using Robust.Client.UserInterface;
+using Robust.Shared.Timing;
+
+namespace Content.Client.Items.UI;
+
+/// <summary>
+/// A base for item status controls that poll data every frame. Avoids UI updates if data didn't change.
+/// </summary>
+/// <typeparam name="TData">The full status control data that is polled every frame.</typeparam>
+public abstract class PollingItemStatusControl<TData> : Control where TData : struct, IEquatable<TData>
+{
+    private TData _lastData;
+
+    protected override void FrameUpdate(FrameEventArgs args)
+    {
+        base.FrameUpdate(args);
+
+        var newData = PollData();
+        if (newData.Equals(_lastData))
+            return;
+
+        _lastData = newData;
+        Update(newData);
+    }
+
+    protected abstract TData PollData();
+    protected abstract void Update(in TData data);
+}
index a589abb83aa93cfd893e573aed8d61935a6f9cfb..5fc17447c37756f33b261ee5d88eaf0135b398b4 100644 (file)
@@ -1234,6 +1234,11 @@ namespace Content.Client.Stylesheets
                     new StyleProperty("font", notoSans10),
                 }),
 
+                Element<RichTextLabel>()
+                    .Class(StyleClassItemStatus)
+                    .Prop(nameof(RichTextLabel.LineHeightScale), 0.7f)
+                    .Prop(nameof(Control.Margin), new Thickness(0, 0, 0, -6)),
+
                 // Slider
                 new StyleRule(SelectorElement.Type(typeof(Slider)), new []
                 {
index 3eb2cc45cbbe4821c2727fbd494205f130872ef2..2207242918ea73abc4ff42c308f9699f59217cad 100644 (file)
@@ -13,7 +13,7 @@ namespace Content.Client.Tools
         {
             base.Initialize();
 
-            Subs.ItemStatus<WelderComponent>(ent => new WelderStatusControl(ent));
+            Subs.ItemStatus<WelderComponent>(ent => new WelderStatusControl(ent, EntityManager, this));
             Subs.ItemStatus<MultipleToolComponent>(ent => new MultipleToolStatusControl(ent));
         }
 
index dae742efc37f81a241248690650c2108ce4c652d..3d44d6fa84a193f85095baf02144c2eef2f63b5f 100644 (file)
@@ -1,55 +1,45 @@
+using Content.Client.Items.UI;
 using Content.Client.Message;
 using Content.Client.Stylesheets;
+using Content.Shared.FixedPoint;
 using Content.Shared.Tools.Components;
-using Robust.Client.UserInterface;
+using Content.Shared.Tools.Systems;
 using Robust.Client.UserInterface.Controls;
-using Robust.Shared.Timing;
 
 namespace Content.Client.Tools.UI;
 
-public sealed class WelderStatusControl : Control
+public sealed class WelderStatusControl : PollingItemStatusControl<WelderStatusControl.Data>
 {
-    [Dependency] private readonly IGameTiming _gameTiming = default!;
-
-    private readonly ToolSystem _tool;
-
     private readonly Entity<WelderComponent> _parent;
+    private readonly IEntityManager _entityManager;
+    private readonly SharedToolSystem _toolSystem;
     private readonly RichTextLabel _label;
 
-    public WelderStatusControl(Entity<WelderComponent> parent)
+    public WelderStatusControl(Entity<WelderComponent> parent, IEntityManager entityManager, SharedToolSystem toolSystem)
     {
-        IoCManager.InjectDependencies(this);
-
         _parent = parent;
-        var entMan = IoCManager.Resolve<IEntityManager>();
-        _tool = entMan.System<ToolSystem>();
-
+        _entityManager = entityManager;
+        _toolSystem = toolSystem;
         _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
         AddChild(_label);
 
         UpdateDraw();
     }
 
-    /// <inheritdoc />
-    protected override void FrameUpdate(FrameEventArgs args)
+    protected override Data PollData()
     {
-        base.FrameUpdate(args);
-
-        Update();
+        var (fuel, capacity) = _toolSystem.GetWelderFuelAndCapacity(_parent, _parent.Comp);
+        return new Data(fuel, capacity, _parent.Comp.Enabled);
     }
 
-    public void Update()
+    protected override void Update(in Data data)
     {
-        if (!_gameTiming.IsFirstTimePredicted)
-            return;
-
-        var (fuel, fuelCap) = _tool.GetWelderFuelAndCapacity(_parent, _parent);
-        var lit = _parent.Comp.Enabled;
-
         _label.SetMarkup(Loc.GetString("welder-component-on-examine-detailed-message",
-            ("colorName", fuel < fuelCap / 4f ? "darkorange" : "orange"),
-            ("fuelLeft", Math.Round(fuel.Float(), 1)),
-            ("fuelCapacity", fuelCap),
-            ("status", Loc.GetString(lit ? "welder-component-on-examine-welder-lit-message" : "welder-component-on-examine-welder-not-lit-message"))));
+            ("colorName", data.Fuel < data.FuelCapacity / 4f ? "darkorange" : "orange"),
+            ("fuelLeft", data.Fuel),
+            ("fuelCapacity", data.FuelCapacity),
+            ("status", Loc.GetString(data.Lit ? "welder-component-on-examine-welder-lit-message" : "welder-component-on-examine-welder-not-lit-message"))));
     }
+
+    public record struct Data(FixedPoint2 Fuel, FixedPoint2 FuelCapacity, bool Lit);
 }
index bb550b043382d2640db4228ef3dc6003534906f6..e57c15462e32f4812f21af28d96129bf2495d75f 100644 (file)
@@ -28,6 +28,15 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
     private readonly Dictionary<string, HandButton> _handLookup = new();
     private HandsComponent? _playerHandsComponent;
     private HandButton? _activeHand = null;
+
+    // We only have two item status controls (left and right hand),
+    // but we may have more than two hands.
+    // We handle this by having the item status be the *last active* hand of that side.
+    // These variables store which that is.
+    // ("middle" hands are hardcoded as right, whatever)
+    private HandButton? _statusHandLeft;
+    private HandButton? _statusHandRight;
+
     private int _backupSuffix = 0; //this is used when autogenerating container names if they don't have names
 
     private HotbarGui? HandsGui => UIManager.GetActiveUIWidgetOrNull<HotbarGui>();
@@ -180,8 +189,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
             hand.Blocked = false;
         }
 
-        if (_playerHandsComponent?.ActiveHand?.Name == name)
-            HandsGui?.UpdatePanelEntity(entity);
+        UpdateHandStatus(hand, entity);
     }
 
     private void OnItemRemoved(string name, EntityUid entity)
@@ -191,8 +199,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
             return;
 
         hand.SetEntity(null);
-        if (_playerHandsComponent?.ActiveHand?.Name == name)
-            HandsGui?.UpdatePanelEntity(null);
+        UpdateHandStatus(hand, null);
     }
 
     private HandsContainer GetFirstAvailableContainer()
@@ -232,7 +239,6 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
             if (_activeHand != null)
                 _activeHand.Highlight = false;
 
-            HandsGui?.UpdatePanelEntity(null);
             return;
         }
 
@@ -250,7 +256,19 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
             _player.LocalSession?.AttachedEntity is { } playerEntity &&
             _handsSystem.TryGetHand(playerEntity, handName, out var hand, _playerHandsComponent))
         {
-            HandsGui.UpdatePanelEntity(hand.HeldEntity);
+            if (hand.Location == HandLocation.Left)
+            {
+                _statusHandLeft = handControl;
+                HandsGui.UpdatePanelEntityLeft(hand.HeldEntity);
+            }
+            else
+            {
+                // Middle or right
+                _statusHandRight = handControl;
+                HandsGui.UpdatePanelEntityRight(hand.HeldEntity);
+            }
+
+            HandsGui.SetHighlightHand(hand.Location);
         }
     }
 
@@ -278,6 +296,14 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
             GetFirstAvailableContainer().AddButton(button);
         }
 
+        // If we don't have a status for this hand type yet, set it.
+        // This means we have status filled by default in most scenarios,
+        // otherwise the user'd need to switch hands to "activate" the hands the first time.
+        if (location == HandLocation.Left)
+            _statusHandLeft ??= button;
+        else
+            _statusHandRight ??= button;
+
         return button;
     }
 
@@ -336,6 +362,11 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
             handContainer.RemoveButton(handButton);
         }
 
+        if (_statusHandLeft == handButton)
+            _statusHandLeft = null;
+        if (_statusHandRight == handButton)
+            _statusHandRight = null;
+
         _handLookup.Remove(handName);
         handButton.Dispose();
         return true;
@@ -407,4 +438,13 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
             }
         }
     }
+
+    private void UpdateHandStatus(HandButton hand, EntityUid? entity)
+    {
+        if (hand == _statusHandLeft)
+            HandsGui?.UpdatePanelEntityLeft(entity);
+
+        if (hand == _statusHandRight)
+            HandsGui?.UpdatePanelEntityRight(entity);
+    }
 }
index daececcd35a222adb58d2d2f697d856966a97e28..2f266cfdd6bf49b8b4c789251ece8c6a2fa9b443 100644 (file)
@@ -31,7 +31,7 @@ public sealed class HotbarUIController : UIController
         ReloadHotbar();
     }
 
-    public void Setup(HandsContainer handsContainer, ItemStatusPanel handStatus, StorageContainer storageContainer)
+    public void Setup(HandsContainer handsContainer, StorageContainer storageContainer)
     {
         _inventory = UIManager.GetUIController<InventoryUIController>();
         _hands = UIManager.GetUIController<HandsUIController>();
index 0e9f0c77f97431f47e9c3f518f753acc3636a1cc..3afe11ba330420dffaea3187e937e2b5404847f4 100644 (file)
     Orientation="Vertical"
     HorizontalAlignment="Center">
     <BoxContainer Orientation="Vertical">
-        <Control HorizontalAlignment="Center">
-            <inventory:ItemStatusPanel
-                Name="StatusPanel"
-                Visible="False"
-                HorizontalAlignment="Center"
-            />
-            <BoxContainer Name="StorageContainer"
-                          Access="Public"
-                          HorizontalAlignment="Center"
-                          Margin="10">
-                <storage:StorageContainer
-                    Name="StoragePanel"
-                    Visible="False"/>
-            </BoxContainer>
-        </Control>
+        <BoxContainer Name="StorageContainer"
+                      Access="Public"
+                      HorizontalAlignment="Center"
+                      Margin="10">
+            <storage:StorageContainer
+                Name="StoragePanel"
+                Visible="False"/>
+        </BoxContainer>
         <BoxContainer Orientation="Horizontal" Name="Hotbar" HorizontalAlignment="Center">
             <inventory:ItemSlotButtonContainer
                 Name="SecondHotbar"
                 ExpandBackwards="True"
                 Columns="6"
                 HorizontalExpand="False"/>
+            <inventory:ItemStatusPanel
+                Name="StatusPanelRight"
+                HorizontalAlignment="Center" Margin="0 0 -2 2"
+                SetWidth="125"
+                MaxHeight="60"/>
             <hands:HandsContainer
                 Name="HandContainer"
                 Access="Public"
                 HorizontalAlignment="Center"
                 HorizontalExpand="False"
-                ColumnLimit="6"
-                Margin="4 0 4 0"/>
+                ColumnLimit="6"/>
+            <inventory:ItemStatusPanel
+                Name="StatusPanelLeft"
+                HorizontalAlignment="Center" Margin="-2 0 0 2"
+                SetWidth="125"
+                MaxHeight="60"/>
             <inventory:ItemSlotButtonContainer
                 Name="MainHotbar"
                 SlotGroup="MainHotbar"
index a6f12a63ac1bb8b714e665c5582a7758fc23488d..923262d4eadc02092076933f4efebfb8c8501398 100644 (file)
@@ -1,4 +1,5 @@
-using Robust.Client.AutoGenerated;
+using Content.Shared.Hands.Components;
+using Robust.Client.AutoGenerated;
 using Robust.Client.UserInterface.Controls;
 using Robust.Client.UserInterface.XAML;
 
@@ -10,22 +11,27 @@ public sealed partial class HotbarGui : UIWidget
     public HotbarGui()
     {
         RobustXamlLoader.Load(this);
-        StatusPanel.Update(null);
+        StatusPanelRight.SetSide(HandLocation.Right);
+        StatusPanelLeft.SetSide(HandLocation.Left);
         var hotbarController = UserInterfaceManager.GetUIController<HotbarUIController>();
 
-        hotbarController.Setup(HandContainer, StatusPanel, StoragePanel);
+        hotbarController.Setup(HandContainer, StoragePanel);
         LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.Begin);
     }
 
-    public void UpdatePanelEntity(EntityUid? entity)
+    public void UpdatePanelEntityLeft(EntityUid? entity)
     {
-        StatusPanel.Update(entity);
-        if (entity == null)
-        {
-            StatusPanel.Visible = false;
-            return;
-        }
+        StatusPanelLeft.Update(entity);
+    }
 
-        StatusPanel.Visible = true;
+    public void UpdatePanelEntityRight(EntityUid? entity)
+    {
+        StatusPanelRight.Update(entity);
+    }
+
+    public void SetHighlightHand(HandLocation? hand)
+    {
+        StatusPanelLeft.UpdateHighlight(hand is HandLocation.Left);
+        StatusPanelRight.UpdateHighlight(hand is HandLocation.Middle or HandLocation.Right);
     }
 }
index d469e6ced037b7caca2525983a45a1f574db43bd..81142d64d28c0a573b6f8a296b121c99f9453891 100644 (file)
@@ -3,22 +3,26 @@
     xmlns:controls="clr-namespace:Content.Client.UserInterface.Systems.Inventory.Controls"
     xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
     VerticalAlignment="Bottom"
-    HorizontalAlignment="Center"
-    MinSize="150 0">
-    <PanelContainer
-        Name="Panel"
-        ModulateSelfOverride="#FFFFFFE6"
-        HorizontalExpand="True">
-        <PanelContainer.PanelOverride>
-            <graphics:StyleBoxTexture
-                ContentMarginLeftOverride="6"
-                ContentMarginRightOverride="6"
-                ContentMarginTopOverride="4"
-                ContentMarginBottomOverride="4" />
-        </PanelContainer.PanelOverride>
-        <BoxContainer Orientation="Vertical" SeparationOverride="0">
-            <BoxContainer Name="StatusContents" Orientation="Vertical"/>
-            <Label Name="ItemNameLabel" StyleClasses="ItemStatus"/>
+    HorizontalAlignment="Center">
+    <Control Name="VisWrapper" Visible="False">
+        <PanelContainer Name="Panel">
+            <PanelContainer.PanelOverride>
+                <graphics:StyleBoxTexture
+                    PatchMarginBottom="4"
+                    PatchMarginTop="6"
+                    TextureScale="2 2"
+                    Mode="Tile"/>
+            </PanelContainer.PanelOverride>
+        </PanelContainer>
+        <PanelContainer Name="HighlightPanel">
+            <PanelContainer.PanelOverride>
+                <graphics:StyleBoxTexture PatchMarginBottom="4" PatchMarginTop="6" TextureScale="2 2">
+                </graphics:StyleBoxTexture>
+            </PanelContainer.PanelOverride>
+        </PanelContainer>
+        <BoxContainer Name="Contents" Orientation="Vertical" Margin="0 6 0 4">
+            <BoxContainer Name="StatusContents" Orientation="Vertical" />
+            <Label Name="ItemNameLabel" ClipText="True" StyleClasses="ItemStatus" Align="Left" />
         </BoxContainer>
-    </PanelContainer>
+    </Control>
 </controls:ItemStatusPanel>
index 90ae571711d91af2eea8b996c71eb740d61884fb..e1fe6ab246c0ba4c610821daac1acce0db69488b 100644 (file)
@@ -1,3 +1,4 @@
+using System.Numerics;
 using Content.Client.Items;
 using Content.Client.Resources;
 using Content.Shared.Hands.Components;
@@ -5,6 +6,7 @@ using Content.Shared.IdentityManagement;
 using Content.Shared.Inventory.VirtualItem;
 using Robust.Client.AutoGenerated;
 using Robust.Client.Graphics;
+using Robust.Client.UserInterface;
 using Robust.Client.UserInterface.Controls;
 using Robust.Client.UserInterface.XAML;
 using Robust.Shared.Timing;
@@ -14,12 +16,15 @@ using static Content.Client.IoC.StaticIoC;
 namespace Content.Client.UserInterface.Systems.Inventory.Controls;
 
 [GenerateTypedNameReferences]
-public sealed partial class ItemStatusPanel : BoxContainer
+public sealed partial class ItemStatusPanel : Control
 {
     [Dependency] private readonly IEntityManager _entityManager = default!;
 
     [ViewVariables] private EntityUid? _entity;
 
+    // Tracked so we can re-run SetSide() if the theme changes.
+    private HandLocation _side;
+
     public ItemStatusPanel()
     {
         RobustXamlLoader.Load(this);
@@ -30,41 +35,65 @@ public sealed partial class ItemStatusPanel : BoxContainer
 
     public void SetSide(HandLocation location)
     {
-        string texture;
+        // AN IMPORTANT REMINDER ABOUT THIS CODE:
+        // In the UI, the RIGHT hand is on the LEFT on the screen.
+        // So that a character facing DOWN matches the hand positions.
+
+        Texture? texture;
+        Texture? textureHighlight;
         StyleBox.Margin cutOut;
         StyleBox.Margin flat;
-        Label.AlignMode textAlign;
+        Thickness contentMargin;
 
         switch (location)
         {
-            case HandLocation.Left:
-                texture = "/Textures/Interface/Nano/item_status_right.svg.96dpi.png";
-                cutOut = StyleBox.Margin.Left | StyleBox.Margin.Top;
-                flat = StyleBox.Margin.Right | StyleBox.Margin.Bottom;
-                textAlign = Label.AlignMode.Right;
+            case HandLocation.Right:
+                texture = Theme.ResolveTexture("item_status_right");
+                textureHighlight = Theme.ResolveTexture("item_status_right_highlight");
+                cutOut = StyleBox.Margin.Left;
+                flat = StyleBox.Margin.Right;
+                contentMargin = MarginFromThemeColor("_itemstatus_content_margin_right");
                 break;
             case HandLocation.Middle:
-                texture = "/Textures/Interface/Nano/item_status_middle.svg.96dpi.png";
-                cutOut = StyleBox.Margin.Right | StyleBox.Margin.Top;
-                flat = StyleBox.Margin.Left | StyleBox.Margin.Bottom;
-                textAlign = Label.AlignMode.Left;
-                break;
-            case HandLocation.Right:
-                texture = "/Textures/Interface/Nano/item_status_left.svg.96dpi.png";
-                cutOut = StyleBox.Margin.Right | StyleBox.Margin.Top;
-                flat = StyleBox.Margin.Left | StyleBox.Margin.Bottom;
-                textAlign = Label.AlignMode.Left;
+            case HandLocation.Left:
+                texture = Theme.ResolveTexture("item_status_left");
+                textureHighlight = Theme.ResolveTexture("item_status_left_highlight");
+                cutOut = StyleBox.Margin.Right;
+                flat = StyleBox.Margin.Left;
+                contentMargin = MarginFromThemeColor("_itemstatus_content_margin_left");
                 break;
             default:
                 throw new ArgumentOutOfRangeException(nameof(location), location, null);
         }
 
+        Contents.Margin = contentMargin;
+
         var panel = (StyleBoxTexture) Panel.PanelOverride!;
-        panel.Texture = ResC.GetTexture(texture);
-        panel.SetPatchMargin(flat, 2);
-        panel.SetPatchMargin(cutOut, 13);
+        panel.Texture = texture;
+        panel.SetPatchMargin(flat, 4);
+        panel.SetPatchMargin(cutOut, 7);
 
-        ItemNameLabel.Align = textAlign;
+        var panelHighlight = (StyleBoxTexture) HighlightPanel.PanelOverride!;
+        panelHighlight.Texture = textureHighlight;
+        panelHighlight.SetPatchMargin(flat, 4);
+        panelHighlight.SetPatchMargin(cutOut, 7);
+
+        _side = location;
+    }
+
+    private Thickness MarginFromThemeColor(string itemName)
+    {
+        // This is the worst thing I've ever programmed
+        // (can you tell I'm a graphics programmer?)
+        // (the margin needs to change depending on the UI theme, so we use a fake color entry to store the value)
+
+        var color = Theme.ResolveColorOrSpecified(itemName);
+        return new Thickness(color.RByte, color.GByte, color.BByte, color.AByte);
+    }
+
+    protected override void OnThemeUpdated()
+    {
+        SetSide(_side);
     }
 
     protected override void FrameUpdate(FrameEventArgs args)
@@ -79,7 +108,7 @@ public sealed partial class ItemStatusPanel : BoxContainer
         {
             ClearOldStatus();
             _entity = null;
-            Panel.Visible = false;
+            VisWrapper.Visible = false;
             return;
         }
 
@@ -91,7 +120,12 @@ public sealed partial class ItemStatusPanel : BoxContainer
             UpdateItemName();
         }
 
-        Panel.Visible = true;
+        VisWrapper.Visible = true;
+    }
+
+    public void UpdateHighlight(bool highlight)
+    {
+        HighlightPanel.Visible = highlight;
     }
 
     private void UpdateItemName()
diff --git a/Content.Client/Weapons/Ranged/ItemStatus/BulletRender.cs b/Content.Client/Weapons/Ranged/ItemStatus/BulletRender.cs
new file mode 100644 (file)
index 0000000..492fad3
--- /dev/null
@@ -0,0 +1,178 @@
+using System.Numerics;
+using Content.Client.Resources;
+using Robust.Client.Graphics;
+using Robust.Client.ResourceManagement;
+using Robust.Client.UserInterface;
+
+namespace Content.Client.Weapons.Ranged.ItemStatus;
+
+/// <summary>
+/// Renders one or more rows of bullets for item status.
+/// </summary>
+/// <remarks>
+/// This is a custom control to allow complex responsive layout logic.
+/// </remarks>
+public sealed class BulletRender : Control
+{
+    private static readonly Color ColorA = Color.FromHex("#b68f0e");
+    private static readonly Color ColorB = Color.FromHex("#d7df60");
+    private static readonly Color ColorGoneA = Color.FromHex("#000000");
+    private static readonly Color ColorGoneB = Color.FromHex("#222222");
+
+    /// <summary>
+    /// Try to ensure there's at least this many bullets on one row.
+    /// </summary>
+    /// <remarks>
+    /// For example, if there are two rows and the second row has only two bullets,
+    /// we "steal" some bullets from the row below it to make it look nicer.
+    /// </remarks>
+    public const int MinCountPerRow = 7;
+
+    public const int BulletHeight = 12;
+    public const int BulletSeparationNormal = 3;
+    public const int BulletSeparationTiny = 2;
+    public const int BulletWidthNormal = 5;
+    public const int BulletWidthTiny = 2;
+    public const int VerticalSeparation = 2;
+
+    private readonly Texture _bulletTiny;
+    private readonly Texture _bulletNormal;
+
+    private int _capacity;
+    private BulletType _type = BulletType.Normal;
+
+    public int Rows { get; set; } = 2;
+    public int Count { get; set; }
+
+    public int Capacity
+    {
+        get => _capacity;
+        set
+        {
+            _capacity = value;
+            InvalidateMeasure();
+        }
+    }
+
+    public BulletType Type
+    {
+        get => _type;
+        set
+        {
+            _type = value;
+            InvalidateMeasure();
+        }
+    }
+
+    public BulletRender()
+    {
+        var resC = IoCManager.Resolve<IResourceCache>();
+        _bulletTiny = resC.GetTexture("/Textures/Interface/ItemStatus/Bullets/tiny.png");
+        _bulletNormal = resC.GetTexture("/Textures/Interface/ItemStatus/Bullets/normal.png");
+    }
+
+    protected override Vector2 MeasureOverride(Vector2 availableSize)
+    {
+        var countPerRow = Math.Min(Capacity, CountPerRow(availableSize.X));
+
+        var rows = Math.Min((int) MathF.Ceiling(Capacity / (float) countPerRow), Rows);
+
+        var height = BulletHeight * rows + (BulletSeparationNormal * rows - 1);
+        var width = RowWidth(countPerRow);
+
+        return new Vector2(width, height);
+    }
+
+    protected override void Draw(DrawingHandleScreen handle)
+    {
+        // Scale rendering in this control by UIScale.
+        var currentTransform = handle.GetTransform();
+        handle.SetTransform(Matrix3.CreateScale(new Vector2(UIScale)) * currentTransform);
+
+        var countPerRow = CountPerRow(Size.X);
+
+        var (separation, _) = BulletParams();
+        var texture = Type == BulletType.Normal ? _bulletNormal : _bulletTiny;
+
+        var pos = new Vector2();
+
+        var altColor = false;
+
+        var spent = Capacity - Count;
+
+        var bulletsDone = 0;
+
+        // Draw by rows, bottom to top.
+        for (var row = 0; row < Rows; row++)
+        {
+            altColor = false;
+
+            var thisRowCount = Math.Min(countPerRow, Capacity - bulletsDone);
+            if (thisRowCount <= 0)
+                break;
+
+            // Handle MinCountPerRow
+            // We only do this if:
+            // 1. The next row would have less than MinCountPerRow bullets.
+            // 2. The next row is actually visible (we aren't the last row).
+            // 3. MinCountPerRow is actually smaller than the count per row (avoid degenerate cases).
+            var nextRowCount = Capacity - bulletsDone - thisRowCount;
+            if (nextRowCount < MinCountPerRow && row != Rows - 1 && MinCountPerRow < countPerRow)
+                thisRowCount -= MinCountPerRow - nextRowCount;
+
+            // Account for row width to right-align.
+            var rowWidth = RowWidth(thisRowCount);
+            pos.X += Size.X - rowWidth;
+
+            // Draw row left to right (so overlapping works)
+            for (var bullet = 0; bullet < thisRowCount; bullet++)
+            {
+                var absIdx = Capacity - bulletsDone - thisRowCount + bullet;
+                Color color;
+                if (absIdx >= spent)
+                    color = altColor ? ColorA : ColorB;
+                else
+                    color = altColor ? ColorGoneA : ColorGoneB;
+
+                var renderPos = pos;
+                renderPos.Y = Size.Y - renderPos.Y - BulletHeight;
+                handle.DrawTexture(texture, renderPos, color);
+                pos.X += separation;
+                altColor ^= true;
+            }
+
+            bulletsDone += thisRowCount;
+            pos.X = 0;
+            pos.Y += BulletHeight + VerticalSeparation;
+        }
+    }
+
+    private int CountPerRow(float width)
+    {
+        var (separation, bulletWidth) = BulletParams();
+        return (int) ((width - bulletWidth + separation) / separation);
+    }
+
+    private (int separation, int width) BulletParams()
+    {
+        return Type switch
+        {
+            BulletType.Normal => (BulletSeparationNormal, BulletWidthNormal),
+            BulletType.Tiny => (BulletSeparationTiny, BulletWidthTiny),
+            _ => throw new ArgumentOutOfRangeException()
+        };
+    }
+
+    private int RowWidth(int count)
+    {
+        var (separation, bulletWidth) = BulletParams();
+
+        return (count - 1) * separation + bulletWidth;
+    }
+
+    public enum BulletType
+    {
+        Normal,
+        Tiny
+    }
+}
index 32343af56f0f1d033b975878140239301b0b66e5..cc7405b04793a2cbb7c221944c82d789f05818c4 100644 (file)
@@ -4,11 +4,11 @@ using Content.Client.Items;
 using Content.Client.Resources;
 using Content.Client.Stylesheets;
 using Content.Client.Weapons.Ranged.Components;
+using Content.Client.Weapons.Ranged.ItemStatus;
 using Robust.Client.Animations;
 using Robust.Client.Graphics;
 using Robust.Client.UserInterface;
 using Robust.Client.UserInterface.Controls;
-using Robust.Shared.Graphics;
 
 namespace Content.Client.Weapons.Ranged.Systems;
 
@@ -91,116 +91,26 @@ public sealed partial class GunSystem
 
     private sealed class DefaultStatusControl : Control
     {
-        private readonly BoxContainer _bulletsListTop;
-        private readonly BoxContainer _bulletsListBottom;
+        private readonly BulletRender _bulletRender;
 
         public DefaultStatusControl()
         {
             MinHeight = 15;
             HorizontalExpand = true;
-            VerticalAlignment = Control.VAlignment.Center;
-            AddChild(new BoxContainer
+            VerticalAlignment = VAlignment.Center;
+            AddChild(_bulletRender = new BulletRender
             {
-                Orientation = BoxContainer.LayoutOrientation.Vertical,
-                HorizontalExpand = true,
-                VerticalAlignment = VAlignment.Center,
-                SeparationOverride = 0,
-                Children =
-                {
-                    (_bulletsListTop = new BoxContainer
-                    {
-                        Orientation = BoxContainer.LayoutOrientation.Horizontal,
-                        SeparationOverride = 0
-                    }),
-                    new BoxContainer
-                    {
-                        Orientation = BoxContainer.LayoutOrientation.Horizontal,
-                        HorizontalExpand = true,
-                        Children =
-                        {
-                            new Control
-                            {
-                                HorizontalExpand = true,
-                                Children =
-                                {
-                                    (_bulletsListBottom = new BoxContainer
-                                    {
-                                        Orientation = BoxContainer.LayoutOrientation.Horizontal,
-                                        VerticalAlignment = VAlignment.Center,
-                                        SeparationOverride = 0
-                                    }),
-                                }
-                            },
-                        }
-                    }
-                }
+                HorizontalAlignment = HAlignment.Right,
+                VerticalAlignment = VAlignment.Bottom
             });
         }
 
         public void Update(int count, int capacity)
         {
-            _bulletsListTop.RemoveAllChildren();
-            _bulletsListBottom.RemoveAllChildren();
-
-            string texturePath;
-            if (capacity <= 20)
-            {
-                texturePath = "/Textures/Interface/ItemStatus/Bullets/normal.png";
-            }
-            else if (capacity <= 30)
-            {
-                texturePath = "/Textures/Interface/ItemStatus/Bullets/small.png";
-            }
-            else
-            {
-                texturePath = "/Textures/Interface/ItemStatus/Bullets/tiny.png";
-            }
-
-            var texture = StaticIoC.ResC.GetTexture(texturePath);
+            _bulletRender.Count = count;
+            _bulletRender.Capacity = capacity;
 
-            const int tinyMaxRow = 60;
-
-            if (capacity > tinyMaxRow)
-            {
-                FillBulletRow(_bulletsListBottom, Math.Min(tinyMaxRow, count), tinyMaxRow, texture);
-                FillBulletRow(_bulletsListTop, Math.Max(0, count - tinyMaxRow), capacity - tinyMaxRow, texture);
-            }
-            else
-            {
-                FillBulletRow(_bulletsListBottom, count, capacity, texture);
-            }
-        }
-
-        private static void FillBulletRow(Control container, int count, int capacity, Texture texture)
-        {
-            var colorA = Color.FromHex("#b68f0e");
-            var colorB = Color.FromHex("#d7df60");
-            var colorGoneA = Color.FromHex("#000000");
-            var colorGoneB = Color.FromHex("#222222");
-
-            var altColor = false;
-
-            for (var i = count; i < capacity; i++)
-            {
-                container.AddChild(new TextureRect
-                {
-                    Texture = texture,
-                    ModulateSelfOverride = altColor ? colorGoneA : colorGoneB
-                });
-
-                altColor ^= true;
-            }
-
-            for (var i = 0; i < count; i++)
-            {
-                container.AddChild(new TextureRect
-                {
-                    Texture = texture,
-                    ModulateSelfOverride = altColor ? colorA : colorB
-                });
-
-                altColor ^= true;
-            }
+            _bulletRender.Type = capacity > 50 ? BulletRender.BulletType.Tiny : BulletRender.BulletType.Normal;
         }
     }
 
@@ -291,7 +201,7 @@ public sealed partial class GunSystem
 
     private sealed class ChamberMagazineStatusControl : Control
     {
-        private readonly BoxContainer _bulletsList;
+        private readonly BulletRender _bulletRender;
         private readonly TextureRect _chamberedBullet;
         private readonly Label _noMagazineLabel;
         private readonly Label _ammoCount;
@@ -308,23 +218,16 @@ public sealed partial class GunSystem
                 HorizontalExpand = true,
                 Children =
                 {
-                    (_chamberedBullet = new TextureRect
-                    {
-                        Texture = StaticIoC.ResC.GetTexture("/Textures/Interface/ItemStatus/Bullets/chambered_rotated.png"),
-                        VerticalAlignment = VAlignment.Center,
-                        HorizontalAlignment = HAlignment.Right,
-                    }),
-                    new Control() { MinSize = new Vector2(5,0) },
                     new Control
                     {
                         HorizontalExpand = true,
+                        Margin = new Thickness(0, 0, 5, 0),
                         Children =
                         {
-                            (_bulletsList = new BoxContainer
+                            (_bulletRender = new BulletRender
                             {
-                                Orientation = BoxContainer.LayoutOrientation.Horizontal,
-                                VerticalAlignment = VAlignment.Center,
-                                SeparationOverride = 0
+                                HorizontalAlignment = HAlignment.Right,
+                                VerticalAlignment = VAlignment.Bottom
                             }),
                             (_noMagazineLabel = new Label
                             {
@@ -333,12 +236,25 @@ public sealed partial class GunSystem
                             })
                         }
                     },
-                    new Control() { MinSize = new Vector2(5,0) },
-                    (_ammoCount = new Label
+                    new BoxContainer
                     {
-                        StyleClasses = {StyleNano.StyleClassItemStatus},
-                        HorizontalAlignment = HAlignment.Right,
-                    }),
+                        Orientation = BoxContainer.LayoutOrientation.Vertical,
+                        VerticalAlignment = VAlignment.Bottom,
+                        Margin = new Thickness(0, 0, 0, 2),
+                        Children =
+                        {
+                            (_ammoCount = new Label
+                            {
+                                StyleClasses = {StyleNano.StyleClassItemStatus},
+                                HorizontalAlignment = HAlignment.Right,
+                            }),
+                            (_chamberedBullet = new TextureRect
+                            {
+                                Texture = StaticIoC.ResC.GetTexture("/Textures/Interface/ItemStatus/Bullets/chambered.png"),
+                                HorizontalAlignment = HAlignment.Left,
+                            }),
+                        }
+                    }
                 }
             });
         }
@@ -348,61 +264,24 @@ public sealed partial class GunSystem
             _chamberedBullet.ModulateSelfOverride =
                 chambered ? Color.FromHex("#d7df60") : Color.Black;
 
-            _bulletsList.RemoveAllChildren();
-
             if (!magazine)
             {
+                _bulletRender.Visible = false;
                 _noMagazineLabel.Visible = true;
                 _ammoCount.Visible = false;
                 return;
             }
 
+            _bulletRender.Visible = true;
             _noMagazineLabel.Visible = false;
             _ammoCount.Visible = true;
 
-            var texturePath = "/Textures/Interface/ItemStatus/Bullets/normal.png";
-            var texture = StaticIoC.ResC.GetTexture(texturePath);
+            _bulletRender.Count = count;
+            _bulletRender.Capacity = capacity;
 
-            _ammoCount.Text = $"x{count:00}";
-            capacity = Math.Min(capacity, 20);
-            FillBulletRow(_bulletsList, count, capacity, texture);
-        }
-
-        private static void FillBulletRow(Control container, int count, int capacity, Texture texture)
-        {
-            var colorA = Color.FromHex("#b68f0e");
-            var colorB = Color.FromHex("#d7df60");
-            var colorGoneA = Color.FromHex("#000000");
-            var colorGoneB = Color.FromHex("#222222");
-
-            var altColor = false;
-
-            // Draw the empty ones
-            for (var i = count; i < capacity; i++)
-            {
-                container.AddChild(new TextureRect
-                {
-                    Texture = texture,
-                    ModulateSelfOverride = altColor ? colorGoneA : colorGoneB,
-                    Stretch = TextureRect.StretchMode.KeepCentered
-                });
-
-                altColor ^= true;
-            }
-
-            // Draw the full ones, but limit the count to the capacity
-            count = Math.Min(count, capacity);
-            for (var i = 0; i < count; i++)
-            {
-                container.AddChild(new TextureRect
-                {
-                    Texture = texture,
-                    ModulateSelfOverride = altColor ? colorA : colorB,
-                    Stretch = TextureRect.StretchMode.KeepCentered
-                });
+            _bulletRender.Type = capacity > 50 ? BulletRender.BulletType.Tiny : BulletRender.BulletType.Normal;
 
-                altColor ^= true;
-            }
+            _ammoCount.Text = $"x{count:00}";
         }
 
         public void PlayAlarmAnimation(Animation animation)
index 8782454eacb7a3de7cf5c5d501e1be4c905d1b0c..70e404fdef0af38961241e015a18060c002ada17 100644 (file)
@@ -28,7 +28,6 @@
   </ItemGroup>
   <ItemGroup>
     <Folder Include="Objectives\Interfaces\" />
-    <Folder Include="Tools\Systems\" />
   </ItemGroup>
   <Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
 </Project>
index fe073da7a49a677eeb7010b5c44919d175384e7d..d9b81c8e5a5bf34cbd57b9dee689840b10c41692 100644 (file)
@@ -14,12 +14,13 @@ namespace Content.Server.Entry
             "Icon",
             "HandheldGPS",
             "CableVisualizer",
+            "SolutionItemStatus",
             "UIFragment",
             "PdaBorderColor",
             "InventorySlots",
             "LightFade",
             "HolidayRsiSwap",
-            "OptionsVisualizer",
+            "OptionsVisualizer"
         };
     }
 }
index 7b2de57efcbae86b3891d754cf3e52e909b34420..7738a6398fada3f9d2ecc706a765298f690a5282 100644 (file)
@@ -45,10 +45,10 @@ public sealed class ToolSystem : SharedToolSystem
             if (welder.WelderTimer < welder.WelderUpdateTimer)
                 continue;
 
-            if (!SolutionContainerSystem.ResolveSolution((uid, solutionContainer), welder.FuelSolutionName, ref welder.FuelSolution, out var solution))
+            if (!SolutionContainerSystem.TryGetSolution((uid, solutionContainer), welder.FuelSolutionName, out var solutionComp, out var solution))
                 continue;
 
-            SolutionContainerSystem.RemoveReagent(welder.FuelSolution.Value, welder.FuelReagent, welder.FuelConsumption * welder.WelderTimer);
+            SolutionContainerSystem.RemoveReagent(solutionComp.Value, welder.FuelReagent, welder.FuelConsumption * welder.WelderTimer);
 
             if (solution.GetTotalPrototypeQuantity(welder.FuelReagent) <= FixedPoint2.Zero)
             {
index f133be675d7767a93c537cb5d51e8f38e5c44115..3c78a03fdeb14e7853f280651a42fee5675c41a9 100644 (file)
@@ -23,12 +23,6 @@ public sealed partial class WelderComponent : Component
     [DataField]
     public string FuelSolutionName = "Welder";
 
-    /// <summary>
-    ///     Solution on the entity that contains the fuel.
-    /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
-    public Entity<SolutionComponent>? FuelSolution;
-
     /// <summary>
     ///     Reagent that will be used as fuel for welding.
     /// </summary>
index 6bab296db5f1b5908b75fd43dd8aaa9bd9334fb9..e790b59cd121ee86d325921081e60e283fbbf0e2 100644 (file)
@@ -24,10 +24,10 @@ public abstract partial class SharedToolSystem
 
     public virtual void TurnOn(Entity<WelderComponent> entity, EntityUid? user)
     {
-        if (!SolutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.FuelSolutionName, ref entity.Comp.FuelSolution))
+        if (!SolutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.FuelSolutionName, out var solutionComp, out _))
             return;
 
-        SolutionContainerSystem.RemoveReagent(entity.Comp.FuelSolution.Value, entity.Comp.FuelReagent, entity.Comp.FuelLitCost);
+        SolutionContainerSystem.RemoveReagent(solutionComp.Value, entity.Comp.FuelReagent, entity.Comp.FuelLitCost);
         AdminLogger.Add(LogType.InteractActivate, LogImpact.Low,
             $"{ToPrettyString(user):user} toggled {ToPrettyString(entity.Owner):welder} on");
 
@@ -45,9 +45,17 @@ public abstract partial class SharedToolSystem
 
     public (FixedPoint2 fuel, FixedPoint2 capacity) GetWelderFuelAndCapacity(EntityUid uid, WelderComponent? welder = null, SolutionContainerManagerComponent? solutionContainer = null)
     {
-        if (!Resolve(uid, ref welder, ref solutionContainer)
-            || !SolutionContainerSystem.ResolveSolution((uid, solutionContainer), welder.FuelSolutionName, ref welder.FuelSolution, out var fuelSolution))
-            return (FixedPoint2.Zero, FixedPoint2.Zero);
+        if (!Resolve(uid, ref welder, ref solutionContainer))
+            return default;
+
+        if (!SolutionContainer.TryGetSolution(
+                (uid, solutionContainer),
+                welder.FuelSolutionName,
+                out _,
+                out var fuelSolution))
+        {
+            return default;
+        }
 
         return (fuelSolution.GetTotalPrototypeQuantity(welder.FuelReagent), fuelSolution.MaxVolume);
     }
@@ -89,13 +97,13 @@ public abstract partial class SharedToolSystem
         if (TryComp(target, out ReagentTankComponent? tank)
             && tank.TankType == ReagentTankType.Fuel
             && SolutionContainerSystem.TryGetDrainableSolution(target, out var targetSoln, out var targetSolution)
-            && SolutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.FuelSolutionName, ref entity.Comp.FuelSolution, out var welderSolution))
+            && SolutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.FuelSolutionName, out var solutionComp, out var welderSolution))
         {
             var trans = FixedPoint2.Min(welderSolution.AvailableVolume, targetSolution.Volume);
             if (trans > 0)
             {
                 var drained = SolutionContainerSystem.Drain(target, targetSoln.Value, trans);
-                SolutionContainerSystem.TryAddSolution(entity.Comp.FuelSolution.Value, drained);
+                SolutionContainerSystem.TryAddSolution(solutionComp.Value, drained);
                 _audioSystem.PlayPredicted(entity.Comp.WelderRefill, entity, user: args.User);
                 _popup.PopupClient(Loc.GetString("welder-component-after-interact-refueled-message"), entity, args.User);
             }
@@ -153,7 +161,7 @@ public abstract partial class SharedToolSystem
 
     private void OnActivateAttempt(Entity<WelderComponent> entity, ref ItemToggleActivateAttemptEvent args)
     {
-        if (!SolutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.FuelSolutionName, ref entity.Comp.FuelSolution, out var solution))
+        if (!SolutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.FuelSolutionName, out _, out var solution))
         {
             args.Cancelled = true;
             args.Popup = Loc.GetString("welder-component-no-fuel-message");
index 362f4f768345424de682522d0f38302ad09c16d1..9edae9b78fd058ecddcb357be2808c7c29208e60 100644 (file)
@@ -31,6 +31,7 @@ public abstract partial class SharedToolSystem : EntitySystem
     [Dependency] private   readonly SharedTransformSystem _transformSystem = default!;
     [Dependency] private   readonly TileSystem _tiles = default!;
     [Dependency] private   readonly TurfSystem _turfs = default!;
+    [Dependency] protected readonly SharedSolutionContainerSystem SolutionContainer = default!;
 
     public override void Initialize()
     {
diff --git a/Resources/Locale/en-US/chemistry/components/solution-status.ftl b/Resources/Locale/en-US/chemistry/components/solution-status.ftl
new file mode 100644 (file)
index 0000000..0ec5f93
--- /dev/null
@@ -0,0 +1,2 @@
+solution-status-volume = Volume: [color=white]{$currentVolume}/{$maxVolume}u[/color]
+solution-status-transfer = Transfer: [color=white]{$volume}u[/color]
index 681975deb832dd37c2947637b4fa8a670682492b..63070685215888bb75672e409fee2db8d0034542 100644 (file)
@@ -4,7 +4,8 @@ welder-component-no-fuel-message = The welder has no fuel left!
 welder-component-no-fuel-in-tank = The {$owner} is empty.
 welder-component-on-examine-welder-lit-message = [color=orange]Lit[/color]
 welder-component-on-examine-welder-not-lit-message = Not lit
-welder-component-on-examine-detailed-message = Fuel: [color={$colorName}]{$fuelLeft}/{$fuelCapacity}[/color]. {$status}
+welder-component-on-examine-detailed-message = Fuel: [color={$colorName}]{$fuelLeft}/{$fuelCapacity}[/color]
+    {$status}
 welder-component-suicide-lit-others-message = {$victim} welds their every orifice closed! It looks like they are trying to commit suicide!
 welder-component-suicide-lit-message = You weld your every orifice closed!
 welder-component-suicide-unlit-others-message = {$victim} bashes themselves with the unlit welding torch!
index 0e19c03deefc250b68636ea9bf71818ffed7b10b..64b3568adf5f069d52c67edb60d35c8404ffa44b 100644 (file)
@@ -24,6 +24,8 @@
     solution: spray
   - type: SolutionTransfer
     canChangeTransferAmount: true
+  - type: SolutionItemStatus
+    solution: spray
   - type: UseDelay
   - type: Spray
     transferAmount: 10
index 01efcfa95024ef4e91ed29a6671ac7ec86db13ac..8034844a82bfb37c95f881856c40705bc06a7c2a 100644 (file)
@@ -32,6 +32,8 @@
       solution: beaker
     - type: SolutionTransfer
       canChangeTransferAmount: true
+    - type: SolutionItemStatus
+      solution: beaker
     - type: UserInterface
       interfaces:
         - key: enum.TransferAmountUiKey.Key
index 9ac699db5a48fbdb2fc9270b8ff7a30e824a5366..fdf58dc48419ad0abed137e00ae5689230ffb07d 100644 (file)
@@ -41,6 +41,8 @@
     solution: beaker
   - type: SolutionTransfer
     canChangeTransferAmount: true
+  - type: SolutionItemStatus
+    solution: beaker
   - type: UserInterface
     interfaces:
     - key: enum.TransferAmountUiKey.Key
index d0a3bb37022ee8618bdbb0ee792d0648d0ffd5d4..77803a13ec67a394ee128c57e87dfbc3293051d9 100644 (file)
@@ -51,6 +51,8 @@
     solution: bucket
   - type: DrainableSolution
     solution: bucket
+  - type: SolutionItemStatus
+    solution: bucket
   - type: Appearance
   - type: SolutionContainerVisuals
     maxFillLevels: 3
index edd2681a624deea38b78bd0375978a6e0540a504..3952687255a68fa37da69573648bb5b5c2f0a392 100644 (file)
@@ -12,6 +12,8 @@
     concerningOrangeFore: "#A5762F"
     dangerousRedFore: "#BB3232"
     disabledFore: "#5A5A5A"
+    _itemstatus_content_margin_right: "#06060404"
+    _itemstatus_content_margin_left: "#04060604"
 - type: uiTheme
   id: SS14PlasmafireTheme
   path: /Textures/Interface/Plasmafire/
@@ -26,6 +28,8 @@
     concerningOrangeFore: "#FFF5EE"
     dangerousRedFore: "#FFF5EE"
     disabledFore: "#FFF5EE"
+    _itemstatus_content_margin_right: "#06060404"
+    _itemstatus_content_margin_left: "#04060604"
 - type: uiTheme
   id: SS14SlimecoreTheme
   path: /Textures/Interface/Slimecore/
@@ -40,6 +44,8 @@
     concerningOrangeFore: "#FFF5EE"
     dangerousRedFore: "#FFF5EE"
     disabledFore: "#FFF5EE"
+    _itemstatus_content_margin_right: "#06060404"
+    _itemstatus_content_margin_left: "#04060604"
 - type: uiTheme
   id: SS14ClockworkTheme
   path: /Textures/Interface/Clockwork/
@@ -54,6 +60,8 @@
     concerningOrangeFore: "#FFF5EE"
     dangerousRedFore: "#FFF5EE"
     disabledFore: "#FFF5EE"
+    _itemstatus_content_margin_right: "#06060404"
+    _itemstatus_content_margin_left: "#04060604"
 - type: uiTheme
   id: SS14RetroTheme
   path: /Textures/Interface/Retro/
@@ -68,6 +76,8 @@
     concerningOrangeFore: "#FFF5EE"
     dangerousRedFore: "#FFF5EE"
     disabledFore: "#FFF5EE"
+    _itemstatus_content_margin_right: "#06060404"
+    _itemstatus_content_margin_left: "#04060604"
 - type: uiTheme
   id: SS14MinimalistTheme
   path: /Textures/Interface/Minimalist/
@@ -82,6 +92,8 @@
     concerningOrangeFore: "#A5762F"
     dangerousRedFore: "#BB3232"
     disabledFore: "#5A5A5A"
+    _itemstatus_content_margin_right: "#06060604"
+    _itemstatus_content_margin_left: "#06060604"
 - type: uiTheme
   id: SS14AshenTheme
   path: /Textures/Interface/Ashen/
     concerningOrangeFore: "#FFF5EE"
     dangerousRedFore: "#FFF5EE"
     disabledFore: "#FFF5EE"
+    _itemstatus_content_margin_right: "#06060604"
+    _itemstatus_content_margin_left: "#06060604"
diff --git a/Resources/Textures/Interface/Ashen/item_status_left.png b/Resources/Textures/Interface/Ashen/item_status_left.png
new file mode 100644 (file)
index 0000000..fb2bf2b
Binary files /dev/null and b/Resources/Textures/Interface/Ashen/item_status_left.png differ
diff --git a/Resources/Textures/Interface/Ashen/item_status_left_highlight.png b/Resources/Textures/Interface/Ashen/item_status_left_highlight.png
new file mode 100644 (file)
index 0000000..91cd15d
Binary files /dev/null and b/Resources/Textures/Interface/Ashen/item_status_left_highlight.png differ
diff --git a/Resources/Textures/Interface/Ashen/item_status_right.png b/Resources/Textures/Interface/Ashen/item_status_right.png
new file mode 100644 (file)
index 0000000..53f4f36
Binary files /dev/null and b/Resources/Textures/Interface/Ashen/item_status_right.png differ
diff --git a/Resources/Textures/Interface/Ashen/item_status_right_highlight.png b/Resources/Textures/Interface/Ashen/item_status_right_highlight.png
new file mode 100644 (file)
index 0000000..ad16bab
Binary files /dev/null and b/Resources/Textures/Interface/Ashen/item_status_right_highlight.png differ
diff --git a/Resources/Textures/Interface/Clockwork/item_status_left.png b/Resources/Textures/Interface/Clockwork/item_status_left.png
new file mode 100644 (file)
index 0000000..1ce9503
Binary files /dev/null and b/Resources/Textures/Interface/Clockwork/item_status_left.png differ
diff --git a/Resources/Textures/Interface/Clockwork/item_status_left_highlight.png b/Resources/Textures/Interface/Clockwork/item_status_left_highlight.png
new file mode 100644 (file)
index 0000000..f715e06
Binary files /dev/null and b/Resources/Textures/Interface/Clockwork/item_status_left_highlight.png differ
diff --git a/Resources/Textures/Interface/Clockwork/item_status_right.png b/Resources/Textures/Interface/Clockwork/item_status_right.png
new file mode 100644 (file)
index 0000000..5ea5ffc
Binary files /dev/null and b/Resources/Textures/Interface/Clockwork/item_status_right.png differ
diff --git a/Resources/Textures/Interface/Clockwork/item_status_right_highlight.png b/Resources/Textures/Interface/Clockwork/item_status_right_highlight.png
new file mode 100644 (file)
index 0000000..315d595
Binary files /dev/null and b/Resources/Textures/Interface/Clockwork/item_status_right_highlight.png differ
diff --git a/Resources/Textures/Interface/Default/item_status_left.png b/Resources/Textures/Interface/Default/item_status_left.png
new file mode 100644 (file)
index 0000000..6c980f2
Binary files /dev/null and b/Resources/Textures/Interface/Default/item_status_left.png differ
diff --git a/Resources/Textures/Interface/Default/item_status_left_highlight.png b/Resources/Textures/Interface/Default/item_status_left_highlight.png
new file mode 100644 (file)
index 0000000..87dea5c
Binary files /dev/null and b/Resources/Textures/Interface/Default/item_status_left_highlight.png differ
diff --git a/Resources/Textures/Interface/Default/item_status_right.png b/Resources/Textures/Interface/Default/item_status_right.png
new file mode 100644 (file)
index 0000000..82ad44b
Binary files /dev/null and b/Resources/Textures/Interface/Default/item_status_right.png differ
diff --git a/Resources/Textures/Interface/Default/item_status_right_highlight.png b/Resources/Textures/Interface/Default/item_status_right_highlight.png
new file mode 100644 (file)
index 0000000..0c1c344
Binary files /dev/null and b/Resources/Textures/Interface/Default/item_status_right_highlight.png differ
diff --git a/Resources/Textures/Interface/Minimalist/item_status_left.png b/Resources/Textures/Interface/Minimalist/item_status_left.png
new file mode 100644 (file)
index 0000000..d70eca2
Binary files /dev/null and b/Resources/Textures/Interface/Minimalist/item_status_left.png differ
diff --git a/Resources/Textures/Interface/Minimalist/item_status_left_highlight.png b/Resources/Textures/Interface/Minimalist/item_status_left_highlight.png
new file mode 100644 (file)
index 0000000..b69872c
Binary files /dev/null and b/Resources/Textures/Interface/Minimalist/item_status_left_highlight.png differ
diff --git a/Resources/Textures/Interface/Minimalist/item_status_right.png b/Resources/Textures/Interface/Minimalist/item_status_right.png
new file mode 100644 (file)
index 0000000..89171b9
Binary files /dev/null and b/Resources/Textures/Interface/Minimalist/item_status_right.png differ
diff --git a/Resources/Textures/Interface/Minimalist/item_status_right_highlight.png b/Resources/Textures/Interface/Minimalist/item_status_right_highlight.png
new file mode 100644 (file)
index 0000000..d1474ce
Binary files /dev/null and b/Resources/Textures/Interface/Minimalist/item_status_right_highlight.png differ
diff --git a/Resources/Textures/Interface/Nano/item_status_left.svg b/Resources/Textures/Interface/Nano/item_status_left.svg
deleted file mode 100644 (file)
index c97f8de..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="17.700001"
-   height="17.700001"
-   viewBox="0 0 4.6831256 4.6831253"
-   version="1.1"
-   id="svg8"
-   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
-   sodipodi:docname="item_status_left.svg"
-   inkscape:export-filename="C:\ss14\space-station-14\Resources\Textures\Interface\Nano\item_status_left.svg.96dpi.png"
-   inkscape:export-xdpi="96"
-   inkscape:export-ydpi="96">
-  <defs
-     id="defs2" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="45.254834"
-     inkscape:cx="2.2968017"
-     inkscape:cy="9.7979333"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     inkscape:pagecheckerboard="true"
-     units="px"
-     showguides="true"
-     inkscape:window-width="2560"
-     inkscape:window-height="1377"
-     inkscape:window-x="1912"
-     inkscape:window-y="-8"
-     inkscape:window-maximized="1"
-     inkscape:guide-bbox="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-bbox="true"
-     inkscape:snap-page="true"
-     inkscape:document-rotation="0"
-     fit-margin-top="0"
-     fit-margin-left="0"
-     fit-margin-right="0"
-     fit-margin-bottom="0">
-    <inkscape:grid
-       type="xygrid"
-       id="grid817"
-       originx="0.44979165"
-       originy="1.5931808" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata5">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(-26.372785,-208.49171)">
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#474747;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 45.902791,212.76114 v 3.23047 l 1.003906,1.00195 h 3.228516 v -3.22852 l -1.001953,-1.0039 h -0.05469 z m 0.263672,0.26367 h 2.855469 l 0.849609,0.84961 v 2.85547 h -2.855468 l -0.84961,-0.84961 z"
-       id="rect815-3"
-       inkscape:connector-curvature="0" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#474747;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 46.344997,207.34832 -0.290893,0.17906 1.133594,1.1102 v 2.91047 h 0.264579 v -3.04271 z"
-       id="rect815-6"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccccc" />
-    <path
-       style="fill:none;stroke:#474747;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       d="m 38.473823,212.26371 v -3.175 l -1.058333,-1.05833"
-       id="path856-6"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccc" />
-    <path
-       id="rect815-9"
-       style="opacity:1;vector-effect:none;fill:#24242b;fill-opacity:0.95;fill-rule:nonzero;stroke:#3a4051;stroke-width:0.449792;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       d="m 26.373445,212.94994 h 3.704167 0.753402 v -1.5875 l -2.870068,-2.64583 h -0.748275 -0.839226"
-       inkscape:export-xdpi="96.000008"
-       inkscape:export-ydpi="96.000008"
-       sodipodi:nodetypes="ccccccc" />
-  </g>
-</svg>
diff --git a/Resources/Textures/Interface/Nano/item_status_left.svg.96dpi.png b/Resources/Textures/Interface/Nano/item_status_left.svg.96dpi.png
deleted file mode 100644 (file)
index 5169343..0000000
Binary files a/Resources/Textures/Interface/Nano/item_status_left.svg.96dpi.png and /dev/null differ
diff --git a/Resources/Textures/Interface/Nano/item_status_middle.svg b/Resources/Textures/Interface/Nano/item_status_middle.svg
deleted file mode 100644 (file)
index a913981..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="17.700001"
-   height="17.700001"
-   viewBox="0 0 4.6831256 4.6831253"
-   version="1.1"
-   id="svg8"
-   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
-   sodipodi:docname="item_status_middle.svg"
-   inkscape:export-filename="C:\ss14\space-station-14\Resources\Textures\Interface\Nano\item_status_left.svg.96dpi.png"
-   inkscape:export-xdpi="96"
-   inkscape:export-ydpi="96">
-  <defs
-     id="defs2" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="256"
-     inkscape:cx="0.27981532"
-     inkscape:cy="1.9876937"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     inkscape:pagecheckerboard="true"
-     units="px"
-     showguides="true"
-     inkscape:window-width="2560"
-     inkscape:window-height="1377"
-     inkscape:window-x="1912"
-     inkscape:window-y="-8"
-     inkscape:window-maximized="1"
-     inkscape:guide-bbox="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-bbox="true"
-     inkscape:snap-page="true"
-     inkscape:document-rotation="0"
-     fit-margin-top="0"
-     fit-margin-left="0"
-     fit-margin-right="0"
-     fit-margin-bottom="0">
-    <inkscape:grid
-       type="xygrid"
-       id="grid817"
-       originx="0.44979165"
-       originy="1.5931808" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata5">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(-26.372785,-208.49171)">
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#474747;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 45.902791,212.76114 v 3.23047 l 1.003906,1.00195 h 3.228516 v -3.22852 l -1.001953,-1.0039 h -0.05469 z m 0.263672,0.26367 h 2.855469 l 0.849609,0.84961 v 2.85547 h -2.855468 l -0.84961,-0.84961 z"
-       id="rect815-3"
-       inkscape:connector-curvature="0" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#474747;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 46.344997,207.34832 -0.290893,0.17906 1.133594,1.1102 v 2.91047 h 0.264579 v -3.04271 z"
-       id="rect815-6"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccccc" />
-    <path
-       style="fill:none;stroke:#474747;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       d="m 38.473823,212.26371 v -3.175 l -1.058333,-1.05833"
-       id="path856-6"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccc" />
-    <path
-       id="rect815-9"
-       style="opacity:1;vector-effect:none;fill:#24242b;fill-opacity:0.95;fill-rule:nonzero;stroke:#3a4051;stroke-width:0.449792;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       d="m 26.598004,212.94994 h 3.479608 0.753402 v -1.5875 l -2.870068,-2.64583 h -0.748275 -0.614667 z"
-       inkscape:export-xdpi="96.000008"
-       inkscape:export-ydpi="96.000008"
-       sodipodi:nodetypes="cccccccc" />
-  </g>
-</svg>
diff --git a/Resources/Textures/Interface/Nano/item_status_middle.svg.96dpi.png b/Resources/Textures/Interface/Nano/item_status_middle.svg.96dpi.png
deleted file mode 100644 (file)
index dc1f84c..0000000
Binary files a/Resources/Textures/Interface/Nano/item_status_middle.svg.96dpi.png and /dev/null differ
diff --git a/Resources/Textures/Interface/Nano/item_status_right.svg b/Resources/Textures/Interface/Nano/item_status_right.svg
deleted file mode 100644 (file)
index d898bb2..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="17.700001"
-   height="17.700001"
-   viewBox="0 0 4.6831256 4.6831253"
-   version="1.1"
-   id="svg8"
-   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
-   sodipodi:docname="item_status_right.svg"
-   inkscape:export-filename="C:\ss14\space-station-14\Resources\Textures\Interface\Nano\item_status_right.svg.96dpi.png"
-   inkscape:export-xdpi="96"
-   inkscape:export-ydpi="96">
-  <defs
-     id="defs2" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="45.254834"
-     inkscape:cx="2.2968017"
-     inkscape:cy="9.7979333"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     inkscape:pagecheckerboard="true"
-     units="px"
-     showguides="true"
-     inkscape:window-width="2560"
-     inkscape:window-height="1377"
-     inkscape:window-x="1912"
-     inkscape:window-y="-8"
-     inkscape:window-maximized="1"
-     inkscape:guide-bbox="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-bbox="true"
-     inkscape:snap-page="true"
-     inkscape:document-rotation="0"
-     fit-margin-top="0"
-     fit-margin-left="0"
-     fit-margin-right="0"
-     fit-margin-bottom="0">
-    <inkscape:grid
-       type="xygrid"
-       id="grid817"
-       originx="0.44979165"
-       originy="1.5931808" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata5">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(-26.372785,-208.49171)">
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#474747;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 45.902791,212.76114 v 3.23047 l 1.003906,1.00195 h 3.228516 v -3.22852 l -1.001953,-1.0039 h -0.05469 z m 0.263672,0.26367 h 2.855469 l 0.849609,0.84961 v 2.85547 h -2.855468 l -0.84961,-0.84961 z"
-       id="rect815-3"
-       inkscape:connector-curvature="0" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#474747;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 46.344997,207.34832 -0.290893,0.17906 1.133594,1.1102 v 2.91047 h 0.264579 v -3.04271 z"
-       id="rect815-6"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccccc" />
-    <path
-       style="fill:none;stroke:#474747;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       d="m 38.473823,212.26371 v -3.175 l -1.058333,-1.05833"
-       id="path856-6"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccc" />
-    <path
-       id="rect815-9"
-       style="opacity:1;vector-effect:none;fill:#24242b;fill-opacity:0.95;fill-rule:nonzero;stroke:#3a4051;stroke-width:0.449792;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       d="m 31.05591,212.94994 h -3.704167 -0.753402 v -1.5875 l 2.870068,-2.64583 h 0.748275 0.839226"
-       inkscape:export-xdpi="96.000008"
-       inkscape:export-ydpi="96.000008"
-       sodipodi:nodetypes="ccccccc" />
-  </g>
-</svg>
diff --git a/Resources/Textures/Interface/Nano/item_status_right.svg.96dpi.png b/Resources/Textures/Interface/Nano/item_status_right.svg.96dpi.png
deleted file mode 100644 (file)
index 72f7e6d..0000000
Binary files a/Resources/Textures/Interface/Nano/item_status_right.svg.96dpi.png and /dev/null differ
diff --git a/Resources/Textures/Interface/Plasmafire/item_status_left.png b/Resources/Textures/Interface/Plasmafire/item_status_left.png
new file mode 100644 (file)
index 0000000..d5d25c9
Binary files /dev/null and b/Resources/Textures/Interface/Plasmafire/item_status_left.png differ
diff --git a/Resources/Textures/Interface/Plasmafire/item_status_left_highlight.png b/Resources/Textures/Interface/Plasmafire/item_status_left_highlight.png
new file mode 100644 (file)
index 0000000..afe3751
Binary files /dev/null and b/Resources/Textures/Interface/Plasmafire/item_status_left_highlight.png differ
diff --git a/Resources/Textures/Interface/Plasmafire/item_status_right.png b/Resources/Textures/Interface/Plasmafire/item_status_right.png
new file mode 100644 (file)
index 0000000..ca97f81
Binary files /dev/null and b/Resources/Textures/Interface/Plasmafire/item_status_right.png differ
diff --git a/Resources/Textures/Interface/Plasmafire/item_status_right_highlight.png b/Resources/Textures/Interface/Plasmafire/item_status_right_highlight.png
new file mode 100644 (file)
index 0000000..b95822b
Binary files /dev/null and b/Resources/Textures/Interface/Plasmafire/item_status_right_highlight.png differ
diff --git a/Resources/Textures/Interface/Retro/item_status_left.png b/Resources/Textures/Interface/Retro/item_status_left.png
new file mode 100644 (file)
index 0000000..21b107b
Binary files /dev/null and b/Resources/Textures/Interface/Retro/item_status_left.png differ
diff --git a/Resources/Textures/Interface/Retro/item_status_left_highlight.png b/Resources/Textures/Interface/Retro/item_status_left_highlight.png
new file mode 100644 (file)
index 0000000..fdd5a4f
Binary files /dev/null and b/Resources/Textures/Interface/Retro/item_status_left_highlight.png differ
diff --git a/Resources/Textures/Interface/Retro/item_status_right.png b/Resources/Textures/Interface/Retro/item_status_right.png
new file mode 100644 (file)
index 0000000..5e7d546
Binary files /dev/null and b/Resources/Textures/Interface/Retro/item_status_right.png differ
diff --git a/Resources/Textures/Interface/Retro/item_status_right_highlight.png b/Resources/Textures/Interface/Retro/item_status_right_highlight.png
new file mode 100644 (file)
index 0000000..c6e12c4
Binary files /dev/null and b/Resources/Textures/Interface/Retro/item_status_right_highlight.png differ
diff --git a/Resources/Textures/Interface/Slimecore/item_status_left.png b/Resources/Textures/Interface/Slimecore/item_status_left.png
new file mode 100644 (file)
index 0000000..a7d940f
Binary files /dev/null and b/Resources/Textures/Interface/Slimecore/item_status_left.png differ
diff --git a/Resources/Textures/Interface/Slimecore/item_status_left_highlight.png b/Resources/Textures/Interface/Slimecore/item_status_left_highlight.png
new file mode 100644 (file)
index 0000000..322355b
Binary files /dev/null and b/Resources/Textures/Interface/Slimecore/item_status_left_highlight.png differ
diff --git a/Resources/Textures/Interface/Slimecore/item_status_right.png b/Resources/Textures/Interface/Slimecore/item_status_right.png
new file mode 100644 (file)
index 0000000..77b5334
Binary files /dev/null and b/Resources/Textures/Interface/Slimecore/item_status_right.png differ
diff --git a/Resources/Textures/Interface/Slimecore/item_status_right_highlight.png b/Resources/Textures/Interface/Slimecore/item_status_right_highlight.png
new file mode 100644 (file)
index 0000000..1e1a631
Binary files /dev/null and b/Resources/Textures/Interface/Slimecore/item_status_right_highlight.png differ