diff --git a/Content.Client/Chemistry/EntitySystems/ChemMasterSystem.cs b/Content.Client/Chemistry/EntitySystems/ChemMasterSystem.cs deleted file mode 100644 index a35e5c04aa..0000000000 --- a/Content.Client/Chemistry/EntitySystems/ChemMasterSystem.cs +++ /dev/null @@ -1,12 +0,0 @@ -using JetBrains.Annotations; -using Content.Shared.Chemistry.EntitySystems; - -namespace Content.Client.Chemistry.EntitySystems -{ - [UsedImplicitly] - public sealed class ChemMasterSystem : SharedChemMasterSystem - { - // gotta love empty client side systems that exist purely because theres one specific thing that can only be - // done server-side which prevents the whole system from being in shared. - } -} diff --git a/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs b/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs index 6217ad2288..5df7a61836 100644 --- a/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs +++ b/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs @@ -1,8 +1,8 @@ +using Content.Shared.Chemistry; using Content.Shared.Chemistry.Dispenser; using Content.Shared.Containers.ItemSlots; using JetBrains.Annotations; using Robust.Client.GameObjects; -using static Content.Shared.Chemistry.Components.SharedChemMasterComponent; namespace Content.Client.Chemistry.UI { @@ -27,7 +27,7 @@ namespace Content.Client.Chemistry.UI { base.Open(); - //Setup window layout/elements + // Setup window layout/elements _window = new ChemMasterWindow { Title = Loc.GetString("chem-master-bound-user-interface-title"), @@ -36,20 +36,20 @@ namespace Content.Client.Chemistry.UI _window.OpenCentered(); _window.OnClose += Close; - //Setup static button actions. - _window.EjectButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(BeakerSlotId)); - _window.BufferTransferButton.OnPressed += _ => PrepareData(UiAction.Transfer, null, null, null, null, null); - _window.BufferDiscardButton.OnPressed += _ => PrepareData(UiAction.Discard, null, null, null, null, null); - _window.CreatePillButton.OnPressed += _ => PrepareData(UiAction.CreatePills, null, _window.LabelLine, null, _window.PillAmount.Value, null); - _window.CreateBottleButton.OnPressed += _ => PrepareData(UiAction.CreateBottles, null, _window.LabelLine, null, null, _window.BottleAmount.Value); + // Setup static button actions. + _window.EjectButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(SharedChemMaster.ContainerSlotName)); + _window.BufferTransferButton.OnPressed += _ => SendMessage(new ChemMasterSetModeMessage(ChemMasterMode.Transfer)); + _window.BufferDiscardButton.OnPressed += _ => SendMessage(new ChemMasterSetModeMessage(ChemMasterMode.Discard)); + _window.CreatePillButton.OnPressed += _ => SendMessage(new ChemMasterCreatePillsMessage(((uint)_window.PillAmount.Value), _window.LabelLine)); + _window.CreateBottleButton.OnPressed += _ => SendMessage(new ChemMasterCreateBottlesMessage(((uint)_window.BottleAmount.Value), _window.LabelLine)); - for(uint i = 0; i < _window.PillTypeButtons.Length; i++) + for (uint i = 0; i < _window.PillTypeButtons.Length; i++) { - uint type = i; - _window.PillTypeButtons[i].OnPressed += _ => PrepareData(UiAction.SetPillType, null, null, type + 1, null, null); + var pillType = i; + _window.PillTypeButtons[i].OnPressed += _ => SendMessage(new ChemMasterSetPillTypeMessage(pillType)); } - _window.OnChemButtonPressed += (args, button) => PrepareData(UiAction.ChemButton, button, null, null, null, null); + _window.OnReagentButtonPressed += (args, button) => SendMessage(new ChemMasterReagentAmountButtonMessage(button.Id, button.Amount, button.IsBuffer)); } /// @@ -65,19 +65,7 @@ namespace Content.Client.Chemistry.UI var castState = (ChemMasterBoundUserInterfaceState) state; - _window?.UpdateState(castState); //Update window state - } - - private void PrepareData(UiAction action, ChemButton? button, string? label, uint? pillType, int? pillAmount, int? bottleAmount) - { - if (button != null) - { - SendMessage(new UiActionMessage(action, button.Amount, button.Id, button.IsBuffer, null, null, null, null)); - } - else - { - SendMessage(new UiActionMessage(action, null, null, null, label, pillType, pillAmount, bottleAmount)); - } + _window?.UpdateState(castState); // Update window state } protected override void Dispose(bool disposing) diff --git a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs index 880992ca54..cd7d82d66a 100644 --- a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs +++ b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs @@ -1,23 +1,15 @@ -using System; using System.Linq; using Content.Client.Stylesheets; -using Content.Client.UserInterface; -using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry; using Content.Shared.Chemistry.Reagent; -using Content.Shared.FixedPoint; using Robust.Client.AutoGenerated; -using Robust.Client.ResourceManagement; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; using Robust.Client.Utility; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Localization; using Robust.Shared.Prototypes; using Robust.Shared.Utility; -using static Content.Shared.Chemistry.Components.SharedChemMasterComponent; using static Robust.Client.UserInterface.Controls.BoxContainer; namespace Content.Client.Chemistry.UI @@ -29,17 +21,11 @@ namespace Content.Client.Chemistry.UI public sealed partial class ChemMasterWindow : DefaultWindow { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - public event Action? OnLabelEntered; - public event Action? OnChemButtonPressed; + public event Action? OnReagentButtonPressed; public readonly Button[] PillTypeButtons; private const string PillsRsiPath = "/Textures/Objects/Specific/Chemistry/pills.rsi"; - private static bool IsSpinValid(int n) - { - return n is > 0 and <= MaxEntitySpawns; - } - /// /// Create and initialize the chem master UI client-side. Creates the basic layout, /// actual data isn't filled in until the server sends data about the chem master. @@ -48,17 +34,16 @@ namespace Content.Client.Chemistry.UI { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); - LabelLineEdit.OnTextEntered += e => OnLabelEntered?.Invoke(e.Text); - //Pill type selection buttons, in total there are 20 pills. - //Pill rsi file should have states named as pill1, pill2, and so on. + // Pill type selection buttons, in total there are 20 pills. + // Pill rsi file should have states named as pill1, pill2, and so on. var resourcePath = new ResourcePath(PillsRsiPath); var pillTypeGroup = new ButtonGroup(); PillTypeButtons = new Button[20]; for (uint i = 0; i < PillTypeButtons.Length; i++) { - //For every button decide which stylebase to have - //Every row has 10 buttons + // For every button decide which stylebase to have + // Every row has 10 buttons String styleBase = StyleBase.ButtonOpenBoth; uint modulo = i % 10; if (i > 0 && modulo == 0) @@ -68,7 +53,7 @@ namespace Content.Client.Chemistry.UI else if (i == 0) styleBase = StyleBase.ButtonOpenRight; - //Generate buttons + // Generate buttons PillTypeButtons[i] = new Button { Access = AccessLevel.Public, @@ -77,7 +62,7 @@ namespace Content.Client.Chemistry.UI Group = pillTypeGroup }; - //Generate buttons textures + // Generate buttons textures var specifier = new SpriteSpecifier.Rsi(resourcePath, "pill" + (i + 1)); TextureRect pillTypeTexture = new TextureRect { @@ -90,17 +75,15 @@ namespace Content.Client.Chemistry.UI Grid.AddChild(PillTypeButtons[i]); } - PillAmount.IsValid = IsSpinValid; - BottleAmount.IsValid = IsSpinValid; PillAmount.InitDefaultButtons(); BottleAmount.InitDefaultButtons(); } - private ChemButton MakeChemButton(string text, FixedPoint2 amount, string id, bool isBuffer, string styleClass) + private ReagentButton MakeReagentButton(string text, ChemMasterReagentAmount amount, string id, bool isBuffer, string styleClass) { - var button = new ChemButton(text, amount, id, isBuffer, styleClass); + var button = new ReagentButton(text, amount, id, isBuffer, styleClass); button.OnPressed += args - => OnChemButtonPressed?.Invoke(args, button); + => OnReagentButtonPressed?.Invoke(args, button); return button; } @@ -112,15 +95,34 @@ namespace Content.Client.Chemistry.UI { var castState = (ChemMasterBoundUserInterfaceState) state; Title = castState.DispenserName; - LabelLine = castState.Label; + if (castState.UpdateLabel) + LabelLine = GenerateLabel(castState); UpdatePanelInfo(castState); if (Contents.Children != null) { - ButtonHelpers.SetButtonDisabledRecursive(Contents, !castState.HasPower); - EjectButton.Disabled = !castState.HasBeaker; + EjectButton.Disabled = !castState.HasContainer(); + } + + PillTypeButtons[castState.SelectedPillType].Pressed = true; + PillAmount.IsValid = x => x > 0 && x <= castState.PillProductionLimit; + BottleAmount.IsValid = x => x > 0 && x <= castState.BottleProductionLimit; + } + + /// + /// Generate a product label based on reagents in the buffer. + /// + /// State data sent by the server. + private string GenerateLabel(ChemMasterBoundUserInterfaceState state) + { + if (state.BufferCurrentVolume == 0) + return ""; + else + { + var reagent = state.BufferReagents.OrderBy(r => r.Quantity).First(); + _prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto); + return proto?.LocalizedName ?? ""; } - PillTypeButtons[castState.SelectedPillType - 1].Pressed = true; } /// @@ -129,13 +131,12 @@ namespace Content.Client.Chemistry.UI /// State data for the dispenser. private void UpdatePanelInfo(ChemMasterBoundUserInterfaceState state) { - var bufferModeTransfer = state.BufferModeTransfer; - BufferTransferButton.Pressed = bufferModeTransfer; - BufferDiscardButton.Pressed = !bufferModeTransfer; + BufferTransferButton.Pressed = state.Mode == Shared.Chemistry.ChemMasterMode.Transfer; + BufferDiscardButton.Pressed = state.Mode == Shared.Chemistry.ChemMasterMode.Discard; ContainerInfo.Children.Clear(); - if (!state.HasBeaker) + if (!state.HasContainer()) { ContainerInfo.Children.Add(new Label {Text = Loc.GetString("chem-master-window-no-container-loaded-text") }); } @@ -149,49 +150,49 @@ namespace Content.Client.Chemistry.UI new Label {Text = $"{state.ContainerName}: "}, new Label { - Text = $"{state.BeakerCurrentVolume}/{state.BeakerMaxVolume}", + Text = $"{state.ContainerCurrentVolume}/{state.ContainerMaxVolume}", StyleClasses = {StyleNano.StyleClassLabelSecondaryColor} } } }); - } - foreach (var reagent in state.ContainerReagents) - { - var name = Loc.GetString("chem-master-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)) + foreach (var reagent in state.ContainerReagents!.OrderBy( + r => {_prototypeManager.TryIndex(r.ReagentId, out ReagentPrototype? p); return p?.LocalizedName;})) { - name = proto.LocalizedName; - } + // Try to get the prototype for the given reagent. This gives us its name. + _prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto); + var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text"); - if (proto != null) - { - ContainerInfo.Children.Add(new BoxContainer + if (proto != null) { - Orientation = LayoutOrientation.Horizontal, - Children = + ContainerInfo.Children.Add(new BoxContainer { - new Label {Text = $"{name}: "}, - new Label + Orientation = LayoutOrientation.Horizontal, + Children = { - Text = $"{reagent.Quantity}u", - StyleClasses = {StyleNano.StyleClassLabelSecondaryColor} - }, + new Label {Text = $"{name}: "}, + new Label + { + Text = $"{reagent.Quantity}u", + StyleClasses = {StyleNano.StyleClassLabelSecondaryColor} + }, - //Padding - new Control {HorizontalExpand = true}, + // Padding + new Control {HorizontalExpand = true}, - MakeChemButton("1", FixedPoint2.New(1), reagent.ReagentId, false, StyleBase.ButtonOpenRight), - MakeChemButton("5", FixedPoint2.New(5), reagent.ReagentId, false, StyleBase.ButtonOpenBoth), - MakeChemButton("10", FixedPoint2.New(10), reagent.ReagentId, false, StyleBase.ButtonOpenBoth), - MakeChemButton("25", FixedPoint2.New(25), reagent.ReagentId, false, StyleBase.ButtonOpenBoth), - MakeChemButton(Loc.GetString("chem-master-window-buffer-all-amount"), FixedPoint2.New(-1), reagent.ReagentId, false, StyleBase.ButtonOpenLeft), - } - }); + MakeReagentButton("1", ChemMasterReagentAmount.U1, reagent.ReagentId, false, StyleBase.ButtonOpenRight), + MakeReagentButton("5", ChemMasterReagentAmount.U5, reagent.ReagentId, false, StyleBase.ButtonOpenBoth), + MakeReagentButton("10", ChemMasterReagentAmount.U10, reagent.ReagentId, false, StyleBase.ButtonOpenBoth), + MakeReagentButton("25", ChemMasterReagentAmount.U25, reagent.ReagentId, false, StyleBase.ButtonOpenBoth), + MakeReagentButton(Loc.GetString("chem-master-window-buffer-all-amount"), ChemMasterReagentAmount.All, reagent.ReagentId, false, StyleBase.ButtonOpenLeft), + } + }); + } } } + + BufferInfo.Children.Clear(); if (!state.BufferReagents.Any()) @@ -218,19 +219,15 @@ namespace Content.Client.Chemistry.UI foreach (var reagent in state.BufferReagents) { - var name = Loc.GetString("chem-master-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)) - { - name = proto.LocalizedName; - } + // Try to get the prototype for the given reagent. This gives us its name. + _prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto); + var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text"); if (proto != null) { BufferInfo.Children.Add(new BoxContainer { Orientation = LayoutOrientation.Horizontal, - //SizeFlagsHorizontal = SizeFlags.ShrinkEnd, Children = { new Label {Text = $"{name}: "}, @@ -240,14 +237,14 @@ namespace Content.Client.Chemistry.UI StyleClasses = {StyleNano.StyleClassLabelSecondaryColor} }, - //Padding + // Padding new Control {HorizontalExpand = true}, - MakeChemButton("1", FixedPoint2.New(1), reagent.ReagentId, true, StyleBase.ButtonOpenRight), - MakeChemButton("5", FixedPoint2.New(5), reagent.ReagentId, true, StyleBase.ButtonOpenBoth), - MakeChemButton("10", FixedPoint2.New(10), reagent.ReagentId, true, StyleBase.ButtonOpenBoth), - MakeChemButton("25", FixedPoint2.New(25), reagent.ReagentId, true, StyleBase.ButtonOpenBoth), - MakeChemButton(Loc.GetString("chem-master-window-buffer-all-amount"), FixedPoint2.New(-1), reagent.ReagentId, true, StyleBase.ButtonOpenLeft), + MakeReagentButton("1", ChemMasterReagentAmount.U1, reagent.ReagentId, true, StyleBase.ButtonOpenRight), + MakeReagentButton("5", ChemMasterReagentAmount.U5, reagent.ReagentId, true, StyleBase.ButtonOpenBoth), + MakeReagentButton("10", ChemMasterReagentAmount.U10, reagent.ReagentId, true, StyleBase.ButtonOpenBoth), + MakeReagentButton("25", ChemMasterReagentAmount.U25, reagent.ReagentId, true, StyleBase.ButtonOpenBoth), + MakeReagentButton(Loc.GetString("chem-master-window-buffer-all-amount"), ChemMasterReagentAmount.All, reagent.ReagentId, true, StyleBase.ButtonOpenLeft), } }); } @@ -267,12 +264,12 @@ namespace Content.Client.Chemistry.UI } } - public sealed class ChemButton : Button + public sealed class ReagentButton : Button { - public FixedPoint2 Amount { get; set; } + public ChemMasterReagentAmount Amount { get; set; } public bool IsBuffer = true; public string Id { get; set; } - public ChemButton(string text, FixedPoint2 amount, string id, bool isBuffer, string styleClass) + public ReagentButton(string text, ChemMasterReagentAmount amount, string id, bool isBuffer, string styleClass) { AddStyleClass(styleClass); Text = text; diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs index 989e0f2c05..42aca55db3 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -66,7 +66,6 @@ namespace Content.Client.Entry factory.RegisterClass(); factory.RegisterClass(); factory.RegisterClass(); - factory.RegisterClass(); factory.RegisterClass(); factory.RegisterClass(); // Do not add to the above, they are legacy diff --git a/Content.Server/Chemistry/Components/ChemMasterComponent.cs b/Content.Server/Chemistry/Components/ChemMasterComponent.cs index 9822395458..4b9db13863 100644 --- a/Content.Server/Chemistry/Components/ChemMasterComponent.cs +++ b/Content.Server/Chemistry/Components/ChemMasterComponent.cs @@ -1,365 +1,30 @@ using Content.Server.Chemistry.EntitySystems; -using Content.Server.Labels.Components; -using Content.Server.Power.Components; -using Content.Server.UserInterface; -using Content.Shared.Chemistry.Components; -using Content.Shared.FixedPoint; -using Content.Shared.Hands.EntitySystems; -using Content.Shared.Popups; -using Robust.Server.GameObjects; +using Content.Shared.Chemistry; using Robust.Shared.Audio; -using Robust.Shared.Player; - namespace Content.Server.Chemistry.Components { /// - /// Contains all the server-side logic for chem masters. 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. + /// An industrial grade chemical manipulator with pill and bottle production included. + /// /// [RegisterComponent] - [ComponentReference(typeof(SharedChemMasterComponent))] - public sealed class ChemMasterComponent : SharedChemMasterComponent + [Access(typeof(ChemMasterSystem))] + public sealed class ChemMasterComponent : Component { - [Dependency] private readonly IEntityManager _entities = default!; - [Dependency] private readonly IEntitySystemManager _sysMan = default!; + [DataField("pillType"), ViewVariables(VVAccess.ReadWrite)] + public uint PillType = 0; - [ViewVariables] - private uint _pillType = 1; + [DataField("mode"), ViewVariables(VVAccess.ReadWrite)] + public ChemMasterMode Mode = ChemMasterMode.Transfer; - [ViewVariables] - private string _label = ""; + [DataField("pillProductionLimit", required: true), ViewVariables(VVAccess.ReadWrite)] + public uint PillProductionLimit; - [ViewVariables] - private bool _bufferModeTransfer = true; + [DataField("bottleProductionLimit", required: true), ViewVariables(VVAccess.ReadWrite)] + public uint BottleProductionLimit; - [ViewVariables] - private bool Powered => !_entities.TryGetComponent(Owner, out ApcPowerReceiverComponent? receiver) || receiver.Powered; - - [ViewVariables] - private Solution BufferSolution => _bufferSolution ??= EntitySystem.Get().EnsureSolution(Owner, SolutionName); - - private Solution? _bufferSolution; - - [ViewVariables] - private BoundUserInterface? UserInterface => Owner.GetUIOrNull(ChemMasterUiKey.Key); - - [DataField("clickSound")] - private SoundSpecifier _clickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg"); - - - /// - /// 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; - } - - _bufferSolution = EntitySystem.Get().EnsureSolution(Owner, SolutionName); - } - - /// - /// 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 is not {Valid: true} player) - return; - - if (obj.Message is not UiActionMessage msg) - return; - - if (!PlayerCanUseChemMaster(player, true)) - return; - - switch (msg.Action) - { - case UiAction.ChemButton: - if (!_bufferModeTransfer) - { - if (msg.IsBuffer) - { - DiscardReagent(msg.Id, msg.Amount, BufferSolution); - } - else if (BeakerSlot.HasItem && - BeakerSlot.Item is {Valid: true} beaker && - _entities.TryGetComponent(beaker, out FitsInDispenserComponent? fits) && - EntitySystem.Get().TryGetSolution(beaker, fits.Solution, out var beakerSolution)) - { - DiscardReagent(msg.Id, msg.Amount, beakerSolution); - } - } - else - { - TransferReagent(msg.Id, msg.Amount, msg.IsBuffer); - } - break; - case UiAction.Transfer: - _bufferModeTransfer = true; - UpdateUserInterface(); - break; - case UiAction.Discard: - _bufferModeTransfer = false; - UpdateUserInterface(); - break; - case UiAction.SetPillType: - _pillType = msg.PillType; - UpdateUserInterface(); - break; - case UiAction.CreatePills: - case UiAction.CreateBottles: - _label = msg.Label; - TryCreatePackage(player, msg.Action, msg.Label, msg.PillAmount, msg.BottleAmount); - break; - default: - throw new ArgumentOutOfRangeException(); - } - UpdateUserInterface(); - ClickSound(); - } - - /// - /// Checks whether the player entity is able to use the chem master. - /// - /// The player entity. - /// whether the device requires power - /// Returns true if the entity can use the chem master, and false if it cannot. - private bool PlayerCanUseChemMaster(EntityUid playerEntity, bool needsPower = true) - { - //Need player entity to check if they are still able to use the chem master - if (playerEntity == default) - 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 ChemMasterBoundUserInterfaceState GetUserInterfaceState() - { - if (BeakerSlot.Item is not {Valid: true} beaker || - !_entities.TryGetComponent(beaker, out FitsInDispenserComponent? fits) || - !EntitySystem.Get().TryGetSolution(beaker, fits.Solution, out var beakerSolution)) - { - return new ChemMasterBoundUserInterfaceState(Powered, false, FixedPoint2.New(0), FixedPoint2.New(0), - "", _label, _entities.GetComponent(Owner).EntityName, new List(), BufferSolution.Contents, _bufferModeTransfer, - BufferSolution.TotalVolume, _pillType); - } - - return new ChemMasterBoundUserInterfaceState(Powered, true, beakerSolution.CurrentVolume, - beakerSolution.MaxVolume, - _entities.GetComponent(beaker).EntityName, _label, _entities.GetComponent(Owner).EntityName, beakerSolution.Contents, BufferSolution.Contents, _bufferModeTransfer, - BufferSolution.TotalVolume, _pillType); - } - - public void UpdateUserInterface() - { - if (!Initialized) return; - - var state = GetUserInterfaceState(); - UserInterface?.SetState(state); - } - - private void DiscardReagent(string id, FixedPoint2 amount, Solution solution) - { - foreach (var reagent in solution.Contents) - { - if (reagent.ReagentId == id) - { - FixedPoint2 actualAmount; - if (amount == FixedPoint2.New(-1)) //amount is FixedPoint2.New(-1) when the client sends a message requesting to remove all solution from the container - { - actualAmount = reagent.Quantity; - } - else - { - actualAmount = FixedPoint2.Min(reagent.Quantity, amount); - } - solution.RemoveReagent(id, actualAmount); - return; - } - } - } - - private void TransferReagent(string id, FixedPoint2 amount, bool isBuffer) - { - if (!BeakerSlot.HasItem || - BeakerSlot.Item is not {Valid: true} beaker || - !_entities.TryGetComponent(beaker, out FitsInDispenserComponent? fits) || - !EntitySystem.Get().TryGetSolution(beaker, fits.Solution, out var beakerSolution)) - return; - - if (isBuffer) - { - foreach (var reagent in BufferSolution.Contents) - { - if (reagent.ReagentId == id) - { - FixedPoint2 actualAmount; - if ( - amount == FixedPoint2 - .New(-1)) //amount is FixedPoint2.New(-1) when the client sends a message requesting to remove all solution from the container - { - actualAmount = FixedPoint2.Min(reagent.Quantity, beakerSolution.AvailableVolume); - } - else - { - actualAmount = FixedPoint2.Min(reagent.Quantity, amount, beakerSolution.AvailableVolume); - } - - - BufferSolution.RemoveReagent(id, actualAmount); - EntitySystem.Get() - .TryAddReagent(beaker, beakerSolution, id, actualAmount, out var _); - break; - } - } - } - else - { - foreach (var reagent in beakerSolution.Contents) - { - if (reagent.ReagentId == id) - { - FixedPoint2 actualAmount; - if (amount == FixedPoint2.New(-1)) - { - actualAmount = reagent.Quantity; - } - else - { - actualAmount = FixedPoint2.Min(reagent.Quantity, amount); - } - - EntitySystem.Get().TryRemoveReagent(beaker, beakerSolution, id, actualAmount); - BufferSolution.AddReagent(id, actualAmount); - break; - } - } - } - _label = GenerateLabel(); - UpdateUserInterface(); - } - - /// - /// Handles label generation depending from solutions and their amount. - /// Label is generated by taking the most significant solution name. - /// - private string GenerateLabel() - { - if (_bufferSolution == null || _bufferSolution.Contents.Count == 0) - return ""; - - _bufferSolution.Contents.Sort(); - return _bufferSolution.Contents[_bufferSolution.Contents.Count - 1].ReagentId; - } - - private void TryCreatePackage(EntityUid user, UiAction action, string label, int pillAmount, int bottleAmount) - { - if (BufferSolution.TotalVolume == 0) - { - user.PopupMessageCursor(Loc.GetString("chem-master-window-buffer-empty-text")); - return; - } - - var handSys = _sysMan.GetEntitySystem(); - var solSys = _sysMan.GetEntitySystem(); - - pillAmount = Math.Clamp(pillAmount, 0, MaxEntitySpawns); - bottleAmount = Math.Clamp(bottleAmount, 0, MaxEntitySpawns); - - if (action == UiAction.CreateBottles) - { - var individualVolume = BufferSolution.TotalVolume / FixedPoint2.New(bottleAmount); - if (individualVolume < FixedPoint2.New(1)) - { - user.PopupMessageCursor(Loc.GetString("chem-master-window-buffer-low-text")); - return; - } - - var actualVolume = FixedPoint2.Min(individualVolume, FixedPoint2.New(30)); - for (int i = 0; i < bottleAmount; i++) - { - var bottle = _entities.SpawnEntity("ChemistryEmptyBottle01", _entities.GetComponent(Owner).Coordinates); - - //Adding label - LabelComponent labelComponent = bottle.EnsureComponent(); - labelComponent.OriginalName = _entities.GetComponent(bottle).EntityName; - string val = _entities.GetComponent(bottle).EntityName + $" ({label})"; - _entities.GetComponent(bottle).EntityName = val; - labelComponent.CurrentLabel = label; - - var bufferSolution = BufferSolution.SplitSolution(actualVolume); - var bottleSolution = solSys.EnsureSolution(bottle, "drink"); - - solSys.TryAddSolution(bottle, bottleSolution, bufferSolution); - - //Try to give them the bottle - handSys.PickupOrDrop(user, bottle); - } - } - else //Pills - { - var individualVolume = BufferSolution.TotalVolume / FixedPoint2.New(pillAmount); - if (individualVolume < FixedPoint2.New(1)) - { - user.PopupMessageCursor(Loc.GetString("chem-master-window-buffer-low-text")); - return; - } - - var actualVolume = FixedPoint2.Min(individualVolume, FixedPoint2.New(50)); - for (int i = 0; i < pillAmount; i++) - { - var pill = _entities.SpawnEntity("Pill", _entities.GetComponent(Owner).Coordinates); - - //Adding label - LabelComponent labelComponent = pill.EnsureComponent(); - labelComponent.OriginalName = _entities.GetComponent(pill).EntityName; - string val = _entities.GetComponent(pill).EntityName + $" ({label})"; - _entities.GetComponent(pill).EntityName = val; - labelComponent.CurrentLabel = label; - - var bufferSolution = BufferSolution.SplitSolution(actualVolume); - var pillSolution = EntitySystem.Get().EnsureSolution(pill, "food"); - solSys.TryAddSolution(pill, pillSolution, bufferSolution); - - //Change pill Sprite component state - if (!_entities.TryGetComponent(pill, out SpriteComponent? sprite)) - { - return; - } - sprite?.LayerSetState(0, "pill" + _pillType); - - //Try to give them the bottle - handSys.PickupOrDrop(user, pill); - } - } - - if (_bufferSolution?.Contents.Count == 0) - _label = ""; - - UpdateUserInterface(); - } - - private void ClickSound() - { - SoundSystem.Play(_clickSound.GetSound(), Filter.Pvs(Owner), Owner, AudioParams.Default.WithVolume(-2f)); - } + [DataField("clickSound"), ViewVariables(VVAccess.ReadWrite)] + public SoundSpecifier ClickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg"); } } diff --git a/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs index e6e616fcdd..60e808d402 100644 --- a/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs @@ -1,20 +1,249 @@ using Content.Server.Chemistry.Components; -using Content.Shared.Chemistry.EntitySystems; +using Content.Server.Labels.Components; +using Content.Server.Popups; +using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Components; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.FixedPoint; +using Content.Shared.Hands.EntitySystems; using JetBrains.Annotations; +using Robust.Server.GameObjects; +using Robust.Shared.Audio; using Robust.Shared.Containers; +using Robust.Shared.Player; + namespace Content.Server.Chemistry.EntitySystems { + + /// + /// Contains all the server-side logic for ChemMasters. + /// + /// [UsedImplicitly] - public sealed class ChemMasterSystem : SharedChemMasterSystem + public sealed class ChemMasterSystem : EntitySystem { + [Dependency] private readonly PopupSystem _popupSystem = default!; + [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 SharedHandsSystem _handsSystem = 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(OnSetModeMessage); + SubscribeLocalEvent(OnSetPillTypeMessage); + SubscribeLocalEvent(OnReagentButtonMessage); + SubscribeLocalEvent(OnCreatePillsMessage); + SubscribeLocalEvent(OnCreateBottlesMessage); + } + + private void UpdateUiState(ChemMasterComponent chemMaster, bool updateLabel = false) + { + if (!_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out var bufferSolution)) + return; + var container = _itemSlotsSystem.GetItem(chemMaster.Owner, SharedChemMaster.ContainerSlotName); + + Solution? containerSolution = null; + + if (container.HasValue && container.Value.Valid) + { + TryComp(container, out FitsInDispenserComponent? fits); + _solutionContainerSystem.TryGetSolution(container.Value, fits!.Solution, out containerSolution); + } + + var containerName = container is null ? null : Name(container.Value); + var dispenserName = Name(chemMaster.Owner); + var bufferReagents = bufferSolution.Contents; + var bufferCurrentVolume = bufferSolution.CurrentVolume; + + var state = new ChemMasterBoundUserInterfaceState( + containerSolution?.CurrentVolume, containerSolution?.MaxVolume, containerName, dispenserName, + containerSolution?.Contents, bufferReagents, chemMaster.Mode, bufferCurrentVolume, chemMaster.PillType, + chemMaster.PillProductionLimit, chemMaster.BottleProductionLimit, updateLabel + ); + _userInterfaceSystem.TrySetUiState(chemMaster.Owner, ChemMasterUiKey.Key, state); + } + + private void OnSetModeMessage(EntityUid uid, ChemMasterComponent chemMaster, ChemMasterSetModeMessage message) + { + // Ensure the mode is valid, either Transfer or Discard. + if (!Enum.IsDefined(typeof(ChemMasterMode), message.ChemMasterMode)) + return; + + chemMaster.Mode = message.ChemMasterMode; + UpdateUiState(chemMaster); + ClickSound(chemMaster); + } + + private void OnSetPillTypeMessage(EntityUid uid, ChemMasterComponent chemMaster, ChemMasterSetPillTypeMessage message) + { + // Ensure valid pill type. There are 20 pills selectable, 0-19. + if (message.PillType > SharedChemMaster.PillTypes - 1) + return; + + chemMaster.PillType = message.PillType; + UpdateUiState(chemMaster); + ClickSound(chemMaster); + } + + private void OnReagentButtonMessage(EntityUid uid, ChemMasterComponent chemMaster, ChemMasterReagentAmountButtonMessage message) + { + // Ensure the amount corresponds to one of the reagent amount buttons. + if (!Enum.IsDefined(typeof(ChemMasterReagentAmount), message.Amount)) + return; + + switch (chemMaster.Mode) + { + case ChemMasterMode.Transfer: + TransferReagents(chemMaster, message.ReagentId, message.Amount.GetFixedPoint(), message.FromBuffer); + break; + case ChemMasterMode.Discard: + DiscardReagents(chemMaster, message.ReagentId, message.Amount.GetFixedPoint(), message.FromBuffer); + break; + default: + // Invalid mode. + return; + } + + ClickSound(chemMaster); + } + + private void TransferReagents(ChemMasterComponent chemMaster, string reagentId, FixedPoint2 amount, bool fromBuffer) + { + var container = _itemSlotsSystem.GetItem(chemMaster.Owner, SharedChemMaster.ContainerSlotName); + if (container is null || + !_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution) || + !_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out var bufferSolution)) + { + return; + } + + if (fromBuffer) // Buffer to container + { + amount = FixedPoint2.Min(amount, containerSolution.AvailableVolume); + amount = bufferSolution.RemoveReagent(reagentId, amount); + _solutionContainerSystem.TryAddReagent(container.Value, containerSolution, reagentId, amount, out var _); + } + else // Container to buffer + { + amount = FixedPoint2.Min(amount, containerSolution.GetReagentQuantity(reagentId)); + _solutionContainerSystem.TryRemoveReagent(container.Value, containerSolution, reagentId, amount); + bufferSolution.AddReagent(reagentId, amount); + } + + UpdateUiState(chemMaster, updateLabel: true); + } + + private void DiscardReagents(ChemMasterComponent chemMaster, string reagentId, FixedPoint2 amount, bool fromBuffer) + { + + if (fromBuffer) + { + if (_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out var bufferSolution)) + bufferSolution.RemoveReagent(reagentId, amount); + else + return; + } + else + { + var container = _itemSlotsSystem.GetItem(chemMaster.Owner, SharedChemMaster.ContainerSlotName); + if (container is not null && + _solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution)) + { + _solutionContainerSystem.TryRemoveReagent(container.Value, containerSolution, reagentId, amount); + } + else + return; + } + + UpdateUiState(chemMaster, updateLabel: fromBuffer); + } + + private void OnCreatePillsMessage(EntityUid uid, ChemMasterComponent chemMaster, ChemMasterCreatePillsMessage message) + { + // Ensure the amount is valid. + if (message.Amount == 0 || message.Amount > chemMaster.PillProductionLimit) + return; + + CreatePillsOrBottles(chemMaster, pills: true, message.Amount, message.Label, message.Session.AttachedEntity); + UpdateUiState(chemMaster); + ClickSound(chemMaster); + } + + private void OnCreateBottlesMessage(EntityUid uid, ChemMasterComponent chemMaster, ChemMasterCreateBottlesMessage message) + { + // Ensure the amount is valid. + if (message.Amount == 0 || message.Amount > chemMaster.BottleProductionLimit) + return; + + CreatePillsOrBottles(chemMaster, pills: false, message.Amount, message.Label, message.Session.AttachedEntity); + UpdateUiState(chemMaster); + ClickSound(chemMaster); + } + + private void CreatePillsOrBottles(ChemMasterComponent chemMaster, bool pills, FixedPoint2 amount, string label, EntityUid? user) + { + if (!_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out var bufferSolution)) + return; + + var filter = user.HasValue ? Filter.Entities(user.Value) : Filter.Empty(); + if (bufferSolution.TotalVolume == 0) + { + _popupSystem.PopupCursor(Loc.GetString("chem-master-window-buffer-empty-text"), filter); + return; + } + + var individualVolume = FixedPoint2.Min(bufferSolution.TotalVolume / amount, FixedPoint2.New(pills ? 50 : 30)); + if (individualVolume < FixedPoint2.New(1)) + { + _popupSystem.PopupCursor(Loc.GetString("chem-master-window-buffer-low-text"), filter); + return; + } + + for (int i = 0; i < amount; i++) + { + var item = Spawn(pills ? "Pill" : "ChemistryEmptyBottle01", Transform(chemMaster.Owner).Coordinates); + + if (label != "") + { + var labelComponent = EnsureComp(item); + labelComponent.OriginalName = Name(item); + string val = Name(item) + $" ({label})"; + Comp(item).EntityName = val; + labelComponent.CurrentLabel = label; + } + + var solution = bufferSolution.SplitSolution(individualVolume); + var itemSolution = _solutionContainerSystem.EnsureSolution(item, + pills ? SharedChemMaster.PillSolutionName : SharedChemMaster.BottleSolutionName); + _solutionContainerSystem.TryAddSolution(item, itemSolution, solution); + + if (pills) + { + if (TryComp(item, out var spriteComp)) + spriteComp.LayerSetState(0, "pill" + (chemMaster.PillType + 1)); + } + + if (user.HasValue) + _handsSystem.PickupOrDrop(user, item); + } + + UpdateUiState(chemMaster); + } + + private void ClickSound(ChemMasterComponent chemMaster) + { + _audioSystem.Play(chemMaster.ClickSound, Filter.Pvs(chemMaster.Owner), chemMaster.Owner, AudioParams.Default.WithVolume(-2f)); } } } diff --git a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.ChemMaster.cs b/Content.Server/Chemistry/EntitySystems/ChemistrySystem.ChemMaster.cs deleted file mode 100644 index 86f374af09..0000000000 --- a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.ChemMaster.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 InitializeChemMaster() - { - SubscribeLocalEvent(OnChemMasterPowerChange); - } - - private static void OnChemMasterPowerChange(EntityUid uid, ChemMasterComponent component, PowerChangedEvent args) - { - component.UpdateUserInterface(); - } -} diff --git a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs b/Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs index 0299eaa4a2..df6c1a5b93 100644 --- a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ChemistrySystem.cs @@ -20,7 +20,6 @@ public sealed partial class ChemistrySystem : EntitySystem public override void Initialize() { // Why ChemMaster duplicates reagentdispenser nobody knows. - InitializeChemMaster(); InitializeHypospray(); InitializeInjector(); InitializeReagentDispenser(); diff --git a/Content.Shared/Chemistry/Components/SharedChemMasterComponent.cs b/Content.Shared/Chemistry/Components/SharedChemMasterComponent.cs deleted file mode 100644 index 2dcf4349dd..0000000000 --- a/Content.Shared/Chemistry/Components/SharedChemMasterComponent.cs +++ /dev/null @@ -1,134 +0,0 @@ -using Content.Shared.Cloning; -using Content.Shared.Containers.ItemSlots; -using Content.Shared.FixedPoint; -using Robust.Shared.Serialization; - -namespace Content.Shared.Chemistry.Components -{ - - /// - /// Shared class for ChemMasterComponent. Provides a way for entities to split reagents from a beaker and produce pills and bottles via a user interface. - /// - [Virtual] - public class SharedChemMasterComponent : Component - { - public const int MaxEntitySpawns = 10; - public static string BeakerSlotId = "ChemMaster-beaker"; - - [DataField("beakerSlot")] - public ItemSlot BeakerSlot = new(); - public const string SolutionName = "buffer"; - - [Serializable, NetSerializable] - public sealed class ChemMasterBoundUserInterfaceState : BoundUserInterfaceState - { - public readonly bool HasPower; - public readonly bool HasBeaker; - public readonly FixedPoint2 BeakerCurrentVolume; - public readonly FixedPoint2 BeakerMaxVolume; - public readonly string ContainerName; - public readonly string Label; - - /// - /// A list of the reagents and their amounts within the beaker/reagent container, if applicable. - /// - public readonly IReadOnlyList ContainerReagents; - /// - /// A list of the reagents and their amounts within the buffer, if applicable. - /// - public readonly IReadOnlyList BufferReagents; - public readonly string DispenserName; - - public readonly bool BufferModeTransfer; - - public readonly FixedPoint2 BufferCurrentVolume; - public readonly uint SelectedPillType; - - public ChemMasterBoundUserInterfaceState(bool hasPower, bool hasBeaker, FixedPoint2 beakerCurrentVolume, FixedPoint2 beakerMaxVolume, string containerName, string label, - string dispenserName, IReadOnlyList containerReagents, IReadOnlyList bufferReagents, bool bufferModeTransfer, FixedPoint2 bufferCurrentVolume, uint selectedPillType) - { - HasPower = hasPower; - HasBeaker = hasBeaker; - BeakerCurrentVolume = beakerCurrentVolume; - BeakerMaxVolume = beakerMaxVolume; - ContainerName = containerName; - Label = label; - DispenserName = dispenserName; - ContainerReagents = containerReagents; - BufferReagents = bufferReagents; - BufferModeTransfer = bufferModeTransfer; - BufferCurrentVolume = bufferCurrentVolume; - SelectedPillType = selectedPillType; - } - } - - /// - /// Message data sent from client to server when a ChemMaster ui button is pressed. - /// - [Serializable, NetSerializable] - public sealed class UiActionMessage : BoundUserInterfaceMessage - { - public readonly UiAction Action; - public readonly FixedPoint2 Amount; - public readonly string Id = ""; - public readonly bool IsBuffer; - public readonly string Label = ""; - public readonly uint PillType; - public readonly int PillAmount; - public readonly int BottleAmount; - - public UiActionMessage(UiAction action, FixedPoint2? amount, string? id, bool? isBuffer, string? label, uint? pillType, int? pillAmount, int? bottleAmount) - { - Action = action; - if (Action == UiAction.ChemButton) - { - Amount = amount.GetValueOrDefault(); - if (id == null) - { - Id = "null"; - } - else - { - Id = id; - } - - IsBuffer = isBuffer.GetValueOrDefault(); - } - else - { - PillAmount = pillAmount.GetValueOrDefault(); - PillType = pillType.GetValueOrDefault(); - BottleAmount = bottleAmount.GetValueOrDefault(); - - if (label == null) - { - Label = "null"; - } - else - { - Label = label; - } - } - } - } - - [Serializable, NetSerializable] - public enum ChemMasterUiKey - { - Key - } - - /// - /// Used in to specify which button was pressed. - /// - public enum UiAction - { - Transfer, - Discard, - ChemButton, - CreatePills, - CreateBottles, - SetPillType - } - } -} diff --git a/Content.Shared/Chemistry/EntitySystems/SharedChemMasterSystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedChemMasterSystem.cs deleted file mode 100644 index 87a941d675..0000000000 --- a/Content.Shared/Chemistry/EntitySystems/SharedChemMasterSystem.cs +++ /dev/null @@ -1,30 +0,0 @@ -using JetBrains.Annotations; -using Content.Shared.Containers.ItemSlots; -using Content.Shared.Chemistry.Components; - -namespace Content.Shared.Chemistry.EntitySystems -{ - [UsedImplicitly] - public abstract class SharedChemMasterSystem : EntitySystem - { - [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnComponentInit); - SubscribeLocalEvent(OnComponentRemove); - } - - private void OnComponentInit(EntityUid uid, SharedChemMasterComponent component, ComponentInit args) - { - _itemSlotsSystem.AddItemSlot(uid, SharedChemMasterComponent.BeakerSlotId, component.BeakerSlot); - } - - private void OnComponentRemove(EntityUid uid, SharedChemMasterComponent component, ComponentRemove args) - { - _itemSlotsSystem.RemoveItemSlot(uid, component.BeakerSlot); - } - } -} diff --git a/Content.Shared/Chemistry/SharedChemMaster.cs b/Content.Shared/Chemistry/SharedChemMaster.cs new file mode 100644 index 0000000000..9fb7db1ee2 --- /dev/null +++ b/Content.Shared/Chemistry/SharedChemMaster.cs @@ -0,0 +1,164 @@ +using Content.Shared.Chemistry.Components; +using Content.Shared.FixedPoint; +using Robust.Shared.Serialization; + +namespace Content.Shared.Chemistry +{ + /// + /// This class holds constants that are shared between client and server. + /// + public sealed class SharedChemMaster + { + public const uint PillTypes = 20; + public const string BufferSolutionName = "buffer"; + public const string ContainerSlotName = "beakerSlot"; + public const string PillSolutionName = "food"; + public const string BottleSolutionName = "drink"; + } + + [Serializable, NetSerializable] + public sealed class ChemMasterSetModeMessage : BoundUserInterfaceMessage + { + public readonly ChemMasterMode ChemMasterMode; + + public ChemMasterSetModeMessage(ChemMasterMode mode) + { + ChemMasterMode = mode; + } + } + + [Serializable, NetSerializable] + public sealed class ChemMasterSetPillTypeMessage : BoundUserInterfaceMessage + { + public readonly uint PillType; + + public ChemMasterSetPillTypeMessage(uint pillType) + { + PillType = pillType; + } + } + + [Serializable, NetSerializable] + public sealed class ChemMasterReagentAmountButtonMessage : BoundUserInterfaceMessage + { + public readonly string ReagentId; + public readonly ChemMasterReagentAmount Amount; + public readonly bool FromBuffer; + + public ChemMasterReagentAmountButtonMessage(string reagentId, ChemMasterReagentAmount amount, bool fromBuffer) + { + ReagentId = reagentId; + Amount = amount; + FromBuffer = fromBuffer; + } + } + + [Serializable, NetSerializable] + public sealed class ChemMasterCreatePillsMessage : BoundUserInterfaceMessage + { + public readonly uint Amount; + public readonly string Label; + + public ChemMasterCreatePillsMessage(uint amount, string label) + { + Amount = amount; + Label = label; + } + } + + [Serializable, NetSerializable] + public sealed class ChemMasterCreateBottlesMessage : BoundUserInterfaceMessage + { + public readonly uint Amount; + public readonly string Label; + + public ChemMasterCreateBottlesMessage(uint amount, string label) + { + Amount = amount; + Label = label; + } + } + + public enum ChemMasterMode + { + Transfer, + Discard, + } + + public enum ChemMasterReagentAmount + { + U1 = 1, + U5 = 5, + U10 = 10, + U25 = 25, + All, + } + + public static class ChemMasterReagentAmountToFixedPoint + { + public static FixedPoint2 GetFixedPoint(this ChemMasterReagentAmount amount) + { + if (amount == ChemMasterReagentAmount.All) + return FixedPoint2.MaxValue; + else + return FixedPoint2.New((int)amount); + } + } + + [Serializable, NetSerializable] + public sealed class ChemMasterBoundUserInterfaceState : BoundUserInterfaceState + { + public readonly FixedPoint2? ContainerCurrentVolume; + public readonly FixedPoint2? ContainerMaxVolume; + public readonly string? ContainerName; + + /// + /// A list of the reagents and their amounts within the beaker/reagent container, if applicable. + /// + public readonly IReadOnlyList? ContainerReagents; + /// + /// 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; + + public readonly FixedPoint2? BufferCurrentVolume; + public readonly uint SelectedPillType; + + public readonly uint PillProductionLimit; + public readonly uint BottleProductionLimit; + + public readonly bool UpdateLabel; + + public ChemMasterBoundUserInterfaceState(FixedPoint2? containerCurrentVolume, FixedPoint2? containerMaxVolume, string? containerName, + string dispenserName, IReadOnlyList? containerReagents, IReadOnlyList bufferReagents, ChemMasterMode mode, + FixedPoint2 bufferCurrentVolume, uint selectedPillType, uint pillProdictionLimit, uint bottleProdictionLimit, bool updateLabel) + { + ContainerCurrentVolume = containerCurrentVolume; + ContainerMaxVolume = containerMaxVolume; + ContainerName = containerName; + DispenserName = dispenserName; + ContainerReagents = containerReagents; + BufferReagents = bufferReagents; + Mode = mode; + BufferCurrentVolume = bufferCurrentVolume; + SelectedPillType = selectedPillType; + PillProductionLimit = pillProdictionLimit; + BottleProductionLimit = bottleProdictionLimit; + UpdateLabel = updateLabel; + } + + public bool HasContainer() + { + return ContainerCurrentVolume is not null; + } + } + + [Serializable, NetSerializable] + public enum ChemMasterUiKey + { + Key + } +} diff --git a/Resources/Maps/centcomm.yml b/Resources/Maps/centcomm.yml index 5b8e5a9532..1eca104214 100644 --- a/Resources/Maps/centcomm.yml +++ b/Resources/Maps/centcomm.yml @@ -24229,21 +24229,6 @@ entities: - pos: 19.5,38.5 parent: 55 type: Transform - - containers: - - machine_parts - - machine_board - type: Construction - - containers: - ChemMaster-beaker: !type:ContainerSlot {} - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - type: ContainerContainer - - solutions: - buffer: - reagents: [] - type: SolutionContainerManager - uid: 2848 type: DrinkWhiskeyGlass components: diff --git a/Resources/Maps/nss_pillar.yml b/Resources/Maps/nss_pillar.yml index 31ef3bdb1f..f9408d69df 100644 --- a/Resources/Maps/nss_pillar.yml +++ b/Resources/Maps/nss_pillar.yml @@ -47936,21 +47936,6 @@ entities: - pos: 11.5,-3.5 parent: 130 type: Transform - - containers: - - machine_parts - - machine_board - type: Construction - - containers: - ChemMaster-beaker: !type:ContainerSlot {} - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - type: ContainerContainer - - solutions: - buffer: - reagents: [] - type: SolutionContainerManager - uid: 4124 type: SignChemistry1 components: @@ -138124,42 +138109,12 @@ entities: - pos: 11.5,-8.5 parent: 130 type: Transform - - containers: - - machine_parts - - machine_board - type: Construction - - containers: - ChemMaster-beaker: !type:ContainerSlot {} - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - type: ContainerContainer - - solutions: - buffer: - reagents: [] - type: SolutionContainerManager - uid: 16059 type: chem_master components: - pos: 11.5,-5.5 parent: 130 type: Transform - - containers: - - machine_parts - - machine_board - type: Construction - - containers: - ChemMaster-beaker: !type:ContainerSlot {} - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - type: ContainerContainer - - solutions: - buffer: - reagents: [] - type: SolutionContainerManager - uid: 16060 type: WallReinforced components: diff --git a/Resources/Maps/nukieplanet.yml b/Resources/Maps/nukieplanet.yml index e22d5bea5c..9d963ba6e8 100644 --- a/Resources/Maps/nukieplanet.yml +++ b/Resources/Maps/nukieplanet.yml @@ -17106,21 +17106,6 @@ entities: - pos: 5.5,-10.5 parent: 104 type: Transform - - containers: - - machine_parts - - machine_board - type: Construction - - containers: - ChemMaster-beaker: !type:ContainerSlot {} - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - type: ContainerContainer - - solutions: - buffer: - reagents: [] - type: SolutionContainerManager - uid: 2036 type: AsteroidRock components: diff --git a/Resources/Maps/packedstation.yml b/Resources/Maps/packedstation.yml index d316274f6b..52dbd78abd 100644 --- a/Resources/Maps/packedstation.yml +++ b/Resources/Maps/packedstation.yml @@ -44578,28 +44578,12 @@ entities: - pos: 49.5,-4.5 parent: 2 type: Transform - - containers: - ChemMaster-reagentContainerContainer: !type:ContainerSlot {} - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - ChemMaster-beaker: !type:ContainerSlot {} - type: ContainerContainer - uid: 5053 type: chem_master components: - pos: 52.5,-3.5 parent: 2 type: Transform - - containers: - ChemMaster-reagentContainerContainer: !type:ContainerSlot {} - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - ChemMaster-beaker: !type:ContainerSlot {} - type: ContainerContainer - uid: 5054 type: Table components: diff --git a/Resources/Maps/splitstation.yml b/Resources/Maps/splitstation.yml index e4dd62f46c..24296fa6df 100644 --- a/Resources/Maps/splitstation.yml +++ b/Resources/Maps/splitstation.yml @@ -74805,21 +74805,6 @@ entities: - pos: -46.5,-33.5 parent: 69 type: Transform - - containers: - - machine_parts - - machine_board - type: Construction - - containers: - ChemMaster-beaker: !type:ContainerSlot {} - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - type: ContainerContainer - - solutions: - buffer: - reagents: [] - type: SolutionContainerManager - uid: 6865 type: TableReinforcedGlass components: @@ -77027,21 +77012,6 @@ entities: - pos: -48.5,-30.5 parent: 69 type: Transform - - containers: - - machine_parts - - machine_board - type: Construction - - containers: - ChemMaster-beaker: !type:ContainerSlot {} - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - type: ContainerContainer - - solutions: - buffer: - reagents: [] - type: SolutionContainerManager - uid: 7180 type: DisposalPipe components: @@ -100515,21 +100485,6 @@ entities: - pos: -56.5,4.5 parent: 69 type: Transform - - containers: - - machine_parts - - machine_board - type: Construction - - containers: - ChemMaster-beaker: !type:ContainerSlot {} - machine_board: !type:Container - ents: [] - machine_parts: !type:Container - ents: [] - type: ContainerContainer - - solutions: - buffer: - reagents: [] - type: SolutionContainerManager - uid: 10453 type: AsteroidRock components: diff --git a/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml b/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml index 8facf9e58c..7d43057a82 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml @@ -18,11 +18,8 @@ sprite: Structures/Machines/mixer.rsi state: mixer_loaded - type: ChemMaster - beakerSlot: - whitelistFailPopup: chem-master-component-cannot-put-entity-message - whitelist: - components: - - FitsInDispenser + pillProductionLimit: 10 + bottleProductionLimit: 10 - type: Physics bodyType: Static - type: Fixtures @@ -63,10 +60,16 @@ - type: MaterialStorage - type: ContainerContainer containers: + beakerSlot: !type:ContainerSlot machine_board: !type:Container machine_parts: !type:Container - ChemMaster-beaker: !type:ContainerSlot - type: ItemSlots + slots: + beakerSlot: + whitelistFailPopup: chem-master-component-cannot-put-entity-message + whitelist: + components: + - FitsInDispenser - type: SolutionContainerManager solutions: buffer: {}