diff --git a/Content.Client/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Client/Chemistry/EntitySystems/ReagentDispenserSystem.cs deleted file mode 100644 index c0ac6c4bc3..0000000000 --- a/Content.Client/Chemistry/EntitySystems/ReagentDispenserSystem.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Content.Shared.Chemistry.EntitySystems; -using JetBrains.Annotations; - -namespace Content.Client.Chemistry.EntitySystems -{ - [UsedImplicitly] - public sealed class ReagentDispenserSystem : SharedReagentDispenserSystem - { - // hello there. - // General Kenobi - } -} diff --git a/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs b/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs index 25604a881f..0577aa1fcb 100644 --- a/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs +++ b/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs @@ -12,6 +12,7 @@ namespace Content.Client.Chemistry.UI [UsedImplicitly] public sealed class ChemMasterBoundUserInterface : BoundUserInterface { + [Dependency] private readonly IEntityManager _entityManager = default!; private ChemMasterWindow? _window; public ChemMasterBoundUserInterface(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey) @@ -30,7 +31,7 @@ namespace Content.Client.Chemistry.UI // Setup window layout/elements _window = new ChemMasterWindow { - Title = Loc.GetString("chem-master-bound-user-interface-title"), + Title = _entityManager.GetComponent(Owner.Owner).EntityName, }; _window.OpenCentered(); diff --git a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs index d282a17cbe..3693faf7a7 100644 --- a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs +++ b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs @@ -98,7 +98,6 @@ namespace Content.Client.Chemistry.UI public void UpdateState(BoundUserInterfaceState state) { var castState = (ChemMasterBoundUserInterfaceState) state; - Title = castState.DispenserName; if (castState.UpdateLabel) LabelLine = GenerateLabel(castState); UpdatePanelInfo(castState); diff --git a/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs b/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs index 7f7b0bb630..c5417caf40 100644 --- a/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs +++ b/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs @@ -1,10 +1,7 @@ -using System.Linq; -using Content.Shared.Chemistry.Dispenser; +using Content.Shared.Chemistry; using Content.Shared.Containers.ItemSlots; using JetBrains.Annotations; using Robust.Client.GameObjects; -using Robust.Client.UserInterface.Controls; -using static Content.Shared.Chemistry.Dispenser.SharedReagentDispenserComponent; namespace Content.Client.Chemistry.UI { @@ -14,6 +11,7 @@ namespace Content.Client.Chemistry.UI [UsedImplicitly] public sealed class ReagentDispenserBoundUserInterface : BoundUserInterface { + [Dependency] private readonly IEntityManager _entityManager = default!; private ReagentDispenserWindow? _window; private ReagentDispenserBoundUserInterfaceState? _lastState; @@ -30,31 +28,45 @@ namespace Content.Client.Chemistry.UI { base.Open(); - //Setup window layout/elements - _window = new(); + // Setup window layout/elements + _window = new() + { + Title = _entityManager.GetComponent(Owner.Owner).EntityName, + }; _window.OpenCentered(); _window.OnClose += Close; - //Setup static button actions. - _window.EjectButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(BeakerSlotId)); - _window.ClearButton.OnPressed += _ => ButtonPressed(UiButton.Clear); - _window.DispenseButton1.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount1); - _window.DispenseButton5.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount5); - _window.DispenseButton10.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount10); - _window.DispenseButton15.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount15); - _window.DispenseButton20.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount20); - _window.DispenseButton25.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount25); - _window.DispenseButton30.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount30); - _window.DispenseButton50.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount50); - _window.DispenseButton100.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount100); + // Setup static button actions. + _window.EjectButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(SharedReagentDispenser.OutputSlotName)); + _window.ClearButton.OnPressed += _ => SendMessage(new ReagentDispenserClearContainerSolutionMessage()); + _window.DispenseButton1.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U1)); + _window.DispenseButton5.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U5)); + _window.DispenseButton10.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U10)); + _window.DispenseButton15.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U15)); + _window.DispenseButton20.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U20)); + _window.DispenseButton25.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U25)); + _window.DispenseButton30.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U30)); + _window.DispenseButton50.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U50)); + _window.DispenseButton100.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U100)); + + // Setup reagent button actions. + _window.OnDispenseReagentButtonPressed += (args, button) => SendMessage(new ReagentDispenserDispenseReagentMessage(button.ReagentId)); + _window.OnDispenseReagentButtonMouseEntered += (args, button) => { + if (_lastState is not null) + _window.UpdateContainerInfo(_lastState, button.ReagentId); + }; + _window.OnDispenseReagentButtonMouseExited += (args, button) => { + if (_lastState is not null) + _window.UpdateContainerInfo(_lastState); + }; } /// - /// Update the ui each time new state data is sent from the server. + /// Update the UI each time new state data is sent from the server. /// /// - /// Data of the that this ui represents. + /// Data of the that this UI represents. /// Sent from the server. /// protected override void UpdateState(BoundUserInterfaceState state) @@ -64,50 +76,9 @@ namespace Content.Client.Chemistry.UI var castState = (ReagentDispenserBoundUserInterfaceState)state; _lastState = castState; - UpdateReagentsList(castState.Inventory); //Update reagents list & reagent button actions _window?.UpdateState(castState); //Update window state } - /// - /// Update the list of reagents that this dispenser can dispense on the UI. - /// - /// A list of the reagents which can be dispensed. - private void UpdateReagentsList(List inventory) - { - if (_window == null) - { - return; - } - - _window.UpdateReagentsList(inventory); - - for (var i = 0; i < _window.ChemicalList.Children.Count(); i++) - { - var button = (Button)_window.ChemicalList.Children.ElementAt(i); - var i1 = i; - button.OnPressed += _ => ButtonPressed(UiButton.Dispense, i1); - button.OnMouseEntered += _ => - { - if (_lastState != null) - { - _window.UpdateContainerInfo(_lastState, inventory[i1].ID); - } - }; - button.OnMouseExited += _ => - { - if (_lastState != null) - { - _window.UpdateContainerInfo(_lastState); - } - }; - } - } - - private void ButtonPressed(UiButton button, int dispenseIndex = -1) - { - SendMessage(new UiButtonPressedMessage(button, dispenseIndex)); - } - protected override void Dispose(bool disposing) { base.Dispose(disposing); diff --git a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs index 5f1eec05a2..64b5f5ddd2 100644 --- a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs +++ b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml.cs @@ -1,28 +1,27 @@ -using System.Collections.Generic; +using System.Linq; using Content.Client.Stylesheets; -using Content.Client.UserInterface; -using Content.Shared.Chemistry.Dispenser; +using Content.Shared.Chemistry; using Content.Shared.Chemistry.Reagent; using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Localization; using Robust.Shared.Prototypes; -using static Content.Shared.Chemistry.Dispenser.SharedReagentDispenserComponent; using static Robust.Client.UserInterface.Controls.BoxContainer; namespace Content.Client.Chemistry.UI { /// - /// Client-side UI used to control a + /// Client-side UI used to control a . /// [GenerateTypedNameReferences] public sealed partial class ReagentDispenserWindow : DefaultWindow { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + public event Action? OnDispenseReagentButtonPressed; + public event Action? OnDispenseReagentButtonMouseEntered; + public event Action? OnDispenseReagentButtonMouseExited; /// /// Create and initialize the dispenser UI client-side. Creates the basic layout, @@ -47,26 +46,27 @@ namespace Content.Client.Chemistry.UI /// /// Update the button grid of reagents which can be dispensed. - /// The actions for these buttons are set in . /// /// Reagents which can be dispensed by this dispenser - public void UpdateReagentsList(List inventory) + public void UpdateReagentsList(List inventory) { if (ChemicalList == null) return; if (inventory == null) return; ChemicalList.Children.Clear(); - foreach (var entry in inventory) + foreach (var entry in inventory + .OrderBy(r => {_prototypeManager.TryIndex(r, out ReagentPrototype? p); return p?.LocalizedName;})) { - if (_prototypeManager.TryIndex(entry.ID, out ReagentPrototype? proto)) - { - ChemicalList.AddChild(new Button {Text = proto.LocalizedName}); - } - else - { - ChemicalList.AddChild(new Button {Text = Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text") }); - } + var localizedName = _prototypeManager.TryIndex(entry, out ReagentPrototype? p) + ? p.LocalizedName + : Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text"); + + var button = new DispenseReagentButton(entry, localizedName); + button.OnPressed += args => OnDispenseReagentButtonPressed?.Invoke(args, button); + button.OnMouseEntered += args => OnDispenseReagentButtonMouseEntered?.Invoke(args, button); + button.OnMouseExited += args => OnDispenseReagentButtonMouseExited?.Invoke(args, button); + ChemicalList.AddChild(button); } } @@ -77,50 +77,40 @@ namespace Content.Client.Chemistry.UI public void UpdateState(BoundUserInterfaceState state) { var castState = (ReagentDispenserBoundUserInterfaceState) state; - Title = castState.DispenserName; UpdateContainerInfo(castState); - - // Disable all buttons if not powered - if (Contents.Children != null) - { - ButtonHelpers.SetButtonDisabledRecursive(Contents, !castState.HasPower); - EjectButton.Disabled = false; - } + UpdateReagentsList(castState.Inventory); // Disable the Clear & Eject button if no beaker - if (!castState.HasBeaker) - { - ClearButton.Disabled = true; - EjectButton.Disabled = true; - } + ClearButton.Disabled = castState.OutputContainer is null; + EjectButton.Disabled = castState.OutputContainer is null; - switch (castState.SelectedDispenseAmount.Int()) + switch (castState.SelectedDispenseAmount) { - case 1: + case ReagentDispenserDispenseAmount.U1: DispenseButton1.Pressed = true; break; - case 5: + case ReagentDispenserDispenseAmount.U5: DispenseButton5.Pressed = true; break; - case 10: + case ReagentDispenserDispenseAmount.U10: DispenseButton10.Pressed = true; break; - case 15: + case ReagentDispenserDispenseAmount.U15: DispenseButton15.Pressed = true; break; - case 20: + case ReagentDispenserDispenseAmount.U20: DispenseButton20.Pressed = true; break; - case 25: + case ReagentDispenserDispenseAmount.U25: DispenseButton25.Pressed = true; break; - case 30: + case ReagentDispenserDispenseAmount.U30: DispenseButton30.Pressed = true; break; - case 50: + case ReagentDispenserDispenseAmount.U50: DispenseButton50.Pressed = true; break; - case 100: + case ReagentDispenserDispenseAmount.U100: DispenseButton100.Pressed = true; break; } @@ -131,12 +121,13 @@ namespace Content.Client.Chemistry.UI /// Also highlights a reagent if it's dispense button is being mouse hovered. /// /// State data for the dispenser. - /// Prototype id of the reagent whose dispense button is currently being mouse hovered. - public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state, string highlightedReagentId = "") + /// Prototype ID of the reagent whose dispense button is currently being mouse hovered, + /// or null if no button is being hovered. + public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state, string? highlightedReagentId = null) { ContainerInfo.Children.Clear(); - if (!state.HasBeaker) + if (state.OutputContainer is null) { ContainerInfo.Children.Add(new Label {Text = Loc.GetString("reagent-dispenser-window-no-container-loaded-text") }); return; @@ -147,67 +138,55 @@ namespace Content.Client.Chemistry.UI Orientation = LayoutOrientation.Horizontal, Children = { - new Label {Text = $"{state.ContainerName}: "}, + new Label {Text = $"{state.OutputContainer.DisplayName}: "}, new Label { - Text = $"{state.BeakerCurrentVolume}/{state.BeakerMaxVolume}", + Text = $"{state.OutputContainer.CurrentVolume}/{state.OutputContainer.MaxVolume}", StyleClasses = {StyleNano.StyleClassLabelSecondaryColor} } } }); - if (state.ContainerReagents == null) + foreach (var reagent in state.OutputContainer.Contents) { - return; - } + // Try get to the prototype for the given reagent. This gives us its name. + var localizedName = _prototypeManager.TryIndex(reagent.Id, out ReagentPrototype? p) + ? p.LocalizedName + : Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text"); - foreach (var reagent in state.ContainerReagents) - { - var name = Loc.GetString("reagent-dispenser-window-unknown-reagent-text"); - //Try to the prototype for the given reagent. This gives us it's name. - if (_prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto)) + var nameLabel = new Label {Text = $"{localizedName}: "}; + var quantityLabel = new Label { - name = proto.LocalizedName; + Text = Loc.GetString("reagent-dispenser-window-quantity-label-text", ("quantity", reagent.Quantity)), + StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}, + }; + + // Check if the reagent is being moused over. If so, color it green. + if (reagent.Id == highlightedReagentId) { + nameLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateGood); + quantityLabel.SetOnlyStyleClass(StyleNano.StyleClassPowerStateGood); } - //Check if the reagent is being moused over. If so, color it green. - if (proto != null && proto.ID == highlightedReagentId) + ContainerInfo.Children.Add(new BoxContainer { - ContainerInfo.Children.Add(new BoxContainer + Orientation = LayoutOrientation.Horizontal, + Children = { - Orientation = LayoutOrientation.Horizontal, - Children = - { - new Label - { - Text = $"{name}: ", - StyleClasses = {StyleNano.StyleClassPowerStateGood} - }, - new Label - { - Text = Loc.GetString("reagent-dispenser-window-quantity-label-text", ("quantity", reagent.Quantity)), - StyleClasses = {StyleNano.StyleClassPowerStateGood} - } - } - }); - } - else //Otherwise, color it the normal colors. - { - ContainerInfo.Children.Add(new BoxContainer - { - Orientation = LayoutOrientation.Horizontal, - Children = - { - new Label {Text = $"{name}: "}, - new Label - { - Text = Loc.GetString("reagent-dispenser-window-quantity-label-text", ("quantity", reagent.Quantity)), - StyleClasses = {StyleNano.StyleClassLabelSecondaryColor} - } - } - }); - } + nameLabel, + quantityLabel, + } + }); } } } + + public sealed class DispenseReagentButton : Button { + public string ReagentId { get; } + + public DispenseReagentButton(string reagentId, string text) + { + ReagentId = reagentId; + Text = text; + } + } } diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs index 295d36d676..586f7d271d 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -103,7 +103,6 @@ namespace Content.Client.Entry // Do not add to these, they are legacy. _componentFactory.RegisterClass(); - _componentFactory.RegisterClass(); _componentFactory.RegisterClass(); _componentFactory.RegisterClass(); // Do not add to the above, they are legacy diff --git a/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs b/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs index 38309437a4..3104a11d35 100644 --- a/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs +++ b/Content.Server/Chemistry/Components/ReagentDispenserComponent.cs @@ -1,281 +1,34 @@ -using System.Linq; -using Content.Server.Administration.Logs; using Content.Server.Chemistry.EntitySystems; -using Content.Server.Power.Components; -using Content.Server.UserInterface; -using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry; using Content.Shared.Chemistry.Dispenser; -using Content.Shared.Database; -using Content.Shared.FixedPoint; -using JetBrains.Annotations; -using Robust.Server.GameObjects; using Robust.Shared.Audio; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Chemistry.Components { /// - /// Contains all the server-side logic for reagent dispensers. See also . - /// This includes initializing the component based on prototype data, and sending and receiving messages from the client. - /// Messages sent to the client are used to update update the user interface for a component instance. - /// Messages sent from the client are used to handle ui button presses. + /// A machine that dispenses reagents into a solution container. /// [RegisterComponent] - [ComponentReference(typeof(SharedReagentDispenserComponent))] - public sealed class ReagentDispenserComponent : SharedReagentDispenserComponent + [Access(typeof(ReagentDispenserSystem))] + public sealed class ReagentDispenserComponent : Component { - private static ReagentInventoryComparer _comparer = new(); - public static string SolutionName = "reagent"; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IEntityManager _entities = default!; - [Dependency] private readonly IAdminLogManager _adminLogger = default!; + [DataField("pack", customTypeSerializer:typeof(PrototypeIdSerializer))] + [ViewVariables(VVAccess.ReadWrite)] + public string? PackPrototypeId = default!; - [ViewVariables] [DataField("pack", customTypeSerializer:typeof(PrototypeIdSerializer))] private string _packPrototypeId = ""; + [DataField("emagPack", customTypeSerializer:typeof(PrototypeIdSerializer))] + [ViewVariables(VVAccess.ReadWrite)] + public string? EmagPackPrototypeId = default!; - [ViewVariables] [DataField("emagPack", customTypeSerializer:typeof(PrototypeIdSerializer))] public string EmagPackPrototypeId = ""; + [ViewVariables(VVAccess.ReadWrite)] + public bool IsEmagged = false; - public bool AlreadyEmagged = false; + [DataField("clickSound"), ViewVariables(VVAccess.ReadWrite)] + public SoundSpecifier ClickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg"); - [DataField("clickSound")] - private SoundSpecifier _clickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg"); - - [ViewVariables] private FixedPoint2 _dispenseAmount = FixedPoint2.New(10); - - [UsedImplicitly] - [ViewVariables] - private Solution? Solution - { - get - { - EntitySystem.Get().TryGetSolution(Owner, SolutionName, out var solution); - return solution; - } - } - - [ViewVariables] - private bool Powered => !_entities.TryGetComponent(Owner, out ApcPowerReceiverComponent? receiver) || receiver.Powered; - - [ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(ReagentDispenserUiKey.Key); - - /// - /// Called once per instance of this component. Gets references to any other components needed - /// by this component and initializes it's UI and other data. - /// - protected override void Initialize() - { - base.Initialize(); - - if (UserInterface != null) - { - UserInterface.OnReceiveMessage += OnUiReceiveMessage; - } - - InitializeFromPrototype(); - } - - /// - /// Checks to see if the pack defined in this components yaml prototype - /// exists. If so, it fills the reagent inventory list. - /// - private void InitializeFromPrototype() - { - if (string.IsNullOrEmpty(_packPrototypeId)) return; - - if (!_prototypeManager.TryIndex(_packPrototypeId, out ReagentDispenserInventoryPrototype? packPrototype)) - { - return; - } - - foreach (var entry in packPrototype.Inventory) - { - Inventory.Add(new ReagentDispenserInventoryEntry(entry)); - } - - Inventory.Sort(_comparer); - } - - public void AddFromPrototype(string pack) - { - if (string.IsNullOrEmpty(pack)) return; - - if (!_prototypeManager.TryIndex(pack, out ReagentDispenserInventoryPrototype? packPrototype)) - { - return; - } - - foreach (var entry in packPrototype.Inventory) - { - Inventory.Add(new ReagentDispenserInventoryEntry(entry)); - } - - Inventory.Sort(_comparer); - } - - - public void OnPowerChanged() - { - UpdateUserInterface(); - } - - /// - /// Handles ui messages from the client. For things such as button presses - /// which interact with the world and require server action. - /// - /// A user interface message from the client. - private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj) - { - if (obj.Session.AttachedEntity == null) - { - return; - } - - if (obj.Message is not UiButtonPressedMessage msg ) - return; - - if (!PlayerCanUseDispenser(obj.Session.AttachedEntity, true)) - return; - - switch (msg.Button) - { - case UiButton.Clear: - TryClear(); - break; - case UiButton.SetDispenseAmount1: - _dispenseAmount = FixedPoint2.New(1); - break; - case UiButton.SetDispenseAmount5: - _dispenseAmount = FixedPoint2.New(5); - break; - case UiButton.SetDispenseAmount10: - _dispenseAmount = FixedPoint2.New(10); - break; - case UiButton.SetDispenseAmount15: - _dispenseAmount = FixedPoint2.New(15); - break; - case UiButton.SetDispenseAmount20: - _dispenseAmount = FixedPoint2.New(20); - break; - case UiButton.SetDispenseAmount25: - _dispenseAmount = FixedPoint2.New(25); - break; - case UiButton.SetDispenseAmount30: - _dispenseAmount = FixedPoint2.New(30); - break; - case UiButton.SetDispenseAmount50: - _dispenseAmount = FixedPoint2.New(50); - break; - case UiButton.SetDispenseAmount100: - _dispenseAmount = FixedPoint2.New(100); - break; - case UiButton.Dispense: - if (BeakerSlot.HasItem) - { - TryDispense(msg.DispenseIndex); - // Ew - if (BeakerSlot.Item != null) - _adminLogger.Add(LogType.ChemicalReaction, LogImpact.Medium, - $"{_entities.ToPrettyString(obj.Session.AttachedEntity.Value):player} dispensed {_dispenseAmount}u of {Inventory[msg.DispenseIndex].ID} into {_entities.ToPrettyString(BeakerSlot.Item.Value):entity}"); - } - - break; - default: - throw new ArgumentOutOfRangeException(); - } - - ClickSound(); - } - - /// - /// Checks whether the player entity is able to use the chem dispenser. - /// - /// The player entity. - /// Returns true if the entity can use the dispenser, and false if it cannot. - private bool PlayerCanUseDispenser(EntityUid? playerEntity, bool needsPower = true) - { - //Need player entity to check if they are still able to use the dispenser - if (playerEntity == null) - return false; - - //Check if device is powered - if (needsPower && !Powered) - return false; - - return true; - } - - /// - /// Gets component data to be used to update the user interface client-side. - /// - /// Returns a - private ReagentDispenserBoundUserInterfaceState GetUserInterfaceState() - { - if (BeakerSlot.Item is not {Valid: true} beaker || - !_entities.TryGetComponent(beaker, out FitsInDispenserComponent? fits) || - !EntitySystem.Get().TryGetSolution(beaker, fits.Solution, out var solution)) - { - return new ReagentDispenserBoundUserInterfaceState(Powered, false, FixedPoint2.New(0), - FixedPoint2.New(0), - string.Empty, Inventory, _entities.GetComponent(Owner).EntityName, null, _dispenseAmount); - } - - return new ReagentDispenserBoundUserInterfaceState(Powered, true, solution.CurrentVolume, - solution.MaxVolume, - _entities.GetComponent(beaker).EntityName, Inventory, _entities.GetComponent(Owner).EntityName, solution.Contents.ToList(), _dispenseAmount); - } - - public void UpdateUserInterface() - { - if (!Initialized) return; - - var state = GetUserInterfaceState(); - UserInterface?.SetState(state); - } - - /// - /// If this component contains an entity with a , remove all of it's reagents / solutions. - /// - private void TryClear() - { - if (BeakerSlot.Item is not {Valid: true} beaker || - !_entities.TryGetComponent(beaker, out FitsInDispenserComponent? fits) || - !EntitySystem.Get().TryGetSolution(beaker, fits.Solution, out var solution)) - return; - - EntitySystem.Get().RemoveAllSolution(beaker, solution); - - UpdateUserInterface(); - } - - /// - /// If this component contains an entity with a , attempt to dispense the specified reagent to it. - /// - /// The index of the reagent in Inventory. - private void TryDispense(int dispenseIndex) - { - if (BeakerSlot.Item is not {Valid: true} beaker || - !_entities.TryGetComponent(beaker, out FitsInDispenserComponent? fits) || - !EntitySystem.Get().TryGetSolution(beaker, fits.Solution, out var solution)) return; - - EntitySystem.Get() - .TryAddReagent(beaker, solution, Inventory[dispenseIndex].ID, _dispenseAmount, out _); - - UpdateUserInterface(); - } - - private void ClickSound() - { - SoundSystem.Play(_clickSound.GetSound(), Filter.Pvs(Owner), Owner, AudioParams.Default.WithVolume(-2f)); - } - - private sealed class ReagentInventoryComparer : Comparer - { - public override int Compare(ReagentDispenserInventoryEntry x, ReagentDispenserInventoryEntry y) - { - return string.Compare(x.ID, y.ID, StringComparison.InvariantCultureIgnoreCase); - } - } + [ViewVariables(VVAccess.ReadWrite)] + public ReagentDispenserDispenseAmount DispenseAmount = ReagentDispenserDispenseAmount.U10; } } diff --git a/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs index 9b03b4eb4b..d37da47006 100644 --- a/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs @@ -35,7 +35,7 @@ namespace Content.Server.Chemistry.EntitySystems [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly StorageSystem _storageSystem = default!; [Dependency] private readonly LabelSystem _labelSystem = default!; - + private const string PillPrototypeId = "Pill"; public override void Initialize() @@ -59,18 +59,15 @@ namespace Content.Server.Chemistry.EntitySystems { if (!_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out var bufferSolution)) return; - var inputContainer = _itemSlotsSystem.GetItem(chemMaster.Owner, SharedChemMaster.InputSlotName); - var outputContainer = _itemSlotsSystem.GetItem(chemMaster.Owner, SharedChemMaster.OutputSlotName); + var inputContainer = _itemSlotsSystem.GetItemOrNull(chemMaster.Owner, SharedChemMaster.InputSlotName); + var outputContainer = _itemSlotsSystem.GetItemOrNull(chemMaster.Owner, SharedChemMaster.OutputSlotName); - var dispenserName = Name(chemMaster.Owner); var bufferReagents = bufferSolution.Contents; var bufferCurrentVolume = bufferSolution.CurrentVolume; var state = new ChemMasterBoundUserInterfaceState( - chemMaster.Mode, dispenserName, - BuildInputContainerInfo(inputContainer), BuildOutputContainerInfo(outputContainer), - bufferReagents, bufferCurrentVolume, - chemMaster.PillType, chemMaster.PillDosageLimit, updateLabel); + chemMaster.Mode, BuildInputContainerInfo(inputContainer), BuildOutputContainerInfo(outputContainer), + bufferReagents, bufferCurrentVolume, chemMaster.PillType, chemMaster.PillDosageLimit, updateLabel); _userInterfaceSystem.TrySetUiState(chemMaster.Owner, ChemMasterUiKey.Key, state); } @@ -121,7 +118,7 @@ namespace Content.Server.Chemistry.EntitySystems private void TransferReagents(ChemMasterComponent chemMaster, string reagentId, FixedPoint2 amount, bool fromBuffer) { - var container = _itemSlotsSystem.GetItem(chemMaster.Owner, SharedChemMaster.InputSlotName); + var container = _itemSlotsSystem.GetItemOrNull(chemMaster.Owner, SharedChemMaster.InputSlotName); if (container is null || !_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution) || !_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out var bufferSolution)) @@ -157,7 +154,7 @@ namespace Content.Server.Chemistry.EntitySystems } else { - var container = _itemSlotsSystem.GetItem(chemMaster.Owner, SharedChemMaster.InputSlotName); + var container = _itemSlotsSystem.GetItemOrNull(chemMaster.Owner, SharedChemMaster.InputSlotName); if (container is not null && _solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution)) { @@ -173,18 +170,18 @@ namespace Content.Server.Chemistry.EntitySystems private void OnCreatePillsMessage(EntityUid uid, ChemMasterComponent chemMaster, ChemMasterCreatePillsMessage message) { var user = message.Session.AttachedEntity; - var maybeContainer = _itemSlotsSystem.GetItem(chemMaster.Owner, SharedChemMaster.OutputSlotName); + var maybeContainer = _itemSlotsSystem.GetItemOrNull(chemMaster.Owner, SharedChemMaster.OutputSlotName); if (maybeContainer is not { Valid: true } container || !TryComp(container, out ServerStorageComponent? storage) || storage.Storage is null) { return; // output can't fit pills } - + // Ensure the number is valid. if (message.Number == 0 || message.Number > storage.StorageCapacityMax - storage.StorageUsed) return; - + // Ensure the amount is valid. if (message.Dosage == 0 || message.Dosage > chemMaster.PillDosageLimit) return; @@ -192,9 +189,9 @@ namespace Content.Server.Chemistry.EntitySystems var needed = message.Dosage * message.Number; if (!WithdrawFromBuffer(chemMaster, needed, user, out var withdrawal)) return; - + _labelSystem.Label(container, message.Label); - + for (var i = 0; i < message.Number; i++) { var item = Spawn(PillPrototypeId, Transform(container).Coordinates); @@ -202,10 +199,10 @@ namespace Content.Server.Chemistry.EntitySystems _labelSystem.Label(item, message.Label); var itemSolution = _solutionContainerSystem.EnsureSolution(item, SharedChemMaster.PillSolutionName); - + _solutionContainerSystem.TryAddSolution( item, itemSolution, withdrawal.SplitSolution(message.Dosage)); - + if (TryComp(item, out var spriteComp)) spriteComp.LayerSetState(0, "pill" + (chemMaster.PillType + 1)); } @@ -218,7 +215,7 @@ namespace Content.Server.Chemistry.EntitySystems EntityUid uid, ChemMasterComponent chemMaster, ChemMasterOutputToBottleMessage message) { var user = message.Session.AttachedEntity; - var maybeContainer = _itemSlotsSystem.GetItem(chemMaster.Owner, SharedChemMaster.OutputSlotName); + var maybeContainer = _itemSlotsSystem.GetItemOrNull(chemMaster.Owner, SharedChemMaster.OutputSlotName); if (maybeContainer is not { Valid: true } container || !_solutionContainerSystem.TryGetSolution( container, SharedChemMaster.BottleSolutionName, out var solution)) @@ -229,7 +226,7 @@ namespace Content.Server.Chemistry.EntitySystems // Ensure the amount is valid. if (message.Dosage == 0 || message.Dosage > solution.AvailableVolume) return; - + if (!WithdrawFromBuffer(chemMaster, message.Dosage, user, out var withdrawal)) return; @@ -247,7 +244,7 @@ namespace Content.Server.Chemistry.EntitySystems [NotNullWhen(returnValue: true)] out Solution? outputSolution) { outputSolution = null; - + if (!_solutionContainerSystem.TryGetSolution( chemMaster.Owner, SharedChemMaster.BufferSolutionName, out var solution)) { @@ -276,7 +273,7 @@ namespace Content.Server.Chemistry.EntitySystems { _audioSystem.Play(chemMaster.ClickSound, Filter.Pvs(chemMaster.Owner), chemMaster.Owner, AudioParams.Default.WithVolume(-2f)); } - + private ContainerInfo? BuildInputContainerInfo(EntityUid? container) { if (container is not { Valid: true }) diff --git a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.ReagentDispenser.cs b/Content.Server/Chemistry/EntitySystems/ChemistrySystem.ReagentDispenser.cs deleted file mode 100644 index d604b6654e..0000000000 --- a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.ReagentDispenser.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Content.Server.Chemistry.Components; -using Content.Server.Power.Components; - -namespace Content.Server.Chemistry.EntitySystems; - -public sealed partial class ChemistrySystem -{ - private void InitializeReagentDispenser() - { - SubscribeLocalEvent(OnReagentDispenserPower); - } - - private static void OnReagentDispenserPower(EntityUid uid, ReagentDispenserComponent component, PowerChangedEvent args) - { - component.OnPowerChanged(); - } -} diff --git a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs b/Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs index df6c1a5b93..191d8249f5 100644 --- a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs @@ -22,6 +22,5 @@ public sealed partial class ChemistrySystem : EntitySystem // Why ChemMaster duplicates reagentdispenser nobody knows. InitializeHypospray(); InitializeInjector(); - InitializeReagentDispenser(); } } diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs index 14bd729f48..bebb0a246d 100644 --- a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs @@ -1,32 +1,145 @@ +using System.Linq; +using Content.Server.Administration.Logs; using Content.Server.Chemistry.Components; -using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Dispenser; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Database; using Content.Shared.Emag.Systems; using JetBrains.Annotations; +using Robust.Server.GameObjects; +using Robust.Shared.Audio; using Robust.Shared.Containers; +using Robust.Shared.Prototypes; namespace Content.Server.Chemistry.EntitySystems { + /// + /// Contains all the server-side logic for reagent dispensers. + /// + /// [UsedImplicitly] - public sealed class ReagentDispenserSystem : SharedReagentDispenserSystem + public sealed class ReagentDispenserSystem : EntitySystem { + [Dependency] private readonly AudioSystem _audioSystem = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; + [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IAdminLogManager _adminLogger = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent((_, comp, _) => comp.UpdateUserInterface()); - SubscribeLocalEvent((_, comp, _) => comp.UpdateUserInterface()); - SubscribeLocalEvent((_, comp, _) => comp.UpdateUserInterface()); - SubscribeLocalEvent((_, comp, _) => comp.UpdateUserInterface()); + + SubscribeLocalEvent((_, comp, _) => UpdateUiState(comp)); + SubscribeLocalEvent((_, comp, _) => UpdateUiState(comp)); + SubscribeLocalEvent((_, comp, _) => UpdateUiState(comp)); + SubscribeLocalEvent((_, comp, _) => UpdateUiState(comp)); + SubscribeLocalEvent((_, comp, _) => UpdateUiState(comp)); SubscribeLocalEvent(OnEmagged); + + SubscribeLocalEvent(OnSetDispenseAmountMessage); + SubscribeLocalEvent(OnDispenseReagentMessage); + SubscribeLocalEvent(OnClearContainerSolutionMessage); } - private void OnEmagged(EntityUid uid, ReagentDispenserComponent comp, GotEmaggedEvent args) + private void UpdateUiState(ReagentDispenserComponent reagentDispenser) { - if (!comp.AlreadyEmagged) + var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser.Owner, SharedReagentDispenser.OutputSlotName); + var outputContainerInfo = BuildOutputContainerInfo(outputContainer); + + var inventory = GetInventory(reagentDispenser); + + var state = new ReagentDispenserBoundUserInterfaceState(outputContainerInfo, inventory, reagentDispenser.DispenseAmount); + _userInterfaceSystem.TrySetUiState(reagentDispenser.Owner, ReagentDispenserUiKey.Key, state); + } + + private ContainerInfo? BuildOutputContainerInfo(EntityUid? container) + { + if (container is not { Valid: true }) + return null; + + if (_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var solution)) { - comp.AddFromPrototype(comp.EmagPackPrototypeId); - comp.AlreadyEmagged = true; - args.Handled = true; + var reagents = solution.Contents.Select(reagent => (reagent.ReagentId, reagent.Quantity)).ToList(); + return new ContainerInfo(Name(container.Value), true, solution.CurrentVolume, solution.MaxVolume, reagents); } + + return null; + } + + private List GetInventory(ReagentDispenserComponent reagentDispenser) + { + var inventory = new List(); + + if (reagentDispenser.PackPrototypeId is not null + && _prototypeManager.TryIndex(reagentDispenser.PackPrototypeId, out ReagentDispenserInventoryPrototype? packPrototype)) + { + inventory.AddRange(packPrototype.Inventory); + } + + if (reagentDispenser.IsEmagged + && reagentDispenser.EmagPackPrototypeId is not null + && _prototypeManager.TryIndex(reagentDispenser.EmagPackPrototypeId, out ReagentDispenserInventoryPrototype? emagPackPrototype)) + { + inventory.AddRange(emagPackPrototype.Inventory); + } + + return inventory; + } + + private void OnEmagged(EntityUid uid, ReagentDispenserComponent reagentDispenser, GotEmaggedEvent args) + { + if (!reagentDispenser.IsEmagged) + { + reagentDispenser.IsEmagged = true; + args.Handled = true; + UpdateUiState(reagentDispenser); + } + } + + private void OnSetDispenseAmountMessage(EntityUid uid, ReagentDispenserComponent reagentDispenser, ReagentDispenserSetDispenseAmountMessage message) + { + reagentDispenser.DispenseAmount = message.ReagentDispenserDispenseAmount; + UpdateUiState(reagentDispenser); + ClickSound(reagentDispenser); + } + + private void OnDispenseReagentMessage(EntityUid uid, ReagentDispenserComponent reagentDispenser, ReagentDispenserDispenseReagentMessage message) + { + // Ensure that the reagent is something this reagent dispenser can dispense. + if (!GetInventory(reagentDispenser).Contains(message.ReagentId)) + return; + + var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser.Owner, SharedReagentDispenser.OutputSlotName); + if (outputContainer is not {Valid: true} || !_solutionContainerSystem.TryGetFitsInDispenser(outputContainer.Value, out var solution)) + return; + + if (_solutionContainerSystem.TryAddReagent(outputContainer.Value, solution, message.ReagentId, (int)reagentDispenser.DispenseAmount, out var dispensedAmount) + && message.Session.AttachedEntity is not null) + { + _adminLogger.Add(LogType.ChemicalReaction, LogImpact.Medium, + $"{ToPrettyString(message.Session.AttachedEntity.Value):player} dispensed {dispensedAmount}u of {message.ReagentId} into {ToPrettyString(outputContainer.Value):entity}"); + } + + UpdateUiState(reagentDispenser); + ClickSound(reagentDispenser); + } + + private void OnClearContainerSolutionMessage(EntityUid uid, ReagentDispenserComponent reagentDispenser, ReagentDispenserClearContainerSolutionMessage message) + { + var outputContainer = _itemSlotsSystem.GetItemOrNull(reagentDispenser.Owner, SharedReagentDispenser.OutputSlotName); + if (outputContainer is not {Valid: true} || !_solutionContainerSystem.TryGetFitsInDispenser(outputContainer.Value, out var solution)) + return; + + _solutionContainerSystem.RemoveAllSolution(outputContainer.Value, solution); + UpdateUiState(reagentDispenser); + ClickSound(reagentDispenser); + } + + private void ClickSound(ReagentDispenserComponent reagentDispenser) + { + _audioSystem.PlayPvs(reagentDispenser.ClickSound, reagentDispenser.Owner, AudioParams.Default.WithVolume(-2f)); } } } diff --git a/Content.Shared/Chemistry/Dispenser/SharedReagentDispenserComponent.cs b/Content.Shared/Chemistry/Dispenser/SharedReagentDispenserComponent.cs deleted file mode 100644 index ca9400ef66..0000000000 --- a/Content.Shared/Chemistry/Dispenser/SharedReagentDispenserComponent.cs +++ /dev/null @@ -1,118 +0,0 @@ -using Content.Shared.Containers.ItemSlots; -using Content.Shared.FixedPoint; -using Robust.Shared.Serialization; - -namespace Content.Shared.Chemistry.Dispenser -{ - - /// - /// Shared class for ReagentDispenserComponent. Provides a way for entities to dispense and remove reagents from other entities with SolutionComponents via a user interface. - /// This is useful for machines such as the chemical dispensers, booze dispensers, or soda dispensers. - /// The chemicals which may be dispensed are defined by specifying a reagent pack. See for more information on that. - /// - [Virtual] - public class SharedReagentDispenserComponent : Component - { - public const string BeakerSlotId = "ReagentDispenser-beaker"; - - [DataField("beakerSlot")] - public ItemSlot BeakerSlot = new(); - - /// - /// A list of reagents which this may dispense. Defined in yaml prototype, see . - /// - public readonly List Inventory = new(); - - [Serializable, NetSerializable] - public sealed class ReagentDispenserBoundUserInterfaceState : BoundUserInterfaceState - { - public readonly bool HasPower; - public readonly bool HasBeaker; - public readonly FixedPoint2 BeakerCurrentVolume; - public readonly FixedPoint2 BeakerMaxVolume; - public readonly string ContainerName; - /// - /// A list of the reagents which this dispenser can dispense. - /// - public readonly List Inventory; - /// - /// A list of the reagents and their amounts within the beaker/reagent container, if applicable. - /// - public readonly List? ContainerReagents; - public readonly string DispenserName; - public readonly FixedPoint2 SelectedDispenseAmount; - - public ReagentDispenserBoundUserInterfaceState(bool hasPower, bool hasBeaker, FixedPoint2 beakerCurrentVolume, FixedPoint2 beakerMaxVolume, string containerName, - List inventory, string dispenserName, List? containerReagents, FixedPoint2 selectedDispenseAmount) - { - HasPower = hasPower; - HasBeaker = hasBeaker; - BeakerCurrentVolume = beakerCurrentVolume; - BeakerMaxVolume = beakerMaxVolume; - ContainerName = containerName; - Inventory = inventory; - DispenserName = dispenserName; - ContainerReagents = containerReagents; - SelectedDispenseAmount = selectedDispenseAmount; - } - } - - /// - /// Message data sent from client to server when a dispenser ui button is pressed. - /// - [Serializable, NetSerializable] - public sealed class UiButtonPressedMessage : BoundUserInterfaceMessage - { - public readonly UiButton Button; - public readonly int DispenseIndex; //Index of dispense button / reagent being pressed. Only used when a dispense button is pressed. - - public UiButtonPressedMessage(UiButton button, int dispenseIndex) - { - Button = button; - DispenseIndex = dispenseIndex; - } - } - - [Serializable, NetSerializable] - public enum ReagentDispenserUiKey - { - Key - } - - /// - /// Used in to specify which button was pressed. - /// - public enum UiButton - { - Clear, - SetDispenseAmount1, - SetDispenseAmount5, - SetDispenseAmount10, - SetDispenseAmount15, - SetDispenseAmount20, - SetDispenseAmount25, - SetDispenseAmount30, - SetDispenseAmount50, - SetDispenseAmount100, - /// - /// Used when any dispense button is pressed. Such as "Carbon", or "Oxygen" buttons on the chem dispenser. - /// The index of the reagent attached to that dispense button is sent as . - /// - Dispense - } - - /// - /// Information about a reagent which the dispenser can dispense. - /// - [Serializable, NetSerializable] - public struct ReagentDispenserInventoryEntry - { - public readonly string ID; - - public ReagentDispenserInventoryEntry(string id) - { - ID = id; - } - } - } -} diff --git a/Content.Shared/Chemistry/EntitySystems/SharedReagentDispenserSystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedReagentDispenserSystem.cs deleted file mode 100644 index 06a8f94339..0000000000 --- a/Content.Shared/Chemistry/EntitySystems/SharedReagentDispenserSystem.cs +++ /dev/null @@ -1,30 +0,0 @@ -using JetBrains.Annotations; -using Content.Shared.Containers.ItemSlots; -using Content.Shared.Chemistry.Dispenser; - -namespace Content.Shared.Chemistry.EntitySystems -{ - [UsedImplicitly] - public abstract class SharedReagentDispenserSystem : EntitySystem - { - [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnComponentInit); - SubscribeLocalEvent(OnComponentRemove); - } - - private void OnComponentInit(EntityUid uid, SharedReagentDispenserComponent component, ComponentInit args) - { - _itemSlotsSystem.AddItemSlot(uid, SharedReagentDispenserComponent.BeakerSlotId, component.BeakerSlot); - } - - private void OnComponentRemove(EntityUid uid, SharedReagentDispenserComponent component, ComponentRemove args) - { - _itemSlotsSystem.RemoveItemSlot(uid, component.BeakerSlot); - } - } -} diff --git a/Content.Shared/Chemistry/SharedChemMaster.cs b/Content.Shared/Chemistry/SharedChemMaster.cs index a46277b39f..7b4fd6ba73 100644 --- a/Content.Shared/Chemistry/SharedChemMaster.cs +++ b/Content.Shared/Chemistry/SharedChemMaster.cs @@ -159,7 +159,6 @@ namespace Content.Shared.Chemistry /// A list of the reagents and their amounts within the buffer, if applicable. /// public readonly IReadOnlyList BufferReagents; - public readonly string DispenserName; public readonly ChemMasterMode Mode; @@ -171,14 +170,12 @@ namespace Content.Shared.Chemistry public readonly bool UpdateLabel; public ChemMasterBoundUserInterfaceState( - ChemMasterMode mode, string dispenserName, - ContainerInfo? inputContainerInfo, ContainerInfo? outputContainerInfo, + ChemMasterMode mode, ContainerInfo? inputContainerInfo, ContainerInfo? outputContainerInfo, IReadOnlyList bufferReagents, FixedPoint2 bufferCurrentVolume, uint selectedPillType, uint pillDosageLimit, bool updateLabel) { InputContainerInfo = inputContainerInfo; OutputContainerInfo = outputContainerInfo; - DispenserName = dispenserName; BufferReagents = bufferReagents; Mode = mode; BufferCurrentVolume = bufferCurrentVolume; diff --git a/Content.Shared/Chemistry/SharedReagentDispenser.cs b/Content.Shared/Chemistry/SharedReagentDispenser.cs new file mode 100644 index 0000000000..3dd9a18f42 --- /dev/null +++ b/Content.Shared/Chemistry/SharedReagentDispenser.cs @@ -0,0 +1,78 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Chemistry +{ + /// + /// This class holds constants that are shared between client and server. + /// + public sealed class SharedReagentDispenser + { + public const string OutputSlotName = "beakerSlot"; + } + + [Serializable, NetSerializable] + public sealed class ReagentDispenserSetDispenseAmountMessage : BoundUserInterfaceMessage + { + public readonly ReagentDispenserDispenseAmount ReagentDispenserDispenseAmount; + + public ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount amount) + { + ReagentDispenserDispenseAmount = amount; + } + } + + [Serializable, NetSerializable] + public sealed class ReagentDispenserDispenseReagentMessage : BoundUserInterfaceMessage + { + public readonly string ReagentId; + + public ReagentDispenserDispenseReagentMessage(string reagentId) + { + ReagentId = reagentId; + } + } + + [Serializable, NetSerializable] + public sealed class ReagentDispenserClearContainerSolutionMessage : BoundUserInterfaceMessage + { + + } + + public enum ReagentDispenserDispenseAmount + { + U1 = 1, + U5 = 5, + U10 = 10, + U15 = 15, + U20 = 20, + U25 = 25, + U30 = 30, + U50 = 50, + U100 = 100, + } + + [Serializable, NetSerializable] + public sealed class ReagentDispenserBoundUserInterfaceState : BoundUserInterfaceState + { + public readonly ContainerInfo? OutputContainer; + /// + /// A list of the reagents which this dispenser can dispense. + /// + public readonly List Inventory; + + public readonly ReagentDispenserDispenseAmount SelectedDispenseAmount; + + public ReagentDispenserBoundUserInterfaceState(ContainerInfo? outputContainer, List inventory, ReagentDispenserDispenseAmount selectedDispenseAmount) + { + OutputContainer = outputContainer; + Inventory = inventory; + SelectedDispenseAmount = selectedDispenseAmount; + } + } + + [Serializable, NetSerializable] + public enum ReagentDispenserUiKey + { + Key + } +} diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs index 72a311e61c..57fbd78b62 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs @@ -532,9 +532,10 @@ namespace Content.Shared.Containers.ItemSlots /// /// Get the contents of some item slot. /// - public EntityUid? GetItem(EntityUid uid, string id, ItemSlotsComponent? itemSlots = null) + /// The item in the slot, or null if the slot is empty or the entity doesn't have an . + public EntityUid? GetItemOrNull(EntityUid uid, string id, ItemSlotsComponent? itemSlots = null) { - if (!Resolve(uid, ref itemSlots)) + if (!Resolve(uid, ref itemSlots, logMissing: false)) return null; return itemSlots.Slots.GetValueOrDefault(id)?.Item; diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml index 59ab255968..d7598f6699 100644 --- a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml +++ b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml @@ -48,16 +48,17 @@ sound: path: /Audio/Effects/metalbreak.ogg - type: ReagentDispenser - beakerSlot: - whitelistFailPopup: reagent-dispenser-component-cannot-put-entity-message - whitelist: - components: - - FitsInDispenser - type: ItemSlots + slots: + beakerSlot: + whitelistFailPopup: reagent-dispenser-component-cannot-put-entity-message + whitelist: + components: + - FitsInDispenser - type: ContainerContainer containers: machine_board: !type:Container machine_parts: !type:Container - ReagentDispenser-beaker: !type:ContainerSlot + beakerSlot: !type:ContainerSlot - type: StaticPrice price: 1000