diff --git a/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs b/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs
index 5df7a61836..25604a881f 100644
--- a/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs
+++ b/Content.Client/Chemistry/UI/ChemMasterBoundUserInterface.cs
@@ -37,11 +37,19 @@ namespace Content.Client.Chemistry.UI
_window.OnClose += Close;
// 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));
+ _window.InputEjectButton.OnPressed += _ => SendMessage(
+ new ItemSlotButtonPressedEvent(SharedChemMaster.InputSlotName));
+ _window.OutputEjectButton.OnPressed += _ => SendMessage(
+ new ItemSlotButtonPressedEvent(SharedChemMaster.OutputSlotName));
+ _window.BufferTransferButton.OnPressed += _ => SendMessage(
+ new ChemMasterSetModeMessage(ChemMasterMode.Transfer));
+ _window.BufferDiscardButton.OnPressed += _ => SendMessage(
+ new ChemMasterSetModeMessage(ChemMasterMode.Discard));
+ _window.CreatePillButton.OnPressed += _ => SendMessage(
+ new ChemMasterCreatePillsMessage(
+ (uint)_window.PillDosage.Value, (uint)_window.PillNumber.Value, _window.LabelLine));
+ _window.CreateBottleButton.OnPressed += _ => SendMessage(
+ new ChemMasterOutputToBottleMessage((uint)_window.BottleDosage.Value, _window.LabelLine));
for (uint i = 0; i < _window.PillTypeButtons.Length; i++)
{
diff --git a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml
index 57bdd1e805..af15fe4684 100644
--- a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml
+++ b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml
@@ -1,76 +1,106 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+ Margin="4 4 4 4"
+ HorizontalExpand="True"
+ VerticalExpand="True"
+ SeparationOverride="5">
@@ -89,30 +119,38 @@
-
+
+
+
+
-
+ Text="{Loc 'chem-master-window-create-button'}" />
-
+
+ Value="0" />
-
+ Text="{Loc 'chem-master-window-create-button'}" />
-
-
+
+
diff --git a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs
index cd7d82d66a..d282a17cbe 100644
--- a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs
+++ b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs
@@ -75,8 +75,12 @@ namespace Content.Client.Chemistry.UI
Grid.AddChild(PillTypeButtons[i]);
}
- PillAmount.InitDefaultButtons();
- BottleAmount.InitDefaultButtons();
+ PillDosage.InitDefaultButtons();
+ PillNumber.InitDefaultButtons();
+ BottleDosage.InitDefaultButtons();
+
+ Tabs.SetTabTitle(0, Loc.GetString("chem-master-window-input-tab"));
+ Tabs.SetTabTitle(1, Loc.GetString("chem-master-window-output-tab"));
}
private ReagentButton MakeReagentButton(string text, ChemMasterReagentAmount amount, string id, bool isBuffer, string styleClass)
@@ -98,14 +102,30 @@ namespace Content.Client.Chemistry.UI
if (castState.UpdateLabel)
LabelLine = GenerateLabel(castState);
UpdatePanelInfo(castState);
- if (Contents.Children != null)
- {
- EjectButton.Disabled = !castState.HasContainer();
- }
+
+ var output = castState.OutputContainerInfo;
+
+ BufferCurrentVolume.Text = $" {castState.BufferCurrentVolume?.Int() ?? 0}u";
+
+ InputEjectButton.Disabled = castState.InputContainerInfo is null;
+ OutputEjectButton.Disabled = output is null;
+ CreateBottleButton.Disabled = output is null || !output.HoldsReagents;
+ CreatePillButton.Disabled = output is null || output.HoldsReagents;
+
+ var remainingCapacity = output is null ? 0 : (output.MaxVolume - output.CurrentVolume).Int();
+ var holdsReagents = output?.HoldsReagents ?? false;
+ var pillNumberMax = holdsReagents ? 0 : remainingCapacity;
+ var bottleAmountMax = holdsReagents ? remainingCapacity : 0;
PillTypeButtons[castState.SelectedPillType].Pressed = true;
- PillAmount.IsValid = x => x > 0 && x <= castState.PillProductionLimit;
- BottleAmount.IsValid = x => x > 0 && x <= castState.BottleProductionLimit;
+ PillNumber.IsValid = x => x >= 0 && x <= pillNumberMax;
+ PillDosage.IsValid = x => x > 0 && x <= castState.PillDosageLimit;
+ BottleDosage.IsValid = x => x >= 0 && x <= bottleAmountMax;
+
+ if (PillNumber.Value > pillNumberMax)
+ PillNumber.Value = pillNumberMax;
+ if (BottleDosage.Value > bottleAmountMax)
+ BottleDosage.Value = bottleAmountMax;
}
///
@@ -134,64 +154,8 @@ namespace Content.Client.Chemistry.UI
BufferTransferButton.Pressed = state.Mode == Shared.Chemistry.ChemMasterMode.Transfer;
BufferDiscardButton.Pressed = state.Mode == Shared.Chemistry.ChemMasterMode.Discard;
- ContainerInfo.Children.Clear();
-
- if (!state.HasContainer())
- {
- ContainerInfo.Children.Add(new Label {Text = Loc.GetString("chem-master-window-no-container-loaded-text") });
- }
- else
- {
- ContainerInfo.Children.Add(new BoxContainer // Name of the container and its fill status (Ex: 44/100u)
- {
- Orientation = LayoutOrientation.Horizontal,
- Children =
- {
- new Label {Text = $"{state.ContainerName}: "},
- new Label
- {
- Text = $"{state.ContainerCurrentVolume}/{state.ContainerMaxVolume}",
- StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
- }
- }
- });
-
- foreach (var reagent in state.ContainerReagents!.OrderBy(
- r => {_prototypeManager.TryIndex(r.ReagentId, out ReagentPrototype? p); return p?.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
- {
- Orientation = LayoutOrientation.Horizontal,
- Children =
- {
- new Label {Text = $"{name}: "},
- new Label
- {
- Text = $"{reagent.Quantity}u",
- StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
- },
-
- // Padding
- new Control {HorizontalExpand = true},
-
- 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),
- }
- });
- }
- }
- }
-
-
+ BuildContainerUI(InputContainerInfo, state.InputContainerInfo, true);
+ BuildContainerUI(OutputContainerInfo, state.OutputContainerInfo, false);
BufferInfo.Children.Clear();
@@ -212,7 +176,7 @@ namespace Content.Client.Chemistry.UI
bufferHBox.AddChild(bufferLabel);
var bufferVol = new Label
{
- Text = $"{state.BufferCurrentVolume}",
+ Text = $"{state.BufferCurrentVolume}u",
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
};
bufferHBox.AddChild(bufferVol);
@@ -251,6 +215,92 @@ namespace Content.Client.Chemistry.UI
}
}
+ private void BuildContainerUI(Control control, ContainerInfo? info, bool addReagentButtons)
+ {
+ control.Children.Clear();
+
+ if (info is null)
+ {
+ control.Children.Add(new Label
+ {
+ Text = Loc.GetString("chem-master-window-no-container-loaded-text")
+ });
+ }
+ else
+ {
+ // Name of the container and its fill status (Ex: 44/100u)
+ control.Children.Add(new BoxContainer
+ {
+ Orientation = LayoutOrientation.Horizontal,
+ Children =
+ {
+ new Label {Text = $"{info.DisplayName}: "},
+ new Label
+ {
+ Text = $"{info.CurrentVolume}/{info.MaxVolume}",
+ StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
+ }
+ }
+ });
+
+ var contents = info.Contents
+ .Select(lineItem =>
+ {
+ if (!info.HoldsReagents)
+ return (lineItem.Id, lineItem.Id, lineItem.Quantity);
+
+ // Try to get the prototype for the given reagent. This gives us its name.
+ _prototypeManager.TryIndex(lineItem.Id, out ReagentPrototype? proto);
+ var name = proto?.LocalizedName
+ ?? Loc.GetString("chem-master-window-unknown-reagent-text");
+
+ return (name, lineItem.Id, lineItem.Quantity);
+
+ })
+ .OrderBy(r => r.Item1);
+
+ foreach (var (name, id, quantity) in contents)
+ {
+ var inner = new BoxContainer
+ {
+ Orientation = LayoutOrientation.Horizontal,
+ Children =
+ {
+ new Label { Text = $"{name}: " },
+ new Label
+ {
+ Text = $"{quantity}u",
+ StyleClasses = { StyleNano.StyleClassLabelSecondaryColor },
+ }
+ }
+ };
+
+ if (addReagentButtons)
+ {
+ var cs = inner.Children;
+
+ // Padding
+ cs.Add(new Control { HorizontalExpand = true });
+
+ cs.Add(MakeReagentButton(
+ "1", ChemMasterReagentAmount.U1, id, false, StyleBase.ButtonOpenRight));
+ cs.Add(MakeReagentButton(
+ "5", ChemMasterReagentAmount.U5, id, false, StyleBase.ButtonOpenBoth));
+ cs.Add(MakeReagentButton(
+ "10", ChemMasterReagentAmount.U10, id, false, StyleBase.ButtonOpenBoth));
+ cs.Add(MakeReagentButton(
+ "25", ChemMasterReagentAmount.U25, id, false, StyleBase.ButtonOpenBoth));
+ cs.Add(MakeReagentButton(
+ Loc.GetString("chem-master-window-buffer-all-amount"),
+ ChemMasterReagentAmount.All, id, false, StyleBase.ButtonOpenLeft));
+ }
+
+ control.Children.Add(inner);
+ }
+
+ }
+ }
+
public String LabelLine
{
get
diff --git a/Content.Server/Chemistry/Components/ChemMasterComponent.cs b/Content.Server/Chemistry/Components/ChemMasterComponent.cs
index 4b9db13863..c1f79909f6 100644
--- a/Content.Server/Chemistry/Components/ChemMasterComponent.cs
+++ b/Content.Server/Chemistry/Components/ChemMasterComponent.cs
@@ -18,11 +18,8 @@ namespace Content.Server.Chemistry.Components
[DataField("mode"), ViewVariables(VVAccess.ReadWrite)]
public ChemMasterMode Mode = ChemMasterMode.Transfer;
- [DataField("pillProductionLimit", required: true), ViewVariables(VVAccess.ReadWrite)]
- public uint PillProductionLimit;
-
- [DataField("bottleProductionLimit", required: true), ViewVariables(VVAccess.ReadWrite)]
- public uint BottleProductionLimit;
+ [DataField("pillDosageLimit", required: true), ViewVariables(VVAccess.ReadWrite)]
+ public uint PillDosageLimit;
[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 60e808d402..e50bc008e0 100644
--- a/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs
+++ b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs
@@ -1,16 +1,20 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using Content.Server.Chemistry.Components;
using Content.Server.Labels.Components;
using Content.Server.Popups;
+using Content.Server.Storage.Components;
+using Content.Server.Storage.EntitySystems;
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;
+using Robust.Shared.Utility;
namespace Content.Server.Chemistry.EntitySystems
@@ -28,7 +32,9 @@ namespace Content.Server.Chemistry.EntitySystems
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
- [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
+ [Dependency] private readonly StorageSystem _storageSystem = default!;
+
+ private const string PillPrototypeId = "Pill";
public override void Initialize()
{
@@ -44,33 +50,26 @@ namespace Content.Server.Chemistry.EntitySystems
SubscribeLocalEvent(OnSetPillTypeMessage);
SubscribeLocalEvent(OnReagentButtonMessage);
SubscribeLocalEvent(OnCreatePillsMessage);
- SubscribeLocalEvent(OnCreateBottlesMessage);
+ SubscribeLocalEvent(OnOutputToBottleMessage);
}
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);
+ var inputContainer = _itemSlotsSystem.GetItem(chemMaster.Owner, SharedChemMaster.InputSlotName);
+ var outputContainer = _itemSlotsSystem.GetItem(chemMaster.Owner, SharedChemMaster.OutputSlotName);
- 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
- );
+ chemMaster.Mode, dispenserName,
+ BuildInputContainerInfo(inputContainer), BuildOutputContainerInfo(outputContainer),
+ bufferReagents, bufferCurrentVolume,
+ chemMaster.PillType, chemMaster.PillDosageLimit, updateLabel);
+
_userInterfaceSystem.TrySetUiState(chemMaster.Owner, ChemMasterUiKey.Key, state);
}
@@ -120,7 +119,7 @@ namespace Content.Server.Chemistry.EntitySystems
private void TransferReagents(ChemMasterComponent chemMaster, string reagentId, FixedPoint2 amount, bool fromBuffer)
{
- var container = _itemSlotsSystem.GetItem(chemMaster.Owner, SharedChemMaster.ContainerSlotName);
+ var container = _itemSlotsSystem.GetItem(chemMaster.Owner, SharedChemMaster.InputSlotName);
if (container is null ||
!_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution) ||
!_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out var bufferSolution))
@@ -156,7 +155,7 @@ namespace Content.Server.Chemistry.EntitySystems
}
else
{
- var container = _itemSlotsSystem.GetItem(chemMaster.Owner, SharedChemMaster.ContainerSlotName);
+ var container = _itemSlotsSystem.GetItem(chemMaster.Owner, SharedChemMaster.InputSlotName);
if (container is not null &&
_solutionContainerSystem.TryGetFitsInDispenser(container.Value, out var containerSolution))
{
@@ -171,79 +170,172 @@ 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);
+ 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.Amount == 0 || message.Amount > chemMaster.PillProductionLimit)
+ if (message.Dosage == 0 || message.Dosage > chemMaster.PillDosageLimit)
return;
- CreatePillsOrBottles(chemMaster, pills: true, message.Amount, message.Label, message.Session.AttachedEntity);
+ var needed = message.Dosage * message.Number;
+ if (!WithdrawFromBuffer(chemMaster, needed, user, out var withdrawal))
+ return;
+
+ Label(container, message.Label);
+
+ for (var i = 0; i < message.Number; i++)
+ {
+ var item = Spawn(PillPrototypeId, Transform(container).Coordinates);
+ DebugTools.Assert(_storageSystem.Insert(container, item, storage));
+ Label(item, message.Label);
+
+ var itemSolution = _solutionContainerSystem.EnsureSolution(item, SharedChemMaster.PillSolutionName);
+
+ DebugTools.Assert(_solutionContainerSystem.TryAddSolution(
+ item, itemSolution, withdrawal.SplitSolution(message.Dosage)));
+
+ if (TryComp(item, out var spriteComp))
+ spriteComp.LayerSetState(0, "pill" + (chemMaster.PillType + 1));
+ }
+
UpdateUiState(chemMaster);
ClickSound(chemMaster);
}
- private void OnCreateBottlesMessage(EntityUid uid, ChemMasterComponent chemMaster, ChemMasterCreateBottlesMessage message)
+ private void OnOutputToBottleMessage(
+ EntityUid uid, ChemMasterComponent chemMaster, ChemMasterOutputToBottleMessage message)
{
+ var user = message.Session.AttachedEntity;
+ var maybeContainer = _itemSlotsSystem.GetItem(chemMaster.Owner, SharedChemMaster.OutputSlotName);
+ if (maybeContainer is not { Valid: true } container
+ || !_solutionContainerSystem.TryGetSolution(
+ container, SharedChemMaster.BottleSolutionName, out var solution))
+ {
+ return; // output can't fit reagents
+ }
+
// Ensure the amount is valid.
- if (message.Amount == 0 || message.Amount > chemMaster.BottleProductionLimit)
+ if (message.Dosage == 0 || message.Dosage > solution.AvailableVolume)
+ return;
+
+ if (!WithdrawFromBuffer(chemMaster, message.Dosage, user, out var withdrawal))
return;
- CreatePillsOrBottles(chemMaster, pills: false, message.Amount, message.Label, message.Session.AttachedEntity);
+ Label(container, message.Label);
+ DebugTools.Assert(_solutionContainerSystem.TryAddSolution(
+ container, solution, withdrawal));
+
UpdateUiState(chemMaster);
ClickSound(chemMaster);
}
- private void CreatePillsOrBottles(ChemMasterComponent chemMaster, bool pills, FixedPoint2 amount, string label, EntityUid? user)
+ private bool WithdrawFromBuffer(
+ IComponent chemMaster,
+ FixedPoint2 neededVolume, EntityUid? user,
+ [NotNullWhen(returnValue: true)] out Solution? outputSolution)
{
- if (!_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out var bufferSolution))
- return;
+ outputSolution = null;
+
+ if (!_solutionContainerSystem.TryGetSolution(
+ chemMaster.Owner, SharedChemMaster.BufferSolutionName, out var solution))
+ {
+ return false;
+ }
var filter = user.HasValue ? Filter.Entities(user.Value) : Filter.Empty();
- if (bufferSolution.TotalVolume == 0)
+ if (solution.TotalVolume == 0)
{
_popupSystem.PopupCursor(Loc.GetString("chem-master-window-buffer-empty-text"), filter);
- return;
+ return false;
}
- var individualVolume = FixedPoint2.Min(bufferSolution.TotalVolume / amount, FixedPoint2.New(pills ? 50 : 30));
- if (individualVolume < FixedPoint2.New(1))
+ // ReSharper disable once InvertIf
+ if (neededVolume > solution.CurrentVolume)
{
_popupSystem.PopupCursor(Loc.GetString("chem-master-window-buffer-low-text"), filter);
+ return false;
+ }
+
+ outputSolution = solution.SplitSolution(neededVolume);
+ return true;
+ }
+
+ private void Label(EntityUid ent, string label)
+ {
+ if (string.IsNullOrEmpty(label))
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);
+ var labelComponent = EnsureComp(ent);
+
+ labelComponent.OriginalName = Name(ent);
+ var val = Name(ent) + $" ({label})";
+ MetaData(ent).EntityName = val;
+ labelComponent.CurrentLabel = label;
}
private void ClickSound(ChemMasterComponent chemMaster)
{
_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 })
+ return null;
+
+ if (!TryComp(container, out FitsInDispenserComponent? fits)
+ || !_solutionContainerSystem.TryGetSolution(container.Value, fits.Solution, out var solution))
+ {
+ return null;
+ }
+
+ return BuildContainerInfo(Name(container.Value), solution);
+ }
+
+ private ContainerInfo? BuildOutputContainerInfo(EntityUid? container)
+ {
+ if (container is not { Valid: true })
+ return null;
+
+ var name = Name(container.Value);
+ {
+ if (_solutionContainerSystem.TryGetSolution(
+ container.Value, SharedChemMaster.BottleSolutionName, out var solution))
+ {
+ return BuildContainerInfo(name, solution);
+ }
+ }
+
+ if (!TryComp(container, out ServerStorageComponent? storage))
+ return null;
+
+ var pills = storage.Storage?.ContainedEntities.Select(pill =>
+ {
+ _solutionContainerSystem.TryGetSolution(pill, SharedChemMaster.PillSolutionName, out var solution);
+ var quantity = solution?.CurrentVolume ?? FixedPoint2.Zero;
+ return (Name(pill), quantity);
+ }).ToList();
+
+ return pills is null
+ ? null
+ : new ContainerInfo(name, false, storage.StorageUsed, storage.StorageCapacityMax, pills);
+ }
+
+ private static ContainerInfo BuildContainerInfo(string name, Solution solution)
+ {
+ var reagents = solution.Contents
+ .Select(reagent => (reagent.ReagentId, reagent.Quantity)).ToList();
+
+ return new ContainerInfo(name, true, solution.CurrentVolume, solution.MaxVolume, reagents);
+ }
}
}
diff --git a/Content.Shared/Chemistry/Components/FitsInDispenserComponent.cs b/Content.Shared/Chemistry/Components/FitsInDispenserComponent.cs
index e008a02a7a..38e3f00d07 100644
--- a/Content.Shared/Chemistry/Components/FitsInDispenserComponent.cs
+++ b/Content.Shared/Chemistry/Components/FitsInDispenserComponent.cs
@@ -2,11 +2,13 @@ using Robust.Shared.GameStates;
namespace Content.Shared.Chemistry.Components
{
+ ///
/// Allows the entity with this component to be placed in a SharedReagentDispenserComponent.
/// Otherwise it's considered to be too large or the improper shape to fit.
/// Allows us to have obscenely large containers that are harder to abuse in chem dispensers
/// since they can't be placed directly in them.
///
+ ///
[RegisterComponent]
[NetworkedComponent] // only needed for white-lists. Client doesn't actually need Solution data;
public sealed class FitsInDispenserComponent : Component
diff --git a/Content.Shared/Chemistry/SharedChemMaster.cs b/Content.Shared/Chemistry/SharedChemMaster.cs
index 9fb7db1ee2..a46277b39f 100644
--- a/Content.Shared/Chemistry/SharedChemMaster.cs
+++ b/Content.Shared/Chemistry/SharedChemMaster.cs
@@ -11,7 +11,8 @@ namespace Content.Shared.Chemistry
{
public const uint PillTypes = 20;
public const string BufferSolutionName = "buffer";
- public const string ContainerSlotName = "beakerSlot";
+ public const string InputSlotName = "beakerSlot";
+ public const string OutputSlotName = "outputSlot";
public const string PillSolutionName = "food";
public const string BottleSolutionName = "drink";
}
@@ -56,25 +57,27 @@ namespace Content.Shared.Chemistry
[Serializable, NetSerializable]
public sealed class ChemMasterCreatePillsMessage : BoundUserInterfaceMessage
{
- public readonly uint Amount;
+ public readonly uint Dosage;
+ public readonly uint Number;
public readonly string Label;
- public ChemMasterCreatePillsMessage(uint amount, string label)
+ public ChemMasterCreatePillsMessage(uint dosage, uint number, string label)
{
- Amount = amount;
+ Dosage = dosage;
+ Number = number;
Label = label;
}
}
[Serializable, NetSerializable]
- public sealed class ChemMasterCreateBottlesMessage : BoundUserInterfaceMessage
+ public sealed class ChemMasterOutputToBottleMessage : BoundUserInterfaceMessage
{
- public readonly uint Amount;
+ public readonly uint Dosage;
public readonly string Label;
- public ChemMasterCreateBottlesMessage(uint amount, string label)
+ public ChemMasterOutputToBottleMessage(uint dosage, string label)
{
- Amount = amount;
+ Dosage = dosage;
Label = label;
}
}
@@ -105,17 +108,53 @@ namespace Content.Shared.Chemistry
}
}
+ ///
+ /// Information about the capacity and contents of a container for display in the UI
+ ///
+ [Serializable, NetSerializable]
+ public sealed class ContainerInfo
+ {
+ ///
+ /// The container name to show to the player
+ ///
+ public readonly string DisplayName;
+ ///
+ /// Whether the container holds reagents or entities
+ ///
+ public readonly bool HoldsReagents;
+ ///
+ /// The currently used volume of the container
+ ///
+ public readonly FixedPoint2 CurrentVolume;
+ ///
+ /// The maximum volume of the container
+ ///
+ public readonly FixedPoint2 MaxVolume;
+ ///
+ /// A list of the reagents/entities and their sizes within the container
+ ///
+ // todo: this causes NetSerializer exceptions if it's an IReadOnlyList (which would be preferred)
+ public readonly List<(string Id, FixedPoint2 Quantity)> Contents;
+
+ public ContainerInfo(
+ string displayName, bool holdsReagents,
+ FixedPoint2 currentVolume, FixedPoint2 maxVolume,
+ List<(string, FixedPoint2)> contents)
+ {
+ DisplayName = displayName;
+ HoldsReagents = holdsReagents;
+ CurrentVolume = currentVolume;
+ MaxVolume = maxVolume;
+ Contents = contents;
+ }
+ }
+
[Serializable, NetSerializable]
public sealed class ChemMasterBoundUserInterfaceState : BoundUserInterfaceState
{
- public readonly FixedPoint2? ContainerCurrentVolume;
- public readonly FixedPoint2? ContainerMaxVolume;
- public readonly string? ContainerName;
+ public readonly ContainerInfo? InputContainerInfo;
+ public readonly ContainerInfo? OutputContainerInfo;
- ///
- /// 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.
///
@@ -127,33 +166,26 @@ namespace Content.Shared.Chemistry
public readonly FixedPoint2? BufferCurrentVolume;
public readonly uint SelectedPillType;
- public readonly uint PillProductionLimit;
- public readonly uint BottleProductionLimit;
+ public readonly uint PillDosageLimit;
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)
+ public ChemMasterBoundUserInterfaceState(
+ ChemMasterMode mode, string dispenserName,
+ ContainerInfo? inputContainerInfo, ContainerInfo? outputContainerInfo,
+ IReadOnlyList bufferReagents, FixedPoint2 bufferCurrentVolume,
+ uint selectedPillType, uint pillDosageLimit, bool updateLabel)
{
- ContainerCurrentVolume = containerCurrentVolume;
- ContainerMaxVolume = containerMaxVolume;
- ContainerName = containerName;
+ InputContainerInfo = inputContainerInfo;
+ OutputContainerInfo = outputContainerInfo;
DispenserName = dispenserName;
- ContainerReagents = containerReagents;
BufferReagents = bufferReagents;
Mode = mode;
BufferCurrentVolume = bufferCurrentVolume;
SelectedPillType = selectedPillType;
- PillProductionLimit = pillProdictionLimit;
- BottleProductionLimit = bottleProdictionLimit;
+ PillDosageLimit = pillDosageLimit;
UpdateLabel = updateLabel;
}
-
- public bool HasContainer()
- {
- return ContainerCurrentVolume is not null;
- }
}
[Serializable, NetSerializable]
diff --git a/Resources/Locale/en-US/chemistry/components/chem-master-component.ftl b/Resources/Locale/en-US/chemistry/components/chem-master-component.ftl
index e24df2466a..51110f12c4 100644
--- a/Resources/Locale/en-US/chemistry/components/chem-master-component.ftl
+++ b/Resources/Locale/en-US/chemistry/components/chem-master-component.ftl
@@ -8,6 +8,8 @@ chem-master-bound-user-interface-title = ChemMaster 4000
## UI
+chem-master-window-input-tab = Input
+chem-master-window-output-tab = Output
chem-master-window-container-label = Container
chem-master-window-eject-button = Eject
chem-master-window-no-container-loaded-text = No container loaded.
@@ -22,9 +24,8 @@ chem-master-window-packaging-text = Packaging
chem-master-current-text-label = Label:
chem-master-window-pills-label = Pills:
chem-master-window-pill-type-label = Pill type:
-chem-master-window-max-pills-volume-text = max 50u/each
-chem-master-window-max-bottles-volume-text = max 30u/each
-chem-master-window-create-pill-button = Create
-chem-master-window-create-bottle-button = Create
+chem-master-window-pills-number-label = Count:
+chem-master-window-dose-label = Dose (u):
+chem-master-window-create-button = Create
chem-master-window-bottles-label = Bottles:
chem-master-window-unknown-reagent-text = Unknown reagent
diff --git a/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml b/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml
index 7d43057a82..1980151a0a 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml
@@ -18,8 +18,7 @@
sprite: Structures/Machines/mixer.rsi
state: mixer_loaded
- type: ChemMaster
- pillProductionLimit: 10
- bottleProductionLimit: 10
+ pillDosageLimit: 50
- type: Physics
bodyType: Static
- type: Fixtures
@@ -61,15 +60,21 @@
- type: ContainerContainer
containers:
beakerSlot: !type:ContainerSlot
+ outputSlot: !type:ContainerSlot
machine_board: !type:Container
machine_parts: !type:Container
- type: ItemSlots
slots:
beakerSlot:
- whitelistFailPopup: chem-master-component-cannot-put-entity-message
whitelist:
components:
- FitsInDispenser
+ outputSlot:
+ whitelistFailPopup: chem-master-component-cannot-put-entity-message
+ whitelist:
+ tags:
+ - Bottle
+ - PillCanister
- type: SolutionContainerManager
solutions:
buffer: {}