From 58f025ba8043bcf5c32204b08eec8ae9e5d2bd41 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sun, 21 Apr 2024 15:16:23 +0200 Subject: [PATCH] THE RETURN OF ITEM STATUS (#22986) * 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 --- .../Components/SolutionItemStatusComponent.cs | 22 ++ .../EntitySystems/SolutionItemStatusSystem.cs | 22 ++ .../Chemistry/UI/SolutionStatusControl.cs | 59 ++++++ .../Items/UI/PollingItemStatusControl.cs | 28 +++ Content.Client/Stylesheets/StyleNano.cs | 5 + Content.Client/Tools/ToolSystem.cs | 2 +- .../Tools/UI/WelderStatusControl.cs | 48 ++--- .../Systems/Hands/HandsUIController.cs | 52 ++++- .../Systems/Hotbar/HotbarUIController.cs | 2 +- .../Systems/Hotbar/Widgets/HotbarGui.xaml | 36 ++-- .../Systems/Hotbar/Widgets/HotbarGui.xaml.cs | 28 ++- .../Inventory/Controls/ItemStatusPanel.xaml | 38 ++-- .../Controls/ItemStatusPanel.xaml.cs | 82 +++++--- .../Weapons/Ranged/ItemStatus/BulletRender.cs | 178 ++++++++++++++++ .../Ranged/Systems/GunSystem.AmmoCounter.cs | 197 ++++-------------- Content.Server/Content.Server.csproj | 1 - Content.Server/Entry/IgnoredComponents.cs | 3 +- Content.Server/Tools/ToolSystem.cs | 4 +- .../Tools/Components/WelderComponent.cs | 6 - .../Tools/Systems/SharedToolSystem.Welder.cs | 24 ++- .../Tools/Systems/SharedToolSystem.cs | 1 + .../chemistry/components/solution-status.ftl | 2 + .../tools/components/welder-component.ftl | 3 +- .../Objects/Specific/Janitorial/spray.yml | 2 + .../Objects/Specific/chemical-containers.yml | 2 + .../Entities/Objects/Specific/chemistry.yml | 2 + .../Entities/Objects/Tools/bucket.yml | 2 + Resources/Prototypes/themes.yml | 14 ++ .../Interface/Ashen/item_status_left.png | Bin 0 -> 255 bytes .../Ashen/item_status_left_highlight.png | Bin 0 -> 195 bytes .../Interface/Ashen/item_status_right.png | Bin 0 -> 246 bytes .../Ashen/item_status_right_highlight.png | Bin 0 -> 189 bytes .../Interface/Clockwork/item_status_left.png | Bin 0 -> 371 bytes .../Clockwork/item_status_left_highlight.png | Bin 0 -> 307 bytes .../Interface/Clockwork/item_status_right.png | Bin 0 -> 389 bytes .../Clockwork/item_status_right_highlight.png | Bin 0 -> 326 bytes .../Interface/Default/item_status_left.png | Bin 0 -> 264 bytes .../Default/item_status_left_highlight.png | Bin 0 -> 251 bytes .../Interface/Default/item_status_right.png | Bin 0 -> 258 bytes .../Default/item_status_right_highlight.png | Bin 0 -> 237 bytes .../Interface/Minimalist/item_status_left.png | Bin 0 -> 208 bytes .../Minimalist/item_status_left_highlight.png | Bin 0 -> 277 bytes .../Minimalist/item_status_right.png | Bin 0 -> 198 bytes .../item_status_right_highlight.png | Bin 0 -> 249 bytes .../Interface/Nano/item_status_left.svg | 100 --------- .../Nano/item_status_left.svg.96dpi.png | Bin 453 -> 0 bytes .../Interface/Nano/item_status_middle.svg | 100 --------- .../Nano/item_status_middle.svg.96dpi.png | Bin 458 -> 0 bytes .../Interface/Nano/item_status_right.svg | 100 --------- .../Nano/item_status_right.svg.96dpi.png | Bin 431 -> 0 bytes .../Interface/Plasmafire/item_status_left.png | Bin 0 -> 271 bytes .../Plasmafire/item_status_left_highlight.png | Bin 0 -> 220 bytes .../Plasmafire/item_status_right.png | Bin 0 -> 271 bytes .../item_status_right_highlight.png | Bin 0 -> 206 bytes .../Interface/Retro/item_status_left.png | Bin 0 -> 249 bytes .../Retro/item_status_left_highlight.png | Bin 0 -> 235 bytes .../Interface/Retro/item_status_right.png | Bin 0 -> 239 bytes .../Retro/item_status_right_highlight.png | Bin 0 -> 253 bytes .../Interface/Slimecore/item_status_left.png | Bin 0 -> 280 bytes .../Slimecore/item_status_left_highlight.png | Bin 0 -> 219 bytes .../Interface/Slimecore/item_status_right.png | Bin 0 -> 267 bytes .../Slimecore/item_status_right_highlight.png | Bin 0 -> 206 bytes 62 files changed, 581 insertions(+), 584 deletions(-) create mode 100644 Content.Client/Chemistry/Components/SolutionItemStatusComponent.cs create mode 100644 Content.Client/Chemistry/EntitySystems/SolutionItemStatusSystem.cs create mode 100644 Content.Client/Chemistry/UI/SolutionStatusControl.cs create mode 100644 Content.Client/Items/UI/PollingItemStatusControl.cs create mode 100644 Content.Client/Weapons/Ranged/ItemStatus/BulletRender.cs create mode 100644 Resources/Locale/en-US/chemistry/components/solution-status.ftl create mode 100644 Resources/Textures/Interface/Ashen/item_status_left.png create mode 100644 Resources/Textures/Interface/Ashen/item_status_left_highlight.png create mode 100644 Resources/Textures/Interface/Ashen/item_status_right.png create mode 100644 Resources/Textures/Interface/Ashen/item_status_right_highlight.png create mode 100644 Resources/Textures/Interface/Clockwork/item_status_left.png create mode 100644 Resources/Textures/Interface/Clockwork/item_status_left_highlight.png create mode 100644 Resources/Textures/Interface/Clockwork/item_status_right.png create mode 100644 Resources/Textures/Interface/Clockwork/item_status_right_highlight.png create mode 100644 Resources/Textures/Interface/Default/item_status_left.png create mode 100644 Resources/Textures/Interface/Default/item_status_left_highlight.png create mode 100644 Resources/Textures/Interface/Default/item_status_right.png create mode 100644 Resources/Textures/Interface/Default/item_status_right_highlight.png create mode 100644 Resources/Textures/Interface/Minimalist/item_status_left.png create mode 100644 Resources/Textures/Interface/Minimalist/item_status_left_highlight.png create mode 100644 Resources/Textures/Interface/Minimalist/item_status_right.png create mode 100644 Resources/Textures/Interface/Minimalist/item_status_right_highlight.png delete mode 100644 Resources/Textures/Interface/Nano/item_status_left.svg delete mode 100644 Resources/Textures/Interface/Nano/item_status_left.svg.96dpi.png delete mode 100644 Resources/Textures/Interface/Nano/item_status_middle.svg delete mode 100644 Resources/Textures/Interface/Nano/item_status_middle.svg.96dpi.png delete mode 100644 Resources/Textures/Interface/Nano/item_status_right.svg delete mode 100644 Resources/Textures/Interface/Nano/item_status_right.svg.96dpi.png create mode 100644 Resources/Textures/Interface/Plasmafire/item_status_left.png create mode 100644 Resources/Textures/Interface/Plasmafire/item_status_left_highlight.png create mode 100644 Resources/Textures/Interface/Plasmafire/item_status_right.png create mode 100644 Resources/Textures/Interface/Plasmafire/item_status_right_highlight.png create mode 100644 Resources/Textures/Interface/Retro/item_status_left.png create mode 100644 Resources/Textures/Interface/Retro/item_status_left_highlight.png create mode 100644 Resources/Textures/Interface/Retro/item_status_right.png create mode 100644 Resources/Textures/Interface/Retro/item_status_right_highlight.png create mode 100644 Resources/Textures/Interface/Slimecore/item_status_left.png create mode 100644 Resources/Textures/Interface/Slimecore/item_status_left_highlight.png create mode 100644 Resources/Textures/Interface/Slimecore/item_status_right.png create mode 100644 Resources/Textures/Interface/Slimecore/item_status_right_highlight.png diff --git a/Content.Client/Chemistry/Components/SolutionItemStatusComponent.cs b/Content.Client/Chemistry/Components/SolutionItemStatusComponent.cs new file mode 100644 index 0000000000..58c5a05894 --- /dev/null +++ b/Content.Client/Chemistry/Components/SolutionItemStatusComponent.cs @@ -0,0 +1,22 @@ +using Content.Client.Chemistry.EntitySystems; +using Content.Client.Chemistry.UI; + +namespace Content.Client.Chemistry.Components; + +/// +/// Exposes a solution container's contents via a basic item status control. +/// +/// +/// Shows the solution volume, max volume, and transfer amount. +/// +/// +/// +[RegisterComponent] +public sealed partial class SolutionItemStatusComponent : Component +{ + /// + /// The ID of the solution that will be shown on the item status control. + /// + [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 index 0000000000..76aab516a7 --- /dev/null +++ b/Content.Client/Chemistry/EntitySystems/SolutionItemStatusSystem.cs @@ -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; + +/// +/// Wires up item status logic for . +/// +/// +public sealed class SolutionItemStatusSystem : EntitySystem +{ + [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; + + public override void Initialize() + { + base.Initialize(); + Subs.ItemStatus( + 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 index 0000000000..1a33ffb0e1 --- /dev/null +++ b/Content.Client/Chemistry/UI/SolutionStatusControl.cs @@ -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; + +/// +/// Displays basic solution information for . +/// +/// +public sealed class SolutionStatusControl : PollingItemStatusControl +{ + private readonly Entity _parent; + private readonly IEntityManager _entityManager; + private readonly SharedSolutionContainerSystem _solutionContainers; + private readonly RichTextLabel _label; + + public SolutionStatusControl( + Entity 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 index 0000000000..39cffb06f6 --- /dev/null +++ b/Content.Client/Items/UI/PollingItemStatusControl.cs @@ -0,0 +1,28 @@ +using Robust.Client.UserInterface; +using Robust.Shared.Timing; + +namespace Content.Client.Items.UI; + +/// +/// A base for item status controls that poll data every frame. Avoids UI updates if data didn't change. +/// +/// The full status control data that is polled every frame. +public abstract class PollingItemStatusControl : Control where TData : struct, IEquatable +{ + 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); +} diff --git a/Content.Client/Stylesheets/StyleNano.cs b/Content.Client/Stylesheets/StyleNano.cs index a589abb83a..5fc17447c3 100644 --- a/Content.Client/Stylesheets/StyleNano.cs +++ b/Content.Client/Stylesheets/StyleNano.cs @@ -1234,6 +1234,11 @@ namespace Content.Client.Stylesheets new StyleProperty("font", notoSans10), }), + Element() + .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 [] { diff --git a/Content.Client/Tools/ToolSystem.cs b/Content.Client/Tools/ToolSystem.cs index 3eb2cc45cb..2207242918 100644 --- a/Content.Client/Tools/ToolSystem.cs +++ b/Content.Client/Tools/ToolSystem.cs @@ -13,7 +13,7 @@ namespace Content.Client.Tools { base.Initialize(); - Subs.ItemStatus(ent => new WelderStatusControl(ent)); + Subs.ItemStatus(ent => new WelderStatusControl(ent, EntityManager, this)); Subs.ItemStatus(ent => new MultipleToolStatusControl(ent)); } diff --git a/Content.Client/Tools/UI/WelderStatusControl.cs b/Content.Client/Tools/UI/WelderStatusControl.cs index dae742efc3..3d44d6fa84 100644 --- a/Content.Client/Tools/UI/WelderStatusControl.cs +++ b/Content.Client/Tools/UI/WelderStatusControl.cs @@ -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 { - [Dependency] private readonly IGameTiming _gameTiming = default!; - - private readonly ToolSystem _tool; - private readonly Entity _parent; + private readonly IEntityManager _entityManager; + private readonly SharedToolSystem _toolSystem; private readonly RichTextLabel _label; - public WelderStatusControl(Entity parent) + public WelderStatusControl(Entity parent, IEntityManager entityManager, SharedToolSystem toolSystem) { - IoCManager.InjectDependencies(this); - _parent = parent; - var entMan = IoCManager.Resolve(); - _tool = entMan.System(); - + _entityManager = entityManager; + _toolSystem = toolSystem; _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } }; AddChild(_label); UpdateDraw(); } - /// - 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); } diff --git a/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs b/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs index bb550b0433..e57c15462e 100644 --- a/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs +++ b/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs @@ -28,6 +28,15 @@ public sealed class HandsUIController : UIController, IOnStateEntered _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(); @@ -180,8 +189,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered(); _hands = UIManager.GetUIController(); diff --git a/Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml b/Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml index 0e9f0c77f9..3afe11ba33 100644 --- a/Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml +++ b/Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml @@ -10,21 +10,14 @@ Orientation="Vertical" HorizontalAlignment="Center"> - - - - - - + + + + + ColumnLimit="6"/> + (); - 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); } } diff --git a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml index d469e6ced0..81142d64d2 100644 --- a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml +++ b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml @@ -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"> - - - - - - - + diff --git a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml.cs b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml.cs index 90ae571711..e1fe6ab246 100644 --- a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml.cs +++ b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml.cs @@ -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 index 0000000000..492fad3872 --- /dev/null +++ b/Content.Client/Weapons/Ranged/ItemStatus/BulletRender.cs @@ -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; + +/// +/// Renders one or more rows of bullets for item status. +/// +/// +/// This is a custom control to allow complex responsive layout logic. +/// +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"); + + /// + /// Try to ensure there's at least this many bullets on one row. + /// + /// + /// 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. + /// + 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(); + _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 + } +} diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.AmmoCounter.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.AmmoCounter.cs index 32343af56f..cc7405b047 100644 --- a/Content.Client/Weapons/Ranged/Systems/GunSystem.AmmoCounter.cs +++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.AmmoCounter.cs @@ -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) diff --git a/Content.Server/Content.Server.csproj b/Content.Server/Content.Server.csproj index 8782454eac..70e404fdef 100644 --- a/Content.Server/Content.Server.csproj +++ b/Content.Server/Content.Server.csproj @@ -28,7 +28,6 @@ - diff --git a/Content.Server/Entry/IgnoredComponents.cs b/Content.Server/Entry/IgnoredComponents.cs index fe073da7a4..d9b81c8e5a 100644 --- a/Content.Server/Entry/IgnoredComponents.cs +++ b/Content.Server/Entry/IgnoredComponents.cs @@ -14,12 +14,13 @@ namespace Content.Server.Entry "Icon", "HandheldGPS", "CableVisualizer", + "SolutionItemStatus", "UIFragment", "PdaBorderColor", "InventorySlots", "LightFade", "HolidayRsiSwap", - "OptionsVisualizer", + "OptionsVisualizer" }; } } diff --git a/Content.Server/Tools/ToolSystem.cs b/Content.Server/Tools/ToolSystem.cs index 7b2de57efc..7738a6398f 100644 --- a/Content.Server/Tools/ToolSystem.cs +++ b/Content.Server/Tools/ToolSystem.cs @@ -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) { diff --git a/Content.Shared/Tools/Components/WelderComponent.cs b/Content.Shared/Tools/Components/WelderComponent.cs index f133be675d..3c78a03fde 100644 --- a/Content.Shared/Tools/Components/WelderComponent.cs +++ b/Content.Shared/Tools/Components/WelderComponent.cs @@ -23,12 +23,6 @@ public sealed partial class WelderComponent : Component [DataField] public string FuelSolutionName = "Welder"; - /// - /// Solution on the entity that contains the fuel. - /// - [ViewVariables(VVAccess.ReadWrite)] - public Entity? FuelSolution; - /// /// Reagent that will be used as fuel for welding. /// diff --git a/Content.Shared/Tools/Systems/SharedToolSystem.Welder.cs b/Content.Shared/Tools/Systems/SharedToolSystem.Welder.cs index 6bab296db5..e790b59cd1 100644 --- a/Content.Shared/Tools/Systems/SharedToolSystem.Welder.cs +++ b/Content.Shared/Tools/Systems/SharedToolSystem.Welder.cs @@ -24,10 +24,10 @@ public abstract partial class SharedToolSystem public virtual void TurnOn(Entity 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 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"); diff --git a/Content.Shared/Tools/Systems/SharedToolSystem.cs b/Content.Shared/Tools/Systems/SharedToolSystem.cs index 362f4f7683..9edae9b78f 100644 --- a/Content.Shared/Tools/Systems/SharedToolSystem.cs +++ b/Content.Shared/Tools/Systems/SharedToolSystem.cs @@ -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 index 0000000000..0ec5f932e0 --- /dev/null +++ b/Resources/Locale/en-US/chemistry/components/solution-status.ftl @@ -0,0 +1,2 @@ +solution-status-volume = Volume: [color=white]{$currentVolume}/{$maxVolume}u[/color] +solution-status-transfer = Transfer: [color=white]{$volume}u[/color] diff --git a/Resources/Locale/en-US/tools/components/welder-component.ftl b/Resources/Locale/en-US/tools/components/welder-component.ftl index 681975deb8..6307068521 100644 --- a/Resources/Locale/en-US/tools/components/welder-component.ftl +++ b/Resources/Locale/en-US/tools/components/welder-component.ftl @@ -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! diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml index 0e19c03dee..64b3568adf 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml @@ -24,6 +24,8 @@ solution: spray - type: SolutionTransfer canChangeTransferAmount: true + - type: SolutionItemStatus + solution: spray - type: UseDelay - type: Spray transferAmount: 10 diff --git a/Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml b/Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml index 01efcfa950..8034844a82 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml @@ -32,6 +32,8 @@ solution: beaker - type: SolutionTransfer canChangeTransferAmount: true + - type: SolutionItemStatus + solution: beaker - type: UserInterface interfaces: - key: enum.TransferAmountUiKey.Key diff --git a/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml b/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml index 9ac699db5a..fdf58dc484 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml @@ -41,6 +41,8 @@ solution: beaker - type: SolutionTransfer canChangeTransferAmount: true + - type: SolutionItemStatus + solution: beaker - type: UserInterface interfaces: - key: enum.TransferAmountUiKey.Key diff --git a/Resources/Prototypes/Entities/Objects/Tools/bucket.yml b/Resources/Prototypes/Entities/Objects/Tools/bucket.yml index d0a3bb3702..77803a13ec 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/bucket.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/bucket.yml @@ -51,6 +51,8 @@ solution: bucket - type: DrainableSolution solution: bucket + - type: SolutionItemStatus + solution: bucket - type: Appearance - type: SolutionContainerVisuals maxFillLevels: 3 diff --git a/Resources/Prototypes/themes.yml b/Resources/Prototypes/themes.yml index edd2681a62..3952687255 100644 --- a/Resources/Prototypes/themes.yml +++ b/Resources/Prototypes/themes.yml @@ -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/ @@ -96,3 +108,5 @@ 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 index 0000000000000000000000000000000000000000..fb2bf2b9b4ab7c9cb58f0ba36fd2928019b283f7 GIT binary patch literal 255 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|c6z!vhFJ98 zoxE4@kO7a|{NkNF53bB=66ra(qQ^0DS;1SL2TZ{%FNK8XJbv**YyOh=KmD8JpOzT< z21_2lD717+>|vYp46z&wB^=IbswVZwmkSH-`7Z6jA27Q;vBLQI9Nqs-HVoHa7e=S` zu2-zOyG}35^RuB__2PA@8IB?G%vpb>6`t2*tjOk3P-+)Ao2l!-?_FR0zVT^)w1D8o zbFEo3L^i2EUo&NjkRr==#xwvI$L#$11Pr)3ZCm1|k{an^LB{Ts5 D6=!3r literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..91cd15dd4cf2f744ba63848496e7a017234e3bef GIT binary patch literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|YCT;XLo9mV zPPXPdYQVvAd?)Ai;`5p>YHj-_i3p0F@0#~nd-K%P8?8wV?OGoLu73_>}g$Tqk}p-Q(dfIH&PlZ^P%gFK5kc rj@eP@V`z|Ib!7Y2YyY#?)^ESXAnNKYt;UxEaw3DLtDnm{r-UW|@I^?M literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Ashen/item_status_right.png b/Resources/Textures/Interface/Ashen/item_status_right.png new file mode 100644 index 0000000000000000000000000000000000000000..53f4f362d0afaaab379cc3606743348ade043cbe GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|Hh8)?hFJ7I zoxEG{kb!{9d>c9OgrniCd>YL`n#~773OBB2V3pw%GW*vd>0~x3uYCR_;Xf-T@ECc$Z z!tdq>?yi37?-5p2+UkP97jtIQ;gyjv;?F^o-elF{r5}E)+bY*A& literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..ad16bab6d10aaec67790a19505c6ade9a46c7f60 GIT binary patch literal 189 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|$~|2iLo9mV zPTt6Sz<`70e$Jt)cQc#ry*BI$cIoqQN?GHv@{{O||LR-}ajSl~xmX$dJEyUQ>Z%{$ z_=sDBzZC lJ^p3MwObEYZoQ{=hhe3zv$RLDuOH9}44$rjF6*2UngG;5MRouH literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Clockwork/item_status_left.png b/Resources/Textures/Interface/Clockwork/item_status_left.png new file mode 100644 index 0000000000000000000000000000000000000000..1ce950362d234f2074fb4c714b59ae23c9d0d83c GIT binary patch literal 371 zcmV-(0gV2MP)Px$ElET{R5*>Llf7!fP!z|1w=|-Sq(~)l3vRa1fJ?I^OUL5gK?;Ho&+Oym*&j0_r=bozoszu&O5y8o&f4HZbo~0bu z9KMULk*OFdTj9MqLX)rz@D1 zJ70S>P65hSXGs8Df99ms7X5gCA-5Ha;plCEU-K0})3Z_)(Q!?lUqv@POA59@uRmQ8 z6l^0+J~9q@dD0Sz04ssnWSL3;K$kOYOh^2UU^gPuqX~V_UhD-N*jb2hp1*MZq?hO_rMxKvjz RnT!Ac002ovPDHLkV1hHPo%{d* literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..f715e062765f7f7a5db413583fa331a4982d5bf4 GIT binary patch literal 307 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|-g>$?hFJ6_ zrz9jKTo6r6NKs(glQQX$Ky#*v(vqFV(VzZIntEPRZ-3&C^9K$bSg@4jVXoe#ox_KBCH*|@o_**v-VI~ZB( z4zV0gIJ9hgn}KtJUggi`hxXje%*=;>hwtZnnGUqwBK^fSV}Y9!KCL`2*;&SDHr>F$ zV2=7x)(A$By5EU^# z`{NvU*G40q`(MS97&hMgc~zW^%}|cz_f!71{|sj?Csv6s`0xPx$KS@MER5*>Lld($zQ5?rV7f(4uDv`V61riN8HyZT^G`R}V=4gYs^f$OfOJjpm zds75165cIA(9q~0`0(tIL{Jo>;nX-W@2cW}3SiOR_#Y_VR3%Uf;*R(*F$%8W&-QkxNA j={WOCbcf$h8bL>2d^&(PG8Wt600000NkvXXu0mjf;xDXN literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..315d595c9251f958367a45ba910583b2f3fad2c9 GIT binary patch literal 326 zcmV-M0lEH(P)Px$07*naR5*>jld(#}P!xv0A1Y*Uk=R`7(kF08r%sOPR_G*xo1;r`@eKqw7azbU z5Zuxya4{K5AHbzzs050G5HJo^xF&^MDfq#G%YpwtKj+@7AUF^;%;%!s+Q@84)#-M7 z0LIz8@jdHWJNc%-ruXtm(tY%R{Fqx&{>7$tos@s))-}443efer`}4I4qogn6p_9m`$V=`XS1uo@kpSbNV0Ycg-{I%n#%xLT Y1>coniB1ZHbpQYW07*qoM6N<$fojT5Z?$9Jzis;D&mkdkYci=oy>9pk&aXYNE#3uVZh zW&81RW+n@RQrZX4=-U>8!e?!tdK=Uw}m5B#$sfidicPw0|ov!D?wLoLtZkDLmzLOXhy#KxTj~ZvqHi6?C!w*JW+E!L2 zuOz|!?3Zzq;U1-ebCtqd*7An4co+MBzHM3J6CIQ}OY=Z|TfVD)?SG%P8O%aFK;JNU My85}Sb4q9e0Hy9`n*aa+ literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..87dea5cf100888c6b4f5465879fb2abf37e11fd9 GIT binary patch literal 251 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|wtBiahFJ6_ zCoEv-==3x+3QTHPu|kDenVFfn*+2Mky;IlrD_4yd-9C2!2v#gfIHaL=(Dj0up#Jmv z7i}zJTMUgfG-nt*y41qAVds=31{Irg^@R=Z9pyAJ{VHh+#1nZHPqJQLAOGk4`Sbls zZbc?_e|}6fVqWo%Ni4`DI^ss6i>0UDj~^eEn>(g|wofr!G}*|=!Ed60zJZ~Efx(^l uot+}Z0(uH836kuRd#b*^u?ISgnPE<^?$!6ZZ47`OVDNPHb6Mw<&;$U-kzBz5 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Default/item_status_right.png b/Resources/Textures/Interface/Default/item_status_right.png new file mode 100644 index 0000000000000000000000000000000000000000..82ad44b48c168761ef47ec8cc0eeb0712d21896d GIT binary patch literal 258 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|_ISEDhFJ8T z4YuYwY``cHr@=qd}NXEIkO3Nfi2&aSKPPOS3SggF8+F8hM}hV)CE9)FnGH9xvXhu&Ios8rd`-NQ2pJ(S* z&*YH&@am|!k7H6!8>1?#u$xG9$5u&gkcRauSDs(7a;1OJngutGoj-rR;O{TjMSB|( z4;=58_q^DBmsLd4K~y{<;>JYz4yCwr|2;}syKy1sORcwdZ7@C7D)@^EYyajYJgQu&X%Q~loCIC@zS_}XH literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Minimalist/item_status_left.png b/Resources/Textures/Interface/Minimalist/item_status_left.png new file mode 100644 index 0000000000000000000000000000000000000000..d70eca2fe9250116a523d1554bad63c91be4507f GIT binary patch literal 208 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|x;$MRLo9le z6BY;<7#SECNJO(N0a9nwoN8a)6qbILVEo|!+-P^sS7Cvhj~TRZMlxDkTl<|kaNq#L z))ccVM`y78`SVj)y^XQNxucBHY&uY{!G;C98$ueA7|yUcG2C1fC?0T-)u(0Z19k)P zC976M3QwLn!BLBu*~EAub8$j8mG5BXW6>0#s~9|8{an^LB{Ts5 DA-P8w literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..b69872cd89cd60112ce57fdc37d24d6bd8607c89 GIT binary patch literal 277 zcmV+w0qXvVP)Px#&q+i*ld%fIFc5~nmOyqPyKmqih-;vWn~x&6JGd8o6gQVbS1CC34Z0V) zXK5W`BrT0ro%|t?9CzRS969(=5x`N?CE5n&tJ!E`RWxKd9CBOZBmfc(Sq^Eb;&3m; zWmmUA=R<_Mq@{|bl)-Xi44adRhY;c0HpYYvJT!##EK4ch9tUwV6Fy*y11Iac=>uNP zAk^q<2cv*Lc2E@!pgwY7YvB10_KUimTI}ZvuzQebNOawXmTA~I%k4WFOIohCz4+~V b?n1yjA3s#Y`m;oE00000NkvXXu0mjfQGaje literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Minimalist/item_status_right.png b/Resources/Textures/Interface/Minimalist/item_status_right.png new file mode 100644 index 0000000000000000000000000000000000000000..89171b9b4785dfc7fab47e93d4b7cb8099ff89b4 GIT binary patch literal 198 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|8a!PbLo9le z6BY;ZX7t#76a+L086 vlnJK}G^m;@F(-F3i2U5FZuZALoS8w3-P$<*$y*tqTNpfD{an^LB{Ts5uCqn4 literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..d1474cee1201ab72e913f9f65e73ef6d984e5633 GIT binary patch literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|Hha1_hFJ6_ zCoB*!Sj25&Y4oSOFeM?tK={zAhX+@d^MFA0H)De2@x zZc8p@+_{ZkUEE>Pat%+t2Gz`pLz>s>|Mo|mJ&_eDA)z37_~^vOgGWDpwU3#}arc?+ tZ^fFPI~_bdx=aC2&dT0EsDqgUj;g!!PC{xWt~$(69AAhVl4mw literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Nano/item_status_left.svg b/Resources/Textures/Interface/Nano/item_status_left.svg deleted file mode 100644 index c97f8de016..0000000000 --- a/Resources/Textures/Interface/Nano/item_status_left.svg +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - 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 index 5169343ea4fe9a88df5a21d069d3dec0c99b8654..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 453 zcmV;$0XqJPP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10a8gs zK~y-6&C0R2fMRCl+W*UCT*0?WK)YPn>Gj?O?3Ehe_j1`qiF-yZ}awhBZ5{-hSIlQ zp?Hu80)UuxMc8^}8rsoBW!Ep35j$TaAOH|NRv&M5CIy!Ojy*p(2Ts23Pi<{l4AZ3SYA&6!9h0=aV%?2Wt#-lZh5&%b zpzPXK+IBJ~fIAyjaptFekJd`JTa7`?dIG>6hqy@u4KakR7bbY5G~gwMMp05~V2;-Q v%Kw9`<@MYG%~}5sAmtbUU=9C5VrZ04gpil)hZ}Vi00000NkvXXu0mjfEQ!1@ diff --git a/Resources/Textures/Interface/Nano/item_status_middle.svg b/Resources/Textures/Interface/Nano/item_status_middle.svg deleted file mode 100644 index a913981db1..0000000000 --- a/Resources/Textures/Interface/Nano/item_status_middle.svg +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - 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 index dc1f84cea83c98404f6a032bcb659a2163e5069f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 458 zcmV;*0X6=KP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10ar;x zK~y-6&C)MV96=a>@n`myTp)!6(WC;0#bL;;xFrOEW1G~VP#6@dwi1Ox*YqPGN>WFP z=L--L4rtoFa(e{sW_M=Zw}O8HnjDMaNnSOXUnY~+w9-rf!_$sq@yiDXdneDFJt+c^ z5Y&}}@HoQ&%z6_aw$f~FWXStcdimM#aXQ1R%ipEK2Zf&tiyxDbpy#rKzU%+JC{dY; zM1Lgh)s~*cHzsz{gEMn;GOpJQ|qcSC~PL}qi^%`8)Gkq;CAPV zyko0!5Y(8p=4AdoO+USRF$AD0+V5TrD|_RCC5PXy1>Ip3-E-?*DGEt#=spQSp~fHm zueS%q0CC)mrr*zu*8;|x(E}7U8YXH?G+y(fe-)&;>sB*rcK`qY07*qoM6N<$f>@Hr AOaK4? diff --git a/Resources/Textures/Interface/Nano/item_status_right.svg b/Resources/Textures/Interface/Nano/item_status_right.svg deleted file mode 100644 index d898bb2ce0..0000000000 --- a/Resources/Textures/Interface/Nano/item_status_right.svg +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - 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 index 72f7e6de6b8775b85f3ef9e546634889061758c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 431 zcmV;g0Z{&lP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10X#`W zK~y-6&C{_?!!QsA;NOX*7MLo;fW*MS$ch93saCgcP2Yf#nSqC3XW#``7+^vI-lnAp zDT&K7dv zly=hYoiWfY5pkIgjMcM+NGTMF_yUYw`NBgc1MQ6Z5di@HY0VRs+-GSuF=?{B;a>UO)0iE7>u5L=_XOh=juzZ3!he5{(!08Vl} zbK~??6V;yPD+J)!SyRa5MXw~x697Q5!YHNCU*!Km8~`|P1g))|wO>O)7_uN-ul&Gu Z@&UC%j@96n?DPNt002ovPDHLkV1kb7wMhT~ diff --git a/Resources/Textures/Interface/Plasmafire/item_status_left.png b/Resources/Textures/Interface/Plasmafire/item_status_left.png new file mode 100644 index 0000000000000000000000000000000000000000..d5d25c980913a1e75aa4b62f8f071990a0fa7a61 GIT binary patch literal 271 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|PI|gHhFJ7o zooLP1Y#`urTx`n*k4zs%Ro+E?`~s@x>h2t>0XtZHJSQtjd<^D!X>@m;iGTi99)>1` zxIMMFbwo`{cY--qWalw|Uol2%oVro1Lo8IKvE?gI%#OFCFqU-hJ zVyDX{@u%k$SIi5@jpkhBA?T{nWh5>%iP2%!y)|reKF12PGwfQo`D?jv!$v&{GVau6{1-oD!MfSgZ>A?H@Yk1z@xBs(#Z7z>_M{8qa<3kzd!_H2#nniO(7x)~zzJC7U zqlR-@xaTA%RB#wSIo$5f^XkIR7ws+f%np2@@~}Jg_C9Z~VtKhOVhy|fbSLTNoaz40^!(8D*SsO6 z?aKtYEl&Gowf{9^V3D37u#6nTyO8n1mnO^TR-6z3@GWh{*yG*^C_uO)tE7 T!e*x^&{GVau6{1-oD!M<)CiX}Z5ZQj&$`i3A8-yJ!EW zVM3ZR8(XE=>lHF>&(+He?(F#~;JR6mx!J~0FCi%*MIqXlDkk~=2+7F`R?5X@lmr`&UH&g3m=G2@Zx#Ksq<&HL;fL! xIaV{4=;<~EzHs^9+<$=MnbMI~hO_IN%;V*)-g#dBwF&5a22WQ%mvv4FO#nX|WYYiu literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..fdd5a4fe7d6e3e13e934454eeb3af6b96a493b39 GIT binary patch literal 235 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|mU_B4hFJ6_ zrz8M@L1s#d0#k?WtWL#aCk`Aq@ZrC~;rb+=1f86eOaJz9US?pM?AFBg=l%M2>83YA z9o+Ap379k(#)uph;GVXZXOfs)fcTfa$`Wk<|GxgWP+BPCwAUWS9XyFjZpwG0rDxCh zx4!1=fruL%;>sr(auTjEu(64;v3YzESltLT<1#~uqZ7k4J_!krGd>!6nm^7TIdJ31 f0gs%pW2_7YC1#-~C*EcSI+(%J)z4*}Q$iB}l$2F# literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Retro/item_status_right.png b/Resources/Textures/Interface/Retro/item_status_right.png new file mode 100644 index 0000000000000000000000000000000000000000..5e7d54618d2c2e7535729f982b577f7aa2c32cea GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|R(iTPhFJ6_ ziwI9nN^xLb|Kw8t;_gJ&^jEqPTetQG^6WVEvV6|N`ni^YY;#gmQY!!cvRrl6;B1et zdZ%IYQ7f(Xhd(=IdDQ<|EY9N7Qa{Gcqr=m~v%|EwMmdA+@~^}DPhLB700>ypCoYt{ z${?ZOXk5nZ!P0!OK|V%AV#7M&L=M9ljg5`0>wW_5U{1d5xkJ%~NlZj;<4g{|SCSHv iK2rV@mT|I1Ffpt=&(LvdZ#|rr^*JNoY4pb8#@{W^A7y~SH*VhkQ>{vg1|{J z2TmkhI&tE|{=SF*S=<)owqPx#(n&-?R5*>*lCcWHFcd}aD}`oB9VCT@OyW042T|Po|JBmP(!nZ}#-S7k2h*Br zvFYgD^57oM`3v{=Lg2pNONR9Pt57|6hc03bQ+CRfN# zDYGFfwdQehe63Jb)z2~!VHt_0%nK%zY=n}?@>ItJP5`jkZzl~r&l@%I0idqyss5-5 zV6G5`p%b7pxn0WsJfSgZ>A?H@Yk1z@xBs(#Z7z>_M{8qaqvD3!kN6eOiZ9!^$!TWK`gr^H z?qmy2agI0JelQz9Io$5f^XkqW8K~95Csc?^|yu63@oLmKO+s=2rtifK!ufCtn_{dt8k+yGJSB@Oe OLkym-elF{r5}E*K|7O<)CiX}Z5ZQj&$`i3AAnY^wXO zwxqK5C8wC!>lHF>&(+He?(F#~;QIO3C1IT-I~qD0CpPAs)o=*BIL&aw?MM74r4k!r z&ibU>w#Z^AV?NQzIi