diff --git a/Content.Client/GameObjects/Components/Disposal/DisposalRouterBoundUserInterface.cs b/Content.Client/GameObjects/Components/Disposal/DisposalRouterBoundUserInterface.cs
new file mode 100644
index 0000000000..00f30c02ff
--- /dev/null
+++ b/Content.Client/GameObjects/Components/Disposal/DisposalRouterBoundUserInterface.cs
@@ -0,0 +1,69 @@
+#nullable enable
+using JetBrains.Annotations;
+using Robust.Client.GameObjects.Components.UserInterface;
+using Robust.Shared.GameObjects.Components.UserInterface;
+using Robust.Shared.Localization;
+using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalRouterComponent;
+
+namespace Content.Client.GameObjects.Components.Disposal
+{
+ ///
+ /// Initializes a and updates it when new server messages are received.
+ ///
+ [UsedImplicitly]
+ public class DisposalRouterBoundUserInterface : BoundUserInterface
+ {
+ private DisposalRouterWindow? _window;
+
+ public DisposalRouterBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
+ {
+ }
+
+ protected override void Open()
+ {
+ base.Open();
+
+ _window = new DisposalRouterWindow
+ {
+ Title = Loc.GetString("Disposal Router"),
+ };
+
+ _window.OpenCentered();
+ _window.OnClose += Close;
+
+ _window.Confirm.OnPressed += _ => ButtonPressed(UiAction.Ok, _window.TagInput.Text);
+
+ }
+
+ private void ButtonPressed(UiAction action, string tag)
+ {
+ SendMessage(new UiActionMessage(action, tag));
+ _window?.Close();
+ }
+
+ protected override void UpdateState(BoundUserInterfaceState state)
+ {
+ base.UpdateState(state);
+
+ if (!(state is DisposalRouterUserInterfaceState cast))
+ {
+ return;
+ }
+
+ _window?.UpdateState(cast);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+
+ if (disposing)
+ {
+ _window?.Dispose();
+ }
+ }
+
+
+ }
+
+}
diff --git a/Content.Client/GameObjects/Components/Disposal/DisposalRouterWindow.cs b/Content.Client/GameObjects/Components/Disposal/DisposalRouterWindow.cs
new file mode 100644
index 0000000000..f9f850ae18
--- /dev/null
+++ b/Content.Client/GameObjects/Components/Disposal/DisposalRouterWindow.cs
@@ -0,0 +1,56 @@
+using System.Runtime.CompilerServices;
+using System.Text.RegularExpressions;
+using Content.Shared.GameObjects.Components.Disposal;
+using Robust.Client.Graphics.Drawing;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Shared.Localization;
+using Robust.Shared.Maths;
+using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalRouterComponent;
+
+namespace Content.Client.GameObjects.Components.Disposal
+{
+ ///
+ /// Client-side UI used to control a
+ ///
+ public class DisposalRouterWindow : SS14Window
+ {
+ public readonly LineEdit TagInput;
+ public readonly Button Confirm;
+
+ protected override Vector2? CustomSize => (400, 80);
+
+ private Regex _tagRegex;
+
+ public DisposalRouterWindow()
+ {
+ _tagRegex = new Regex("^[a-zA-Z0-9, ]*$", RegexOptions.Compiled);
+
+ Contents.AddChild(new VBoxContainer
+ {
+ Children =
+ {
+ new Label {Text = Loc.GetString("Tags:")},
+ new Control {CustomMinimumSize = (0, 10)},
+ new HBoxContainer
+ {
+ Children =
+ {
+ (TagInput = new LineEdit {SizeFlagsHorizontal = SizeFlags.Expand, CustomMinimumSize = (320, 0),
+ ToolTip = Loc.GetString("A comma separated list of tags"), IsValid = tags => _tagRegex.IsMatch(tags)}),
+ new Control {CustomMinimumSize = (10, 0)},
+ (Confirm = new Button {Text = Loc.GetString("Confirm")})
+ }
+ }
+ }
+ });
+ }
+
+
+ public void UpdateState(DisposalRouterUserInterfaceState state)
+ {
+ TagInput.Text = state.Tags;
+ }
+ }
+}
diff --git a/Content.Client/GameObjects/Components/Disposal/DisposalTaggerBoundUserInterface.cs b/Content.Client/GameObjects/Components/Disposal/DisposalTaggerBoundUserInterface.cs
new file mode 100644
index 0000000000..273b8c06ec
--- /dev/null
+++ b/Content.Client/GameObjects/Components/Disposal/DisposalTaggerBoundUserInterface.cs
@@ -0,0 +1,69 @@
+#nullable enable
+using JetBrains.Annotations;
+using Robust.Client.GameObjects.Components.UserInterface;
+using Robust.Shared.GameObjects.Components.UserInterface;
+using Robust.Shared.Localization;
+using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalTaggerComponent;
+
+namespace Content.Client.GameObjects.Components.Disposal
+{
+ ///
+ /// Initializes a and updates it when new server messages are received.
+ ///
+ [UsedImplicitly]
+ public class DisposalTaggerBoundUserInterface : BoundUserInterface
+ {
+ private DisposalTaggerWindow? _window;
+
+ public DisposalTaggerBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
+ {
+ }
+
+ protected override void Open()
+ {
+ base.Open();
+
+ _window = new DisposalTaggerWindow
+ {
+ Title = Loc.GetString("Disposal Tagger"),
+ };
+
+ _window.OpenCentered();
+ _window.OnClose += Close;
+
+ _window.Confirm.OnPressed += _ => ButtonPressed(UiAction.Ok, _window.TagInput.Text);
+
+ }
+
+ private void ButtonPressed(UiAction action, string tag)
+ {
+ SendMessage(new UiActionMessage(action, tag));
+ _window?.Close();
+ }
+
+ protected override void UpdateState(BoundUserInterfaceState state)
+ {
+ base.UpdateState(state);
+
+ if (!(state is DisposalTaggerUserInterfaceState cast))
+ {
+ return;
+ }
+
+ _window?.UpdateState(cast);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+
+ if (disposing)
+ {
+ _window?.Dispose();
+ }
+ }
+
+
+ }
+
+}
diff --git a/Content.Client/GameObjects/Components/Disposal/DisposalTaggerWindow.cs b/Content.Client/GameObjects/Components/Disposal/DisposalTaggerWindow.cs
new file mode 100644
index 0000000000..7fb6dc6937
--- /dev/null
+++ b/Content.Client/GameObjects/Components/Disposal/DisposalTaggerWindow.cs
@@ -0,0 +1,56 @@
+using System.Runtime.CompilerServices;
+using System.Text.RegularExpressions;
+using Content.Shared.GameObjects.Components.Disposal;
+using Robust.Client.Graphics.Drawing;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Shared.Localization;
+using Robust.Shared.Maths;
+using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalTaggerComponent;
+
+namespace Content.Client.GameObjects.Components.Disposal
+{
+ ///
+ /// Client-side UI used to control a
+ ///
+ public class DisposalTaggerWindow : SS14Window
+ {
+ public readonly LineEdit TagInput;
+ public readonly Button Confirm;
+
+ protected override Vector2? CustomSize => (400, 80);
+
+ private Regex _tagRegex;
+
+ public DisposalTaggerWindow()
+ {
+ _tagRegex = new Regex("^[a-zA-Z0-9 ]*$", RegexOptions.Compiled);
+
+ Contents.AddChild(new VBoxContainer
+ {
+ Children =
+ {
+ new Label {Text = Loc.GetString("Tag:")},
+ new Control {CustomMinimumSize = (0, 10)},
+ new HBoxContainer
+ {
+ Children =
+ {
+ (TagInput = new LineEdit {SizeFlagsHorizontal = SizeFlags.Expand, CustomMinimumSize = (320, 0),
+ IsValid = tag => _tagRegex.IsMatch(tag)}),
+ new Control {CustomMinimumSize = (10, 0)},
+ (Confirm = new Button {Text = Loc.GetString("Confirm")})
+ }
+ }
+ }
+ });
+ }
+
+
+ public void UpdateState(DisposalTaggerUserInterfaceState state)
+ {
+ TagInput.Text = state.Tag;
+ }
+ }
+}
diff --git a/Content.Client/IgnoredComponents.cs b/Content.Client/IgnoredComponents.cs
index d4588e5c84..6e67d79995 100644
--- a/Content.Client/IgnoredComponents.cs
+++ b/Content.Client/IgnoredComponents.cs
@@ -142,6 +142,8 @@
"Listening",
"Radio",
"DisposalHolder",
+ "DisposalTagger",
+ "DisposalRouter",
"DisposalTransit",
"DisposalEntry",
"DisposalJunction",
diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalHolderComponent.cs b/Content.Server/GameObjects/Components/Disposal/DisposalHolderComponent.cs
index ee39a57d76..44c1a1eab3 100644
--- a/Content.Server/GameObjects/Components/Disposal/DisposalHolderComponent.cs
+++ b/Content.Server/GameObjects/Components/Disposal/DisposalHolderComponent.cs
@@ -1,4 +1,5 @@
#nullable enable
+using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Shared.GameObjects.Components.Body;
@@ -41,6 +42,12 @@ namespace Content.Server.GameObjects.Components.Disposal
[ViewVariables]
public IDisposalTubeComponent? NextTube { get; set; }
+ ///
+ /// A list of tags attached to the content, used for sorting
+ ///
+ [ViewVariables]
+ public HashSet Tags { get; set; } = new HashSet();
+
private bool CanInsert(IEntity entity)
{
if (!_contents.CanInsert(entity))
diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalRouterComponent.cs b/Content.Server/GameObjects/Components/Disposal/DisposalRouterComponent.cs
new file mode 100644
index 0000000000..1abf3a7b2d
--- /dev/null
+++ b/Content.Server/GameObjects/Components/Disposal/DisposalRouterComponent.cs
@@ -0,0 +1,179 @@
+using Content.Server.Interfaces;
+using Content.Server.Interfaces.GameObjects.Components.Items;
+using Content.Shared.GameObjects.EntitySystems;
+using Content.Shared.Interfaces.GameObjects.Components;
+using Robust.Server.GameObjects.Components.UserInterface;
+using Robust.Server.GameObjects.EntitySystems;
+using Robust.Server.Interfaces.GameObjects;
+using Robust.Shared.Audio;
+using Robust.Shared.GameObjects;
+using Robust.Shared.GameObjects.Components;
+using Robust.Shared.GameObjects.Systems;
+using Robust.Shared.Interfaces.GameObjects;
+using Robust.Shared.IoC;
+using Robust.Shared.Localization;
+using Robust.Shared.Maths;
+using Robust.Shared.ViewVariables;
+using System;
+using System.Collections.Generic;
+using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalRouterComponent;
+
+namespace Content.Server.GameObjects.Components.Disposal
+{
+ [RegisterComponent]
+ [ComponentReference(typeof(IActivate))]
+ [ComponentReference(typeof(IDisposalTubeComponent))]
+ public class DisposalRouterComponent : DisposalJunctionComponent, IActivate
+ {
+#pragma warning disable 649
+ [Dependency] private readonly IServerNotifyManager _notifyManager;
+#pragma warning restore 649
+ public override string Name => "DisposalRouter";
+
+ [ViewVariables]
+ private BoundUserInterface _userInterface;
+
+ [ViewVariables]
+ private HashSet _tags;
+
+ [ViewVariables]
+ public bool Anchored =>
+ !Owner.TryGetComponent(out CollidableComponent collidable) ||
+ collidable.Anchored;
+
+ public override Direction NextDirection(DisposalHolderComponent holder)
+ {
+ var directions = ConnectableDirections();
+
+ if (holder.Tags.Overlaps(_tags))
+ {
+ return directions[1];
+ }
+
+ return Owner.Transform.LocalRotation.GetDir();
+ }
+
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ _userInterface = Owner.GetComponent()
+ .GetBoundUserInterface(DisposalRouterUiKey.Key);
+ _userInterface.OnReceiveMessage += OnUiReceiveMessage;
+
+ _tags = new HashSet();
+
+ UpdateUserInterface();
+ }
+
+ ///
+ /// Handles ui messages from the client. For things such as button presses
+ /// which interact with the world and require server action.
+ ///
+ /// A user interface message from the client.
+ private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
+ {
+ var msg = (UiActionMessage) obj.Message;
+
+ if (!PlayerCanUseDisposalTagger(obj.Session.AttachedEntity))
+ return;
+
+ if (msg.Action == UiAction.Ok)
+ {
+ _tags.Clear();
+ foreach (var tag in msg.Tags.Split(',', StringSplitOptions.RemoveEmptyEntries))
+ {
+ _tags.Add(tag.Trim());
+ }
+ }
+
+ ClickSound();
+ }
+
+ ///
+ /// Checks whether the player entity is able to use the configuration interface of the pipe tagger.
+ ///
+ /// The player entity.
+ /// Returns true if the entity can use the configuration interface, and false if it cannot.
+ private bool PlayerCanUseDisposalTagger(IEntity playerEntity)
+ {
+ //Need player entity to check if they are still able to use the configuration interface
+ if (playerEntity == null)
+ return false;
+ if (!Anchored)
+ return false;
+ //Check if player can interact in their current state
+ if (!ActionBlockerSystem.CanInteract(playerEntity) || !ActionBlockerSystem.CanUse(playerEntity))
+ return false;
+
+ return true;
+ }
+
+ ///
+ /// Gets component data to be used to update the user interface client-side.
+ ///
+ /// Returns a
+ private DisposalRouterUserInterfaceState GetUserInterfaceState()
+ {
+ if(_tags == null || _tags.Count <= 0)
+ {
+ return new DisposalRouterUserInterfaceState("");
+ }
+
+ var taglist = new System.Text.StringBuilder();
+
+ foreach (var tag in _tags)
+ {
+ taglist.Append(tag);
+ taglist.Append(", ");
+ }
+
+ taglist.Remove(taglist.Length - 2, 2);
+
+ return new DisposalRouterUserInterfaceState(taglist.ToString());
+ }
+
+ private void UpdateUserInterface()
+ {
+ var state = GetUserInterfaceState();
+ _userInterface.SetState(state);
+ }
+
+ private void ClickSound()
+ {
+ EntitySystem.Get().PlayFromEntity("/Audio/Machines/machine_switch.ogg", Owner, AudioParams.Default.WithVolume(-2f));
+ }
+
+ ///
+ /// Called when you click the owner entity with an empty hand. Opens the UI client-side if possible.
+ ///
+ /// Data relevant to the event such as the actor which triggered it.
+ void IActivate.Activate(ActivateEventArgs args)
+ {
+ if (!args.User.TryGetComponent(out IActorComponent actor))
+ {
+ return;
+ }
+
+ if (!args.User.TryGetComponent(out IHandsComponent hands))
+ {
+ _notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
+ Loc.GetString("You have no hands."));
+ return;
+ }
+
+ var activeHandEntity = hands.GetActiveHand?.Owner;
+ if (activeHandEntity == null)
+ {
+ UpdateUserInterface();
+ _userInterface.Open(actor.playerSession);
+ }
+ }
+
+ public override void OnRemove()
+ {
+ _userInterface.CloseAll();
+ base.OnRemove();
+ }
+ }
+}
diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalTaggerComponent.cs b/Content.Server/GameObjects/Components/Disposal/DisposalTaggerComponent.cs
new file mode 100644
index 0000000000..f949e37090
--- /dev/null
+++ b/Content.Server/GameObjects/Components/Disposal/DisposalTaggerComponent.cs
@@ -0,0 +1,150 @@
+using Content.Server.Interfaces;
+using Content.Server.Interfaces.GameObjects.Components.Items;
+using Content.Shared.GameObjects.EntitySystems;
+using Content.Shared.Interfaces.GameObjects.Components;
+using Robust.Server.GameObjects.Components.UserInterface;
+using Robust.Server.GameObjects.EntitySystems;
+using Robust.Server.Interfaces.GameObjects;
+using Robust.Shared.Audio;
+using Robust.Shared.GameObjects;
+using Robust.Shared.GameObjects.Components;
+using Robust.Shared.GameObjects.Systems;
+using Robust.Shared.Interfaces.GameObjects;
+using Robust.Shared.IoC;
+using Robust.Shared.Localization;
+using Robust.Shared.Maths;
+using Robust.Shared.ViewVariables;
+using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalTaggerComponent;
+
+namespace Content.Server.GameObjects.Components.Disposal
+{
+ [RegisterComponent]
+ [ComponentReference(typeof(IActivate))]
+ [ComponentReference(typeof(IDisposalTubeComponent))]
+ public class DisposalTaggerComponent : DisposalTransitComponent, IActivate
+ {
+#pragma warning disable 649
+ [Dependency] private readonly IServerNotifyManager _notifyManager;
+#pragma warning restore 649
+ public override string Name => "DisposalTagger";
+
+ [ViewVariables]
+ private BoundUserInterface _userInterface;
+
+ [ViewVariables(VVAccess.ReadWrite)]
+ private string _tag = "";
+
+ [ViewVariables]
+ public bool Anchored =>
+ !Owner.TryGetComponent(out CollidableComponent collidable) ||
+ collidable.Anchored;
+
+ public override Direction NextDirection(DisposalHolderComponent holder)
+ {
+ holder.Tags.Add(_tag);
+ return base.NextDirection(holder);
+ }
+
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ _userInterface = Owner.GetComponent()
+ .GetBoundUserInterface(DisposalTaggerUiKey.Key);
+ _userInterface.OnReceiveMessage += OnUiReceiveMessage;
+
+ UpdateUserInterface();
+ }
+
+ ///
+ /// Handles ui messages from the client. For things such as button presses
+ /// which interact with the world and require server action.
+ ///
+ /// A user interface message from the client.
+ private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
+ {
+ var msg = (UiActionMessage) obj.Message;
+
+ if (!PlayerCanUseDisposalTagger(obj.Session.AttachedEntity))
+ return;
+
+ if (msg.Action == UiAction.Ok)
+ {
+ _tag = msg.Tag;
+ }
+
+ ClickSound();
+ }
+
+ ///
+ /// Checks whether the player entity is able to use the configuration interface of the pipe tagger.
+ ///
+ /// The player entity.
+ /// Returns true if the entity can use the configuration interface, and false if it cannot.
+ private bool PlayerCanUseDisposalTagger(IEntity playerEntity)
+ {
+ //Need player entity to check if they are still able to use the configuration interface
+ if (playerEntity == null)
+ return false;
+ if (!Anchored)
+ return false;
+ //Check if player can interact in their current state
+ if (!ActionBlockerSystem.CanInteract(playerEntity) || !ActionBlockerSystem.CanUse(playerEntity))
+ return false;
+
+ return true;
+ }
+
+ ///
+ /// Gets component data to be used to update the user interface client-side.
+ ///
+ /// Returns a
+ private DisposalTaggerUserInterfaceState GetUserInterfaceState()
+ {
+ return new DisposalTaggerUserInterfaceState(_tag);
+ }
+
+ private void UpdateUserInterface()
+ {
+ var state = GetUserInterfaceState();
+ _userInterface.SetState(state);
+ }
+
+ private void ClickSound()
+ {
+ EntitySystem.Get().PlayFromEntity("/Audio/Machines/machine_switch.ogg", Owner, AudioParams.Default.WithVolume(-2f));
+ }
+
+ ///
+ /// Called when you click the owner entity with an empty hand. Opens the UI client-side if possible.
+ ///
+ /// Data relevant to the event such as the actor which triggered it.
+ void IActivate.Activate(ActivateEventArgs args)
+ {
+ if (!args.User.TryGetComponent(out IActorComponent actor))
+ {
+ return;
+ }
+
+ if (!args.User.TryGetComponent(out IHandsComponent hands))
+ {
+ _notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
+ Loc.GetString("You have no hands."));
+ return;
+ }
+
+ var activeHandEntity = hands.GetActiveHand?.Owner;
+ if (activeHandEntity == null)
+ {
+ UpdateUserInterface();
+ _userInterface.Open(actor.playerSession);
+ }
+ }
+
+ public override void OnRemove()
+ {
+ base.OnRemove();
+ _userInterface.CloseAll();
+ }
+ }
+}
diff --git a/Content.Shared/GameObjects/Components/Disposal/SharedDisposalRouterComponent.cs b/Content.Shared/GameObjects/Components/Disposal/SharedDisposalRouterComponent.cs
new file mode 100644
index 0000000000..c0323cbaa1
--- /dev/null
+++ b/Content.Shared/GameObjects/Components/Disposal/SharedDisposalRouterComponent.cs
@@ -0,0 +1,52 @@
+using Robust.Shared.GameObjects;
+using Robust.Shared.GameObjects.Components.UserInterface;
+using Robust.Shared.Serialization;
+using System;
+
+namespace Content.Shared.GameObjects.Components.Disposal
+{
+ public class SharedDisposalRouterComponent : Component
+ {
+ public override string Name => "DisposalRouter";
+
+ [Serializable, NetSerializable]
+ public class DisposalRouterUserInterfaceState : BoundUserInterfaceState
+ {
+ public readonly string Tags;
+
+ public DisposalRouterUserInterfaceState(string tags)
+ {
+ Tags = tags;
+ }
+ }
+
+ [Serializable, NetSerializable]
+ public class UiActionMessage : BoundUserInterfaceMessage
+ {
+ public readonly UiAction Action;
+ public readonly string Tags = "";
+
+ public UiActionMessage(UiAction action, string tags)
+ {
+ Action = action;
+
+ if (Action == UiAction.Ok)
+ {
+ Tags = tags.Substring(0, Math.Min(tags.Length, 150));
+ }
+ }
+ }
+
+ [Serializable, NetSerializable]
+ public enum UiAction
+ {
+ Ok
+ }
+
+ [Serializable, NetSerializable]
+ public enum DisposalRouterUiKey
+ {
+ Key
+ }
+ }
+}
diff --git a/Content.Shared/GameObjects/Components/Disposal/SharedDisposalTaggerComponent.cs b/Content.Shared/GameObjects/Components/Disposal/SharedDisposalTaggerComponent.cs
new file mode 100644
index 0000000000..d12e9060e9
--- /dev/null
+++ b/Content.Shared/GameObjects/Components/Disposal/SharedDisposalTaggerComponent.cs
@@ -0,0 +1,53 @@
+#nullable enable
+using Robust.Shared.GameObjects;
+using Robust.Shared.GameObjects.Components.UserInterface;
+using Robust.Shared.Serialization;
+using System;
+
+namespace Content.Shared.GameObjects.Components.Disposal
+{
+ public class SharedDisposalTaggerComponent : Component
+ {
+ public override string Name => "DisposalTagger";
+
+ [Serializable, NetSerializable]
+ public class DisposalTaggerUserInterfaceState : BoundUserInterfaceState
+ {
+ public readonly string Tag;
+
+ public DisposalTaggerUserInterfaceState(string tag)
+ {
+ Tag = tag;
+ }
+ }
+
+ [Serializable, NetSerializable]
+ public class UiActionMessage : BoundUserInterfaceMessage
+ {
+ public readonly UiAction Action;
+ public readonly string Tag = "";
+
+ public UiActionMessage(UiAction action, string tag)
+ {
+ Action = action;
+
+ if (Action == UiAction.Ok)
+ {
+ Tag = tag;
+ }
+ }
+ }
+
+ [Serializable, NetSerializable]
+ public enum UiAction
+ {
+ Ok
+ }
+
+ [Serializable, NetSerializable]
+ public enum DisposalTaggerUiKey
+ {
+ Key
+ }
+ }
+}
diff --git a/Resources/Prototypes/Entities/Constructible/disposal.yml b/Resources/Prototypes/Entities/Constructible/disposal.yml
index 5187ce2a88..1d85dd33a6 100644
--- a/Resources/Prototypes/Entities/Constructible/disposal.yml
+++ b/Resources/Prototypes/Entities/Constructible/disposal.yml
@@ -44,6 +44,31 @@
state_anchored: pipe-s
state_broken: pipe-b
+- type: entity
+ id: DisposalTagger
+ parent: DisposalPipeBase
+ name: disposal pipe tagger
+ description: A pipe that tags entities for routing
+ components:
+ - type: Sprite
+ drawdepth: BelowFloor
+ sprite: Constructible/Power/disposal.rsi
+ state: conpipe-tagger
+ - type: Icon
+ sprite: Constructible/Power/disposal.rsi
+ state: conpipe-tagger
+ - type: DisposalTagger
+ - type: Appearance
+ visuals:
+ - type: DisposalVisualizer
+ state_free: conpipe-tagger
+ state_anchored: pipe-tagger
+ state_broken: pipe-b
+ - type: UserInterface
+ interfaces:
+ - key: enum.DisposalTaggerUiKey.Key
+ type: DisposalTaggerBoundUserInterface
+
- type: entity
id: DisposalTrunk
parent: DisposalPipeBase
@@ -131,6 +156,63 @@
- key: enum.DisposalUnitUiKey.Key
type: DisposalUnitBoundUserInterface
+- type: entity
+ id: DisposalRouter
+ parent: DisposalPipeBase
+ name: disposal router
+ description: A three-way router. Entities with matching tags get routed to the side
+ components:
+ - type: Sprite
+ drawdepth: BelowFloor
+ sprite: Constructible/Power/disposal.rsi
+ state: conpipe-j1s
+ - type: Icon
+ sprite: Constructible/Power/disposal.rsi
+ state: conpipe-j1s
+ - type: DisposalRouter
+ degrees:
+ - 0
+ - -90
+ - 180
+ - type: Appearance
+ visuals:
+ - type: DisposalVisualizer
+ state_free: conpipe-j1s
+ state_anchored: pipe-j1s
+ state_broken: pipe-b
+ - type: Flippable
+ entity: DisposalRouterFlipped
+ - type: UserInterface
+ interfaces:
+ - key: enum.DisposalRouterUiKey.Key
+ type: DisposalRouterBoundUserInterface
+
+- type: entity
+ id: DisposalRouterFlipped
+ parent: DisposalRouter
+ name: flipped router junction
+ components:
+ - type: Sprite
+ drawdepth: BelowFloor
+ sprite: Constructible/Power/disposal.rsi
+ state: conpipe-j2s
+ - type: Icon
+ sprite: Constructible/Power/disposal.rsi
+ state: conpipe-j2s
+ - type: DisposalRouter
+ degrees:
+ - 0
+ - 90
+ - 180
+ - type: Appearance
+ visuals:
+ - type: DisposalVisualizer
+ state_free: conpipe-j2s
+ state_anchored: pipe-j2s
+ state_broken: pipe-b
+ - type: Flippable
+ entity: DisposalRouter
+
- type: entity
id: DisposalJunction
parent: DisposalPipeBase
diff --git a/Resources/Textures/Constructible/Power/disposal.rsi/conpipe-tagger.png b/Resources/Textures/Constructible/Power/disposal.rsi/conpipe-tagger.png
new file mode 100644
index 0000000000..e1c4637244
Binary files /dev/null and b/Resources/Textures/Constructible/Power/disposal.rsi/conpipe-tagger.png differ
diff --git a/Resources/Textures/Constructible/Power/disposal.rsi/meta.json b/Resources/Textures/Constructible/Power/disposal.rsi/meta.json
index 96aadcfa66..f11b589795 100644
--- a/Resources/Textures/Constructible/Power/disposal.rsi/meta.json
+++ b/Resources/Textures/Constructible/Power/disposal.rsi/meta.json
@@ -1 +1,779 @@
-{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "https://github.com/discordia-space/CEV-Eris/blob/bbe32606902c90f5290b57d905a3f31b84dc6d7d/icons/obj/pipes/disposal.dmi and modified by DrSmugleaf", "states": [{"name": "condisposal", "directions": 1, "delays": [[1.0]]}, {"name": "conpipe-c", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-j1", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-j1s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-j2", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-j2s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-t", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-y", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "disposal", "directions": 1, "delays": [[1.0]]}, {"name": "disposal-charging", "directions": 1, "delays": [[1.0]]}, {"name": "disposal-flush", "directions": 1, "delays": [[0.1, 0.1, 0.1, 0.1, 0.1, 0.5, 0.1, 0.1, 0.1]]}, {"name": "dispover-charge", "directions": 1, "delays": [[0.4, 0.4]]}, {"name": "dispover-full", "directions": 1, "delays": [[0.2, 0.2]]}, {"name": "dispover-handle", "directions": 1, "delays": [[1.0]]}, {"name": "dispover-ready", "directions": 1, "delays": [[1.0]]}, {"name": "intake", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "intake-closing", "directions": 4, "delays": [[0.5, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.5, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.5, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.5, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]]}, {"name": "outlet", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "outlet-open", "directions": 4, "delays": [[0.5, 0.5, 0.5, 0.5, 0.5, 0.1, 1.5, 0.1], [0.5, 0.5, 0.5, 0.5, 0.5, 0.1, 1.5, 0.1], [0.5, 0.5, 0.5, 0.5, 0.5, 0.1, 1.5, 0.1], [0.5, 0.5, 0.5, 0.5, 0.5, 0.1, 1.5, 0.1]]}, {"name": "pipe-b", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-bf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-c", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-cf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-d", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j1", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j1f", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j1s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j1sf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j2", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j2f", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j2s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j2sf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-sf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-t", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-tagger", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-tagger-partial", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-tf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-u", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-y", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-yf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]}
\ No newline at end of file
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "license": "CC-BY-SA-3.0",
+ "copyright": "https://github.com/discordia-space/CEV-Eris/blob/bbe32606902c90f5290b57d905a3f31b84dc6d7d/icons/obj/pipes/disposal.dmi and modified by DrSmugleaf",
+ "states": [
+ {
+ "name": "condisposal",
+ "directions": 1,
+ "delays": [
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "conpipe-c",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "conpipe-j1",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "conpipe-j1s",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "conpipe-j2",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "conpipe-j2s",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "conpipe-s",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "conpipe-t",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "conpipe-tagger",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "conpipe-y",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "disposal",
+ "directions": 1,
+ "delays": [
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "disposal-charging",
+ "directions": 1,
+ "delays": [
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "disposal-flush",
+ "directions": 1,
+ "delays": [
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.5,
+ 0.1,
+ 0.1,
+ 0.1
+ ]
+ ]
+ },
+ {
+ "name": "dispover-charge",
+ "directions": 1,
+ "delays": [
+ [
+ 0.4,
+ 0.4
+ ]
+ ]
+ },
+ {
+ "name": "dispover-full",
+ "directions": 1,
+ "delays": [
+ [
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "dispover-handle",
+ "directions": 1,
+ "delays": [
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "dispover-ready",
+ "directions": 1,
+ "delays": [
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "intake",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "intake-closing",
+ "directions": 4,
+ "delays": [
+ [
+ 0.5,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ [
+ 0.5,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ [
+ 0.5,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ [
+ 0.5,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ]
+ ]
+ },
+ {
+ "name": "outlet",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "outlet-open",
+ "directions": 4,
+ "delays": [
+ [
+ 0.5,
+ 0.5,
+ 0.5,
+ 0.5,
+ 0.5,
+ 0.1,
+ 1.5,
+ 0.1
+ ],
+ [
+ 0.5,
+ 0.5,
+ 0.5,
+ 0.5,
+ 0.5,
+ 0.1,
+ 1.5,
+ 0.1
+ ],
+ [
+ 0.5,
+ 0.5,
+ 0.5,
+ 0.5,
+ 0.5,
+ 0.1,
+ 1.5,
+ 0.1
+ ],
+ [
+ 0.5,
+ 0.5,
+ 0.5,
+ 0.5,
+ 0.5,
+ 0.1,
+ 1.5,
+ 0.1
+ ]
+ ]
+ },
+ {
+ "name": "pipe-b",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-bf",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-c",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-cf",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-d",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-j1",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-j1f",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-j1s",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-j1sf",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-j2",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-j2f",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-j2s",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-j2sf",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-s",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-sf",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-t",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-tagger",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-tagger-partial",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-tf",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-u",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-y",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ },
+ {
+ "name": "pipe-yf",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ],
+ [
+ 1.0
+ ]
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/Constructible/Power/disposal.rsi/pipe-tagger.png b/Resources/Textures/Constructible/Power/disposal.rsi/pipe-tagger.png
index a8463fd0c1..ce48830a50 100644
Binary files a/Resources/Textures/Constructible/Power/disposal.rsi/pipe-tagger.png and b/Resources/Textures/Constructible/Power/disposal.rsi/pipe-tagger.png differ