diff --git a/Content.Client/Configurable/ConfigurationSystem.cs b/Content.Client/Configurable/ConfigurationSystem.cs new file mode 100644 index 0000000000..6594375ba2 --- /dev/null +++ b/Content.Client/Configurable/ConfigurationSystem.cs @@ -0,0 +1,25 @@ +using Content.Client.Configurable.UI; +using Content.Shared.Configurable; + +namespace Content.Client.Configurable; + +public sealed class ConfigurationSystem : SharedConfigurationSystem +{ + [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnConfigurationState); + } + + private void OnConfigurationState(Entity ent, ref AfterAutoHandleStateEvent args) + { + if (_uiSystem.TryGetOpenUi(ent.Owner, + ConfigurationComponent.ConfigurationUiKey.Key, + out var bui)) + { + bui.Refresh(ent); + } + } +} diff --git a/Content.Client/Configurable/UI/ConfigurationBoundUserInterface.cs b/Content.Client/Configurable/UI/ConfigurationBoundUserInterface.cs index e4966f1ec4..a3845ba1eb 100644 --- a/Content.Client/Configurable/UI/ConfigurationBoundUserInterface.cs +++ b/Content.Client/Configurable/UI/ConfigurationBoundUserInterface.cs @@ -1,6 +1,8 @@ -using System.Text.RegularExpressions; -using Robust.Client.GameObjects; +using System.Numerics; +using System.Text.RegularExpressions; +using Content.Shared.Configurable; using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; using static Content.Shared.Configurable.ConfigurationComponent; namespace Content.Client.Configurable.UI @@ -19,16 +21,53 @@ namespace Content.Client.Configurable.UI base.Open(); _menu = this.CreateWindow(); _menu.OnConfiguration += SendConfiguration; + if (EntMan.TryGetComponent(Owner, out ConfigurationComponent? component)) + Refresh((Owner, component)); } - protected override void UpdateState(BoundUserInterfaceState state) + public void Refresh(Entity entity) { - base.UpdateState(state); - - if (state is not ConfigurationBoundUserInterfaceState configurationState) + if (_menu == null) return; - _menu?.Populate(configurationState); + _menu.Column.Children.Clear(); + _menu.Inputs.Clear(); + + foreach (var field in entity.Comp.Config) + { + var label = new Label + { + Margin = new Thickness(0, 0, 8, 0), + Name = field.Key, + Text = field.Key + ":", + VerticalAlignment = Control.VAlignment.Center, + HorizontalExpand = true, + SizeFlagsStretchRatio = .2f, + MinSize = new Vector2(60, 0) + }; + + var input = new LineEdit + { + Name = field.Key + "-input", + Text = field.Value ?? "", + IsValid = _menu.Validate, + HorizontalExpand = true, + SizeFlagsStretchRatio = .8f + }; + + _menu.Inputs.Add((field.Key, input)); + + var row = new BoxContainer + { + Orientation = BoxContainer.LayoutOrientation.Horizontal + }; + + ConfigurationMenu.CopyProperties(_menu.Row, row); + + row.AddChild(label); + row.AddChild(input); + _menu.Column.AddChild(row); + } } protected override void ReceiveMessage(BoundUserInterfaceMessage message) diff --git a/Content.Client/Configurable/UI/ConfigurationMenu.cs b/Content.Client/Configurable/UI/ConfigurationMenu.cs index 29217eef7b..4ca68c5fa0 100644 --- a/Content.Client/Configurable/UI/ConfigurationMenu.cs +++ b/Content.Client/Configurable/UI/ConfigurationMenu.cs @@ -1,12 +1,8 @@ -using System.Collections.Generic; -using System.Numerics; +using System.Numerics; using System.Text.RegularExpressions; 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.Configurable.ConfigurationComponent; using static Robust.Client.UserInterface.Controls.BaseButton; using static Robust.Client.UserInterface.Controls.BoxContainer; @@ -14,10 +10,10 @@ namespace Content.Client.Configurable.UI { public sealed class ConfigurationMenu : DefaultWindow { - private readonly BoxContainer _column; - private readonly BoxContainer _row; + public readonly BoxContainer Column; + public readonly BoxContainer Row; - private readonly List<(string name, LineEdit input)> _inputs; + public readonly List<(string name, LineEdit input)> Inputs; [ViewVariables] public Regex? Validation { get; internal set; } @@ -28,7 +24,7 @@ namespace Content.Client.Configurable.UI { MinSize = SetSize = new Vector2(300, 250); - _inputs = new List<(string name, LineEdit input)>(); + Inputs = new List<(string name, LineEdit input)>(); Title = Loc.GetString("configuration-menu-device-title"); @@ -39,14 +35,14 @@ namespace Content.Client.Configurable.UI HorizontalExpand = true }; - _column = new BoxContainer + Column = new BoxContainer { Orientation = LayoutOrientation.Vertical, Margin = new Thickness(8), SeparationOverride = 16, }; - _row = new BoxContainer + Row = new BoxContainer { Orientation = LayoutOrientation.Horizontal, SeparationOverride = 16, @@ -69,61 +65,20 @@ namespace Content.Client.Configurable.UI ModulateSelfOverride = Color.FromHex("#202025") }; - outerColumn.AddChild(_column); + outerColumn.AddChild(Column); baseContainer.AddChild(outerColumn); baseContainer.AddChild(confirmButton); Contents.AddChild(baseContainer); } - public void Populate(ConfigurationBoundUserInterfaceState state) - { - _column.Children.Clear(); - _inputs.Clear(); - - foreach (var field in state.Config) - { - var label = new Label - { - Margin = new Thickness(0, 0, 8, 0), - Name = field.Key, - Text = field.Key + ":", - VerticalAlignment = VAlignment.Center, - HorizontalExpand = true, - SizeFlagsStretchRatio = .2f, - MinSize = new Vector2(60, 0) - }; - - var input = new LineEdit - { - Name = field.Key + "-input", - Text = field.Value ?? "", - IsValid = Validate, - HorizontalExpand = true, - SizeFlagsStretchRatio = .8f - }; - - _inputs.Add((field.Key, input)); - - var row = new BoxContainer - { - Orientation = LayoutOrientation.Horizontal - }; - CopyProperties(_row, row); - - row.AddChild(label); - row.AddChild(input); - _column.AddChild(row); - } - } - private void OnConfirm(ButtonEventArgs args) { - var config = GenerateDictionary(_inputs, "Text"); + var config = GenerateDictionary(Inputs, "Text"); OnConfiguration?.Invoke(config); Close(); } - private bool Validate(string value) + public bool Validate(string value) { return Validation?.IsMatch(value) != false; } @@ -140,7 +95,7 @@ namespace Content.Client.Configurable.UI return dictionary; } - private static void CopyProperties(T from, T to) where T : Control + public static void CopyProperties(T from, T to) where T : Control { foreach (var property in from.AllAttachedProperties) { diff --git a/Content.Client/Decals/DecalSystem.cs b/Content.Client/Decals/DecalSystem.cs index 41e5f39c28..172a06c4cd 100644 --- a/Content.Client/Decals/DecalSystem.cs +++ b/Content.Client/Decals/DecalSystem.cs @@ -13,7 +13,7 @@ namespace Content.Client.Decals [Dependency] private readonly IOverlayManager _overlayManager = default!; [Dependency] private readonly SpriteSystem _sprites = default!; - private DecalOverlay _overlay = default!; + private DecalOverlay? _overlay; private HashSet _removedUids = new(); private readonly List _removedChunks = new(); @@ -31,6 +31,9 @@ namespace Content.Client.Decals public void ToggleOverlay() { + if (_overlay == null) + return; + if (_overlayManager.HasOverlay()) { _overlayManager.RemoveOverlay(_overlay); @@ -44,6 +47,10 @@ namespace Content.Client.Decals public override void Shutdown() { base.Shutdown(); + + if (_overlay == null) + return; + _overlayManager.RemoveOverlay(_overlay); } diff --git a/Content.Client/DeviceNetwork/Systems/DeviceNetworkSystem.cs b/Content.Client/DeviceNetwork/Systems/DeviceNetworkSystem.cs new file mode 100644 index 0000000000..5b11b2bfe7 --- /dev/null +++ b/Content.Client/DeviceNetwork/Systems/DeviceNetworkSystem.cs @@ -0,0 +1,8 @@ +using Content.Shared.DeviceNetwork.Systems; + +namespace Content.Client.DeviceNetwork.Systems; + +public sealed class DeviceNetworkSystem : SharedDeviceNetworkSystem +{ + +} diff --git a/Content.Client/Disposal/DisposalUnitComponent.cs b/Content.Client/Disposal/DisposalUnitComponent.cs deleted file mode 100644 index e63a3fd45e..0000000000 --- a/Content.Client/Disposal/DisposalUnitComponent.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Content.Shared.Disposal.Components; - -namespace Content.Client.Disposal; - -[RegisterComponent] -public sealed partial class DisposalUnitComponent : SharedDisposalUnitComponent -{ - -} diff --git a/Content.Client/Disposal/Mailing/MailingUnitBoundUserInterface.cs b/Content.Client/Disposal/Mailing/MailingUnitBoundUserInterface.cs new file mode 100644 index 0000000000..013c4eaa1b --- /dev/null +++ b/Content.Client/Disposal/Mailing/MailingUnitBoundUserInterface.cs @@ -0,0 +1,79 @@ +using Content.Client.Disposal.Unit; +using Content.Client.Power.EntitySystems; +using Content.Shared.Disposal; +using Content.Shared.Disposal.Components; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; + +namespace Content.Client.Disposal.Mailing; + +public sealed class MailingUnitBoundUserInterface : BoundUserInterface +{ + [ViewVariables] + public MailingUnitWindow? MailingUnitWindow; + + public MailingUnitBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + private void ButtonPressed(DisposalUnitComponent.UiButton button) + { + SendMessage(new DisposalUnitComponent.UiButtonPressedMessage(button)); + // If we get client-side power stuff then we can predict the button presses but for now we won't as it stuffs + // the pressure lerp up. + } + + private void TargetSelected(ItemList.ItemListSelectedEventArgs args) + { + var item = args.ItemList[args.ItemIndex]; + SendMessage(new TargetSelectedMessage(item.Text)); + } + + protected override void Open() + { + base.Open(); + + MailingUnitWindow = this.CreateWindow(); + MailingUnitWindow.OpenCenteredRight(); + + MailingUnitWindow.Eject.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Eject); + MailingUnitWindow.Engage.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Engage); + MailingUnitWindow.Power.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Power); + + MailingUnitWindow.TargetListContainer.OnItemSelected += TargetSelected; + + if (EntMan.TryGetComponent(Owner, out MailingUnitComponent? component)) + Refresh((Owner, component)); + } + + public void Refresh(Entity entity) + { + if (MailingUnitWindow == null) + return; + + // TODO: This should be decoupled from disposals + if (EntMan.TryGetComponent(entity.Owner, out DisposalUnitComponent? disposals)) + { + var disposalSystem = EntMan.System(); + + var disposalState = disposalSystem.GetState(Owner, disposals); + var fullPressure = disposalSystem.EstimatedFullPressure(Owner, disposals); + + MailingUnitWindow.UnitState.Text = Loc.GetString($"disposal-unit-state-{disposalState}"); + MailingUnitWindow.FullPressure = fullPressure; + MailingUnitWindow.PressureBar.UpdatePressure(fullPressure); + MailingUnitWindow.Power.Pressed = EntMan.System().IsPowered(Owner); + MailingUnitWindow.Engage.Pressed = disposals.Engaged; + } + + MailingUnitWindow.Title = Loc.GetString("ui-mailing-unit-window-title", ("tag", entity.Comp.Tag ?? " ")); + //UnitTag.Text = state.Tag; + MailingUnitWindow.Target.Text = entity.Comp.Target; + + MailingUnitWindow.TargetListContainer.Clear(); + foreach (var target in entity.Comp.TargetList) + { + MailingUnitWindow.TargetListContainer.AddItem(target); + } + } +} diff --git a/Content.Client/Disposal/Mailing/MailingUnitSystem.cs b/Content.Client/Disposal/Mailing/MailingUnitSystem.cs new file mode 100644 index 0000000000..780656b4f0 --- /dev/null +++ b/Content.Client/Disposal/Mailing/MailingUnitSystem.cs @@ -0,0 +1,22 @@ +using Content.Shared.Disposal; +using Content.Shared.Disposal.Components; +using Content.Shared.Disposal.Mailing; + +namespace Content.Client.Disposal.Mailing; + +public sealed class MailingUnitSystem : SharedMailingUnitSystem +{ + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnMailingState); + } + + private void OnMailingState(Entity ent, ref AfterAutoHandleStateEvent args) + { + if (UserInterfaceSystem.TryGetOpenUi(ent.Owner, MailingUnitUiKey.Key, out var bui)) + { + bui.Refresh(ent); + } + } +} diff --git a/Content.Client/Disposal/UI/MailingUnitWindow.xaml b/Content.Client/Disposal/Mailing/MailingUnitWindow.xaml similarity index 80% rename from Content.Client/Disposal/UI/MailingUnitWindow.xaml rename to Content.Client/Disposal/Mailing/MailingUnitWindow.xaml index c57ca7b4c1..0acd300895 100644 --- a/Content.Client/Disposal/UI/MailingUnitWindow.xaml +++ b/Content.Client/Disposal/Mailing/MailingUnitWindow.xaml @@ -1,12 +1,14 @@ - - + diff --git a/Content.Client/Disposal/Mailing/MailingUnitWindow.xaml.cs b/Content.Client/Disposal/Mailing/MailingUnitWindow.xaml.cs new file mode 100644 index 0000000000..8f1dcaf7dc --- /dev/null +++ b/Content.Client/Disposal/Mailing/MailingUnitWindow.xaml.cs @@ -0,0 +1,27 @@ +using Content.Client.UserInterface.Controls; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Timing; + +namespace Content.Client.Disposal.Mailing +{ + /// + /// Client-side UI used to control a + /// + [GenerateTypedNameReferences] + public sealed partial class MailingUnitWindow : FancyWindow + { + public TimeSpan FullPressure; + + public MailingUnitWindow() + { + RobustXamlLoader.Load(this); + } + + protected override void FrameUpdate(FrameEventArgs args) + { + base.FrameUpdate(args); + PressureBar.UpdatePressure(FullPressure); + } + } +} diff --git a/Content.Client/Disposal/UI/PressureBar.cs b/Content.Client/Disposal/PressureBar.cs similarity index 96% rename from Content.Client/Disposal/UI/PressureBar.cs rename to Content.Client/Disposal/PressureBar.cs index bff95ca430..7b2ebacaf7 100644 --- a/Content.Client/Disposal/UI/PressureBar.cs +++ b/Content.Client/Disposal/PressureBar.cs @@ -1,9 +1,10 @@ using Content.Shared.Disposal; +using Content.Shared.Disposal.Unit; using Robust.Client.Graphics; using Robust.Client.UserInterface.Controls; using Robust.Shared.Timing; -namespace Content.Client.Disposal.UI; +namespace Content.Client.Disposal; public sealed class PressureBar : ProgressBar { diff --git a/Content.Client/Disposal/Systems/DisposalUnitSystem.cs b/Content.Client/Disposal/Systems/DisposalUnitSystem.cs deleted file mode 100644 index da548c1e54..0000000000 --- a/Content.Client/Disposal/Systems/DisposalUnitSystem.cs +++ /dev/null @@ -1,187 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Content.Shared.Disposal; -using Content.Shared.Disposal.Components; -using Content.Shared.DragDrop; -using Content.Shared.Emag.Systems; -using Robust.Client.GameObjects; -using Robust.Client.Animations; -using Robust.Client.Graphics; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.GameStates; -using Robust.Shared.Physics.Events; -using static Content.Shared.Disposal.Components.SharedDisposalUnitComponent; - -namespace Content.Client.Disposal.Systems; - -public sealed class DisposalUnitSystem : SharedDisposalUnitSystem -{ - [Dependency] private readonly AppearanceSystem _appearanceSystem = default!; - [Dependency] private readonly AnimationPlayerSystem _animationSystem = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; - - private const string AnimationKey = "disposal_unit_animation"; - - private const string DefaultFlushState = "disposal-flush"; - private const string DefaultChargeState = "disposal-charging"; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnHandleState); - SubscribeLocalEvent(OnPreventCollide); - SubscribeLocalEvent(OnCanDragDropOn); - SubscribeLocalEvent(OnEmagged); - - SubscribeLocalEvent(OnComponentInit); - SubscribeLocalEvent(OnAppearanceChange); - } - - private void OnHandleState(EntityUid uid, DisposalUnitComponent component, ref ComponentHandleState args) - { - if (args.Current is not DisposalUnitComponentState state) - return; - - component.FlushSound = state.FlushSound; - component.State = state.State; - component.NextPressurized = state.NextPressurized; - component.AutomaticEngageTime = state.AutomaticEngageTime; - component.NextFlush = state.NextFlush; - component.Powered = state.Powered; - component.Engaged = state.Engaged; - component.RecentlyEjected.Clear(); - component.RecentlyEjected.AddRange(EnsureEntityList(state.RecentlyEjected, uid)); - } - - public override bool HasDisposals(EntityUid? uid) - { - return HasComp(uid); - } - - public override bool ResolveDisposals(EntityUid uid, [NotNullWhen(true)] ref SharedDisposalUnitComponent? component) - { - if (component != null) - return true; - - TryComp(uid, out var storage); - component = storage; - return component != null; - } - - public override void DoInsertDisposalUnit(EntityUid uid, EntityUid toInsert, EntityUid user, SharedDisposalUnitComponent? disposal = null) - { - return; - } - - private void OnComponentInit(EntityUid uid, SharedDisposalUnitComponent sharedDisposalUnit, ComponentInit args) - { - if (!TryComp(uid, out var sprite) || !TryComp(uid, out var appearance)) - return; - - UpdateState(uid, sharedDisposalUnit, sprite, appearance); - } - - private void OnAppearanceChange(EntityUid uid, SharedDisposalUnitComponent unit, ref AppearanceChangeEvent args) - { - if (args.Sprite == null) - return; - - UpdateState(uid, unit, args.Sprite, args.Component); - } - - /// - /// Update visuals and tick animation - /// - private void UpdateState(EntityUid uid, SharedDisposalUnitComponent unit, SpriteComponent sprite, AppearanceComponent appearance) - { - if (!_appearanceSystem.TryGetData(uid, Visuals.VisualState, out var state, appearance)) - return; - - sprite.LayerSetVisible(DisposalUnitVisualLayers.Unanchored, state == VisualState.UnAnchored); - sprite.LayerSetVisible(DisposalUnitVisualLayers.Base, state == VisualState.Anchored); - sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFlush, state is VisualState.OverlayFlushing or VisualState.OverlayCharging); - - var chargingState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.BaseCharging, out var chargingLayer) - ? sprite.LayerGetState(chargingLayer) - : new RSI.StateId(DefaultChargeState); - - // This is a transient state so not too worried about replaying in range. - if (state == VisualState.OverlayFlushing) - { - if (!_animationSystem.HasRunningAnimation(uid, AnimationKey)) - { - var flushState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.OverlayFlush, out var flushLayer) - ? sprite.LayerGetState(flushLayer) - : new RSI.StateId(DefaultFlushState); - - // Setup the flush animation to play - var anim = new Animation - { - Length = unit.FlushDelay, - AnimationTracks = - { - new AnimationTrackSpriteFlick - { - LayerKey = DisposalUnitVisualLayers.OverlayFlush, - KeyFrames = - { - // Play the flush animation - new AnimationTrackSpriteFlick.KeyFrame(flushState, 0), - // Return to base state (though, depending on how the unit is - // configured we might get an appearance change event telling - // us to go to charging state) - new AnimationTrackSpriteFlick.KeyFrame(chargingState, (float) unit.FlushDelay.TotalSeconds) - } - }, - } - }; - - if (unit.FlushSound != null) - { - anim.AnimationTracks.Add( - new AnimationTrackPlaySound - { - KeyFrames = - { - new AnimationTrackPlaySound.KeyFrame(_audioSystem.ResolveSound(unit.FlushSound), 0) - } - }); - } - - _animationSystem.Play(uid, anim, AnimationKey); - } - } - else if (state == VisualState.OverlayCharging) - sprite.LayerSetState(DisposalUnitVisualLayers.OverlayFlush, chargingState); - else - _animationSystem.Stop(uid, AnimationKey); - - if (!_appearanceSystem.TryGetData(uid, Visuals.Handle, out var handleState, appearance)) - handleState = HandleState.Normal; - - sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayEngaged, handleState != HandleState.Normal); - - if (!_appearanceSystem.TryGetData(uid, Visuals.Light, out var lightState, appearance)) - lightState = LightStates.Off; - - sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayCharging, - (lightState & LightStates.Charging) != 0); - sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayReady, - (lightState & LightStates.Ready) != 0); - sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFull, - (lightState & LightStates.Full) != 0); - } -} - -public enum DisposalUnitVisualLayers : byte -{ - Unanchored, - Base, - BaseCharging, - OverlayFlush, - OverlayCharging, - OverlayReady, - OverlayFull, - OverlayEngaged -} diff --git a/Content.Client/Disposal/UI/DisposalRouterBoundUserInterface.cs b/Content.Client/Disposal/Tube/DisposalRouterBoundUserInterface.cs similarity index 95% rename from Content.Client/Disposal/UI/DisposalRouterBoundUserInterface.cs rename to Content.Client/Disposal/Tube/DisposalRouterBoundUserInterface.cs index 296e71d3a9..1116dc7257 100644 --- a/Content.Client/Disposal/UI/DisposalRouterBoundUserInterface.cs +++ b/Content.Client/Disposal/Tube/DisposalRouterBoundUserInterface.cs @@ -1,9 +1,8 @@ using JetBrains.Annotations; -using Robust.Client.GameObjects; using Robust.Client.UserInterface; using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent; -namespace Content.Client.Disposal.UI +namespace Content.Client.Disposal.Tube { /// /// Initializes a and updates it when new server messages are received. diff --git a/Content.Client/Disposal/UI/DisposalRouterWindow.xaml b/Content.Client/Disposal/Tube/DisposalRouterWindow.xaml similarity index 100% rename from Content.Client/Disposal/UI/DisposalRouterWindow.xaml rename to Content.Client/Disposal/Tube/DisposalRouterWindow.xaml diff --git a/Content.Client/Disposal/UI/DisposalRouterWindow.xaml.cs b/Content.Client/Disposal/Tube/DisposalRouterWindow.xaml.cs similarity index 90% rename from Content.Client/Disposal/UI/DisposalRouterWindow.xaml.cs rename to Content.Client/Disposal/Tube/DisposalRouterWindow.xaml.cs index 39ee9fbe23..c9d7d286eb 100644 --- a/Content.Client/Disposal/UI/DisposalRouterWindow.xaml.cs +++ b/Content.Client/Disposal/Tube/DisposalRouterWindow.xaml.cs @@ -1,11 +1,10 @@ using Content.Shared.Disposal.Components; using Robust.Client.AutoGenerated; -using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent; -namespace Content.Client.Disposal.UI +namespace Content.Client.Disposal.Tube { /// /// Client-side UI used to control a diff --git a/Content.Client/Disposal/UI/DisposalTaggerBoundUserInterface.cs b/Content.Client/Disposal/Tube/DisposalTaggerBoundUserInterface.cs similarity index 95% rename from Content.Client/Disposal/UI/DisposalTaggerBoundUserInterface.cs rename to Content.Client/Disposal/Tube/DisposalTaggerBoundUserInterface.cs index 7fc0eb8540..5618e83ecf 100644 --- a/Content.Client/Disposal/UI/DisposalTaggerBoundUserInterface.cs +++ b/Content.Client/Disposal/Tube/DisposalTaggerBoundUserInterface.cs @@ -1,9 +1,8 @@ using JetBrains.Annotations; -using Robust.Client.GameObjects; using Robust.Client.UserInterface; using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent; -namespace Content.Client.Disposal.UI +namespace Content.Client.Disposal.Tube { /// /// Initializes a and updates it when new server messages are received. diff --git a/Content.Client/Disposal/UI/DisposalTaggerWindow.xaml b/Content.Client/Disposal/Tube/DisposalTaggerWindow.xaml similarity index 100% rename from Content.Client/Disposal/UI/DisposalTaggerWindow.xaml rename to Content.Client/Disposal/Tube/DisposalTaggerWindow.xaml diff --git a/Content.Client/Disposal/UI/DisposalTaggerWindow.xaml.cs b/Content.Client/Disposal/Tube/DisposalTaggerWindow.xaml.cs similarity index 90% rename from Content.Client/Disposal/UI/DisposalTaggerWindow.xaml.cs rename to Content.Client/Disposal/Tube/DisposalTaggerWindow.xaml.cs index b49d5d997b..ae8286cf14 100644 --- a/Content.Client/Disposal/UI/DisposalTaggerWindow.xaml.cs +++ b/Content.Client/Disposal/Tube/DisposalTaggerWindow.xaml.cs @@ -1,11 +1,10 @@ using Content.Shared.Disposal.Components; using Robust.Client.AutoGenerated; -using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent; -namespace Content.Client.Disposal.UI +namespace Content.Client.Disposal.Tube { /// /// Client-side UI used to control a diff --git a/Content.Client/Disposal/Tube/DisposalTubeSystem.cs b/Content.Client/Disposal/Tube/DisposalTubeSystem.cs new file mode 100644 index 0000000000..fd86f7ec88 --- /dev/null +++ b/Content.Client/Disposal/Tube/DisposalTubeSystem.cs @@ -0,0 +1,8 @@ +using Content.Shared.Disposal.Unit; + +namespace Content.Client.Disposal.Tube; + +public sealed class DisposalTubeSystem : SharedDisposalTubeSystem +{ + +} diff --git a/Content.Client/Disposal/UI/DisposalUnitBoundUserInterface.cs b/Content.Client/Disposal/UI/DisposalUnitBoundUserInterface.cs deleted file mode 100644 index d2bec6e94f..0000000000 --- a/Content.Client/Disposal/UI/DisposalUnitBoundUserInterface.cs +++ /dev/null @@ -1,103 +0,0 @@ -using Content.Client.Disposal.Systems; -using Content.Shared.Disposal; -using Content.Shared.Disposal.Components; -using JetBrains.Annotations; -using Robust.Client.GameObjects; -using Robust.Client.UserInterface.Controls; -using static Content.Shared.Disposal.Components.SharedDisposalUnitComponent; - -namespace Content.Client.Disposal.UI -{ - /// - /// Initializes a or a and updates it when new server messages are received. - /// - [UsedImplicitly] - public sealed class DisposalUnitBoundUserInterface : BoundUserInterface - { - // What are you doing here - [ViewVariables] - public MailingUnitWindow? MailingUnitWindow; - - [ViewVariables] - public DisposalUnitWindow? DisposalUnitWindow; - - public DisposalUnitBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) - { - } - - private void ButtonPressed(UiButton button) - { - SendMessage(new UiButtonPressedMessage(button)); - // If we get client-side power stuff then we can predict the button presses but for now we won't as it stuffs - // the pressure lerp up. - } - - private void TargetSelected(ItemList.ItemListSelectedEventArgs args) - { - var item = args.ItemList[args.ItemIndex]; - SendMessage(new TargetSelectedMessage(item.Text)); - } - - protected override void Open() - { - base.Open(); - - if (UiKey is MailingUnitUiKey) - { - MailingUnitWindow = new MailingUnitWindow(); - - MailingUnitWindow.OpenCenteredRight(); - MailingUnitWindow.OnClose += Close; - - MailingUnitWindow.Eject.OnPressed += _ => ButtonPressed(UiButton.Eject); - MailingUnitWindow.Engage.OnPressed += _ => ButtonPressed(UiButton.Engage); - MailingUnitWindow.Power.OnPressed += _ => ButtonPressed(UiButton.Power); - - MailingUnitWindow.TargetListContainer.OnItemSelected += TargetSelected; - } - else if (UiKey is DisposalUnitUiKey) - { - DisposalUnitWindow = new DisposalUnitWindow(); - - DisposalUnitWindow.OpenCenteredRight(); - DisposalUnitWindow.OnClose += Close; - - DisposalUnitWindow.Eject.OnPressed += _ => ButtonPressed(UiButton.Eject); - DisposalUnitWindow.Engage.OnPressed += _ => ButtonPressed(UiButton.Engage); - DisposalUnitWindow.Power.OnPressed += _ => ButtonPressed(UiButton.Power); - } - } - - protected override void UpdateState(BoundUserInterfaceState state) - { - base.UpdateState(state); - - if (state is not MailingUnitBoundUserInterfaceState && state is not DisposalUnitBoundUserInterfaceState) - { - return; - } - - switch (state) - { - case MailingUnitBoundUserInterfaceState mailingUnitState: - MailingUnitWindow?.UpdateState(mailingUnitState); - break; - - case DisposalUnitBoundUserInterfaceState disposalUnitState: - DisposalUnitWindow?.UpdateState(disposalUnitState); - break; - } - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (!disposing) - return; - - MailingUnitWindow?.Dispose(); - DisposalUnitWindow?.Dispose(); - } - } -} diff --git a/Content.Client/Disposal/UI/DisposalUnitWindow.xaml.cs b/Content.Client/Disposal/UI/DisposalUnitWindow.xaml.cs deleted file mode 100644 index 3440fe208a..0000000000 --- a/Content.Client/Disposal/UI/DisposalUnitWindow.xaml.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Content.Shared.Disposal.Components; -using Robust.Client.AutoGenerated; -using Robust.Client.UserInterface.CustomControls; -using Robust.Client.UserInterface.XAML; -using Robust.Shared.Timing; -using static Content.Shared.Disposal.Components.SharedDisposalUnitComponent; - -namespace Content.Client.Disposal.UI -{ - /// - /// Client-side UI used to control a - /// - [GenerateTypedNameReferences] - public sealed partial class DisposalUnitWindow : DefaultWindow - { - public TimeSpan FullPressure; - - public DisposalUnitWindow() - { - IoCManager.InjectDependencies(this); - RobustXamlLoader.Load(this); - } - - /// - /// Update the interface state for the disposals window. - /// - /// true if we should stop updating every frame. - public void UpdateState(DisposalUnitBoundUserInterfaceState state) - { - Title = state.UnitName; - UnitState.Text = state.UnitState; - Power.Pressed = state.Powered; - Engage.Pressed = state.Engaged; - FullPressure = state.FullPressureTime; - } - - protected override void FrameUpdate(FrameEventArgs args) - { - base.FrameUpdate(args); - PressureBar.UpdatePressure(FullPressure); - } - } -} diff --git a/Content.Client/Disposal/UI/MailingUnitWindow.xaml.cs b/Content.Client/Disposal/UI/MailingUnitWindow.xaml.cs deleted file mode 100644 index 489d749a0c..0000000000 --- a/Content.Client/Disposal/UI/MailingUnitWindow.xaml.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Content.Shared.Disposal; -using Robust.Client.AutoGenerated; -using Robust.Client.UserInterface.CustomControls; -using Robust.Client.UserInterface.XAML; -using Robust.Shared.Timing; - -namespace Content.Client.Disposal.UI -{ - /// - /// Client-side UI used to control a - /// - [GenerateTypedNameReferences] - public sealed partial class MailingUnitWindow : DefaultWindow - { - public TimeSpan FullPressure; - - public MailingUnitWindow() - { - RobustXamlLoader.Load(this); - } - - /// - /// Update the interface state for the disposals window. - /// - /// true if we should stop updating every frame. - public bool UpdateState(MailingUnitBoundUserInterfaceState state) - { - var disposalState = state.DisposalState; - - Title = Loc.GetString("ui-mailing-unit-window-title", ("tag", state.Tag ?? " ")); - UnitState.Text = disposalState.UnitState; - FullPressure = disposalState.FullPressureTime; - var pressureReached = PressureBar.UpdatePressure(disposalState.FullPressureTime); - Power.Pressed = disposalState.Powered; - Engage.Pressed = disposalState.Engaged; - - //UnitTag.Text = state.Tag; - Target.Text = state.Target; - - TargetListContainer.Clear(); - foreach (var target in state.TargetList) - { - TargetListContainer.AddItem(target); - } - - return !disposalState.Powered || pressureReached; - } - - protected override void FrameUpdate(FrameEventArgs args) - { - base.FrameUpdate(args); - PressureBar.UpdatePressure(FullPressure); - } - } -} diff --git a/Content.Client/Disposal/Unit/DisposalUnitBoundUserInterface.cs b/Content.Client/Disposal/Unit/DisposalUnitBoundUserInterface.cs new file mode 100644 index 0000000000..62386291a4 --- /dev/null +++ b/Content.Client/Disposal/Unit/DisposalUnitBoundUserInterface.cs @@ -0,0 +1,63 @@ +using Content.Client.Disposal.Mailing; +using Content.Client.Power.EntitySystems; +using Content.Shared.Disposal.Components; +using JetBrains.Annotations; +using Robust.Client.UserInterface; + +namespace Content.Client.Disposal.Unit +{ + /// + /// Initializes a or a and updates it when new server messages are received. + /// + [UsedImplicitly] + public sealed class DisposalUnitBoundUserInterface : BoundUserInterface + { + [ViewVariables] private DisposalUnitWindow? _disposalUnitWindow; + + public DisposalUnitBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + private void ButtonPressed(DisposalUnitComponent.UiButton button) + { + SendPredictedMessage(new DisposalUnitComponent.UiButtonPressedMessage(button)); + // If we get client-side power stuff then we can predict the button presses but for now we won't as it stuffs + // the pressure lerp up. + } + + protected override void Open() + { + base.Open(); + + _disposalUnitWindow = this.CreateWindow(); + + _disposalUnitWindow.OpenCenteredRight(); + + _disposalUnitWindow.Eject.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Eject); + _disposalUnitWindow.Engage.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Engage); + _disposalUnitWindow.Power.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Power); + + if (EntMan.TryGetComponent(Owner, out DisposalUnitComponent? component)) + { + Refresh((Owner, component)); + } + } + + public void Refresh(Entity entity) + { + if (_disposalUnitWindow == null) + return; + + var disposalSystem = EntMan.System(); + + _disposalUnitWindow.Title = EntMan.GetComponent(entity.Owner).EntityName; + + var state = disposalSystem.GetState(entity.Owner, entity.Comp); + + _disposalUnitWindow.UnitState.Text = Loc.GetString($"disposal-unit-state-{state}"); + _disposalUnitWindow.Power.Pressed = EntMan.System().IsPowered(Owner); + _disposalUnitWindow.Engage.Pressed = entity.Comp.Engaged; + _disposalUnitWindow.FullPressure = disposalSystem.EstimatedFullPressure(entity.Owner, entity.Comp); + } + } +} diff --git a/Content.Client/Disposal/Unit/DisposalUnitSystem.cs b/Content.Client/Disposal/Unit/DisposalUnitSystem.cs new file mode 100644 index 0000000000..30ca320a2a --- /dev/null +++ b/Content.Client/Disposal/Unit/DisposalUnitSystem.cs @@ -0,0 +1,156 @@ +using Content.Shared.Disposal.Components; +using Content.Shared.Disposal.Unit; +using Robust.Client.Animations; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Shared.Audio.Systems; + +namespace Content.Client.Disposal.Unit; + +public sealed class DisposalUnitSystem : SharedDisposalUnitSystem +{ + [Dependency] private readonly AppearanceSystem _appearanceSystem = default!; + [Dependency] private readonly AnimationPlayerSystem _animationSystem = default!; + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; + + private const string AnimationKey = "disposal_unit_animation"; + + private const string DefaultFlushState = "disposal-flush"; + private const string DefaultChargeState = "disposal-charging"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnHandleState); + + SubscribeLocalEvent(OnAppearanceChange); + } + + private void OnHandleState(EntityUid uid, DisposalUnitComponent component, ref AfterAutoHandleStateEvent args) + { + UpdateUI((uid, component)); + } + + protected override void UpdateUI(Entity entity) + { + if (_uiSystem.TryGetOpenUi(entity.Owner, DisposalUnitComponent.DisposalUnitUiKey.Key, out var bui)) + { + bui.Refresh(entity); + } + } + + protected override void OnDisposalInit(Entity ent, ref ComponentInit args) + { + base.OnDisposalInit(ent, ref args); + + if (!TryComp(ent, out var sprite) || !TryComp(ent, out var appearance)) + return; + + UpdateState(ent, sprite, appearance); + } + + private void OnAppearanceChange(Entity ent, ref AppearanceChangeEvent args) + { + if (args.Sprite == null) + return; + + UpdateState(ent, args.Sprite, args.Component); + } + + /// + /// Update visuals and tick animation + /// + private void UpdateState(Entity ent, SpriteComponent sprite, AppearanceComponent appearance) + { + if (!_appearanceSystem.TryGetData(ent, DisposalUnitComponent.Visuals.VisualState, out var state, appearance)) + return; + + sprite.LayerSetVisible(DisposalUnitVisualLayers.Unanchored, state == DisposalUnitComponent.VisualState.UnAnchored); + sprite.LayerSetVisible(DisposalUnitVisualLayers.Base, state == DisposalUnitComponent.VisualState.Anchored); + sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFlush, state is DisposalUnitComponent.VisualState.OverlayFlushing or DisposalUnitComponent.VisualState.OverlayCharging); + + var chargingState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.BaseCharging, out var chargingLayer) + ? sprite.LayerGetState(chargingLayer) + : new RSI.StateId(DefaultChargeState); + + // This is a transient state so not too worried about replaying in range. + if (state == DisposalUnitComponent.VisualState.OverlayFlushing) + { + if (!_animationSystem.HasRunningAnimation(ent, AnimationKey)) + { + var flushState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.OverlayFlush, out var flushLayer) + ? sprite.LayerGetState(flushLayer) + : new RSI.StateId(DefaultFlushState); + + // Setup the flush animation to play + var anim = new Animation + { + Length = ent.Comp.FlushDelay, + AnimationTracks = + { + new AnimationTrackSpriteFlick + { + LayerKey = DisposalUnitVisualLayers.OverlayFlush, + KeyFrames = + { + // Play the flush animation + new AnimationTrackSpriteFlick.KeyFrame(flushState, 0), + // Return to base state (though, depending on how the unit is + // configured we might get an appearance change event telling + // us to go to charging state) + new AnimationTrackSpriteFlick.KeyFrame(chargingState, (float) ent.Comp.FlushDelay.TotalSeconds) + } + }, + } + }; + + if (ent.Comp.FlushSound != null) + { + anim.AnimationTracks.Add( + new AnimationTrackPlaySound + { + KeyFrames = + { + new AnimationTrackPlaySound.KeyFrame(_audioSystem.ResolveSound(ent.Comp.FlushSound), 0) + } + }); + } + + _animationSystem.Play(ent, anim, AnimationKey); + } + } + else if (state == DisposalUnitComponent.VisualState.OverlayCharging) + sprite.LayerSetState(DisposalUnitVisualLayers.OverlayFlush, chargingState); + else + _animationSystem.Stop(ent.Owner, AnimationKey); + + if (!_appearanceSystem.TryGetData(ent, DisposalUnitComponent.Visuals.Handle, out var handleState, appearance)) + handleState = DisposalUnitComponent.HandleState.Normal; + + sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayEngaged, handleState != DisposalUnitComponent.HandleState.Normal); + + if (!_appearanceSystem.TryGetData(ent, DisposalUnitComponent.Visuals.Light, out var lightState, appearance)) + lightState = DisposalUnitComponent.LightStates.Off; + + sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayCharging, + (lightState & DisposalUnitComponent.LightStates.Charging) != 0); + sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayReady, + (lightState & DisposalUnitComponent.LightStates.Ready) != 0); + sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFull, + (lightState & DisposalUnitComponent.LightStates.Full) != 0); + } +} + +public enum DisposalUnitVisualLayers : byte +{ + Unanchored, + Base, + BaseCharging, + OverlayFlush, + OverlayCharging, + OverlayReady, + OverlayFull, + OverlayEngaged +} diff --git a/Content.Client/Disposal/UI/DisposalUnitWindow.xaml b/Content.Client/Disposal/Unit/DisposalUnitWindow.xaml similarity index 72% rename from Content.Client/Disposal/UI/DisposalUnitWindow.xaml rename to Content.Client/Disposal/Unit/DisposalUnitWindow.xaml index c8acef98ea..60ca7ba0db 100644 --- a/Content.Client/Disposal/UI/DisposalUnitWindow.xaml +++ b/Content.Client/Disposal/Unit/DisposalUnitWindow.xaml @@ -1,20 +1,21 @@ - - + - + [UsedImplicitly] - public sealed class DeviceNetworkSystem : EntitySystem + public sealed class DeviceNetworkSystem : SharedDeviceNetworkSystem { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IPrototypeManager _protoMan = default!; @@ -60,16 +60,7 @@ namespace Content.Server.DeviceNetwork.Systems SwapQueues(); } - /// - /// Sends the given payload as a device network packet to the entity with the given address and frequency. - /// Addresses are given to the DeviceNetworkComponent of an entity when connecting. - /// - /// The EntityUid of the sending entity - /// The address of the entity that the packet gets sent to. If null, the message is broadcast to all devices on that frequency (except the sender) - /// The frequency to send on - /// The data to be sent - /// Returns true when the packet was successfully enqueued. - public bool QueuePacket(EntityUid uid, string? address, NetworkPayload data, uint? frequency = null, int? network = null, DeviceNetworkComponent? device = null) + public override bool QueuePacket(EntityUid uid, string? address, NetworkPayload data, uint? frequency = null, int? network = null, DeviceNetworkComponent? device = null) { if (!Resolve(uid, ref device, false)) return false; @@ -368,96 +359,4 @@ namespace Content.Server.DeviceNetwork.Systems } } } - - /// - /// Event raised before a device network packet is send. - /// Subscribed to by other systems to prevent the packet from being sent. - /// - public sealed class BeforePacketSentEvent : CancellableEntityEventArgs - { - /// - /// The EntityUid of the entity the packet was sent from. - /// - public readonly EntityUid Sender; - - public readonly TransformComponent SenderTransform; - - /// - /// The senders current position in world coordinates. - /// - public readonly Vector2 SenderPosition; - - /// - /// The network the packet will be sent to. - /// - public readonly string NetworkId; - - public BeforePacketSentEvent(EntityUid sender, TransformComponent xform, Vector2 senderPosition, string networkId) - { - Sender = sender; - SenderTransform = xform; - SenderPosition = senderPosition; - NetworkId = networkId; - } - } - - /// - /// Sent to the sending entity before broadcasting network packets to recipients - /// - public sealed class BeforeBroadcastAttemptEvent : CancellableEntityEventArgs - { - public readonly IReadOnlySet Recipients; - public HashSet? ModifiedRecipients; - - public BeforeBroadcastAttemptEvent(IReadOnlySet recipients) - { - Recipients = recipients; - } - } - - /// - /// Event raised when a device network packet gets sent. - /// - public sealed class DeviceNetworkPacketEvent : EntityEventArgs - { - /// - /// The id of the network that this packet is being sent on. - /// - public int NetId; - - /// - /// The frequency the packet is sent on. - /// - public readonly uint Frequency; - - /// - /// Address of the intended recipient. Null if the message was broadcast. - /// - public string? Address; - - /// - /// The device network address of the sending entity. - /// - public readonly string SenderAddress; - - /// - /// The entity that sent the packet. - /// - public EntityUid Sender; - - /// - /// The data that is being sent. - /// - public readonly NetworkPayload Data; - - public DeviceNetworkPacketEvent(int netId, string? address, uint frequency, string senderAddress, EntityUid sender, NetworkPayload data) - { - NetId = netId; - Address = address; - Frequency = frequency; - SenderAddress = senderAddress; - Sender = sender; - Data = data; - } - } } diff --git a/Content.Server/DeviceNetwork/Systems/Devices/ApcNetSwitchSystem.cs b/Content.Server/DeviceNetwork/Systems/Devices/ApcNetSwitchSystem.cs index 9a4a81a4c0..588020a963 100644 --- a/Content.Server/DeviceNetwork/Systems/Devices/ApcNetSwitchSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/Devices/ApcNetSwitchSystem.cs @@ -1,7 +1,8 @@ -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Components.Devices; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Interaction; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.DeviceNetwork.Systems.Devices { diff --git a/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs b/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs index 645d28f6d2..6bcbe30456 100644 --- a/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs @@ -1,7 +1,6 @@ using System.Linq; using Content.Server.Administration.Logs; using Content.Server.DeviceLinking.Systems; -using Content.Server.DeviceNetwork.Components; using Content.Shared.Access.Components; using Content.Shared.Access.Systems; using Content.Shared.Database; diff --git a/Content.Server/DeviceNetwork/Systems/SingletonDeviceNetServerSystem.cs b/Content.Server/DeviceNetwork/Systems/SingletonDeviceNetServerSystem.cs index 6c997828fa..8c1f48e93f 100644 --- a/Content.Server/DeviceNetwork/Systems/SingletonDeviceNetServerSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/SingletonDeviceNetServerSystem.cs @@ -1,9 +1,9 @@ using System.Diagnostics.CodeAnalysis; using Content.Server.DeviceNetwork.Components; using Content.Server.Medical.CrewMonitoring; -using Content.Server.Power.Components; using Content.Server.Station.Systems; using Content.Shared.Power; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.DeviceNetwork.Systems; diff --git a/Content.Server/DeviceNetwork/Systems/StationLimitedNetworkSystem.cs b/Content.Server/DeviceNetwork/Systems/StationLimitedNetworkSystem.cs index 675cacc4d7..cebe1a3b9d 100644 --- a/Content.Server/DeviceNetwork/Systems/StationLimitedNetworkSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/StationLimitedNetworkSystem.cs @@ -1,5 +1,6 @@ using Content.Server.DeviceNetwork.Components; using Content.Server.Station.Systems; +using Content.Shared.DeviceNetwork.Events; using JetBrains.Annotations; using Robust.Shared.Map; diff --git a/Content.Server/DeviceNetwork/Systems/WiredNetworkSystem.cs b/Content.Server/DeviceNetwork/Systems/WiredNetworkSystem.cs index 758a333c7a..54bd5256c7 100644 --- a/Content.Server/DeviceNetwork/Systems/WiredNetworkSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/WiredNetworkSystem.cs @@ -1,4 +1,5 @@ using Content.Server.DeviceNetwork.Components; +using Content.Shared.DeviceNetwork.Events; using JetBrains.Annotations; namespace Content.Server.DeviceNetwork.Systems diff --git a/Content.Server/DeviceNetwork/Systems/WirelessNetworkSystem.cs b/Content.Server/DeviceNetwork/Systems/WirelessNetworkSystem.cs index 1741afd4ce..8bca47e041 100644 --- a/Content.Server/DeviceNetwork/Systems/WirelessNetworkSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/WirelessNetworkSystem.cs @@ -1,4 +1,5 @@ using Content.Server.DeviceNetwork.Components; +using Content.Shared.DeviceNetwork.Events; using JetBrains.Annotations; namespace Content.Server.DeviceNetwork.Systems diff --git a/Content.Server/Disposal/Mailing/MailingUnitSystem.cs b/Content.Server/Disposal/Mailing/MailingUnitSystem.cs index 6249b9497d..6ee282a310 100644 --- a/Content.Server/Disposal/Mailing/MailingUnitSystem.cs +++ b/Content.Server/Disposal/Mailing/MailingUnitSystem.cs @@ -1,204 +1,8 @@ -using Content.Server.Configurable; -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; -using Content.Server.DeviceNetwork.Systems; -using Content.Server.Disposal.Unit.EntitySystems; -using Content.Server.Power.Components; -using Content.Shared.DeviceNetwork; -using Content.Shared.Disposal; -using Content.Shared.Interaction; -using Robust.Server.GameObjects; -using Robust.Shared.Player; -using Robust.Shared.Utility; +using Content.Shared.Disposal.Mailing; namespace Content.Server.Disposal.Mailing; -public sealed class MailingUnitSystem : EntitySystem +public sealed class MailingUnitSystem : SharedMailingUnitSystem { - [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!; - [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; - private const string MailTag = "mail"; - - private const string TagConfigurationKey = "tag"; - - private const string NetTag = "tag"; - private const string NetSrc = "src"; - private const string NetTarget = "target"; - private const string NetCmdSent = "mail_sent"; - private const string NetCmdRequest = "get_mailer_tag"; - private const string NetCmdResponse = "mailer_tag"; - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnComponentInit); - SubscribeLocalEvent(OnPacketReceived); - SubscribeLocalEvent(OnBeforeFlush); - SubscribeLocalEvent(OnConfigurationUpdated); - SubscribeLocalEvent(HandleActivate, before: new[] { typeof(DisposalUnitSystem) }); - SubscribeLocalEvent(OnDisposalUnitUIStateChange); - SubscribeLocalEvent(OnTargetSelected); - } - - - private void OnComponentInit(EntityUid uid, MailingUnitComponent component, ComponentInit args) - { - UpdateTargetList(uid, component); - } - - private void OnPacketReceived(EntityUid uid, MailingUnitComponent component, DeviceNetworkPacketEvent args) - { - if (!args.Data.TryGetValue(DeviceNetworkConstants.Command, out string? command) || !IsPowered(uid)) - return; - - switch (command) - { - case NetCmdRequest: - SendTagRequestResponse(uid, args, component.Tag); - break; - case NetCmdResponse when args.Data.TryGetValue(NetTag, out string? tag): - //Add the received tag request response to the list of targets - component.TargetList.Add(tag); - UpdateUserInterface(uid, component); - break; - } - } - - /// - /// Sends the given tag as a response to a if it's not null - /// - private void SendTagRequestResponse(EntityUid uid, DeviceNetworkPacketEvent args, string? tag) - { - if (tag == null) - return; - - var payload = new NetworkPayload - { - [DeviceNetworkConstants.Command] = NetCmdResponse, - [NetTag] = tag - }; - - _deviceNetworkSystem.QueuePacket(uid, args.Address, payload, args.Frequency); - } - - /// - /// Prevents the unit from flushing if no target is selected - /// - private void OnBeforeFlush(EntityUid uid, MailingUnitComponent component, BeforeDisposalFlushEvent args) - { - if (string.IsNullOrEmpty(component.Target)) - { - args.Cancel(); - return; - } - - args.Tags.Add(MailTag); - args.Tags.Add(component.Target); - - BroadcastSentMessage(uid, component); - } - - /// - /// Broadcast that a mail was sent including the src and target tags - /// - private void BroadcastSentMessage(EntityUid uid, MailingUnitComponent component, DeviceNetworkComponent? device = null) - { - if (string.IsNullOrEmpty(component.Tag) || string.IsNullOrEmpty(component.Target) || !Resolve(uid, ref device)) - return; - - var payload = new NetworkPayload - { - [DeviceNetworkConstants.Command] = NetCmdSent, - [NetSrc] = component.Tag, - [NetTarget] = component.Target - }; - - _deviceNetworkSystem.QueuePacket(uid, null, payload, null, null, device); - } - - /// - /// Clears the units target list and broadcasts a . - /// The target list will then get populated with responses from all active mailing units on the same grid - /// - private void UpdateTargetList(EntityUid uid, MailingUnitComponent component, DeviceNetworkComponent? device = null) - { - if (!Resolve(uid, ref device, false)) - return; - - var payload = new NetworkPayload - { - [DeviceNetworkConstants.Command] = NetCmdRequest - }; - - component.TargetList.Clear(); - _deviceNetworkSystem.QueuePacket(uid, null, payload, null, null, device); - } - - /// - /// Gets called when the units tag got updated - /// - private void OnConfigurationUpdated(EntityUid uid, MailingUnitComponent component, ConfigurationSystem.ConfigurationUpdatedEvent args) - { - var configuration = args.Configuration.Config; - if (!configuration.ContainsKey(TagConfigurationKey) || configuration[TagConfigurationKey] == string.Empty) - { - component.Tag = null; - return; - } - - component.Tag = configuration[TagConfigurationKey]; - UpdateUserInterface(uid, component); - } - - private void HandleActivate(EntityUid uid, MailingUnitComponent component, ActivateInWorldEvent args) - { - if (args.Handled || !args.Complex) - return; - - if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) - { - return; - } - - args.Handled = true; - UpdateTargetList(uid, component); - _userInterfaceSystem.OpenUi(uid, MailingUnitUiKey.Key, actor.PlayerSession); - } - - /// - /// Gets called when the disposal unit components ui state changes. This is required because the mailing unit requires a disposal unit component and overrides its ui - /// - private void OnDisposalUnitUIStateChange(EntityUid uid, MailingUnitComponent component, DisposalUnitUIStateUpdatedEvent args) - { - component.DisposalUnitInterfaceState = args.State; - UpdateUserInterface(uid, component); - } - - private void UpdateUserInterface(EntityUid uid, MailingUnitComponent component) - { - if (component.DisposalUnitInterfaceState == null) - return; - - var state = new MailingUnitBoundUserInterfaceState(component.DisposalUnitInterfaceState, component.Target, component.TargetList.ShallowClone(), component.Tag); - _userInterfaceSystem.SetUiState(uid, MailingUnitUiKey.Key, state); - } - - private void OnTargetSelected(EntityUid uid, MailingUnitComponent component, TargetSelectedMessage args) - { - component.Target = args.Target; - UpdateUserInterface(uid, component); - } - - /// - /// Checks if the unit is powered if an is present - /// - /// True if the power receiver component is powered or not present - private bool IsPowered(EntityUid uid, ApcPowerReceiverComponent? powerReceiver = null) - { - if (Resolve(uid, ref powerReceiver) && !powerReceiver.Powered) - return false; - - return true; - } } diff --git a/Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs deleted file mode 100644 index e4b8955c9c..0000000000 --- a/Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Content.Server.Disposal.Unit.EntitySystems; - -namespace Content.Server.Disposal.Tube.Components -{ - [RegisterComponent] - [Access(typeof(DisposalTubeSystem), typeof(DisposalUnitSystem))] - public sealed partial class DisposalEntryComponent : Component - { - public const string HolderPrototypeId = "DisposalHolder"; - } -} diff --git a/Content.Server/Disposal/Tube/Components/DisposalBendComponent.cs b/Content.Server/Disposal/Tube/DisposalBendComponent.cs similarity index 70% rename from Content.Server/Disposal/Tube/Components/DisposalBendComponent.cs rename to Content.Server/Disposal/Tube/DisposalBendComponent.cs index ace8a68462..7aa48f4bd3 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalBendComponent.cs +++ b/Content.Server/Disposal/Tube/DisposalBendComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Disposal.Tube.Components; +namespace Content.Server.Disposal.Tube; [RegisterComponent] [Access(typeof(DisposalTubeSystem))] diff --git a/Content.Server/Disposal/Tube/Components/DisposalJunctionComponent.cs b/Content.Server/Disposal/Tube/DisposalJunctionComponent.cs similarity index 84% rename from Content.Server/Disposal/Tube/Components/DisposalJunctionComponent.cs rename to Content.Server/Disposal/Tube/DisposalJunctionComponent.cs index d929dd16bf..af0d7eda68 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalJunctionComponent.cs +++ b/Content.Server/Disposal/Tube/DisposalJunctionComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Disposal.Tube.Components; +namespace Content.Server.Disposal.Tube; [RegisterComponent] [Access(typeof(DisposalTubeSystem))] diff --git a/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs b/Content.Server/Disposal/Tube/DisposalRouterComponent.cs similarity index 57% rename from Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs rename to Content.Server/Disposal/Tube/DisposalRouterComponent.cs index 18c0945cef..2446cf27f7 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs +++ b/Content.Server/Disposal/Tube/DisposalRouterComponent.cs @@ -1,12 +1,6 @@ -using Content.Server.UserInterface; -using Robust.Server.GameObjects; using Robust.Shared.Audio; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Components; -using Robust.Shared.Player; -using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent; -namespace Content.Server.Disposal.Tube.Components +namespace Content.Server.Disposal.Tube { [RegisterComponent] [Access(typeof(DisposalTubeSystem))] diff --git a/Content.Server/Disposal/Tube/Components/DisposalSignalRouterComponent.cs b/Content.Server/Disposal/Tube/DisposalSignalRouterComponent.cs similarity index 91% rename from Content.Server/Disposal/Tube/Components/DisposalSignalRouterComponent.cs rename to Content.Server/Disposal/Tube/DisposalSignalRouterComponent.cs index b4ef81d898..bee8293f58 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalSignalRouterComponent.cs +++ b/Content.Server/Disposal/Tube/DisposalSignalRouterComponent.cs @@ -1,8 +1,7 @@ -using Content.Server.Disposal.Tube.Systems; using Content.Shared.DeviceLinking; using Robust.Shared.Prototypes; -namespace Content.Server.Disposal.Tube.Components; +namespace Content.Server.Disposal.Tube; /// /// Requires to function. diff --git a/Content.Server/Disposal/Tube/Systems/DisposalSignalRouterSystem.cs b/Content.Server/Disposal/Tube/DisposalSignalRouterSystem.cs similarity index 94% rename from Content.Server/Disposal/Tube/Systems/DisposalSignalRouterSystem.cs rename to Content.Server/Disposal/Tube/DisposalSignalRouterSystem.cs index f1fdedb522..0d6c03b8c5 100644 --- a/Content.Server/Disposal/Tube/Systems/DisposalSignalRouterSystem.cs +++ b/Content.Server/Disposal/Tube/DisposalSignalRouterSystem.cs @@ -1,8 +1,7 @@ using Content.Server.DeviceLinking.Systems; -using Content.Server.Disposal.Tube.Components; using Content.Shared.DeviceLinking.Events; -namespace Content.Server.Disposal.Tube.Systems; +namespace Content.Server.Disposal.Tube; /// /// Handles signals and the routing get next direction event. diff --git a/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs b/Content.Server/Disposal/Tube/DisposalTaggerComponent.cs similarity index 53% rename from Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs rename to Content.Server/Disposal/Tube/DisposalTaggerComponent.cs index a291f4a941..3f072a80c7 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs +++ b/Content.Server/Disposal/Tube/DisposalTaggerComponent.cs @@ -1,13 +1,6 @@ -using Content.Server.Disposal.Unit.Components; -using Content.Server.UserInterface; -using Robust.Server.GameObjects; using Robust.Shared.Audio; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Components; -using Robust.Shared.Player; -using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent; -namespace Content.Server.Disposal.Tube.Components +namespace Content.Server.Disposal.Tube { [RegisterComponent] public sealed partial class DisposalTaggerComponent : DisposalTransitComponent diff --git a/Content.Server/Disposal/Tube/Components/DisposalTransitComponent.cs b/Content.Server/Disposal/Tube/DisposalTransitComponent.cs similarity index 82% rename from Content.Server/Disposal/Tube/Components/DisposalTransitComponent.cs rename to Content.Server/Disposal/Tube/DisposalTransitComponent.cs index e916327516..10bc7d5df7 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalTransitComponent.cs +++ b/Content.Server/Disposal/Tube/DisposalTransitComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Disposal.Tube.Components +namespace Content.Server.Disposal.Tube { // TODO: Different types of tubes eject in random direction with no exit point [RegisterComponent] diff --git a/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs b/Content.Server/Disposal/Tube/DisposalTubeComponent.cs similarity index 90% rename from Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs rename to Content.Server/Disposal/Tube/DisposalTubeComponent.cs index c16f1fcc22..15d02ad1ef 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs +++ b/Content.Server/Disposal/Tube/DisposalTubeComponent.cs @@ -1,9 +1,9 @@ -using Content.Server.Disposal.Unit.EntitySystems; +using Content.Server.Disposal.Unit; using Content.Shared.Damage; using Robust.Shared.Audio; using Robust.Shared.Containers; -namespace Content.Server.Disposal.Tube.Components; +namespace Content.Server.Disposal.Tube; [RegisterComponent] [Access(typeof(DisposalTubeSystem), typeof(DisposableSystem))] diff --git a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs index 20626a5eee..f1e094db20 100644 --- a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs +++ b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs @@ -2,12 +2,12 @@ using System.Linq; using System.Text; using Content.Server.Atmos.EntitySystems; using Content.Server.Construction.Completions; -using Content.Server.Disposal.Tube.Components; -using Content.Server.Disposal.Unit.Components; -using Content.Server.Disposal.Unit.EntitySystems; +using Content.Server.Disposal.Unit; using Content.Server.Popups; using Content.Shared.Destructible; using Content.Shared.Disposal.Components; +using Content.Shared.Disposal.Tube; +using Content.Shared.Disposal.Unit; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; @@ -16,12 +16,10 @@ using Robust.Shared.Map.Components; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Random; -using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent; -using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent; namespace Content.Server.Disposal.Tube { - public sealed class DisposalTubeSystem : EntitySystem + public sealed class DisposalTubeSystem : SharedDisposalTubeSystem { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; @@ -49,8 +47,8 @@ namespace Content.Server.Disposal.Tube SubscribeLocalEvent(OnGetBendConnectableDirections); SubscribeLocalEvent(OnGetBendNextDirection); - SubscribeLocalEvent(OnGetEntryConnectableDirections); - SubscribeLocalEvent(OnGetEntryNextDirection); + SubscribeLocalEvent(OnGetEntryConnectableDirections); + SubscribeLocalEvent(OnGetEntryNextDirection); SubscribeLocalEvent(OnGetJunctionConnectableDirections); SubscribeLocalEvent(OnGetJunctionNextDirection); @@ -64,13 +62,13 @@ namespace Content.Server.Disposal.Tube SubscribeLocalEvent(OnGetTaggerConnectableDirections); SubscribeLocalEvent(OnGetTaggerNextDirection); - Subs.BuiEvents(DisposalRouterUiKey.Key, subs => + Subs.BuiEvents(SharedDisposalRouterComponent.DisposalRouterUiKey.Key, subs => { subs.Event(OnOpenRouterUI); subs.Event(OnUiAction); }); - Subs.BuiEvents(DisposalTaggerUiKey.Key, subs => + Subs.BuiEvents(SharedDisposalTaggerComponent.DisposalTaggerUiKey.Key, subs => { subs.Event(OnOpenTaggerUI); subs.Event(OnUiAction); @@ -161,12 +159,12 @@ namespace Content.Server.Disposal.Tube args.Next = previousDF == ev.Connectable[0] ? ev.Connectable[1] : ev.Connectable[0]; } - private void OnGetEntryConnectableDirections(EntityUid uid, DisposalEntryComponent component, ref GetDisposalsConnectableDirectionsEvent args) + private void OnGetEntryConnectableDirections(EntityUid uid, Shared.Disposal.Tube.DisposalEntryComponent component, ref GetDisposalsConnectableDirectionsEvent args) { args.Connectable = new[] { Transform(uid).LocalRotation.GetDir() }; } - private void OnGetEntryNextDirection(EntityUid uid, DisposalEntryComponent component, ref GetDisposalsNextDirectionEvent args) + private void OnGetEntryNextDirection(EntityUid uid, Shared.Disposal.Tube.DisposalEntryComponent component, ref GetDisposalsNextDirectionEvent args) { // Ejects contents when they come from the same direction the entry is facing. if (args.Holder.PreviousDirectionFrom != Direction.Invalid) @@ -283,10 +281,10 @@ namespace Content.Server.Disposal.Tube private void OnOpenTaggerUI(EntityUid uid, DisposalTaggerComponent tagger, BoundUIOpenedEvent args) { - if (_uiSystem.HasUi(uid, DisposalTaggerUiKey.Key)) + if (_uiSystem.HasUi(uid, SharedDisposalTaggerComponent.DisposalTaggerUiKey.Key)) { - _uiSystem.SetUiState(uid, DisposalTaggerUiKey.Key, - new DisposalTaggerUserInterfaceState(tagger.Tag)); + _uiSystem.SetUiState(uid, SharedDisposalTaggerComponent.DisposalTaggerUiKey.Key, + new SharedDisposalTaggerComponent.DisposalTaggerUserInterfaceState(tagger.Tag)); } } @@ -298,7 +296,7 @@ namespace Content.Server.Disposal.Tube { if (router.Tags.Count <= 0) { - _uiSystem.SetUiState(uid, DisposalRouterUiKey.Key, new DisposalRouterUserInterfaceState("")); + _uiSystem.SetUiState(uid, SharedDisposalRouterComponent.DisposalRouterUiKey.Key, new SharedDisposalRouterComponent.DisposalRouterUserInterfaceState("")); return; } @@ -312,7 +310,7 @@ namespace Content.Server.Disposal.Tube taglist.Remove(taglist.Length - 2, 2); - _uiSystem.SetUiState(uid, DisposalRouterUiKey.Key, new DisposalRouterUserInterfaceState(taglist.ToString())); + _uiSystem.SetUiState(uid, SharedDisposalRouterComponent.DisposalRouterUiKey.Key, new SharedDisposalRouterComponent.DisposalRouterUserInterfaceState(taglist.ToString())); } private void OnAnchorChange(EntityUid uid, DisposalTubeComponent component, ref AnchorStateChangedEvent args) @@ -419,13 +417,13 @@ namespace Content.Server.Disposal.Tube _popups.PopupEntity(Loc.GetString("disposal-tube-component-popup-directions-text", ("directions", directions)), tubeId, recipient); } - public bool TryInsert(EntityUid uid, DisposalUnitComponent from, IEnumerable? tags = default, DisposalEntryComponent? entry = null) + public override bool TryInsert(EntityUid uid, DisposalUnitComponent from, IEnumerable? tags = default, DisposalEntryComponent? entry = null) { if (!Resolve(uid, ref entry)) return false; var xform = Transform(uid); - var holder = Spawn(DisposalEntryComponent.HolderPrototypeId, _transform.GetMapCoordinates(uid, xform: xform)); + var holder = Spawn(entry.HolderPrototypeId, _transform.GetMapCoordinates(uid, xform: xform)); var holderComponent = Comp(holder); foreach (var entity in from.Container.ContainedEntities.ToArray()) @@ -436,7 +434,7 @@ namespace Content.Server.Disposal.Tube _atmosSystem.Merge(holderComponent.Air, from.Air); from.Air.Clear(); - if (tags != default) + if (tags != null) holderComponent.Tags.UnionWith(tags); return _disposableSystem.EnterTube(holder, uid, holderComponent); diff --git a/Content.Server/Disposal/Tube/GetDisposalsNextDirectionEvent.cs b/Content.Server/Disposal/Tube/GetDisposalsNextDirectionEvent.cs index 2872a0e6d0..30dd1c3769 100644 --- a/Content.Server/Disposal/Tube/GetDisposalsNextDirectionEvent.cs +++ b/Content.Server/Disposal/Tube/GetDisposalsNextDirectionEvent.cs @@ -1,4 +1,4 @@ -using Content.Server.Disposal.Unit.Components; +using Content.Server.Disposal.Unit; namespace Content.Server.Disposal.Tube; diff --git a/Content.Server/Disposal/TubeConnectionsCommand.cs b/Content.Server/Disposal/TubeConnectionsCommand.cs index 564c46be7a..6719a9a2ab 100644 --- a/Content.Server/Disposal/TubeConnectionsCommand.cs +++ b/Content.Server/Disposal/TubeConnectionsCommand.cs @@ -1,6 +1,5 @@ using Content.Server.Administration; using Content.Server.Disposal.Tube; -using Content.Server.Disposal.Tube.Components; using Content.Shared.Administration; using Robust.Shared.Console; diff --git a/Content.Server/Disposal/Unit/Components/BeingDisposedComponent.cs b/Content.Server/Disposal/Unit/BeingDisposedComponent.cs similarity index 81% rename from Content.Server/Disposal/Unit/Components/BeingDisposedComponent.cs rename to Content.Server/Disposal/Unit/BeingDisposedComponent.cs index 060c7c98bd..3cff669855 100644 --- a/Content.Server/Disposal/Unit/Components/BeingDisposedComponent.cs +++ b/Content.Server/Disposal/Unit/BeingDisposedComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Disposal.Unit.Components; +namespace Content.Server.Disposal.Unit; /// /// A component added to entities that are currently in disposals. diff --git a/Content.Server/Disposal/Unit/EntitySystems/BeingDisposedSystem.cs b/Content.Server/Disposal/Unit/BeingDisposedSystem.cs similarity index 92% rename from Content.Server/Disposal/Unit/EntitySystems/BeingDisposedSystem.cs rename to Content.Server/Disposal/Unit/BeingDisposedSystem.cs index 6fbfb1523a..fcff4ba3b5 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/BeingDisposedSystem.cs +++ b/Content.Server/Disposal/Unit/BeingDisposedSystem.cs @@ -1,8 +1,7 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Body.Systems; -using Content.Server.Disposal.Unit.Components; -namespace Content.Server.Disposal.Unit.EntitySystems; +namespace Content.Server.Disposal.Unit; public sealed class BeingDisposedSystem : EntitySystem { diff --git a/Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs b/Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs deleted file mode 100644 index 548af039da..0000000000 --- a/Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Content.Server.Atmos; -using Content.Shared.Atmos; -using Content.Shared.Disposal.Components; - -namespace Content.Server.Disposal.Unit.Components; - -// GasMixture life. -[RegisterComponent] -public sealed partial class DisposalUnitComponent : SharedDisposalUnitComponent -{ - [DataField("air")] - public GasMixture Air = new(Atmospherics.CellVolume); -} diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs b/Content.Server/Disposal/Unit/DisposableSystem.cs similarity index 98% rename from Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs rename to Content.Server/Disposal/Unit/DisposableSystem.cs index 0e624ca6f5..a94176246c 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs +++ b/Content.Server/Disposal/Unit/DisposableSystem.cs @@ -1,9 +1,8 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Disposal.Tube; -using Content.Server.Disposal.Tube.Components; -using Content.Server.Disposal.Unit.Components; using Content.Shared.Body.Components; using Content.Shared.Damage; +using Content.Shared.Disposal.Components; using Content.Shared.Item; using Content.Shared.Throwing; using Robust.Shared.Audio.Systems; @@ -12,7 +11,7 @@ using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; -namespace Content.Server.Disposal.Unit.EntitySystems +namespace Content.Server.Disposal.Unit { public sealed class DisposableSystem : EntitySystem { diff --git a/Content.Server/Disposal/Unit/Components/DisposalHolderComponent.cs b/Content.Server/Disposal/Unit/DisposalHolderComponent.cs similarity index 94% rename from Content.Server/Disposal/Unit/Components/DisposalHolderComponent.cs rename to Content.Server/Disposal/Unit/DisposalHolderComponent.cs index 690b33968b..be90ae9f9c 100644 --- a/Content.Server/Disposal/Unit/Components/DisposalHolderComponent.cs +++ b/Content.Server/Disposal/Unit/DisposalHolderComponent.cs @@ -1,9 +1,8 @@ using Content.Server.Atmos; -using Content.Server.Disposal.Tube.Components; using Content.Shared.Atmos; using Robust.Shared.Containers; -namespace Content.Server.Disposal.Unit.Components +namespace Content.Server.Disposal.Unit { [RegisterComponent] public sealed partial class DisposalHolderComponent : Component, IGasMixtureHolder diff --git a/Content.Server/Disposal/Unit/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/DisposalUnitSystem.cs new file mode 100644 index 0000000000..4c6436c6c9 --- /dev/null +++ b/Content.Server/Disposal/Unit/DisposalUnitSystem.cs @@ -0,0 +1,44 @@ +using Content.Server.Atmos.EntitySystems; +using Content.Shared.Atmos; +using Content.Shared.Destructible; +using Content.Shared.Disposal.Components; +using Content.Shared.Disposal.Unit; +using Content.Shared.Explosion; + +namespace Content.Server.Disposal.Unit; + +public sealed class DisposalUnitSystem : SharedDisposalUnitSystem +{ + [Dependency] private readonly AtmosphereSystem _atmosSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnDestruction); + SubscribeLocalEvent(OnExploded); + } + + protected override void HandleAir(EntityUid uid, DisposalUnitComponent component, TransformComponent xform) + { + var air = component.Air; + var indices = TransformSystem.GetGridTilePositionOrDefault((uid, xform)); + + if (_atmosSystem.GetTileMixture(xform.GridUid, xform.MapUid, indices, true) is { Temperature: > 0f } environment) + { + var transferMoles = 0.1f * (0.25f * Atmospherics.OneAtmosphere * 1.01f - air.Pressure) * air.Volume / (environment.Temperature * Atmospherics.R); + + component.Air = environment.Remove(transferMoles); + } + } + + private void OnDestruction(EntityUid uid, DisposalUnitComponent component, DestructionEventArgs args) + { + TryEjectContents(uid, component); + } + + private void OnExploded(Entity ent, ref BeforeExplodeEvent args) + { + args.Contents.AddRange(ent.Comp.Container.ContainedEntities); + } +} diff --git a/Content.Server/Disposal/Unit/EntitySystems/DoInsertDisposalUnitEvent.cs b/Content.Server/Disposal/Unit/DoInsertDisposalUnitEvent.cs similarity index 65% rename from Content.Server/Disposal/Unit/EntitySystems/DoInsertDisposalUnitEvent.cs rename to Content.Server/Disposal/Unit/DoInsertDisposalUnitEvent.cs index 6fdfce61da..3f3fee9785 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DoInsertDisposalUnitEvent.cs +++ b/Content.Server/Disposal/Unit/DoInsertDisposalUnitEvent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Disposal.Unit.EntitySystems +namespace Content.Server.Disposal.Unit { public record DoInsertDisposalUnitEvent(EntityUid? User, EntityUid ToInsert, EntityUid Unit); } diff --git a/Content.Server/Fax/AdminUI/AdminFaxEui.cs b/Content.Server/Fax/AdminUI/AdminFaxEui.cs index fe6b03fab7..2921bd5ef6 100644 --- a/Content.Server/Fax/AdminUI/AdminFaxEui.cs +++ b/Content.Server/Fax/AdminUI/AdminFaxEui.cs @@ -7,6 +7,7 @@ using Content.Shared.Fax; using Content.Shared.Follower; using Content.Shared.Ghost; using Content.Shared.Paper; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.Fax.AdminUI; diff --git a/Content.Server/Fax/FaxSystem.cs b/Content.Server/Fax/FaxSystem.cs index 1ac7bd23ca..79710e3f97 100644 --- a/Content.Server/Fax/FaxSystem.cs +++ b/Content.Server/Fax/FaxSystem.cs @@ -1,8 +1,6 @@ using Content.Server.Administration; using Content.Server.Administration.Managers; using Content.Server.Chat.Managers; -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.Popups; using Content.Server.Power.Components; @@ -12,7 +10,7 @@ using Content.Shared.Administration.Logs; using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; using Content.Shared.DeviceNetwork; -using Content.Shared.Emag.Components; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Emag.Systems; using Content.Shared.Fax; using Content.Shared.Fax.Systems; @@ -27,9 +25,9 @@ using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.Player; -using Robust.Shared.Prototypes; using Content.Shared.NameModifier.Components; using Content.Shared.Power; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.Fax; diff --git a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs index 53c60296d5..b98ba15623 100644 --- a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs +++ b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs @@ -19,6 +19,8 @@ using Robust.Shared.Timing; using Robust.Shared.Audio.Systems; using Content.Shared.Damage.Systems; using Content.Shared.Damage.Components; +using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Power; namespace Content.Server.Light.EntitySystems diff --git a/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs b/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs index a53df6dbae..0e1d27a3c5 100644 --- a/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs +++ b/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs @@ -2,6 +2,8 @@ using System.Linq; using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Systems; using Content.Server.PowerCell; +using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Medical.CrewMonitoring; using Content.Shared.Medical.SuitSensor; using Content.Shared.Pinpointer; diff --git a/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerSystem.cs b/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerSystem.cs index d7b8cc67a5..ad50a8f957 100644 --- a/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerSystem.cs +++ b/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerSystem.cs @@ -1,10 +1,10 @@ -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.Medical.SuitSensors; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Medical.SuitSensor; using Robust.Shared.Timing; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.Medical.CrewMonitoring; diff --git a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs index fa4344cc78..2ab09e746f 100644 --- a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs +++ b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs @@ -1,7 +1,5 @@ using System.Numerics; using Content.Server.Access.Systems; -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.Emp; using Content.Server.Medical.CrewMonitoring; @@ -26,6 +24,7 @@ using Robust.Shared.Map; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.Medical.SuitSensors; diff --git a/Content.Server/PDA/PdaSystem.cs b/Content.Server/PDA/PdaSystem.cs index a9527020b0..bdf688efe7 100644 --- a/Content.Server/PDA/PdaSystem.cs +++ b/Content.Server/PDA/PdaSystem.cs @@ -2,27 +2,23 @@ using Content.Server.Access.Systems; using Content.Server.AlertLevel; using Content.Server.CartridgeLoader; using Content.Server.Chat.Managers; -using Content.Server.DeviceNetwork.Components; using Content.Server.Instruments; -using Content.Server.Light.EntitySystems; using Content.Server.PDA.Ringer; using Content.Server.Station.Systems; -using Content.Server.Store.Components; using Content.Server.Store.Systems; using Content.Server.Traitor.Uplink; using Content.Shared.Access.Components; using Content.Shared.CartridgeLoader; using Content.Shared.Chat; using Content.Shared.Light; -using Content.Shared.Light.Components; using Content.Shared.Light.EntitySystems; using Content.Shared.PDA; -using Content.Shared.Store.Components; using Robust.Server.Containers; using Robust.Server.GameObjects; using Robust.Shared.Containers; using Robust.Shared.Player; using Robust.Shared.Utility; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.PDA { diff --git a/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs b/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs index 0239273455..f3405486e6 100644 --- a/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs @@ -15,7 +15,6 @@ namespace Content.Server.Power.EntitySystems public sealed class PowerReceiverSystem : SharedPowerReceiverSystem { [Dependency] private readonly IAdminManager _adminManager = default!; - private EntityQuery _recQuery; private EntityQuery _provQuery; diff --git a/Content.Server/Power/Generation/Teg/TegSystem.cs b/Content.Server/Power/Generation/Teg/TegSystem.cs index 437d805dcd..04f876c2c2 100644 --- a/Content.Server/Power/Generation/Teg/TegSystem.cs +++ b/Content.Server/Power/Generation/Teg/TegSystem.cs @@ -9,6 +9,7 @@ using Content.Server.NodeContainer.Nodes; using Content.Server.Power.Components; using Content.Shared.Atmos; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Examine; using Content.Shared.Power; using Content.Shared.Power.EntitySystems; diff --git a/Content.Server/Radio/EntitySystems/JammerSystem.cs b/Content.Server/Radio/EntitySystems/JammerSystem.cs index 1fe48d22b4..1cea981d3c 100644 --- a/Content.Server/Radio/EntitySystems/JammerSystem.cs +++ b/Content.Server/Radio/EntitySystems/JammerSystem.cs @@ -1,4 +1,3 @@ -using Content.Server.DeviceNetwork.Components; using Content.Server.Power.EntitySystems; using Content.Server.PowerCell; using Content.Shared.DeviceNetwork.Components; diff --git a/Content.Server/Robotics/Systems/RoboticsConsoleSystem.cs b/Content.Server/Robotics/Systems/RoboticsConsoleSystem.cs index 916694fdd8..c4554d65d6 100644 --- a/Content.Server/Robotics/Systems/RoboticsConsoleSystem.cs +++ b/Content.Server/Robotics/Systems/RoboticsConsoleSystem.cs @@ -1,5 +1,4 @@ using Content.Server.Administration.Logs; -using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Systems; using Content.Server.Radio.EntitySystems; using Content.Shared.Lock; @@ -10,7 +9,7 @@ using Content.Shared.Robotics.Components; using Content.Shared.Robotics.Systems; using Robust.Server.GameObjects; using Robust.Shared.Timing; -using System.Diagnostics.CodeAnalysis; +using Content.Shared.DeviceNetwork.Events; namespace Content.Server.Research.Systems; diff --git a/Content.Server/RoundEnd/RoundEndSystem.cs b/Content.Server/RoundEnd/RoundEndSystem.cs index bb5934f3f0..900a52057e 100644 --- a/Content.Server/RoundEnd/RoundEndSystem.cs +++ b/Content.Server/RoundEnd/RoundEndSystem.cs @@ -4,8 +4,6 @@ using Content.Server.AlertLevel; using Content.Shared.CCVar; using Content.Server.Chat.Managers; using Content.Server.Chat.Systems; -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.GameTicking; using Content.Server.Screens.Components; @@ -21,6 +19,7 @@ using Robust.Shared.Configuration; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Timing; +using Content.Shared.DeviceNetwork.Components; using Timer = Robust.Shared.Timing.Timer; namespace Content.Server.RoundEnd diff --git a/Content.Server/Screens/Systems/ScreenSystem.cs b/Content.Server/Screens/Systems/ScreenSystem.cs index 782fe38c88..c159bfe1d5 100644 --- a/Content.Server/Screens/Systems/ScreenSystem.cs +++ b/Content.Server/Screens/Systems/ScreenSystem.cs @@ -2,6 +2,8 @@ using Content.Shared.TextScreen; using Content.Server.Screens.Components; using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; +using Content.Shared.DeviceNetwork.Components; +using Content.Shared.DeviceNetwork.Events; using Robust.Shared.Timing; @@ -63,7 +65,7 @@ public sealed class ScreenSystem : EntitySystem /// /// Determines if/how a timer packet affects this screen. /// Currently there are 2 broadcast domains: Arrivals, and every other screen. - /// Domain is determined by the on each timer. + /// Domain is determined by the on each timer. /// Each broadcast domain is divided into subnets. Screen MapUid determines subnet. /// Subnets are the shuttle, source, and dest. Source/dest change each jump. /// This is required to send different timers to the shuttle/terminal/station. diff --git a/Content.Server/SensorMonitoring/BatterySensorSystem.cs b/Content.Server/SensorMonitoring/BatterySensorSystem.cs index 501b094c89..5047cd1d29 100644 --- a/Content.Server/SensorMonitoring/BatterySensorSystem.cs +++ b/Content.Server/SensorMonitoring/BatterySensorSystem.cs @@ -2,6 +2,7 @@ using Content.Server.DeviceNetwork.Systems; using Content.Server.Power.Components; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; namespace Content.Server.SensorMonitoring; diff --git a/Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.UI.cs b/Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.UI.cs index dec3e6c36e..a562abd926 100644 --- a/Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.UI.cs +++ b/Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.UI.cs @@ -1,7 +1,7 @@ -using Content.Server.DeviceNetwork.Components; -using Content.Shared.SensorMonitoring; +using Content.Shared.SensorMonitoring; using Robust.Shared.Collections; using ConsoleUIState = Content.Shared.SensorMonitoring.SensorMonitoringConsoleBoundInterfaceState; +using Content.Shared.DeviceNetwork.Components; using IncrementalUIState = Content.Shared.SensorMonitoring.SensorMonitoringIncrementalUpdate; namespace Content.Server.SensorMonitoring; diff --git a/Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.cs b/Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.cs index ddd7812394..ebe8f304ba 100644 --- a/Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.cs +++ b/Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.cs @@ -1,10 +1,7 @@ using Content.Server.Atmos.Monitor.Components; using Content.Server.Atmos.Monitor.Systems; -using Content.Server.Atmos.Piping.Binary.Components; using Content.Server.Atmos.Piping.Components; using Content.Server.Atmos.Piping.Unary.Components; -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.Power.Generation.Teg; using Content.Shared.Atmos.Monitor; @@ -12,6 +9,7 @@ using Content.Shared.Atmos.Piping.Binary.Components; using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork.Components; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.DeviceNetwork.Systems; using Content.Shared.SensorMonitoring; using Robust.Server.GameObjects; diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index 896570fb34..aa1c2e6dff 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Numerics; using Content.Server.Administration; using Content.Server.Chat.Managers; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.GameTicking; using Content.Server.GameTicking.Events; @@ -19,6 +18,7 @@ using Content.Shared.Administration; using Content.Shared.CCVar; using Content.Shared.Damage.Components; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Components; using Content.Shared.GameTicking; using Content.Shared.Mobs.Components; using Content.Shared.Movement.Components; @@ -26,20 +26,17 @@ using Content.Shared.Parallax.Biomes; using Content.Shared.Salvage; using Content.Shared.Shuttles.Components; using Content.Shared.Tiles; -using Robust.Server.GameObjects; using Robust.Shared.Collections; using Robust.Shared.Configuration; using Robust.Shared.Console; -using Robust.Shared.EntitySerialization; using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Map; -using Robust.Shared.Map.Components; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Spawners; using Robust.Shared.Timing; using Robust.Shared.Utility; -using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent; namespace Content.Server.Shuttles.Systems; diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.Console.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.Console.cs index 09aad1d931..95c6ab5a1b 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.Console.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.Console.cs @@ -1,5 +1,4 @@ using System.Threading; -using Content.Server.DeviceNetwork.Components; using Content.Server.Screens.Components; using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Events; @@ -14,6 +13,7 @@ using Content.Shared.Shuttles.Systems; using Content.Shared.UserInterface; using Robust.Shared.Map; using Robust.Shared.Player; +using Content.Shared.DeviceNetwork.Components; using Timer = Robust.Shared.Timing.Timer; namespace Content.Server.Shuttles.Systems; diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index afa77421bd..8e3e01bfb6 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -6,7 +6,6 @@ using Content.Server.Administration.Logs; using Content.Server.Administration.Managers; using Content.Server.Chat.Systems; using Content.Server.Communications; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.GameTicking.Events; using Content.Server.Pinpointer; @@ -37,6 +36,7 @@ using Robust.Shared.Player; using Robust.Shared.Random; using Robust.Shared.Timing; using Robust.Shared.Utility; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.Shuttles.Systems; diff --git a/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs b/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs index 4d2a8912e8..e950d3f288 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs @@ -1,13 +1,10 @@ using Content.Shared.DeviceNetwork; -using Content.Shared.Emag.Components; using Content.Shared.Movement.Components; using Content.Shared.Popups; using Content.Shared.Robotics; using Content.Shared.Silicons.Borgs.Components; -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; -using Content.Server.DeviceNetwork.Systems; -using Content.Server.Explosion.Components; +using Content.Shared.DeviceNetwork.Components; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Emag.Systems; using Robust.Shared.Utility; diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs index 21e71c4316..30bbb2f0e9 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs @@ -3,6 +3,7 @@ using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Systems; using Content.Server.Power.Components; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Power; using Content.Shared.UserInterface; using Content.Shared.SurveillanceCamera; diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraRouterSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraRouterSystem.cs index 315273a0cc..c6886dee33 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraRouterSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraRouterSystem.cs @@ -1,15 +1,13 @@ -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; -using Content.Server.Power.Components; using Content.Shared.ActionBlocker; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Power; using Content.Shared.SurveillanceCamera; using Content.Shared.Verbs; using Robust.Server.GameObjects; -using Robust.Shared.Player; using Robust.Shared.Prototypes; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.SurveillanceCamera; diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs index f1d1b58bf5..709e383c06 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs @@ -1,16 +1,15 @@ -using Content.Server.DeviceNetwork; -using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.Emp; -using Content.Server.Power.Components; using Content.Shared.ActionBlocker; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Power; using Content.Shared.SurveillanceCamera; using Content.Shared.Verbs; using Robust.Server.GameObjects; using Robust.Shared.Player; using Robust.Shared.Prototypes; +using Content.Shared.DeviceNetwork.Components; namespace Content.Server.SurveillanceCamera; diff --git a/Content.Server/Turrets/DeployableTurretSystem.cs b/Content.Server/Turrets/DeployableTurretSystem.cs index 359d91fd1d..72c011bc90 100644 --- a/Content.Server/Turrets/DeployableTurretSystem.cs +++ b/Content.Server/Turrets/DeployableTurretSystem.cs @@ -8,6 +8,8 @@ using Content.Server.Power.Components; using Content.Server.Repairable; using Content.Shared.Destructible; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Components; +using Content.Shared.DeviceNetwork.Events; using Content.Shared.Power; using Content.Shared.Turrets; using Content.Shared.Weapons.Ranged.Events; diff --git a/Content.Shared/Climbing/Components/ClimbableComponent.cs b/Content.Shared/Climbing/Components/ClimbableComponent.cs index 1a924e5c30..22a42dea78 100644 --- a/Content.Shared/Climbing/Components/ClimbableComponent.cs +++ b/Content.Shared/Climbing/Components/ClimbableComponent.cs @@ -13,7 +13,13 @@ namespace Content.Shared.Climbing.Components /// /// The range from which this entity can be climbed. /// - [DataField("range")] public float Range = SharedInteractionSystem.InteractionRange / 1.4f; + [DataField] public float Range = SharedInteractionSystem.InteractionRange; + + /// + /// Can drag-drop / verb vaulting be done? Set to false if climbing is being handled manually. + /// + [DataField] + public bool Vaultable = true; /// /// The time it takes to climb onto the entity. diff --git a/Content.Shared/Climbing/Systems/ClimbSystem.cs b/Content.Shared/Climbing/Systems/ClimbSystem.cs index f8f2ffda88..d7d9df7fdd 100644 --- a/Content.Shared/Climbing/Systems/ClimbSystem.cs +++ b/Content.Shared/Climbing/Systems/ClimbSystem.cs @@ -149,7 +149,7 @@ public sealed partial class ClimbSystem : VirtualController private void OnCanDragDropOn(EntityUid uid, ClimbableComponent component, ref CanDropTargetEvent args) { - if (args.Handled) + if (args.Handled || !component.Vaultable) return; // If already climbing then don't show outlines. @@ -261,7 +261,7 @@ public sealed partial class ClimbSystem : VirtualController args.Handled = true; } - private void Climb(EntityUid uid, EntityUid user, EntityUid climbable, bool silent = false, ClimbingComponent? climbing = null, + public void Climb(EntityUid uid, EntityUid user, EntityUid climbable, bool silent = false, ClimbingComponent? climbing = null, PhysicsComponent? physics = null, FixturesComponent? fixtures = null, ClimbableComponent? comp = null) { if (!Resolve(uid, ref climbing, ref physics, ref fixtures, false)) @@ -456,6 +456,12 @@ public sealed partial class ClimbSystem : VirtualController /// The reason why it cant be dropped public bool CanVault(ClimbableComponent component, EntityUid user, EntityUid target, out string reason) { + if (!component.Vaultable) + { + reason = string.Empty; + return false; + } + if (!_actionBlockerSystem.CanInteract(user, target)) { reason = Loc.GetString("comp-climbable-cant-interact"); diff --git a/Content.Shared/Configurable/ConfigurationComponent.cs b/Content.Shared/Configurable/ConfigurationComponent.cs index 621871af3c..63c0845083 100644 --- a/Content.Shared/Configurable/ConfigurationComponent.cs +++ b/Content.Shared/Configurable/ConfigurationComponent.cs @@ -2,34 +2,38 @@ using System.Text.RegularExpressions; using Content.Shared.Tools; using Content.Shared.Tools.Systems; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Configurable { - [RegisterComponent, NetworkedComponent] + /// + /// Configuration for mailing units. + /// + /// + /// If you want a more detailed description ask the original coder. + /// + [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class ConfigurationComponent : Component { - [DataField("config")] + /// + /// Tags for mail unit routing. + /// + [DataField, AutoNetworkedField] public Dictionary Config = new(); - [DataField("qualityNeeded", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string QualityNeeded = SharedToolSystem.PulseQuality; + /// + /// Quality to open up the configuration UI. + /// + [DataField] + public ProtoId QualityNeeded = SharedToolSystem.PulseQuality; - [DataField("validation")] + /// + /// Validate tags in . + /// + [DataField] public Regex Validation = new("^[a-zA-Z0-9 ]*$", RegexOptions.Compiled); - [Serializable, NetSerializable] - public sealed class ConfigurationBoundUserInterfaceState : BoundUserInterfaceState - { - public Dictionary Config { get; } - - public ConfigurationBoundUserInterfaceState(Dictionary config) - { - Config = config; - } - } - /// /// Message data sent from client to server when the device configuration is updated. /// diff --git a/Content.Shared/Configurable/SharedConfigurationSystem.cs b/Content.Shared/Configurable/SharedConfigurationSystem.cs new file mode 100644 index 0000000000..704965188e --- /dev/null +++ b/Content.Shared/Configurable/SharedConfigurationSystem.cs @@ -0,0 +1,77 @@ +using Content.Shared.Interaction; +using Content.Shared.Tools.Systems; +using Robust.Shared.Containers; +using static Content.Shared.Configurable.ConfigurationComponent; + +namespace Content.Shared.Configurable; + +/// +/// +/// +public abstract class SharedConfigurationSystem : EntitySystem +{ + [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly SharedToolSystem _toolSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnUpdate); + SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnInsert); + } + + private void OnInteractUsing(EntityUid uid, ConfigurationComponent component, InteractUsingEvent args) + { + // TODO use activatable ui system + if (args.Handled) + return; + + if (!_toolSystem.HasQuality(args.Used, component.QualityNeeded)) + return; + + args.Handled = _uiSystem.TryOpenUi(uid, ConfigurationUiKey.Key, args.User); + } + + private void OnUpdate(EntityUid uid, ConfigurationComponent component, ConfigurationUpdatedMessage args) + { + foreach (var key in component.Config.Keys) + { + var value = args.Config.GetValueOrDefault(key); + + if (string.IsNullOrWhiteSpace(value) || component.Validation != null && !component.Validation.IsMatch(value)) + continue; + + component.Config[key] = value; + } + + Dirty(uid, component); + var updatedEvent = new ConfigurationUpdatedEvent(component); + RaiseLocalEvent(uid, updatedEvent); + + // TODO support float (spinbox) and enum (drop-down) configurations + // TODO support verbs. + } + + private void OnInsert(EntityUid uid, ConfigurationComponent component, ContainerIsInsertingAttemptEvent args) + { + if (!_toolSystem.HasQuality(args.EntityUid, component.QualityNeeded)) + return; + + args.Cancel(); + } +} + +/// +/// Sent when configuration values got changes +/// +public sealed class ConfigurationUpdatedEvent : EntityEventArgs +{ + public ConfigurationComponent Configuration; + + public ConfigurationUpdatedEvent(ConfigurationComponent configuration) + { + Configuration = configuration; + } +} diff --git a/Content.Shared/Containers/SharedThrowInsertContainerSystem.cs b/Content.Shared/Containers/SharedThrowInsertContainerSystem.cs new file mode 100644 index 0000000000..a5c300c284 --- /dev/null +++ b/Content.Shared/Containers/SharedThrowInsertContainerSystem.cs @@ -0,0 +1,8 @@ +namespace Content.Shared.Containers; + +/// +/// Sent before the insertion is made. +/// Allows preventing the insertion if any system on the entity should need to. +/// +[ByRefEvent] +public record struct BeforeThrowInsertEvent(EntityUid ThrownEntity, bool Cancelled = false); diff --git a/Content.Server/DeviceNetwork/Components/DeviceNetworkComponent.cs b/Content.Shared/DeviceNetwork/Components/DeviceNetworkComponent.cs similarity index 93% rename from Content.Server/DeviceNetwork/Components/DeviceNetworkComponent.cs rename to Content.Shared/DeviceNetwork/Components/DeviceNetworkComponent.cs index 186da57e5d..37a86e7161 100644 --- a/Content.Server/DeviceNetwork/Components/DeviceNetworkComponent.cs +++ b/Content.Shared/DeviceNetwork/Components/DeviceNetworkComponent.cs @@ -1,11 +1,10 @@ -using Content.Server.DeviceNetwork.Systems; -using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Systems; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -namespace Content.Server.DeviceNetwork.Components +namespace Content.Shared.DeviceNetwork.Components { [RegisterComponent] - [Access(typeof(DeviceNetworkSystem), typeof(DeviceNet))] + [Access(typeof(SharedDeviceNetworkSystem), typeof(DeviceNet))] public sealed partial class DeviceNetworkComponent : Component { public enum DeviceNetIdDefaults @@ -113,14 +112,14 @@ namespace Content.Server.DeviceNetwork.Components /// A list of device-lists that this device is on. /// [DataField] - [Access(typeof(DeviceListSystem))] + [Access(typeof(SharedDeviceListSystem))] public HashSet DeviceLists = new(); /// /// A list of configurators that this device is on. /// [DataField] - [Access(typeof(NetworkConfiguratorSystem))] + [Access(typeof(SharedNetworkConfiguratorSystem))] public HashSet Configurators = new(); } } diff --git a/Content.Server/DeviceNetwork/DeviceNet.cs b/Content.Shared/DeviceNetwork/DeviceNet.cs similarity index 97% rename from Content.Server/DeviceNetwork/DeviceNet.cs rename to Content.Shared/DeviceNetwork/DeviceNet.cs index c77d45c061..4234b30385 100644 --- a/Content.Server/DeviceNetwork/DeviceNet.cs +++ b/Content.Shared/DeviceNetwork/DeviceNet.cs @@ -1,8 +1,7 @@ -using Content.Server.DeviceNetwork.Components; using Robust.Shared.Random; -using static Content.Server.DeviceNetwork.Components.DeviceNetworkComponent; +using Content.Shared.DeviceNetwork.Components; -namespace Content.Server.DeviceNetwork; +namespace Content.Shared.DeviceNetwork; /// /// Data class for storing and retrieving information about devices connected to a device network. diff --git a/Content.Server/DeviceNetwork/DeviceNetworkConstants.cs b/Content.Shared/DeviceNetwork/DeviceNetworkConstants.cs similarity index 96% rename from Content.Server/DeviceNetwork/DeviceNetworkConstants.cs rename to Content.Shared/DeviceNetwork/DeviceNetworkConstants.cs index 6cbad603b4..7fec72bb69 100644 --- a/Content.Server/DeviceNetwork/DeviceNetworkConstants.cs +++ b/Content.Shared/DeviceNetwork/DeviceNetworkConstants.cs @@ -1,7 +1,7 @@ -using Content.Server.DeviceNetwork.Components; using Robust.Shared.Utility; +using Content.Shared.DeviceNetwork.Components; -namespace Content.Server.DeviceNetwork +namespace Content.Shared.DeviceNetwork { /// /// A collection of constants to help with using device networks diff --git a/Content.Shared/DeviceNetwork/Events/BeforeBroadcastAttemptEvent.cs b/Content.Shared/DeviceNetwork/Events/BeforeBroadcastAttemptEvent.cs new file mode 100644 index 0000000000..f495847482 --- /dev/null +++ b/Content.Shared/DeviceNetwork/Events/BeforeBroadcastAttemptEvent.cs @@ -0,0 +1,17 @@ +using Content.Shared.DeviceNetwork.Components; + +namespace Content.Shared.DeviceNetwork.Events; + +/// +/// Sent to the sending entity before broadcasting network packets to recipients +/// +public sealed class BeforeBroadcastAttemptEvent : CancellableEntityEventArgs +{ + public readonly IReadOnlySet Recipients; + public HashSet? ModifiedRecipients; + + public BeforeBroadcastAttemptEvent(IReadOnlySet recipients) + { + Recipients = recipients; + } +} diff --git a/Content.Shared/DeviceNetwork/Events/BeforePacketSentEvent.cs b/Content.Shared/DeviceNetwork/Events/BeforePacketSentEvent.cs new file mode 100644 index 0000000000..5d5c038dbf --- /dev/null +++ b/Content.Shared/DeviceNetwork/Events/BeforePacketSentEvent.cs @@ -0,0 +1,35 @@ +using System.Numerics; + +namespace Content.Shared.DeviceNetwork.Events; + +/// +/// Event raised before a device network packet is send. +/// Subscribed to by other systems to prevent the packet from being sent. +/// +public sealed class BeforePacketSentEvent : CancellableEntityEventArgs +{ + /// + /// The EntityUid of the entity the packet was sent from. + /// + public readonly EntityUid Sender; + + public readonly TransformComponent SenderTransform; + + /// + /// The senders current position in world coordinates. + /// + public readonly Vector2 SenderPosition; + + /// + /// The network the packet will be sent to. + /// + public readonly string NetworkId; + + public BeforePacketSentEvent(EntityUid sender, TransformComponent xform, Vector2 senderPosition, string networkId) + { + Sender = sender; + SenderTransform = xform; + SenderPosition = senderPosition; + NetworkId = networkId; + } +} \ No newline at end of file diff --git a/Content.Shared/DeviceNetwork/Events/DeviceNetworkPacketEvent.cs b/Content.Shared/DeviceNetwork/Events/DeviceNetworkPacketEvent.cs new file mode 100644 index 0000000000..4ae6afeef7 --- /dev/null +++ b/Content.Shared/DeviceNetwork/Events/DeviceNetworkPacketEvent.cs @@ -0,0 +1,47 @@ +namespace Content.Shared.DeviceNetwork.Events; + +/// +/// Event raised when a device network packet gets sent. +/// +public sealed class DeviceNetworkPacketEvent : EntityEventArgs +{ + /// + /// The id of the network that this packet is being sent on. + /// + public int NetId; + + /// + /// The frequency the packet is sent on. + /// + public readonly uint Frequency; + + /// + /// Address of the intended recipient. Null if the message was broadcast. + /// + public string? Address; + + /// + /// The device network address of the sending entity. + /// + public readonly string SenderAddress; + + /// + /// The entity that sent the packet. + /// + public EntityUid Sender; + + /// + /// The data that is being sent. + /// + public readonly NetworkPayload Data; + + public DeviceNetworkPacketEvent(int netId, string? address, uint frequency, string senderAddress, EntityUid sender, NetworkPayload data) + { + NetId = netId; + Address = address; + Frequency = frequency; + SenderAddress = senderAddress; + Sender = sender; + Data = data; + } +} \ No newline at end of file diff --git a/Content.Shared/DeviceNetwork/Systems/SharedDeviceNetworkSystem.cs b/Content.Shared/DeviceNetwork/Systems/SharedDeviceNetworkSystem.cs new file mode 100644 index 0000000000..5992c40413 --- /dev/null +++ b/Content.Shared/DeviceNetwork/Systems/SharedDeviceNetworkSystem.cs @@ -0,0 +1,25 @@ +using Content.Shared.DeviceNetwork.Components; + +namespace Content.Shared.DeviceNetwork.Systems; + +public abstract class SharedDeviceNetworkSystem : EntitySystem +{ + /// + /// Sends the given payload as a device network packet to the entity with the given address and frequency. + /// Addresses are given to the DeviceNetworkComponent of an entity when connecting. + /// + /// The EntityUid of the sending entity + /// The address of the entity that the packet gets sent to. If null, the message is broadcast to all devices on that frequency (except the sender) + /// The frequency to send on + /// The data to be sent + /// Returns true when the packet was successfully enqueued. + public virtual bool QueuePacket(EntityUid uid, + string? address, + NetworkPayload data, + uint? frequency = null, + int? network = null, + DeviceNetworkComponent? device = null) + { + return false; + } +} diff --git a/Content.Server/Disposal/Mailing/MailingUnitComponent.cs b/Content.Shared/Disposal/Mailing/MailingUnitComponent.cs similarity index 57% rename from Content.Server/Disposal/Mailing/MailingUnitComponent.cs rename to Content.Shared/Disposal/Mailing/MailingUnitComponent.cs index be5eca99c4..255d6cdf01 100644 --- a/Content.Server/Disposal/Mailing/MailingUnitComponent.cs +++ b/Content.Shared/Disposal/Mailing/MailingUnitComponent.cs @@ -1,30 +1,28 @@ -using Content.Shared.Disposal.Components; +using Content.Shared.Disposal.Mailing; +using Robust.Shared.GameStates; -namespace Content.Server.Disposal.Mailing; +namespace Content.Shared.Disposal.Components; -[Access(typeof(MailingUnitSystem))] -[RegisterComponent] +[Access(typeof(SharedMailingUnitSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] public sealed partial class MailingUnitComponent : Component { /// /// List of targets the mailing unit can send to. /// Each target is just a disposal routing tag /// - [DataField("targetList")] + [DataField, AutoNetworkedField] public List TargetList = new(); /// /// The target that gets attached to the disposal holders tag list on flush /// - [DataField("target")] + [DataField, AutoNetworkedField] public string? Target; /// /// The tag for this mailing unit /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("tag")] + [DataField, AutoNetworkedField] public string? Tag; - - public SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState? DisposalUnitInterfaceState; } diff --git a/Content.Shared/Disposal/MailingUnitUiMessages.cs b/Content.Shared/Disposal/Mailing/MailingUnitUiMessages.cs similarity index 100% rename from Content.Shared/Disposal/MailingUnitUiMessages.cs rename to Content.Shared/Disposal/Mailing/MailingUnitUiMessages.cs diff --git a/Content.Shared/Disposal/Components/SharedDisposalRouterComponent.cs b/Content.Shared/Disposal/Mailing/SharedDisposalRouterComponent.cs similarity index 100% rename from Content.Shared/Disposal/Components/SharedDisposalRouterComponent.cs rename to Content.Shared/Disposal/Mailing/SharedDisposalRouterComponent.cs diff --git a/Content.Shared/Disposal/Components/SharedDisposalTaggerComponent.cs b/Content.Shared/Disposal/Mailing/SharedDisposalTaggerComponent.cs similarity index 100% rename from Content.Shared/Disposal/Components/SharedDisposalTaggerComponent.cs rename to Content.Shared/Disposal/Mailing/SharedDisposalTaggerComponent.cs diff --git a/Content.Shared/Disposal/Mailing/SharedMailingUnitSystem.cs b/Content.Shared/Disposal/Mailing/SharedMailingUnitSystem.cs new file mode 100644 index 0000000000..cb7a8c46c8 --- /dev/null +++ b/Content.Shared/Disposal/Mailing/SharedMailingUnitSystem.cs @@ -0,0 +1,174 @@ +using Content.Shared.Configurable; +using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Components; +using Content.Shared.DeviceNetwork.Events; +using Content.Shared.DeviceNetwork.Systems; +using Content.Shared.Disposal.Components; +using Content.Shared.Disposal.Unit; +using Content.Shared.Disposal.Unit.Events; +using Content.Shared.Interaction; +using Content.Shared.Power.EntitySystems; +using Robust.Shared.Player; + +namespace Content.Shared.Disposal.Mailing; + +public abstract class SharedMailingUnitSystem : EntitySystem +{ + [Dependency] private readonly SharedDeviceNetworkSystem _deviceNetworkSystem = default!; + [Dependency] private readonly SharedPowerReceiverSystem _power = default!; + [Dependency] protected readonly SharedUserInterfaceSystem UserInterfaceSystem = default!; + + private const string MailTag = "mail"; + + private const string TagConfigurationKey = "tag"; + + private const string NetTag = "tag"; + private const string NetSrc = "src"; + private const string NetTarget = "target"; + private const string NetCmdSent = "mail_sent"; + private const string NetCmdRequest = "get_mailer_tag"; + private const string NetCmdResponse = "mailer_tag"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent(OnPacketReceived); + SubscribeLocalEvent(OnBeforeFlush); + SubscribeLocalEvent(OnConfigurationUpdated); + SubscribeLocalEvent(HandleActivate, before: new[] { typeof(SharedDisposalUnitSystem) }); + SubscribeLocalEvent(OnTargetSelected); + } + + private void OnComponentInit(EntityUid uid, MailingUnitComponent component, ComponentInit args) + { + UpdateTargetList(uid, component); + } + + private void OnPacketReceived(EntityUid uid, MailingUnitComponent component, DeviceNetworkPacketEvent args) + { + if (!args.Data.TryGetValue(DeviceNetworkConstants.Command, out string? command) || !_power.IsPowered(uid)) + return; + + switch (command) + { + case NetCmdRequest: + SendTagRequestResponse(uid, args, component.Tag); + break; + case NetCmdResponse when args.Data.TryGetValue(NetTag, out string? tag): + //Add the received tag request response to the list of targets + component.TargetList.Add(tag); + Dirty(uid, component); + break; + } + } + + /// + /// Sends the given tag as a response to a if it's not null + /// + private void SendTagRequestResponse(EntityUid uid, DeviceNetworkPacketEvent args, string? tag) + { + if (tag == null) + return; + + var payload = new NetworkPayload + { + [DeviceNetworkConstants.Command] = NetCmdResponse, + [NetTag] = tag + }; + + _deviceNetworkSystem.QueuePacket(uid, args.Address, payload, args.Frequency); + } + + /// + /// Prevents the unit from flushing if no target is selected + /// + private void OnBeforeFlush(EntityUid uid, MailingUnitComponent component, BeforeDisposalFlushEvent args) + { + if (string.IsNullOrEmpty(component.Target)) + { + args.Cancel(); + return; + } + + Dirty(uid, component); + args.Tags.Add(MailTag); + args.Tags.Add(component.Target); + + BroadcastSentMessage(uid, component); + } + + /// + /// Broadcast that a mail was sent including the src and target tags + /// + private void BroadcastSentMessage(EntityUid uid, MailingUnitComponent component, DeviceNetworkComponent? device = null) + { + if (string.IsNullOrEmpty(component.Tag) || string.IsNullOrEmpty(component.Target) || !Resolve(uid, ref device)) + return; + + var payload = new NetworkPayload + { + [DeviceNetworkConstants.Command] = NetCmdSent, + [NetSrc] = component.Tag, + [NetTarget] = component.Target + }; + + _deviceNetworkSystem.QueuePacket(uid, null, payload, null, null, device); + } + + /// + /// Clears the units target list and broadcasts a . + /// The target list will then get populated with responses from all active mailing units on the same grid + /// + private void UpdateTargetList(EntityUid uid, MailingUnitComponent component, DeviceNetworkComponent? device = null) + { + if (!Resolve(uid, ref device, false)) + return; + + var payload = new NetworkPayload + { + [DeviceNetworkConstants.Command] = NetCmdRequest + }; + + component.TargetList.Clear(); + _deviceNetworkSystem.QueuePacket(uid, null, payload, null, null, device); + } + + /// + /// Gets called when the units tag got updated + /// + private void OnConfigurationUpdated(EntityUid uid, MailingUnitComponent component, ConfigurationUpdatedEvent args) + { + var configuration = args.Configuration.Config; + if (!configuration.ContainsKey(TagConfigurationKey) || configuration[TagConfigurationKey] == string.Empty) + { + component.Tag = null; + return; + } + + component.Tag = configuration[TagConfigurationKey]; + Dirty(uid, component); + } + + private void HandleActivate(EntityUid uid, MailingUnitComponent component, ActivateInWorldEvent args) + { + if (args.Handled || !args.Complex) + return; + + if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) + { + return; + } + + args.Handled = true; + UpdateTargetList(uid, component); + UserInterfaceSystem.OpenUi(uid, MailingUnitUiKey.Key, actor.PlayerSession); + } + + private void OnTargetSelected(EntityUid uid, MailingUnitComponent component, TargetSelectedMessage args) + { + component.Target = args.Target; + Dirty(uid, component); + } +} diff --git a/Content.Shared/Disposal/MailingUnitBoundUserInterfaceState.cs b/Content.Shared/Disposal/MailingUnitBoundUserInterfaceState.cs deleted file mode 100644 index 65be092072..0000000000 --- a/Content.Shared/Disposal/MailingUnitBoundUserInterfaceState.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Content.Shared.Disposal.Components; -using Robust.Shared.Serialization; - -namespace Content.Shared.Disposal; - -[Serializable, NetSerializable] -public sealed class MailingUnitBoundUserInterfaceState : BoundUserInterfaceState, IEquatable -{ - public string? Target; - public List TargetList; - public string? Tag; - public SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState DisposalState; - - public MailingUnitBoundUserInterfaceState(SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState disposalState, string? target, List targetList, string? tag) - { - DisposalState = disposalState; - Target = target; - TargetList = targetList; - Tag = tag; - } - - public bool Equals(MailingUnitBoundUserInterfaceState? other) - { - if (other is null) - return false; - if (ReferenceEquals(this, other)) - return true; - return DisposalState.Equals(other.DisposalState) - && Target == other.Target - && TargetList.Equals(other.TargetList) - && Tag == other.Tag; - } - - public override bool Equals(object? other) - { - if (other is MailingUnitBoundUserInterfaceState otherState) - return Equals(otherState); - return false; - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } -} diff --git a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs deleted file mode 100644 index a650ef72f8..0000000000 --- a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs +++ /dev/null @@ -1,162 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Content.Shared.Body.Components; -using Content.Shared.Disposal.Components; -using Content.Shared.DoAfter; -using Content.Shared.DragDrop; -using Content.Shared.Emag.Systems; -using Content.Shared.Item; -using Content.Shared.Throwing; -using Content.Shared.Whitelist; -using Robust.Shared.Audio; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Events; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Serialization; -using Robust.Shared.Timing; - -namespace Content.Shared.Disposal; - -[Serializable, NetSerializable] -public sealed partial class DisposalDoAfterEvent : SimpleDoAfterEvent -{ -} - -public abstract class SharedDisposalUnitSystem : EntitySystem -{ - [Dependency] protected readonly IGameTiming GameTiming = default!; - [Dependency] protected readonly EmagSystem _emag = default!; - [Dependency] protected readonly MetaDataSystem Metadata = default!; - [Dependency] protected readonly SharedJointSystem Joints = default!; - [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; - - protected static TimeSpan ExitAttemptDelay = TimeSpan.FromSeconds(0.5); - - // Percentage - public const float PressurePerSecond = 0.05f; - - public abstract bool HasDisposals([NotNullWhen(true)] EntityUid? uid); - - public abstract bool ResolveDisposals(EntityUid uid, [NotNullWhen(true)] ref SharedDisposalUnitComponent? component); - - /// - /// Gets the current pressure state of a disposals unit. - /// - /// - /// - /// - /// - public DisposalsPressureState GetState(EntityUid uid, SharedDisposalUnitComponent component, MetaDataComponent? metadata = null) - { - var nextPressure = Metadata.GetPauseTime(uid, metadata) + component.NextPressurized - GameTiming.CurTime; - var pressurizeTime = 1f / PressurePerSecond; - var pressurizeDuration = pressurizeTime - component.FlushDelay.TotalSeconds; - - if (nextPressure.TotalSeconds > pressurizeDuration) - { - return DisposalsPressureState.Flushed; - } - - if (nextPressure > TimeSpan.Zero) - { - return DisposalsPressureState.Pressurizing; - } - - return DisposalsPressureState.Ready; - } - - public float GetPressure(EntityUid uid, SharedDisposalUnitComponent component, MetaDataComponent? metadata = null) - { - if (!Resolve(uid, ref metadata)) - return 0f; - - var pauseTime = Metadata.GetPauseTime(uid, metadata); - return MathF.Min(1f, - (float) (GameTiming.CurTime - pauseTime - component.NextPressurized).TotalSeconds / PressurePerSecond); - } - - protected void OnPreventCollide(EntityUid uid, SharedDisposalUnitComponent component, - ref PreventCollideEvent args) - { - var otherBody = args.OtherEntity; - - // Items dropped shouldn't collide but items thrown should - if (HasComp(otherBody) && !HasComp(otherBody)) - { - args.Cancelled = true; - return; - } - - if (component.RecentlyEjected.Contains(otherBody)) - { - args.Cancelled = true; - } - } - - protected void OnCanDragDropOn(EntityUid uid, SharedDisposalUnitComponent component, ref CanDropTargetEvent args) - { - if (args.Handled) - return; - - args.CanDrop = CanInsert(uid, component, args.Dragged); - args.Handled = true; - } - - protected void OnEmagged(EntityUid uid, SharedDisposalUnitComponent component, ref GotEmaggedEvent args) - { - if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) - return; - - if (component.DisablePressure == true) - return; - - component.DisablePressure = true; - args.Handled = true; - } - - public virtual bool CanInsert(EntityUid uid, SharedDisposalUnitComponent component, EntityUid entity) - { - if (!Transform(uid).Anchored) - return false; - - var storable = HasComp(entity); - if (!storable && !HasComp(entity)) - return false; - - if (_whitelistSystem.IsBlacklistPass(component.Blacklist, entity) || - _whitelistSystem.IsWhitelistFail(component.Whitelist, entity)) - return false; - - if (TryComp(entity, out var physics) && (physics.CanCollide) || storable) - return true; - else - return false; - - } - - public abstract void DoInsertDisposalUnit(EntityUid uid, EntityUid toInsert, EntityUid user, SharedDisposalUnitComponent? disposal = null); - - [Serializable, NetSerializable] - protected sealed class DisposalUnitComponentState : ComponentState - { - public SoundSpecifier? FlushSound; - public DisposalsPressureState State; - public TimeSpan NextPressurized; - public TimeSpan AutomaticEngageTime; - public TimeSpan? NextFlush; - public bool Powered; - public bool Engaged; - public List RecentlyEjected; - - public DisposalUnitComponentState(SoundSpecifier? flushSound, DisposalsPressureState state, TimeSpan nextPressurized, TimeSpan automaticEngageTime, TimeSpan? nextFlush, bool powered, bool engaged, List recentlyEjected) - { - FlushSound = flushSound; - State = state; - NextPressurized = nextPressurized; - AutomaticEngageTime = automaticEngageTime; - NextFlush = nextFlush; - Powered = powered; - Engaged = engaged; - RecentlyEjected = recentlyEjected; - } - } -} diff --git a/Content.Shared/Disposal/Tube/DisposalEntryComponent.cs b/Content.Shared/Disposal/Tube/DisposalEntryComponent.cs new file mode 100644 index 0000000000..066b16ad1f --- /dev/null +++ b/Content.Shared/Disposal/Tube/DisposalEntryComponent.cs @@ -0,0 +1,12 @@ +using Content.Shared.Disposal.Unit; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Disposal.Tube; + +[RegisterComponent] +[Access(typeof(SharedDisposalTubeSystem), typeof(SharedDisposalUnitSystem))] +public sealed partial class DisposalEntryComponent : Component +{ + [DataField] + public EntProtoId HolderPrototypeId = "DisposalHolder"; +} diff --git a/Content.Shared/Disposal/Components/SharedDisposalTubeComponent.cs b/Content.Shared/Disposal/Tube/SharedDisposalTubeComponent.cs similarity index 100% rename from Content.Shared/Disposal/Components/SharedDisposalTubeComponent.cs rename to Content.Shared/Disposal/Tube/SharedDisposalTubeComponent.cs diff --git a/Content.Shared/Disposal/Unit/BeforeDisposalFlushEvent.cs b/Content.Shared/Disposal/Unit/BeforeDisposalFlushEvent.cs new file mode 100644 index 0000000000..ee141d6c4d --- /dev/null +++ b/Content.Shared/Disposal/Unit/BeforeDisposalFlushEvent.cs @@ -0,0 +1,10 @@ +namespace Content.Shared.Disposal.Unit.Events; + +/// +/// Sent before the disposal unit flushes it's contents. +/// Allows adding tags for sorting and preventing the disposal unit from flushing. +/// +public sealed class BeforeDisposalFlushEvent : CancellableEntityEventArgs +{ + public readonly List Tags = new(); +} \ No newline at end of file diff --git a/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs b/Content.Shared/Disposal/Unit/DisposalUnitComponent.cs similarity index 68% rename from Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs rename to Content.Shared/Disposal/Unit/DisposalUnitComponent.cs index 36dd14f9b2..34fe223013 100644 --- a/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs +++ b/Content.Shared/Disposal/Unit/DisposalUnitComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Atmos; using Robust.Shared.Audio; using Content.Shared.Whitelist; using Robust.Shared.Containers; @@ -7,15 +8,24 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Disposal.Components; -[NetworkedComponent] -public abstract partial class SharedDisposalUnitComponent : Component +/// +/// Takes in entities and flushes them out to attached disposals tubes after a timer. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] +public sealed partial class DisposalUnitComponent : Component { public const string ContainerId = "disposals"; + /// + /// Air contained in the disposal unit. + /// + [DataField] + public GasMixture Air = new(Atmospherics.CellVolume); + /// /// Sounds played upon the unit flushing. /// - [ViewVariables(VVAccess.ReadWrite), DataField("soundFlush")] + [DataField("soundFlush"), AutoNetworkedField] public SoundSpecifier? FlushSound = new SoundPathSpecifier("/Audio/Machines/disposalflush.ogg"); /// @@ -39,20 +49,13 @@ public abstract partial class SharedDisposalUnitComponent : Component /// /// State for this disposals unit. /// - [DataField] + [DataField, AutoNetworkedField] public DisposalsPressureState State; - // TODO: Just make this use vaulting. - /// - /// We'll track whatever just left disposals so we know what collision we need to ignore until they stop intersecting our BB. - /// - [ViewVariables, DataField] - public List RecentlyEjected = new(); - /// /// Next time the disposal unit will be pressurized. /// - [DataField(customTypeSerializer:typeof(TimeOffsetSerializer))] + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField] public TimeSpan NextPressurized = TimeSpan.Zero; /// @@ -70,26 +73,24 @@ public abstract partial class SharedDisposalUnitComponent : Component /// /// Removes the pressure requirement for flushing. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public bool DisablePressure; /// /// Last time that an entity tried to exit this disposal unit. /// - [ViewVariables] + [DataField, AutoNetworkedField] public TimeSpan LastExitAttempt; [DataField] public bool AutomaticEngage = true; - [ViewVariables(VVAccess.ReadWrite)] - [DataField] + [DataField, AutoNetworkedField] public TimeSpan AutomaticEngageTime = TimeSpan.FromSeconds(30); /// /// Delay from trying to enter disposals ourselves. /// - [ViewVariables(VVAccess.ReadWrite)] [DataField] public float EntryDelay = 0.5f; @@ -104,20 +105,16 @@ public abstract partial class SharedDisposalUnitComponent : Component /// [ViewVariables] public Container Container = default!; - // TODO: Network power shit instead fam. - [ViewVariables, DataField] - public bool Powered; - /// /// Was the disposals unit engaged for a manual flush. /// - [ViewVariables(VVAccess.ReadWrite), DataField] + [DataField, AutoNetworkedField] public bool Engaged; /// /// Next time this unit will flush. Is the lesser of and /// - [ViewVariables, DataField(customTypeSerializer:typeof(TimeOffsetSerializer))] + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField] public TimeSpan? NextFlush; [Serializable, NetSerializable] @@ -162,37 +159,6 @@ public abstract partial class SharedDisposalUnitComponent : Component Power } - [Serializable, NetSerializable] - public sealed class DisposalUnitBoundUserInterfaceState : BoundUserInterfaceState, IEquatable - { - public readonly string UnitName; - public readonly string UnitState; - public readonly TimeSpan FullPressureTime; - public readonly bool Powered; - public readonly bool Engaged; - - public DisposalUnitBoundUserInterfaceState(string unitName, string unitState, TimeSpan fullPressureTime, bool powered, - bool engaged) - { - UnitName = unitName; - UnitState = unitState; - FullPressureTime = fullPressureTime; - Powered = powered; - Engaged = engaged; - } - - public bool Equals(DisposalUnitBoundUserInterfaceState? other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return UnitName == other.UnitName && - UnitState == other.UnitState && - Powered == other.Powered && - Engaged == other.Engaged && - FullPressureTime.Equals(other.FullPressureTime); - } - } - /// /// Message data sent from client to server when a disposal unit ui button is pressed. /// diff --git a/Content.Shared/Disposal/Unit/SharedDisposalTubeSystem.cs b/Content.Shared/Disposal/Unit/SharedDisposalTubeSystem.cs new file mode 100644 index 0000000000..58d4da7eba --- /dev/null +++ b/Content.Shared/Disposal/Unit/SharedDisposalTubeSystem.cs @@ -0,0 +1,14 @@ +using Content.Shared.Disposal.Components; + +namespace Content.Shared.Disposal.Unit; + +public abstract class SharedDisposalTubeSystem : EntitySystem +{ + public virtual bool TryInsert(EntityUid uid, + DisposalUnitComponent from, + IEnumerable? tags = default, + Tube.DisposalEntryComponent? entry = null) + { + return false; + } +} diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.cs similarity index 53% rename from Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs rename to Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.cs index 136ac2c440..1db24c700d 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.cs @@ -1,24 +1,15 @@ -using System.Diagnostics.CodeAnalysis; using System.Linq; -using Content.Server.Administration.Logs; -using Content.Server.Atmos.EntitySystems; -using Content.Server.Containers; -using Content.Server.Disposal.Tube; -using Content.Server.Disposal.Tube.Components; -using Content.Server.Disposal.Unit.Components; -using Content.Server.Popups; -using Content.Server.Power.Components; -using Content.Server.Power.EntitySystems; using Content.Shared.ActionBlocker; -using Content.Shared.Atmos; +using Content.Shared.Administration.Logs; +using Content.Shared.Body.Components; +using Content.Shared.Climbing.Systems; +using Content.Shared.Containers; using Content.Shared.Database; -using Content.Shared.Destructible; -using Content.Shared.Disposal; using Content.Shared.Disposal.Components; +using Content.Shared.Disposal.Unit.Events; using Content.Shared.DoAfter; using Content.Shared.DragDrop; using Content.Shared.Emag.Systems; -using Content.Shared.Explosion; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.IdentityManagement; @@ -27,59 +18,59 @@ using Content.Shared.Item; using Content.Shared.Movement.Events; using Content.Shared.Popups; using Content.Shared.Power; +using Content.Shared.Power.EntitySystems; +using Content.Shared.Throwing; using Content.Shared.Verbs; -using Robust.Server.Audio; -using Robust.Server.GameObjects; +using Content.Shared.Whitelist; +using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; -using Robust.Shared.GameStates; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; -using Robust.Shared.Player; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Serialization; +using Robust.Shared.Timing; using Robust.Shared.Utility; -namespace Content.Server.Disposal.Unit.EntitySystems; +namespace Content.Shared.Disposal.Unit; -public sealed class DisposalUnitSystem : SharedDisposalUnitSystem +[Serializable, NetSerializable] +public sealed partial class DisposalDoAfterEvent : SimpleDoAfterEvent { - [Dependency] private readonly IAdminLogManager _adminLogger = default!; - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; - [Dependency] private readonly AppearanceSystem _appearance = default!; - [Dependency] private readonly AtmosphereSystem _atmosSystem = default!; - [Dependency] private readonly AudioSystem _audioSystem = default!; - [Dependency] private readonly DisposalTubeSystem _disposalTubeSystem = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly PowerReceiverSystem _power = default!; - [Dependency] private readonly SharedContainerSystem _containerSystem = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; - [Dependency] private readonly TransformSystem _transformSystem = default!; - [Dependency] private readonly UserInterfaceSystem _ui = default!; - [Dependency] private readonly SharedMapSystem _map = default!; +} + +public abstract class SharedDisposalUnitSystem : EntitySystem +{ + [Dependency] protected readonly ActionBlockerSystem ActionBlockerSystem = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] protected readonly MetaDataSystem Metadata = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] protected readonly SharedAudioSystem Audio = default!; + [Dependency] protected readonly IGameTiming GameTiming = default!; + [Dependency] private readonly ISharedAdminLogManager _adminLog = default!; + [Dependency] private readonly ClimbSystem _climb = default!; + [Dependency] protected readonly SharedContainerSystem Containers = default!; + [Dependency] protected readonly SharedJointSystem Joints = default!; + [Dependency] private readonly SharedPowerReceiverSystem _power = default!; + [Dependency] private readonly SharedDisposalTubeSystem _disposalTubeSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] protected readonly SharedTransformSystem TransformSystem = default!; + [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; + [Dependency] private readonly SharedMapSystem _map = default!; + + protected static TimeSpan ExitAttemptDelay = TimeSpan.FromSeconds(0.5); + + // Percentage + public const float PressurePerSecond = 0.05f; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnGetState); SubscribeLocalEvent(OnPreventCollide); SubscribeLocalEvent(OnCanDragDropOn); - SubscribeLocalEvent(OnEmagged); - - // Shouldn't need re-anchoring. - SubscribeLocalEvent(OnAnchorChanged); - // TODO: Predict me when hands predicted - SubscribeLocalEvent(OnMovement); - SubscribeLocalEvent(OnPowerChange); - SubscribeLocalEvent(OnDisposalInit); - - SubscribeLocalEvent(OnActivate); - SubscribeLocalEvent(OnAfterInteractUsing); - SubscribeLocalEvent(OnDragDropOn); - SubscribeLocalEvent(OnDestruction); - SubscribeLocalEvent(OnExploded); - SubscribeLocalEvent>(AddInsertVerb); SubscribeLocalEvent>(AddDisposalAltVerbs); SubscribeLocalEvent>(AddClimbInsideVerb); @@ -88,27 +79,27 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem SubscribeLocalEvent(OnThrowInsert); - SubscribeLocalEvent(OnUiButtonPressed); + SubscribeLocalEvent(OnUiButtonPressed); + + SubscribeLocalEvent(OnEmagged); + SubscribeLocalEvent(OnAnchorChanged); + SubscribeLocalEvent(OnPowerChange); + SubscribeLocalEvent(OnDisposalInit); + + SubscribeLocalEvent(OnActivate); + SubscribeLocalEvent(OnAfterInteractUsing); + SubscribeLocalEvent(OnDragDropOn); + SubscribeLocalEvent(OnMovement); } - private void OnGetState(EntityUid uid, DisposalUnitComponent component, ref ComponentGetState args) - { - args.State = new DisposalUnitComponentState( - component.FlushSound, - component.State, - component.NextPressurized, - component.AutomaticEngageTime, - component.NextFlush, - component.Powered, - component.Engaged, - GetNetEntityList(component.RecentlyEjected)); - } - - private void AddDisposalAltVerbs(EntityUid uid, SharedDisposalUnitComponent component, GetVerbsEvent args) + private void AddDisposalAltVerbs(Entity ent, ref GetVerbsEvent args) { if (!args.CanAccess || !args.CanInteract) return; + var uid = ent.Owner; + var component = ent.Comp; + // Behavior for if the disposals bin has items in it if (component.Container.ContainedEntities.Count > 0) { @@ -133,41 +124,12 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem } } - private void AddClimbInsideVerb(EntityUid uid, SharedDisposalUnitComponent component, GetVerbsEvent args) - { - // This is not an interaction, activation, or alternative verb type because unfortunately most users are - // unwilling to accept that this is where they belong and don't want to accidentally climb inside. - if (!args.CanAccess || - !args.CanInteract || - component.Container.ContainedEntities.Contains(args.User) || - !_actionBlockerSystem.CanMove(args.User)) - { - return; - } - - if (!CanInsert(uid, component, args.User)) - return; - - // Add verb to climb inside of the unit, - Verb verb = new() - { - Act = () => TryInsert(uid, args.User, args.User), - DoContactInteraction = true, - Text = Loc.GetString("disposal-self-insert-verb-get-data-text") - }; - // TODO VERB ICON - // TODO VERB CATEGORY - // create a verb category for "enter"? - // See also, medical scanner. Also maybe add verbs for entering lockers/body bags? - args.Verbs.Add(verb); - } - - private void AddInsertVerb(EntityUid uid, SharedDisposalUnitComponent component, GetVerbsEvent args) + private void AddInsertVerb(EntityUid uid, DisposalUnitComponent component, GetVerbsEvent args) { if (!args.CanAccess || !args.CanInteract || args.Hands == null || args.Using == null) return; - if (!_actionBlockerSystem.CanDrop(args.User)) + if (!ActionBlockerSystem.CanDrop(args.User)) return; if (!CanInsert(uid, component, args.Using.Value)) @@ -180,7 +142,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem Act = () => { _handsSystem.TryDropIntoContainer(args.User, args.Using.Value, component.Container, checkActionBlocker: false, args.Hands); - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Using.Value)} into {ToPrettyString(uid)}"); + _adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Using.Value)} into {ToPrettyString(uid)}"); AfterInsert(uid, component, args.Using.Value, args.User); } }; @@ -188,7 +150,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem args.Verbs.Add(insertVerb); } - private void OnDoAfter(EntityUid uid, SharedDisposalUnitComponent component, DoAfterEvent args) + private void OnDoAfter(EntityUid uid, DisposalUnitComponent component, DoAfterEvent args) { if (args.Handled || args.Cancelled || args.Args.Target == null || args.Args.Used == null) return; @@ -204,89 +166,46 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem args.Cancelled = true; } - public override void DoInsertDisposalUnit(EntityUid uid, EntityUid toInsert, EntityUid user, SharedDisposalUnitComponent? disposal = null) - { - if (!ResolveDisposals(uid, ref disposal)) - return; - - if (!_containerSystem.Insert(toInsert, disposal.Container)) - return; - - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user):player} inserted {ToPrettyString(toInsert)} into {ToPrettyString(uid)}"); - AfterInsert(uid, disposal, toInsert, user); - } - public override void Update(float frameTime) { base.Update(frameTime); - var query = AllEntityQuery(); + var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var unit, out var metadata)) { - if (!metadata.EntityPaused) - Update(uid, unit, metadata, frameTime); + Update(uid, unit, metadata); } } - #region UI Handlers - private void OnUiButtonPressed(EntityUid uid, SharedDisposalUnitComponent component, SharedDisposalUnitComponent.UiButtonPressedMessage args) + // TODO: This should just use the same thing as entity storage? + private void OnMovement(EntityUid uid, DisposalUnitComponent component, ref ContainerRelayMovementEntityEvent args) { - if (args.Actor is not { Valid: true } player) - { + var currentTime = GameTiming.CurTime; + + if (!ActionBlockerSystem.CanMove(args.Entity)) return; - } - switch (args.Button) - { - case SharedDisposalUnitComponent.UiButton.Eject: - TryEjectContents(uid, component); - _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):player} hit eject button on {ToPrettyString(uid)}"); - break; - case SharedDisposalUnitComponent.UiButton.Engage: - ToggleEngage(uid, component); - _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):player} hit flush button on {ToPrettyString(uid)}, it's now {(component.Engaged ? "on" : "off")}"); - break; - case SharedDisposalUnitComponent.UiButton.Power: - _power.TogglePower(uid, user: args.Actor); - break; - default: - throw new ArgumentOutOfRangeException($"{ToPrettyString(player):player} attempted to hit a nonexistant button on {ToPrettyString(uid)}"); - } + if (!TryComp(args.Entity, out HandsComponent? hands) || + hands.Count == 0 || + currentTime < component.LastExitAttempt + ExitAttemptDelay) + return; + + Dirty(uid, component); + component.LastExitAttempt = currentTime; + Remove(uid, component, args.Entity); + UpdateUI((uid, component)); } - public void ToggleEngage(EntityUid uid, SharedDisposalUnitComponent component) - { - component.Engaged ^= true; - - if (component.Engaged) - { - ManualEngage(uid, component); - } - else - { - Disengage(uid, component); - } - } - - #endregion - - #region Eventbus Handlers - - private void OnActivate(EntityUid uid, SharedDisposalUnitComponent component, ActivateInWorldEvent args) + private void OnActivate(EntityUid uid, DisposalUnitComponent component, ActivateInWorldEvent args) { if (args.Handled || !args.Complex) return; - if (!TryComp(args.User, out ActorComponent? actor)) - { - return; - } - args.Handled = true; - _ui.OpenUi(uid, SharedDisposalUnitComponent.DisposalUnitUiKey.Key, actor.PlayerSession); + _ui.TryToggleUi(uid, DisposalUnitComponent.DisposalUnitUiKey.Key, args.User); } - private void OnAfterInteractUsing(EntityUid uid, SharedDisposalUnitComponent component, AfterInteractUsingEvent args) + private void OnAfterInteractUsing(EntityUid uid, DisposalUnitComponent component, AfterInteractUsingEvent args) { if (args.Handled || !args.CanReach) return; @@ -301,26 +220,23 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem return; } - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Used)} into {ToPrettyString(uid)}"); + _adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Used)} into {ToPrettyString(uid)}"); AfterInsert(uid, component, args.Used, args.User); args.Handled = true; } - private void OnDisposalInit(EntityUid uid, SharedDisposalUnitComponent component, ComponentInit args) + protected virtual void OnDisposalInit(Entity ent, ref ComponentInit args) { - component.Container = _containerSystem.EnsureContainer(uid, SharedDisposalUnitComponent.ContainerId); - - UpdateInterface(uid, component, component.Powered); + ent.Comp.Container = Containers.EnsureContainer(ent, DisposalUnitComponent.ContainerId); } - private void OnPowerChange(EntityUid uid, SharedDisposalUnitComponent component, ref PowerChangedEvent args) + private void OnPowerChange(EntityUid uid, DisposalUnitComponent component, ref PowerChangedEvent args) { - if (!component.Running || args.Powered == component.Powered) + if (!component.Running) return; - component.Powered = args.Powered; + UpdateUI((uid, component)); UpdateVisualState(uid, component); - UpdateInterface(uid, component, args.Powered); if (!args.Powered) { @@ -336,24 +252,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem } } - // TODO: This should just use the same thing as entity storage? - private void OnMovement(EntityUid uid, SharedDisposalUnitComponent component, ref ContainerRelayMovementEntityEvent args) - { - var currentTime = GameTiming.CurTime; - - if (!_actionBlockerSystem.CanMove(args.Entity)) - return; - - if (!TryComp(args.Entity, out HandsComponent? hands) || - hands.Count == 0 || - currentTime < component.LastExitAttempt + ExitAttemptDelay) - return; - - component.LastExitAttempt = currentTime; - Remove(uid, component, args.Entity); - } - - private void OnAnchorChanged(EntityUid uid, SharedDisposalUnitComponent component, ref AnchorStateChangedEvent args) + private void OnAnchorChanged(EntityUid uid, DisposalUnitComponent component, ref AnchorStateChangedEvent args) { if (Terminating(uid)) return; @@ -363,108 +262,238 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem TryEjectContents(uid, component); } - private void OnDestruction(EntityUid uid, SharedDisposalUnitComponent component, DestructionEventArgs args) - { - TryEjectContents(uid, component); - } - - private void OnDragDropOn(EntityUid uid, SharedDisposalUnitComponent component, ref DragDropTargetEvent args) + private void OnDragDropOn(EntityUid uid, DisposalUnitComponent component, ref DragDropTargetEvent args) { args.Handled = TryInsert(uid, args.Dragged, args.User); } - #endregion - - private void UpdateState(EntityUid uid, DisposalsPressureState state, SharedDisposalUnitComponent component, MetaDataComponent metadata) + protected virtual void UpdateUI(Entity entity) { - if (component.State == state) - return; - component.State = state; - UpdateVisualState(uid, component); - UpdateInterface(uid, component, component.Powered); - Dirty(uid, component, metadata); - - if (state == DisposalsPressureState.Ready) - { - component.NextPressurized = TimeSpan.Zero; - - // Manually engaged - if (component.Engaged) - { - component.NextFlush = GameTiming.CurTime + component.ManualFlushTime; - } - else if (component.Container.ContainedEntities.Count > 0) - { - component.NextFlush = GameTiming.CurTime + component.AutomaticEngageTime; - } - else - { - component.NextFlush = null; - } - } } /// - /// Work out if we can stop updating this disposals component i.e. full pressure and nothing colliding. + /// Returns the estimated time when the disposal unit will be back to full pressure. /// - private void Update(EntityUid uid, SharedDisposalUnitComponent component, MetaDataComponent metadata, float frameTime) + public TimeSpan EstimatedFullPressure(EntityUid uid, DisposalUnitComponent component) { - var state = GetState(uid, component, metadata); + if (component.NextPressurized < GameTiming.CurTime) + return TimeSpan.Zero; - // Pressurizing, just check if we need a state update. - if (component.NextPressurized > GameTiming.CurTime) + return component.NextPressurized; + } + + public bool CanFlush(EntityUid unit, DisposalUnitComponent component) + { + return GetState(unit, component) == DisposalsPressureState.Ready + && _power.IsPowered(unit) + && Comp(unit).Anchored; + } + + public void Remove(EntityUid uid, DisposalUnitComponent component, EntityUid toRemove) + { + if (GameTiming.ApplyingState) + return; + + if (!Containers.Remove(toRemove, component.Container)) + return; + + if (component.Container.ContainedEntities.Count == 0) + { + // If not manually engaged then reset the flushing entirely. + if (!component.Engaged) + { + component.NextFlush = null; + Dirty(uid, component); + UpdateUI((uid, component)); + } + } + + _climb.Climb(toRemove, toRemove, uid, silent: true); + + UpdateVisualState(uid, component); + } + + public void UpdateVisualState(EntityUid uid, DisposalUnitComponent component, bool flush = false) + { + if (!TryComp(uid, out AppearanceComponent? appearance)) { - UpdateState(uid, state, component, metadata); return; } - if (component.NextFlush != null) + if (!Transform(uid).Anchored) { - if (component.NextFlush.Value < GameTiming.CurTime) - { - TryFlush(uid, component); - } + _appearance.SetData(uid, DisposalUnitComponent.Visuals.VisualState, DisposalUnitComponent.VisualState.UnAnchored, appearance); + _appearance.SetData(uid, DisposalUnitComponent.Visuals.Handle, DisposalUnitComponent.HandleState.Normal, appearance); + _appearance.SetData(uid, DisposalUnitComponent.Visuals.Light, DisposalUnitComponent.LightStates.Off, appearance); + return; } - UpdateState(uid, state, component, metadata); + var state = GetState(uid, component); - Box2? disposalsBounds = null; - var count = component.RecentlyEjected.Count; - - if (count > 0) + switch (state) { - if (!HasComp(uid)) - { - component.RecentlyEjected.Clear(); - } - else - { - disposalsBounds = _lookup.GetWorldAABB(uid); - } + case DisposalsPressureState.Flushed: + _appearance.SetData(uid, DisposalUnitComponent.Visuals.VisualState, DisposalUnitComponent.VisualState.OverlayFlushing, appearance); + break; + case DisposalsPressureState.Pressurizing: + _appearance.SetData(uid, DisposalUnitComponent.Visuals.VisualState, DisposalUnitComponent.VisualState.OverlayCharging, appearance); + break; + case DisposalsPressureState.Ready: + _appearance.SetData(uid, DisposalUnitComponent.Visuals.VisualState, DisposalUnitComponent.VisualState.Anchored, appearance); + break; } - for (var i = 0; i < component.RecentlyEjected.Count; i++) - { - var ejectedId = component.RecentlyEjected[i]; - if (HasComp(ejectedId)) - { - // TODO: We need to use a specific collision method (which sloth hasn't coded yet) for actual bounds overlaps. - // TODO: Come do this sloth :^) - // Check for itemcomp as we won't just block the disposal unit "sleeping" for something it can't collide with anyway. - if (!HasComp(ejectedId) - && _lookup.GetWorldAABB(ejectedId).Intersects(disposalsBounds!.Value)) - { - continue; - } + _appearance.SetData(uid, DisposalUnitComponent.Visuals.Handle, component.Engaged + ? DisposalUnitComponent.HandleState.Engaged + : DisposalUnitComponent.HandleState.Normal, appearance); - component.RecentlyEjected.RemoveAt(i); - i--; - } + if (!_power.IsPowered(uid)) + { + _appearance.SetData(uid, DisposalUnitComponent.Visuals.Light, DisposalUnitComponent.LightStates.Off, appearance); + return; } - if (count != component.RecentlyEjected.Count) - Dirty(uid, component, metadata); + var lightState = DisposalUnitComponent.LightStates.Off; + + if (component.Container.ContainedEntities.Count > 0) + { + lightState |= DisposalUnitComponent.LightStates.Full; + } + + if (state is DisposalsPressureState.Pressurizing or DisposalsPressureState.Flushed) + { + lightState |= DisposalUnitComponent.LightStates.Charging; + } + else + { + lightState |= DisposalUnitComponent.LightStates.Ready; + } + + _appearance.SetData(uid, DisposalUnitComponent.Visuals.Light, lightState, appearance); + } + + /// + /// Gets the current pressure state of a disposals unit. + /// + /// + /// + /// + /// + public DisposalsPressureState GetState(EntityUid uid, DisposalUnitComponent component, MetaDataComponent? metadata = null) + { + var nextPressure = Metadata.GetPauseTime(uid, metadata) + component.NextPressurized - GameTiming.CurTime; + var pressurizeTime = 1f / PressurePerSecond; + var pressurizeDuration = pressurizeTime - component.FlushDelay.TotalSeconds; + + if (nextPressure.TotalSeconds > pressurizeDuration) + { + return DisposalsPressureState.Flushed; + } + + if (nextPressure > TimeSpan.Zero) + { + return DisposalsPressureState.Pressurizing; + } + + return DisposalsPressureState.Ready; + } + + public float GetPressure(EntityUid uid, DisposalUnitComponent component, MetaDataComponent? metadata = null) + { + if (!Resolve(uid, ref metadata)) + return 0f; + + var pauseTime = Metadata.GetPauseTime(uid, metadata); + return MathF.Min(1f, + (float)(GameTiming.CurTime - pauseTime - component.NextPressurized).TotalSeconds / PressurePerSecond); + } + + protected void OnPreventCollide(EntityUid uid, DisposalUnitComponent component, + ref PreventCollideEvent args) + { + var otherBody = args.OtherEntity; + + // Items dropped shouldn't collide but items thrown should + if (HasComp(otherBody) && !HasComp(otherBody)) + { + args.Cancelled = true; + } + } + + protected void OnCanDragDropOn(EntityUid uid, DisposalUnitComponent component, ref CanDropTargetEvent args) + { + if (args.Handled) + return; + + args.CanDrop = CanInsert(uid, component, args.Dragged); + args.Handled = true; + } + + protected void OnEmagged(EntityUid uid, DisposalUnitComponent component, ref GotEmaggedEvent args) + { + component.DisablePressure = true; + args.Handled = true; + } + + public virtual bool CanInsert(EntityUid uid, DisposalUnitComponent component, EntityUid entity) + { + // TODO: All of the below should be using the EXISTING EVENT + if (!Containers.CanInsert(entity, component.Container)) + return false; + + if (!Transform(uid).Anchored) + return false; + + var storable = HasComp(entity); + if (!storable && !HasComp(entity)) + return false; + + if (_whitelistSystem.IsBlacklistPass(component.Blacklist, entity) || + _whitelistSystem.IsWhitelistFail(component.Whitelist, entity)) + return false; + + if (TryComp(entity, out var physics) && (physics.CanCollide) || storable) + return true; + else + return false; + } + + public void DoInsertDisposalUnit(EntityUid uid, + EntityUid toInsert, + EntityUid user, + DisposalUnitComponent? disposal = null) + { + if (!Resolve(uid, ref disposal)) + return; + + if (!Containers.Insert(toInsert, disposal.Container)) + return; + + _adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user):player} inserted {ToPrettyString(toInsert)} into {ToPrettyString(uid)}"); + AfterInsert(uid, disposal, toInsert, user); + } + + public virtual void AfterInsert(EntityUid uid, + DisposalUnitComponent component, + EntityUid inserted, + EntityUid? user = null, + bool doInsert = false) + { + Audio.PlayPredicted(component.InsertSound, uid, user: user); + if (doInsert && !Containers.Insert(inserted, component.Container)) + return; + + if (user != inserted && user != null) + _adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user.Value):player} inserted {ToPrettyString(inserted)} into {ToPrettyString(uid)}"); + + QueueAutomaticEngage(uid, component); + + _ui.CloseUi(uid, DisposalUnitComponent.DisposalUnitUiKey.Key, inserted); + + // Maybe do pullable instead? Eh still fine. + Joints.RecursiveClearJoints(inserted); + UpdateVisualState(uid, component); } public bool TryInsert(EntityUid unitId, EntityUid toInsertId, EntityUid? userId, DisposalUnitComponent? unit = null) @@ -507,8 +536,61 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem return true; } + private void UpdateState(EntityUid uid, DisposalsPressureState state, DisposalUnitComponent component, MetaDataComponent metadata) + { + if (component.State == state) + return; - public bool TryFlush(EntityUid uid, SharedDisposalUnitComponent component) + component.State = state; + UpdateVisualState(uid, component); + Dirty(uid, component, metadata); + + if (state == DisposalsPressureState.Ready) + { + component.NextPressurized = TimeSpan.Zero; + + // Manually engaged + if (component.Engaged) + { + component.NextFlush = GameTiming.CurTime + component.ManualFlushTime; + } + else if (component.Container.ContainedEntities.Count > 0) + { + component.NextFlush = GameTiming.CurTime + component.AutomaticEngageTime; + } + else + { + component.NextFlush = null; + } + } + } + + /// + /// Work out if we can stop updating this disposals component i.e. full pressure and nothing colliding. + /// + private void Update(EntityUid uid, DisposalUnitComponent component, MetaDataComponent metadata) + { + var state = GetState(uid, component, metadata); + + // Pressurizing, just check if we need a state update. + if (component.NextPressurized > GameTiming.CurTime) + { + UpdateState(uid, state, component, metadata); + return; + } + + if (component.NextFlush != null) + { + if (component.NextFlush.Value < GameTiming.CurTime) + { + TryFlush(uid, component); + } + } + + UpdateState(uid, state, component, metadata); + } + + public bool TryFlush(EntityUid uid, DisposalUnitComponent component) { if (!CanFlush(uid, component)) { @@ -533,11 +615,12 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem var coords = xform.Coordinates; var entry = _map.GetLocal(xform.GridUid.Value, grid, coords) - .FirstOrDefault(HasComp); + .FirstOrDefault(HasComp); if (entry == default || component is not DisposalUnitComponent sDisposals) { component.Engaged = false; + UpdateUI((uid, component)); Dirty(uid, component); return false; } @@ -555,140 +638,23 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem component.NextFlush = null; UpdateVisualState(uid, component, true); - UpdateInterface(uid, component, component.Powered); - Dirty(uid, component); + UpdateUI((uid, component)); return true; } - private void HandleAir(EntityUid uid, DisposalUnitComponent component, TransformComponent xform) + protected virtual void HandleAir(EntityUid uid, DisposalUnitComponent component, TransformComponent xform) { - var air = component.Air; - var indices = _transformSystem.GetGridTilePositionOrDefault((uid, xform)); - if (_atmosSystem.GetTileMixture(xform.GridUid, xform.MapUid, indices, true) is { Temperature: > 0f } environment) - { - var transferMoles = 0.1f * (0.25f * Atmospherics.OneAtmosphere * 1.01f - air.Pressure) * air.Volume / (environment.Temperature * Atmospherics.R); - - component.Air = environment.Remove(transferMoles); - } } - public void UpdateInterface(EntityUid uid, SharedDisposalUnitComponent component, bool powered) - { - var compState = GetState(uid, component); - var stateString = Loc.GetString($"disposal-unit-state-{compState}"); - var state = new SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState(Name(uid), stateString, EstimatedFullPressure(uid, component), powered, component.Engaged); - _ui.SetUiState(uid, SharedDisposalUnitComponent.DisposalUnitUiKey.Key, state); - - var stateUpdatedEvent = new DisposalUnitUIStateUpdatedEvent(state); - RaiseLocalEvent(uid, stateUpdatedEvent); - } - - /// - /// Returns the estimated time when the disposal unit will be back to full pressure. - /// - private TimeSpan EstimatedFullPressure(EntityUid uid, SharedDisposalUnitComponent component) - { - if (component.NextPressurized < GameTiming.CurTime) - return TimeSpan.Zero; - - return component.NextPressurized; - } - - public void UpdateVisualState(EntityUid uid, SharedDisposalUnitComponent component, bool flush = false) - { - if (!TryComp(uid, out AppearanceComponent? appearance)) - { - return; - } - - if (!Transform(uid).Anchored) - { - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.UnAnchored, appearance); - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Handle, SharedDisposalUnitComponent.HandleState.Normal, appearance); - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Light, SharedDisposalUnitComponent.LightStates.Off, appearance); - return; - } - - var state = GetState(uid, component); - - switch (state) - { - case DisposalsPressureState.Flushed: - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.OverlayFlushing, appearance); - break; - case DisposalsPressureState.Pressurizing: - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.OverlayCharging, appearance); - break; - case DisposalsPressureState.Ready: - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.Anchored, appearance); - break; - } - - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Handle, component.Engaged - ? SharedDisposalUnitComponent.HandleState.Engaged - : SharedDisposalUnitComponent.HandleState.Normal, appearance); - - if (!component.Powered) - { - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Light, SharedDisposalUnitComponent.LightStates.Off, appearance); - return; - } - - var lightState = SharedDisposalUnitComponent.LightStates.Off; - - if (component.Container.ContainedEntities.Count > 0) - { - lightState |= SharedDisposalUnitComponent.LightStates.Full; - } - - if (state is DisposalsPressureState.Pressurizing or DisposalsPressureState.Flushed) - { - lightState |= SharedDisposalUnitComponent.LightStates.Charging; - } - else - { - lightState |= SharedDisposalUnitComponent.LightStates.Ready; - } - - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Light, lightState, appearance); - } - - public void Remove(EntityUid uid, SharedDisposalUnitComponent component, EntityUid toRemove) - { - _containerSystem.Remove(toRemove, component.Container); - - if (component.Container.ContainedEntities.Count == 0) - { - // If not manually engaged then reset the flushing entirely. - if (!component.Engaged) - { - component.NextFlush = null; - } - } - - if (!component.RecentlyEjected.Contains(toRemove)) - component.RecentlyEjected.Add(toRemove); - - UpdateVisualState(uid, component); - Dirty(uid, component); - } - - public bool CanFlush(EntityUid unit, SharedDisposalUnitComponent component) - { - return GetState(unit, component) == DisposalsPressureState.Ready - && component.Powered - && Comp(unit).Anchored; - } - - public void ManualEngage(EntityUid uid, SharedDisposalUnitComponent component, MetaDataComponent? metadata = null) + public void ManualEngage(EntityUid uid, DisposalUnitComponent component, MetaDataComponent? metadata = null) { component.Engaged = true; UpdateVisualState(uid, component); - UpdateInterface(uid, component, component.Powered); Dirty(uid, component); + UpdateUI((uid, component)); if (!CanFlush(uid, component)) return; @@ -701,7 +667,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem component.NextFlush = TimeSpan.FromSeconds(Math.Min((component.NextFlush ?? TimeSpan.MaxValue).TotalSeconds, nextEngage.TotalSeconds)); } - public void Disengage(EntityUid uid, SharedDisposalUnitComponent component) + public void Disengage(EntityUid uid, DisposalUnitComponent component) { component.Engaged = false; @@ -711,14 +677,14 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem } UpdateVisualState(uid, component); - UpdateInterface(uid, component, component.Powered); Dirty(uid, component); + UpdateUI((uid, component)); } /// /// Remove all entities currently in the disposal unit. /// - public void TryEjectContents(EntityUid uid, SharedDisposalUnitComponent component) + public void TryEjectContents(EntityUid uid, DisposalUnitComponent component) { foreach (var entity in component.Container.ContainedEntities.ToArray()) { @@ -729,38 +695,16 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem { component.NextFlush = null; Dirty(uid, component); + UpdateUI((uid, component)); } } - public override bool HasDisposals(EntityUid? uid) - { - return HasComp(uid); - } - - public override bool ResolveDisposals(EntityUid uid, [NotNullWhen(true)] ref SharedDisposalUnitComponent? component) - { - if (component != null) - return true; - - TryComp(uid, out var storage); - component = storage; - return component != null; - } - - public override bool CanInsert(EntityUid uid, SharedDisposalUnitComponent component, EntityUid entity) - { - if (!base.CanInsert(uid, component, entity)) - return false; - - return _containerSystem.CanInsert(entity, component.Container); - } - /// /// If something is inserted (or the likes) then we'll queue up an automatic flush in the future. /// - public void QueueAutomaticEngage(EntityUid uid, SharedDisposalUnitComponent component, MetaDataComponent? metadata = null) + public void QueueAutomaticEngage(EntityUid uid, DisposalUnitComponent component, MetaDataComponent? metadata = null) { - if (component.Deleted || !component.AutomaticEngage || !component.Powered && component.Container.ContainedEntities.Count == 0) + if (component.Deleted || !component.AutomaticEngage || !_power.IsPowered(uid) && component.Container.ContainedEntities.Count == 0) { return; } @@ -771,53 +715,74 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem component.NextFlush = flushTime; Dirty(uid, component); + UpdateUI((uid, component)); } - public void AfterInsert(EntityUid uid, SharedDisposalUnitComponent component, EntityUid inserted, EntityUid? user = null, bool doInsert = false) + private void OnUiButtonPressed(EntityUid uid, DisposalUnitComponent component, DisposalUnitComponent.UiButtonPressedMessage args) { - _audioSystem.PlayPvs(component.InsertSound, uid); + if (args.Actor is not { Valid: true } player) + { + return; + } - if (doInsert && !_containerSystem.Insert(inserted, component.Container)) + switch (args.Button) + { + case DisposalUnitComponent.UiButton.Eject: + TryEjectContents(uid, component); + _adminLog.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):player} hit eject button on {ToPrettyString(uid)}"); + break; + case DisposalUnitComponent.UiButton.Engage: + ToggleEngage(uid, component); + _adminLog.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):player} hit flush button on {ToPrettyString(uid)}, it's now {(component.Engaged ? "on" : "off")}"); + break; + case DisposalUnitComponent.UiButton.Power: + _power.TogglePower(uid, user: args.Actor); + break; + default: + throw new ArgumentOutOfRangeException($"{ToPrettyString(player):player} attempted to hit a nonexistant button on {ToPrettyString(uid)}"); + } + } + + public void ToggleEngage(EntityUid uid, DisposalUnitComponent component) + { + component.Engaged ^= true; + + if (component.Engaged) + { + ManualEngage(uid, component); + } + else + { + Disengage(uid, component); + } + } + + private void AddClimbInsideVerb(EntityUid uid, DisposalUnitComponent component, GetVerbsEvent args) + { + // This is not an interaction, activation, or alternative verb type because unfortunately most users are + // unwilling to accept that this is where they belong and don't want to accidentally climb inside. + if (!args.CanAccess || + !args.CanInteract || + component.Container.ContainedEntities.Contains(args.User) || + !ActionBlockerSystem.CanMove(args.User)) + { + return; + } + + if (!CanInsert(uid, component, args.User)) return; - if (user != inserted && user != null) - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user.Value):player} inserted {ToPrettyString(inserted)} into {ToPrettyString(uid)}"); - - QueueAutomaticEngage(uid, component); - - _ui.CloseUi(uid, SharedDisposalUnitComponent.DisposalUnitUiKey.Key, inserted); - - // Maybe do pullable instead? Eh still fine. - Joints.RecursiveClearJoints(inserted); - UpdateVisualState(uid, component); - } - - private void OnExploded(Entity ent, ref BeforeExplodeEvent args) - { - args.Contents.AddRange(ent.Comp.Container.ContainedEntities); - } - -} - -/// -/// Sent before the disposal unit flushes it's contents. -/// Allows adding tags for sorting and preventing the disposal unit from flushing. -/// -public sealed class DisposalUnitUIStateUpdatedEvent : EntityEventArgs -{ - public SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState State; - - public DisposalUnitUIStateUpdatedEvent(SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState state) - { - State = state; + // Add verb to climb inside of the unit, + Verb verb = new() + { + Act = () => TryInsert(uid, args.User, args.User), + DoContactInteraction = true, + Text = Loc.GetString("disposal-self-insert-verb-get-data-text") + }; + // TODO VERB ICON + // TODO VERB CATEGORY + // create a verb category for "enter"? + // See also, medical scanner. Also maybe add verbs for entering lockers/body bags? + args.Verbs.Add(verb); } } - -/// -/// Sent before the disposal unit flushes it's contents. -/// Allows adding tags for sorting and preventing the disposal unit from flushing. -/// -public sealed class BeforeDisposalFlushEvent : CancellableEntityEventArgs -{ - public readonly List Tags = new(); -} diff --git a/Content.Shared/Power/Components/SharedApcPowerReceiverComponent.cs b/Content.Shared/Power/Components/SharedApcPowerReceiverComponent.cs index 69c3bd790e..80bacd24bd 100644 --- a/Content.Shared/Power/Components/SharedApcPowerReceiverComponent.cs +++ b/Content.Shared/Power/Components/SharedApcPowerReceiverComponent.cs @@ -8,9 +8,15 @@ public abstract partial class SharedApcPowerReceiverComponent : Component [ViewVariables] public bool Powered; - [ViewVariables] - public virtual bool NeedsPower { get; set; } + /// + /// When false, causes this to appear powered even if not receiving power from an Apc. + /// + [ViewVariables(VVAccess.ReadWrite)] + public virtual bool NeedsPower { get; set;} - [ViewVariables] + /// + /// When true, causes this to never appear powered. + /// + [ViewVariables(VVAccess.ReadWrite)] public virtual bool PowerDisabled { get; set; } } diff --git a/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs b/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs index 2d152d8b45..d86273974b 100644 --- a/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs +++ b/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs @@ -62,8 +62,13 @@ public abstract class SharedPowerReceiverSystem : EntitySystem return !receiver.PowerDisabled; // i.e. PowerEnabled } - /// - /// Checks if entity is APC-powered device, and if it have power. + protected virtual void RaisePower(Entity entity) + { + // NOOP on server because client has 0 idea of load so we can't raise it properly in shared. + } + + /// + /// Checks if entity is APC-powered device, and if it have power. /// public bool IsPowered(Entity entity) { diff --git a/Content.Shared/Storage/EntitySystems/DumpableSystem.cs b/Content.Shared/Storage/EntitySystems/DumpableSystem.cs index 93c4b69e4d..d0ad27eee5 100644 --- a/Content.Shared/Storage/EntitySystems/DumpableSystem.cs +++ b/Content.Shared/Storage/EntitySystems/DumpableSystem.cs @@ -1,5 +1,7 @@ using System.Linq; using Content.Shared.Disposal; +using Content.Shared.Disposal.Components; +using Content.Shared.Disposal.Unit; using Content.Shared.DoAfter; using Content.Shared.Interaction; using Content.Shared.Item; @@ -40,7 +42,7 @@ public sealed class DumpableSystem : EntitySystem if (!args.CanReach || args.Handled) return; - if (!_disposalUnitSystem.HasDisposals(args.Target) && !HasComp(args.Target)) + if (!HasComp(args.Target) && !HasComp(args.Target)) return; if (!TryComp(uid, out var storage)) @@ -81,7 +83,7 @@ public sealed class DumpableSystem : EntitySystem if (!TryComp(uid, out var storage) || !storage.Container.ContainedEntities.Any()) return; - if (_disposalUnitSystem.HasDisposals(args.Target)) + if (HasComp(args.Target)) { UtilityVerb verb = new() { @@ -146,7 +148,7 @@ public sealed class DumpableSystem : EntitySystem var dumped = false; - if (_disposalUnitSystem.HasDisposals(args.Args.Target)) + if (HasComp(args.Args.Target)) { dumped = true; diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml index cb3ae3d065..2605af8019 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml @@ -32,8 +32,6 @@ components: - HumanoidAppearance - type: DisposalUnit - autoEngageEnabled: false - noUI: true blacklist: components: - HumanoidAppearance diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml index 9d04b32563..7388a814f7 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml @@ -8,6 +8,8 @@ snap: - Disposal components: + - type: Climbable + vaultable: false - type: Sprite sprite: Structures/Piping/disposal.rsi layers: @@ -29,6 +31,17 @@ map: [ "enum.DisposalUnitVisualLayers.OverlayEngaged" ] - type: Physics bodyType: Static + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.45,-0.45,0.45,0.45" + density: 55 + mask: + - TableMask + layer: + - TableLayer - type: Destructible thresholds: - trigger: @@ -116,7 +129,6 @@ graph: DisposalMachine node: mailing_unit - type: DisposalUnit - autoEngageEnabled: false whitelist: components: - Item @@ -136,6 +148,6 @@ - type: UserInterface interfaces: enum.MailingUnitUiKey.Key: - type: DisposalUnitBoundUserInterface + type: MailingUnitBoundUserInterface enum.ConfigurationUiKey.Key: type: ConfigurationBoundUserInterface