diff --git a/Content.Client/Disposal/Tagger/DisposalTaggerBoundUserInterface.cs b/Content.Client/Disposal/Tagger/DisposalTaggerBoundUserInterface.cs
index d5f7408df5..3a67718224 100644
--- a/Content.Client/Disposal/Tagger/DisposalTaggerBoundUserInterface.cs
+++ b/Content.Client/Disposal/Tagger/DisposalTaggerBoundUserInterface.cs
@@ -8,48 +8,36 @@ namespace Content.Client.Disposal.Tagger
/// Initializes a and updates it when new server messages are received.
///
[UsedImplicitly]
- public sealed class DisposalTaggerBoundUserInterface : BoundUserInterface
+ public sealed class DisposalTaggerBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
{
private DisposalTaggerWindow? _window;
- private const int TagLimit = 30;
-
- public DisposalTaggerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
- {
- }
+ public const int TagLimit = 30;
protected override void Open()
{
base.Open();
_window = this.CreateWindow();
+ _window.OnRouteChanged += NewRoute;
- _window.Confirm.OnPressed += _ => AcceptButtonPressed(_window.TagInput.Text);
- _window.TagInput.OnTextEntered += args => AcceptButtonPressed(args.Text);
-
- if (EntMan.TryGetComponent(Owner, out var tagger) &&
- tagger.Tag != string.Empty)
- {
- _window.TagInput.Text = tagger.Tag;
- }
+ Update();
}
- private void AcceptButtonPressed(string tag)
+ private void NewRoute(string route)
{
- SendMessage(new DisposalTaggerUiActionMessage(tag, TagLimit));
+ SendMessage(new DisposalTaggerUiActionMessage(route, TagLimit));
Close();
}
- protected override void UpdateState(BoundUserInterfaceState state)
+ public override void Update()
{
- base.UpdateState(state);
+ base.Update();
- if (state is not DisposalTaggerUserInterfaceState cast)
- {
+ if (_window == null || !EntMan.TryGetComponent(Owner, out var tagger))
return;
- }
- _window?.UpdateState(cast);
+ _window.Populate(tagger.Tag, tagger.Editable);
}
}
}
diff --git a/Content.Client/Disposal/Tagger/DisposalTaggerWindow.xaml b/Content.Client/Disposal/Tagger/DisposalTaggerWindow.xaml
index 68cb049184..5c524bf9c2 100644
--- a/Content.Client/Disposal/Tagger/DisposalTaggerWindow.xaml
+++ b/Content.Client/Disposal/Tagger/DisposalTaggerWindow.xaml
@@ -7,13 +7,10 @@
-
+
diff --git a/Content.Client/Disposal/Tagger/DisposalTaggerWindow.xaml.cs b/Content.Client/Disposal/Tagger/DisposalTaggerWindow.xaml.cs
index 3b715bbef8..670296c85a 100644
--- a/Content.Client/Disposal/Tagger/DisposalTaggerWindow.xaml.cs
+++ b/Content.Client/Disposal/Tagger/DisposalTaggerWindow.xaml.cs
@@ -14,20 +14,32 @@ public sealed partial class DisposalTaggerWindow : DefaultWindow
{
[Dependency] private IEntityManager _entManager = default!;
- private readonly SharedDisposalHolderSystem _disposalHolder;
+ public event Action? OnRouteChanged;
public DisposalTaggerWindow()
{
RobustXamlLoader.Load(this);
+
IoCManager.InjectDependencies(this);
- _disposalHolder = _entManager.System();
+ var disposalHolder = _entManager.System();
+ TagInput.IsValid = s => disposalHolder.TagIsValid(s) && s.Length <= DisposalTaggerBoundUserInterface.TagLimit;
- TagInput.IsValid = tag => _disposalHolder.TagIsValid(tag);
+ Confirm.OnPressed += _ => NewRoute(TagInput.Text);
+ TagInput.OnTextEntered += args => NewRoute(args.Text);
}
- public void UpdateState(DisposalTaggerUserInterfaceState state)
+ public void Populate(string route, bool editable)
{
- TagInput.Text = state.Tags;
+ TagInput.Text = route;
+ TagInput.Editable = editable;
+
+ Confirm.Disabled = !editable;
+ Confirm.Text = Loc.GetString(editable ? "generic-confirm" : "generic-disabled");
+ }
+
+ private void NewRoute(string route)
+ {
+ OnRouteChanged?.Invoke(route);
}
}
diff --git a/Content.Client/Disposal/Unit/DisposalUnitBoundUserInterface.cs b/Content.Client/Disposal/Unit/DisposalUnitBoundUserInterface.cs
index bcc9143d57..b37db6d15f 100644
--- a/Content.Client/Disposal/Unit/DisposalUnitBoundUserInterface.cs
+++ b/Content.Client/Disposal/Unit/DisposalUnitBoundUserInterface.cs
@@ -1,4 +1,6 @@
using Content.Client.Power.EntitySystems;
+using Content.Client.UserInterface.Controls;
+using Content.Shared.Access.Systems;
using Content.Shared.Disposal.Components;
using JetBrains.Annotations;
using Robust.Client.UserInterface;
@@ -6,13 +8,61 @@ using Robust.Client.UserInterface;
namespace Content.Client.Disposal.Unit
{
[UsedImplicitly]
- public sealed class DisposalUnitBoundUserInterface : BoundUserInterface
+ public sealed class DisposalUnitBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
{
[ViewVariables]
private DisposalUnitWindow? _disposalUnitWindow;
- public DisposalUnitBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+ protected override void Open()
{
+ base.Open();
+ _disposalUnitWindow = this.CreateWindow();
+ _disposalUnitWindow.OpenCenteredRight();
+ _disposalUnitWindow.SetInfoFromEntity(EntMan, Owner);
+
+ _disposalUnitWindow.OnTogglePower += ButtonPressed;
+ _disposalUnitWindow.OnEject += ButtonPressed;
+ _disposalUnitWindow.OnEngage += ButtonPressed;
+
+ _disposalUnitWindow.OnChangeRouting += OpenRoutingWindow;
+
+ Update();
+ }
+
+ public override void Update()
+ {
+ base.Update();
+
+ if (_disposalUnitWindow == null)
+ return;
+
+ // Update general fields
+ if (!EntMan.TryGetComponent(Owner, out var component))
+ return;
+
+ var disposalSystem = EntMan.System();
+ _disposalUnitWindow.Populate(
+ EntMan.System().IsPowered(Owner),
+ component.Engaged,
+ disposalSystem.GetState((Owner, component)),
+ disposalSystem.EstimatedFullPressure((Owner, component)),
+ component.PressurePerSecond);
+
+ // Update if routing is enabled or accessible
+ if (!EntMan.TryGetComponent(Owner, out var tagger))
+ {
+ _disposalUnitWindow.PopulateRouting(false, false);
+ return;
+ }
+
+ var hasAccess = false;
+ if (PlayerManager.LocalEntity != null)
+ {
+ var accessReader = EntMan.System();
+ hasAccess = accessReader.IsAllowed((EntityUid)PlayerManager.LocalEntity, Owner);
+ }
+
+ _disposalUnitWindow.PopulateRouting(tagger.Editable, hasAccess);
}
private void ButtonPressed(DisposalUnitUiButton button)
@@ -20,55 +70,9 @@ namespace Content.Client.Disposal.Unit
SendPredictedMessage(new DisposalUnitUiButtonPressedMessage(button));
}
- protected override void Open()
+ private void OpenRoutingWindow()
{
- base.Open();
-
- _disposalUnitWindow = this.CreateWindow();
- _disposalUnitWindow.OpenCenteredRight();
-
- _disposalUnitWindow.Eject.OnPressed += _ => ButtonPressed(DisposalUnitUiButton.Eject);
- _disposalUnitWindow.Engage.OnPressed += _ => ButtonPressed(DisposalUnitUiButton.Engage);
- _disposalUnitWindow.Power.OnPressed += _ => ButtonPressed(DisposalUnitUiButton.Power);
-
- if (EntMan.TryGetComponent(Owner, out DisposalUnitComponent? component))
- {
- Refresh((Owner, component));
- }
- }
-
- public override void Update()
- {
- base.Update();
-
- if (EntMan.TryGetComponent(Owner, out DisposalUnitComponent? component))
- {
- Refresh((Owner, component));
- }
- }
-
- public void Refresh(Entity entity)
- {
- if (_disposalUnitWindow == null)
- return;
-
- var name = EntMan.GetComponent(entity.Owner).EntityName;
- _disposalUnitWindow.Title = Loc.GetString("ui-disposal-unit-title", ("name", name));
-
- if (!EntMan.TryGetComponent(entity.Owner, out DisposalUnitComponent? disposals))
- return;
-
- var disposalUnit = EntMan.System();
- var disposalState = disposalUnit.GetState(entity);
- var fullPressure = disposalUnit.EstimatedFullPressure((Owner, disposals));
- var pressurePerSecond = disposals.PressurePerSecond;
-
- _disposalUnitWindow.UnitState.Text = Loc.GetString($"disposal-unit-state-{disposalState}");
- _disposalUnitWindow.FullPressure = disposalUnit.EstimatedFullPressure(entity);
- _disposalUnitWindow.PressurePerSecond = entity.Comp.PressurePerSecond;
- _disposalUnitWindow.PressureBar.UpdatePressure(fullPressure, pressurePerSecond);
- _disposalUnitWindow.Power.Pressed = EntMan.System().IsPowered(Owner);
- _disposalUnitWindow.Engage.Pressed = entity.Comp.Engaged;
+ SendPredictedMessage(new DisposalTaggerOpenUiMessage());
}
}
}
diff --git a/Content.Client/Disposal/Unit/DisposalUnitWindow.xaml b/Content.Client/Disposal/Unit/DisposalUnitWindow.xaml
index 92d003077b..51c327b7fa 100644
--- a/Content.Client/Disposal/Unit/DisposalUnitWindow.xaml
+++ b/Content.Client/Disposal/Unit/DisposalUnitWindow.xaml
@@ -1,21 +1,25 @@
+ SetSize="300 160"
+ Resizable="False">
-
+
+
+
-
+
diff --git a/Content.Client/Disposal/Unit/DisposalUnitWindow.xaml.cs b/Content.Client/Disposal/Unit/DisposalUnitWindow.xaml.cs
index e436a349e7..89bfcf5624 100644
--- a/Content.Client/Disposal/Unit/DisposalUnitWindow.xaml.cs
+++ b/Content.Client/Disposal/Unit/DisposalUnitWindow.xaml.cs
@@ -1,4 +1,5 @@
using Content.Client.UserInterface.Controls;
+using Content.Shared.Disposal.Components;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Timing;
@@ -11,18 +12,79 @@ namespace Content.Client.Disposal.Unit
[GenerateTypedNameReferences]
public sealed partial class DisposalUnitWindow : FancyWindow
{
- public TimeSpan FullPressure;
- public float PressurePerSecond;
+ // Values for managing the pressure bar
+ private TimeSpan _fullPressure;
+ private float _pressurePerSecond;
+
+ public event Action? OnTogglePower;
+ public event Action? OnEject;
+ public event Action? OnEngage;
+ public event Action? OnChangeRouting;
public DisposalUnitWindow()
{
RobustXamlLoader.Load(this);
+
+ Power.OnPressed += args => TogglePower(args.Button.Pressed);
+ Eject.OnPressed += _ => BeginEject();
+ Engage.OnPressed += _ => BeginEngage();
+ Routing.OnPressed += _ => ChangeRouting();
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
- PressureBar.UpdatePressure(FullPressure, PressurePerSecond);
+ PressureBar.UpdatePressure(_fullPressure, _pressurePerSecond);
+ }
+
+ public void Populate(bool powered, bool engaged, DisposalsPressureState disposalState, TimeSpan fullPressure, float pressurePerSecond)
+ {
+ ToggleUnitStateText(powered);
+ UnitState.Text = Loc.GetString($"disposal-unit-state-{disposalState}");
+
+ _fullPressure = fullPressure;
+ _pressurePerSecond = pressurePerSecond;
+
+ Power.Pressed = powered;
+ Engage.Pressed = engaged;
+ }
+
+ public void PopulateRouting(bool editable, bool accessible)
+ {
+ Routing.Visible = editable;
+
+ Routing.Disabled = !accessible;
+ Routing.ToolTip = accessible ? null : Loc.GetString("generic-no-access");
+ }
+
+ ///
+ /// This trick is used to hide power being unpredicted so the UI feels responsive.
+ ///
+ private void ToggleUnitStateText(bool powered)
+ {
+ UnitState.Visible = powered;
+ UnitStateUnpowered.Visible = !powered;
+ }
+
+ private void TogglePower(bool powered)
+ {
+ ToggleUnitStateText(powered);
+ OnTogglePower?.Invoke(DisposalUnitUiButton.Power);
+ }
+
+ private void BeginEject()
+ {
+ OnEject?.Invoke(DisposalUnitUiButton.Eject);
+ }
+
+ private void BeginEngage()
+ {
+ OnEngage?.Invoke(DisposalUnitUiButton.Engage);
+ }
+
+ private void ChangeRouting()
+ {
+ OnChangeRouting?.Invoke();
}
}
}
diff --git a/Content.Shared/Disposal/Tagger/DisposalTaggerComponent.cs b/Content.Shared/Disposal/Tagger/DisposalTaggerComponent.cs
index e2aacec45c..47fcae15d7 100644
--- a/Content.Shared/Disposal/Tagger/DisposalTaggerComponent.cs
+++ b/Content.Shared/Disposal/Tagger/DisposalTaggerComponent.cs
@@ -7,13 +7,14 @@ namespace Content.Shared.Disposal.Components;
///
/// Entities that pass through disposal tubes with this component can be marked with a tag.
+/// Entities flushed from a disposal unit with this component can be marked with a tag.
///
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
[Access(typeof(DisposalTaggerSystem))]
public sealed partial class DisposalTaggerComponent : Component
{
///
- /// Tag to apply to passing entities.
+ /// Tag to apply to passing or flushing entities.
///
[DataField, AutoNetworkedField]
public string Tag = string.Empty;
@@ -23,6 +24,12 @@ public sealed partial class DisposalTaggerComponent : Component
///
[DataField]
public SoundSpecifier ClickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg");
+
+ ///
+ /// If false editing the tag is disabled.
+ ///
+ [DataField, AutoNetworkedField]
+ public bool Editable = true;
}
///
@@ -40,18 +47,10 @@ public sealed class DisposalTaggerUiActionMessage : BoundUserInterfaceMessage
}
///
-/// Sends tag data to disposal tagger UIs.
+/// A message to open the disposal tagger UI sent from a separate UI.
///
[Serializable, NetSerializable]
-public sealed class DisposalTaggerUserInterfaceState : BoundUserInterfaceState
-{
- public readonly string Tags;
-
- public DisposalTaggerUserInterfaceState(string tags)
- {
- Tags = tags;
- }
-}
+public sealed class DisposalTaggerOpenUiMessage : BoundUserInterfaceMessage;
///
/// Key for opening disposal tagger UIs.
diff --git a/Content.Shared/Disposal/Tagger/DisposalTaggerSystem.cs b/Content.Shared/Disposal/Tagger/DisposalTaggerSystem.cs
index dbac93a01b..67cf13b027 100644
--- a/Content.Shared/Disposal/Tagger/DisposalTaggerSystem.cs
+++ b/Content.Shared/Disposal/Tagger/DisposalTaggerSystem.cs
@@ -1,3 +1,4 @@
+using Content.Shared.Access.Systems;
using Content.Shared.Disposal.Components;
using Content.Shared.Disposal.Holder;
using Content.Shared.Disposal.Tube;
@@ -12,14 +13,19 @@ namespace Content.Shared.Disposal.Tagger;
///
public sealed partial class DisposalTaggerSystem : EntitySystem
{
+ [Dependency] private AccessReaderSystem _accessReaderSystem = default!;
[Dependency] private SharedDisposalHolderSystem _disposalHolder = default!;
[Dependency] private SharedAudioSystem _audio = default!;
+ [Dependency] private SharedUserInterfaceSystem _uiSystem = default!;
public override void Initialize()
{
base.Initialize();
- SubscribeLocalEvent(OnGetTaggerNextDirection, after: new[] { typeof(DisposalTubeSystem) });
+ Subs.BuiEvents(DisposalUnitUiKey.Key, subs =>
+ {
+ subs.Event(OnOpenUiAction);
+ });
Subs.BuiEvents(DisposalTaggerUiKey.Key, subs =>
{
@@ -27,11 +33,24 @@ public sealed partial class DisposalTaggerSystem : EntitySystem
});
}
+ [SubscribeLocalEvent]
+ private void OnDisposalFlush(Entity ent, ref BeforeDisposalFlushEvent args)
+ {
+ args.Tags.Add(ent.Comp.Tag);
+ }
+
+ [SubscribeLocalEvent(after: [typeof(DisposalTubeSystem)])]
private void OnGetTaggerNextDirection(Entity ent, ref GetDisposalsNextDirectionEvent args)
{
_disposalHolder.AddTag(args.Holder, ent.Comp.Tag);
}
+ private void OnOpenUiAction(Entity ent, ref DisposalTaggerOpenUiMessage args)
+ {
+ if (_accessReaderSystem.IsAllowed(args.Actor, ent))
+ _uiSystem.TryToggleUi(ent.Owner, DisposalTaggerUiKey.Key, args.Actor);
+ }
+
///
/// Handles UI messages from the client. For things such as button presses
/// which interact with the world and require server action.
@@ -39,7 +58,7 @@ public sealed partial class DisposalTaggerSystem : EntitySystem
/// A user interface message from the client.
private void OnUiAction(Entity ent, ref DisposalTaggerUiActionMessage msg)
{
- if (!Exists(msg.Actor))
+ if (!Exists(msg.Actor) || !ent.Comp.Editable)
return;
// Check for correct message and ignore maleformed strings
diff --git a/Content.Shared/Disposal/Unit/DisposalUnitComponent.cs b/Content.Shared/Disposal/Unit/DisposalUnitComponent.cs
index 0098e0f8b0..331ea1ba49 100644
--- a/Content.Shared/Disposal/Unit/DisposalUnitComponent.cs
+++ b/Content.Shared/Disposal/Unit/DisposalUnitComponent.cs
@@ -188,12 +188,6 @@ public record DisposalSystemTransitionEvent;
[Serializable, NetSerializable]
public sealed partial class DisposalDoAfterEvent : SimpleDoAfterEvent;
-///
-/// Message sent from the server to the client to update the UI of disposal units.
-///
-[Serializable, NetSerializable]
-public sealed class DisposalUnitBoundUserInterfaceState : BoundUserInterfaceState;
-
///
/// Disposal unit pressure states.
///
diff --git a/Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.VisualsAndUi.cs b/Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.VisualsAndUi.cs
index 5d2bd5acd8..8e554a2249 100644
--- a/Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.VisualsAndUi.cs
+++ b/Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.VisualsAndUi.cs
@@ -44,12 +44,9 @@ public abstract partial class SharedDisposalUnitSystem
protected void UpdateUI(Entity entity)
{
- if (_timing.ApplyingState)
- return;
-
if (_ui.TryGetOpenUi(entity.Owner, DisposalUnitUiKey.Key, out var bui))
{
- bui.Update();
+ bui.Update();
}
}
diff --git a/Resources/Locale/en-US/disposal/tube/components/disposal-tagger-window.ftl b/Resources/Locale/en-US/disposal/tube/components/disposal-tagger-window.ftl
index 55523c4b95..9dbeaaa512 100644
--- a/Resources/Locale/en-US/disposal/tube/components/disposal-tagger-window.ftl
+++ b/Resources/Locale/en-US/disposal/tube/components/disposal-tagger-window.ftl
@@ -1,3 +1,2 @@
disposal-tagger-window-title = Disposal Tagger
disposal-tagger-window-tag-input-label = Tag:
-disposal-tagger-window-tag-confirm-button = Confirm
diff --git a/Resources/Locale/en-US/disposal/unit/components/disposal-unit-component.ftl b/Resources/Locale/en-US/disposal/unit/components/disposal-unit-component.ftl
index 5aec16e91d..41bdbd92c7 100644
--- a/Resources/Locale/en-US/disposal/unit/components/disposal-unit-component.ftl
+++ b/Resources/Locale/en-US/disposal/unit/components/disposal-unit-component.ftl
@@ -8,6 +8,7 @@ ui-disposal-unit-label-status = Ready
ui-disposal-unit-button-flush = Flush
ui-disposal-unit-button-eject = Eject Contents
+ui-disposal-unit-button-routing = Routing
ui-disposal-unit-button-power = Power
## Verbs
@@ -23,6 +24,7 @@ disposal-unit-state-Ready = Ready
# Yes I want it to always say Pressurizing
disposal-unit-state-Flushed = Pressurizing
disposal-unit-state-Pressurizing = Pressurizing
+disposal-unit-state-Unpowered = Unpowered
# putting people in
disposal-unit-being-inserted = {CAPITALIZE($user)} is trying to force you into a disposal chute!
diff --git a/Resources/Locale/en-US/generic.ftl b/Resources/Locale/en-US/generic.ftl
index c963f0e0fb..5d8d631c95 100644
--- a/Resources/Locale/en-US/generic.ftl
+++ b/Resources/Locale/en-US/generic.ftl
@@ -19,3 +19,6 @@ generic-seconds = seconds
generic-playtime-title = Playtime
generic-confirm = Confirm
+generic-disabled = Disabled
+
+generic-no-access = Insufficient access.
diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml
index 5ac6acd70a..2c80edb6c7 100644
--- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml
+++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml
@@ -519,3 +519,27 @@
True: { state: signal-router-flipped }
- type: Construction
node: signal_router_flipped
+
+# Specific
+
+- type: entity
+ parent: DisposalRouter
+ id: DisposalRouterWaste
+ suffix: Waste
+ components:
+ - type: DisposalRouter
+ tags:
+ - waste
+ - type: Flippable
+ mirrorEntity: DisposalRouterFlippedWaste
+
+- type: entity
+ parent: DisposalRouterFlipped
+ id: DisposalRouterFlippedWaste
+ suffix: Flipped, Waste
+ components:
+ - type: DisposalRouter
+ tags:
+ - waste
+ - type: Flippable
+ mirrorEntity: DisposalRouterWaste
diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml
index ecee7ae7d8..d968a5b0a2 100644
--- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml
+++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml
@@ -146,10 +146,16 @@
interfaces:
enum.DisposalUnitUiKey.Key:
type: DisposalUnitBoundUserInterface
+ enum.DisposalTaggerUiKey.Key:
+ type: DisposalTaggerBoundUserInterface
- type: Rummageable
table: !type:NestedSelector
tableId: RatKingLoot
- type: RequireProjectileTarget
+ - type: DisposalTagger
+ tag: waste
+ - type: AccessReader
+ access: [["Engineering"]] # Too easy to break disposals when the tag can be edited by anyone
- type: entity
id: MailingUnit