diff --git a/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs b/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs
index 7bbd7fad17..58e2ac2b8d 100644
--- a/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs
+++ b/Content.Client/Kitchen/UI/GrinderMenu.xaml.cs
@@ -1,16 +1,11 @@
-using System.Collections.Generic;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Kitchen.Components;
+using Content.Shared.Kitchen;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
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.Maths;
using Robust.Shared.Prototypes;
namespace Content.Client.Kitchen.UI
@@ -68,15 +63,15 @@ namespace Content.Client.Kitchen.UI
{
switch (message)
{
- case SharedReagentGrinderComponent.ReagentGrinderWorkStartedMessage workStarted:
+ case ReagentGrinderWorkStartedMessage workStarted:
GrindButton.Disabled = true;
- GrindButton.Modulate = workStarted.GrinderProgram == SharedReagentGrinderComponent.GrinderProgram.Grind ? Color.Green : Color.White;
+ GrindButton.Modulate = workStarted.GrinderProgram == GrinderProgram.Grind ? Color.Green : Color.White;
JuiceButton.Disabled = true;
- JuiceButton.Modulate = workStarted.GrinderProgram == SharedReagentGrinderComponent.GrinderProgram.Juice ? Color.Green : Color.White;
+ JuiceButton.Modulate = workStarted.GrinderProgram == GrinderProgram.Juice ? Color.Green : Color.White;
BeakerContentBox.EjectButton.Disabled = true;
ChamberContentBox.EjectButton.Disabled = true;
break;
- case SharedReagentGrinderComponent.ReagentGrinderWorkCompleteMessage:
+ case ReagentGrinderWorkCompleteMessage:
GrindButton.Disabled = false;
JuiceButton.Disabled = false;
GrindButton.Modulate = Color.White;
diff --git a/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs b/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs
index e4e8c6a073..e05c871146 100644
--- a/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs
+++ b/Content.Client/Kitchen/UI/ReagentGrinderBoundUserInterface.cs
@@ -1,11 +1,8 @@
using Content.Shared.Containers.ItemSlots;
-using Content.Shared.Kitchen.Components;
+using Content.Shared.Kitchen;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
-using static Content.Shared.Chemistry.Components.Solution;
namespace Content.Client.Kitchen.UI
{
@@ -55,10 +52,10 @@ namespace Content.Client.Kitchen.UI
_menu?.HandleMessage(message);
}
- public void StartGrinding(BaseButton.ButtonEventArgs? args = null) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderGrindStartMessage());
- public void StartJuicing(BaseButton.ButtonEventArgs? args = null) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderJuiceStartMessage());
- public void EjectAll(BaseButton.ButtonEventArgs? args = null) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderEjectChamberAllMessage());
- public void EjectBeaker(BaseButton.ButtonEventArgs? args = null) => SendMessage(new ItemSlotButtonPressedEvent(SharedReagentGrinderComponent.BeakerSlotId));
- public void EjectChamberContent(EntityUid uid) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderEjectChamberContentMessage(uid));
+ public void StartGrinding(BaseButton.ButtonEventArgs? args = null) => SendMessage(new ReagentGrinderStartMessage(GrinderProgram.Grind));
+ public void StartJuicing(BaseButton.ButtonEventArgs? args = null) => SendMessage(new ReagentGrinderStartMessage(GrinderProgram.Juice));
+ public void EjectAll(BaseButton.ButtonEventArgs? args = null) => SendMessage(new ReagentGrinderEjectChamberAllMessage());
+ public void EjectBeaker(BaseButton.ButtonEventArgs? args = null) => SendMessage(new ItemSlotButtonPressedEvent(SharedReagentGrinder.BeakerSlotId));
+ public void EjectChamberContent(EntityUid uid) => SendMessage(new ReagentGrinderEjectChamberContentMessage(uid));
}
}
diff --git a/Content.Client/Kitchen/Visualizers/ReagentGrinderVisualizer.cs b/Content.Client/Kitchen/Visualizers/ReagentGrinderVisualizer.cs
index e02cc96ccb..04d3e178fc 100644
--- a/Content.Client/Kitchen/Visualizers/ReagentGrinderVisualizer.cs
+++ b/Content.Client/Kitchen/Visualizers/ReagentGrinderVisualizer.cs
@@ -1,7 +1,5 @@
using Robust.Client.GameObjects;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
-using static Content.Shared.Kitchen.Components.SharedReagentGrinderComponent;
+using Content.Shared.Kitchen;
namespace Content.Client.Kitchen.Visualizers
{
diff --git a/Content.Server/Kitchen/Components/ReagentGrinderComponent.cs b/Content.Server/Kitchen/Components/ReagentGrinderComponent.cs
index 5d697ba2db..e8762c8203 100644
--- a/Content.Server/Kitchen/Components/ReagentGrinderComponent.cs
+++ b/Content.Server/Kitchen/Components/ReagentGrinderComponent.cs
@@ -1,8 +1,6 @@
-using Content.Shared.Chemistry.Components;
-using Content.Shared.Containers.ItemSlots;
-using Content.Shared.Kitchen.Components;
+using Content.Shared.Kitchen;
+using Content.Server.Kitchen.EntitySystems;
using Robust.Shared.Audio;
-using Robust.Shared.Containers;
namespace Content.Server.Kitchen.Components
{
@@ -12,32 +10,36 @@ namespace Content.Server.Kitchen.Components
/// converting something into its single juice form. E.g, grind an apple and get the nutriment and sugar
/// it contained, juice an apple and get "apple juice".
///
- [RegisterComponent]
- public sealed class ReagentGrinderComponent : SharedReagentGrinderComponent
+ [Access(typeof(ReagentGrinderSystem)), RegisterComponent]
+ public sealed class ReagentGrinderComponent : Component
+ {
+ //YAML serialization vars
+ [DataField("storageMaxEntities"), ViewVariables(VVAccess.ReadWrite)]
+ public int StorageMaxEntities = 16;
+
+ [DataField("workTime"), ViewVariables(VVAccess.ReadWrite)]
+ public TimeSpan WorkTime = TimeSpan.FromSeconds(3.5); // Roughly matches the grind/juice sounds.
+
+ [DataField("clickSound"), ViewVariables(VVAccess.ReadWrite)]
+ public SoundSpecifier ClickSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg");
+
+ [DataField("grindSound"), ViewVariables(VVAccess.ReadWrite)]
+ public SoundSpecifier GrindSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/blender.ogg");
+
+ [DataField("juiceSound"), ViewVariables(VVAccess.ReadWrite)]
+ public SoundSpecifier JuiceSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/juicer.ogg");
+ }
+
+ [Access(typeof(ReagentGrinderSystem)), RegisterComponent]
+ public sealed class ActiveReagentGrinderComponent : Component
{
///
- /// Can be null since we won't always have a beaker in the grinder.
+ /// Remaining time until the grinder finishes grinding/juicing.
///
- [ViewVariables] public Solution? BeakerSolution;
+ [ViewVariables]
+ public float WorkTimer;
- ///
- /// Contains the things that are going to be ground or juiced.
- ///
- [ViewVariables] public Container Chamber = default!;
-
- ///
- /// Is the machine actively doing something and can't be used right now?
- ///
- public bool Busy;
-
- //YAML serialization vars
- [ViewVariables(VVAccess.ReadWrite)] [DataField("chamberCapacity")] public int StorageCap = 16;
- [ViewVariables(VVAccess.ReadWrite)] [DataField("workTime")] public int WorkTime = 3500; //3.5 seconds, completely arbitrary for now.
- [DataField("clickSound")] public SoundSpecifier ClickSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg");
- [DataField("grindSound")] public SoundSpecifier GrindSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/blender.ogg");
- [DataField("juiceSound")] public SoundSpecifier JuiceSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/juicer.ogg");
-
- [DataField("beakerSlot")]
- public ItemSlot BeakerSlot = new();
+ [ViewVariables]
+ public GrinderProgram Program;
}
}
diff --git a/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs b/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs
index e498cdf656..1a90051471 100644
--- a/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs
+++ b/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs
@@ -1,14 +1,15 @@
using System.Linq;
using Content.Server.Chemistry.EntitySystems;
using Content.Server.Kitchen.Components;
-using Content.Server.Kitchen.Events;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Stack;
-using Content.Server.UserInterface;
+using Content.Shared.Chemistry.Components;
using Content.Shared.Containers.ItemSlots;
+using Content.Shared.FixedPoint;
using Content.Shared.Interaction;
-using Content.Shared.Kitchen.Components;
+using Content.Shared.Kitchen;
+using Content.Shared.Popups;
using Content.Shared.Random.Helpers;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
@@ -24,287 +25,272 @@ namespace Content.Server.Kitchen.EntitySystems
{
[Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!;
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
-
- private Queue _uiUpdateQueue = new();
+ [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
+ [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
+ [Dependency] private readonly StackSystem _stackSystem = default!;
+ [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
+ [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
+ [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
public override void Initialize()
{
base.Initialize();
- SubscribeLocalEvent(OnComponentInit);
- SubscribeLocalEvent(OnComponentRemove);
-
- SubscribeLocalEvent(OnPowerChange);
+ SubscribeLocalEvent((uid, component, _) => UpdateUiState(uid, component));
+ SubscribeLocalEvent(
+ (EntityUid uid, ReagentGrinderComponent component, ref PowerChangedEvent _) => UpdateUiState(uid, component));
SubscribeLocalEvent(OnInteractUsing);
- SubscribeLocalEvent(ExtractableScaling);
SubscribeLocalEvent(OnContainerModified);
SubscribeLocalEvent(OnContainerModified);
SubscribeLocalEvent(OnEntRemoveAttempt);
- }
- private void OnPowerChange(EntityUid uid, ReagentGrinderComponent component, ref PowerChangedEvent args)
- {
- EnqueueUiUpdate(component);
- }
-
- private void OnEntRemoveAttempt(EntityUid uid, ReagentGrinderComponent component, ContainerIsRemovingAttemptEvent args)
- {
- if (component.Busy)
- args.Cancel();
- }
-
- private void OnContainerModified(EntityUid uid, ReagentGrinderComponent component, ContainerModifiedMessage args)
- {
- EnqueueUiUpdate(component);
-
- if (args.Container.ID != SharedReagentGrinderComponent.BeakerSlotId)
- return;
-
- if (TryComp(component.Owner, out AppearanceComponent? appearance))
- appearance.SetData(SharedReagentGrinderComponent.ReagentGrinderVisualState.BeakerAttached, component.BeakerSlot.HasItem);
-
- component.BeakerSolution = null;
- if (component.BeakerSlot.Item != null)
- _solutionsSystem.TryGetFitsInDispenser(component.BeakerSlot.Item.Value, out component.BeakerSolution);
- }
-
- private void ExtractableScaling(EntityUid uid, StackComponent component, ExtractableScalingEvent args)
- {
- args.Scalar *= component.Count; // multiply scalar by amount of items in stack
- }
-
- private void OnInteractUsing(EntityUid uid, ReagentGrinderComponent component, InteractUsingEvent args)
- {
- if (args.Handled) return;
-
- var heldEnt = args.Used;
-
- //See if the user is trying to insert something they want to be ground/juiced.
- if (!HasComp(heldEnt))
- {
- //Entity did NOT pass the whitelist for grind/juice.
- //Wouldn't want the clown grinding up the Captain's ID card now would you?
- //Why am I asking you? You're biased.
- return;
- }
-
- //Cap the chamber. Don't want someone putting in 500 entities and ejecting them all at once.
- //Maybe I should have done that for the microwave too?
- if (component.Chamber.ContainedEntities.Count >= component.StorageCap)
- return;
-
- if (!component.Chamber.Insert(heldEnt, EntityManager))
- return;
-
- args.Handled = true;
- }
-
- private void EnqueueUiUpdate(ReagentGrinderComponent component)
- {
- if (!_uiUpdateQueue.Contains(component)) _uiUpdateQueue.Enqueue(component);
- }
-
- private void OnComponentInit(EntityUid uid, ReagentGrinderComponent component, ComponentInit args)
- {
- EnqueueUiUpdate(component);
-
- _itemSlotsSystem.AddItemSlot(uid, SharedReagentGrinderComponent.BeakerSlotId, component.BeakerSlot);
-
- //A container for the things that WILL be ground/juiced. Useful for ejecting them instead of deleting them from the hands of the user.
- component.Chamber =
- ContainerHelpers.EnsureContainer(component.Owner,
- $"{component.Name}-entityContainerContainer");
-
- // TODO just directly subscribe to UI events.
- var bui = component.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key);
- if (bui != null)
- {
- bui.OnReceiveMessage += msg => OnUIMessageReceived(uid, component, msg);
- }
- }
-
- private void OnComponentRemove(EntityUid uid, ReagentGrinderComponent component, ComponentRemove args)
- {
- _itemSlotsSystem.RemoveItemSlot(uid, component.BeakerSlot);
- }
-
- private void OnUIMessageReceived(EntityUid uid, ReagentGrinderComponent component,
- ServerBoundUserInterfaceMessage message)
- {
- if (component.Busy || message.Session.AttachedEntity is not {} attached)
- {
- return;
- }
-
- switch (message.Message)
- {
- case SharedReagentGrinderComponent.ReagentGrinderGrindStartMessage msg:
- if (!this.IsPowered(component.Owner, EntityManager)) break;
- ClickSound(component);
- DoWork(component, attached,
- SharedReagentGrinderComponent.GrinderProgram.Grind);
- break;
-
- case SharedReagentGrinderComponent.ReagentGrinderJuiceStartMessage msg:
- if (!this.IsPowered(component.Owner, EntityManager)) break;
- ClickSound(component);
- DoWork(component, attached,
- SharedReagentGrinderComponent.GrinderProgram.Juice);
- break;
-
- case SharedReagentGrinderComponent.ReagentGrinderEjectChamberAllMessage msg:
- if (component.Chamber.ContainedEntities.Count > 0)
- {
- ClickSound(component);
- for (var i = component.Chamber.ContainedEntities.Count - 1; i >= 0; i--)
- {
- var entity = component.Chamber.ContainedEntities[i];
- component.Chamber.Remove(entity);
- entity.RandomOffset(0.4f);
- }
-
- EnqueueUiUpdate(component);
- }
-
- break;
-
- case SharedReagentGrinderComponent.ReagentGrinderEjectChamberContentMessage msg:
- if (component.Chamber.ContainedEntities.TryFirstOrNull(x => x == msg.EntityID, out var ent))
- {
- component.Chamber.Remove(ent.Value);
- SharedEntityExtensions.RandomOffset(ent.Value, 0.4f);
- EnqueueUiUpdate(component);
- ClickSound(component);
- }
-
- break;
- }
+ SubscribeLocalEvent(OnStartMessage);
+ SubscribeLocalEvent(OnEjectChamberAllMessage);
+ SubscribeLocalEvent(OnEjectChamberContentMessage);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
- while (_uiUpdateQueue.TryDequeue(out var comp))
+ foreach (var (active, reagentGrinder) in EntityQuery())
{
- if (comp.Deleted)
+ var uid = reagentGrinder.Owner;
+ active.WorkTimer -= frameTime;
+
+ if (active.WorkTimer > 0)
continue;
- bool canJuice = false;
- bool canGrind = false;
- if (comp.BeakerSlot.HasItem)
- {
- foreach (var entity in comp.Chamber.ContainedEntities)
- {
- if (canJuice || !EntityManager.TryGetComponent(entity, out ExtractableComponent? component)) continue;
+ RemCompDeferred(uid);
- canJuice = component.JuiceSolution != null;
- canGrind = component.GrindableSolution != null
- && _solutionsSystem.TryGetSolution(entity, component.GrindableSolution, out _);
+ var inputContainer = _containerSystem.EnsureContainer(uid, SharedReagentGrinder.InputContainerId);
+ var outputContainer = _itemSlotsSystem.GetItemOrNull(uid, SharedReagentGrinder.BeakerSlotId);
+ if (outputContainer is null || !_solutionsSystem.TryGetFitsInDispenser(outputContainer.Value, out var containerSolution))
+ continue;
+
+ foreach (var item in inputContainer.ContainedEntities.ToList())
+ {
+ var solution = active.Program switch
+ {
+ GrinderProgram.Grind => GetGrindSolution(item),
+ GrinderProgram.Juice => CompOrNull(item)?.JuiceSolution,
+ _ => null,
+ };
+
+ if (solution is null)
+ continue;
+
+ if (TryComp(item, out var stack))
+ {
+ var totalVolume = solution.TotalVolume * stack.Count;
+ if (totalVolume <= 0)
+ continue;
+
+ // Maximum number of items we can process in the stack without going over AvailableVolume
+ // We add a small tolerance, because floats are inaccurate.
+ var fitsCount = (int) (stack.Count * FixedPoint2.Min(containerSolution.AvailableVolume / totalVolume + 0.01, 1));
+ if (fitsCount <= 0)
+ continue;
+
+ solution.ScaleSolution(fitsCount);
+ _stackSystem.SetCount(item, stack.Count - fitsCount); // Setting to 0 will QueueDel
}
+ else
+ {
+ if (solution.TotalVolume > containerSolution.AvailableVolume)
+ continue;
+
+ QueueDel(item);
+ }
+
+ _solutionsSystem.TryAddSolution(outputContainer.Value, containerSolution, solution);
}
- comp.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key)?.SetState(
- new ReagentGrinderInterfaceState
- (
- comp.Busy,
- comp.BeakerSlot.HasItem,
- this.IsPowered(comp.Owner, EntityManager),
- canJuice,
- canGrind,
- comp.Chamber.ContainedEntities.Select(item => item).ToArray(),
- //Remember the beaker can be null!
- comp.BeakerSolution?.Contents.ToArray()
- ));
+ _userInterfaceSystem.TrySendUiMessage(uid, ReagentGrinderUiKey.Key,
+ new ReagentGrinderWorkCompleteMessage());
+
+ UpdateUiState(uid, reagentGrinder);
+ }
+ }
+
+ private void OnEntRemoveAttempt(EntityUid uid, ReagentGrinderComponent reagentGrinder, ContainerIsRemovingAttemptEvent args)
+ {
+ if (HasComp(uid))
+ args.Cancel();
+ }
+
+ private void OnContainerModified(EntityUid uid, ReagentGrinderComponent reagentGrinder, ContainerModifiedMessage args)
+ {
+ UpdateUiState(uid, reagentGrinder);
+
+ var outputContainer = _itemSlotsSystem.GetItemOrNull(uid, SharedReagentGrinder.BeakerSlotId);
+ _appearanceSystem.SetData(uid, ReagentGrinderVisualState.BeakerAttached, outputContainer.HasValue);
+ }
+
+ private void OnInteractUsing(EntityUid uid, ReagentGrinderComponent reagentGrinder, InteractUsingEvent args)
+ {
+ var heldEnt = args.Used;
+ var inputContainer = _containerSystem.EnsureContainer(uid, SharedReagentGrinder.InputContainerId);
+
+ if (!HasComp(heldEnt))
+ {
+ if (!HasComp(heldEnt))
+ {
+ // This is ugly but we can't use whitelistFailPopup because there are 2 containers with different whitelists.
+ _popupSystem.PopupEntity(Loc.GetString("reagent-grinder-component-cannot-put-entity-message"), uid, Filter.Entities(args.User));
+ }
+
+ // Entity did NOT pass the whitelist for grind/juice.
+ // Wouldn't want the clown grinding up the Captain's ID card now would you?
+ // Why am I asking you? You're biased.
+ return;
+ }
+
+ if (args.Handled)
+ return;
+
+ // Cap the chamber. Don't want someone putting in 500 entities and ejecting them all at once.
+ // Maybe I should have done that for the microwave too?
+ if (inputContainer.ContainedEntities.Count >= reagentGrinder.StorageMaxEntities)
+ return;
+
+ if (!inputContainer.Insert(heldEnt, EntityManager))
+ return;
+
+ args.Handled = true;
+ }
+
+ private void UpdateUiState(EntityUid uid, ReagentGrinderComponent reagentGrinder)
+ {
+ var inputContainer = _containerSystem.EnsureContainer(uid, SharedReagentGrinder.InputContainerId);
+ var outputContainer = _itemSlotsSystem.GetItemOrNull(uid, SharedReagentGrinder.BeakerSlotId);
+ Solution? containerSolution = null;
+ var isBusy = HasComp(uid);
+ var canJuice = false;
+ var canGrind = false;
+
+ if (outputContainer is not null
+ && _solutionsSystem.TryGetFitsInDispenser(outputContainer.Value, out containerSolution)
+ && inputContainer.ContainedEntities.Count > 0)
+ {
+ canGrind = inputContainer.ContainedEntities.All(CanGrind);
+ canJuice = inputContainer.ContainedEntities.All(CanJuice);
+ }
+
+ var state = new ReagentGrinderInterfaceState(
+ isBusy,
+ outputContainer.HasValue,
+ this.IsPowered(uid, EntityManager),
+ canJuice,
+ canGrind,
+ inputContainer.ContainedEntities.Select(item => item).ToArray(),
+ containerSolution?.Contents.ToArray()
+ );
+ _userInterfaceSystem.TrySetUiState(uid, ReagentGrinderUiKey.Key, state);
+ }
+
+ private void OnStartMessage(EntityUid uid, ReagentGrinderComponent reagentGrinder, ReagentGrinderStartMessage message)
+ {
+ if (!this.IsPowered(uid, EntityManager) || HasComp(uid))
+ return;
+
+ DoWork(uid, reagentGrinder, message.Session.AttachedEntity, message.Program);
+ }
+
+ private void OnEjectChamberAllMessage(EntityUid uid, ReagentGrinderComponent reagentGrinder, ReagentGrinderEjectChamberAllMessage message)
+ {
+ var inputContainer = _containerSystem.EnsureContainer(uid, SharedReagentGrinder.InputContainerId);
+
+ if (HasComp(uid) || inputContainer.ContainedEntities.Count <= 0)
+ return;
+
+ ClickSound(uid, reagentGrinder);
+ foreach (var entity in inputContainer.ContainedEntities.ToList())
+ {
+ inputContainer.Remove(entity);
+ entity.RandomOffset(0.4f);
+ }
+ UpdateUiState(uid, reagentGrinder);
+ }
+
+ private void OnEjectChamberContentMessage(EntityUid uid, ReagentGrinderComponent reagentGrinder, ReagentGrinderEjectChamberContentMessage message)
+ {
+ if (HasComp(uid))
+ return;
+
+ var inputContainer = _containerSystem.EnsureContainer(uid, SharedReagentGrinder.InputContainerId);
+
+ if (inputContainer.Remove(message.EntityId))
+ {
+ message.EntityId.RandomOffset(0.4f);
+ ClickSound(uid, reagentGrinder);
+ UpdateUiState(uid, reagentGrinder);
}
}
///
/// The wzhzhzh of the grinder. Processes the contents of the grinder and puts the output in the beaker.
///
- /// true for wanting to juice, false for wanting to grind.
- private void DoWork(ReagentGrinderComponent component, EntityUid user,
- SharedReagentGrinderComponent.GrinderProgram program)
+ /// Which program, such as grind or juice
+ private void DoWork(EntityUid uid, ReagentGrinderComponent reagentGrinder, EntityUid? user, GrinderProgram program)
{
- //Have power, are we busy, chamber has anything to grind, a beaker for the grounds to go?
- if (!this.IsPowered(component.Owner, EntityManager) ||
- component.Busy || component.Chamber.ContainedEntities.Count <= 0 ||
- component.BeakerSlot.Item is not EntityUid beakerEntity ||
- component.BeakerSolution == null)
- {
+ var inputContainer = _containerSystem.EnsureContainer(uid, SharedReagentGrinder.InputContainerId);
+ var outputContainer = _itemSlotsSystem.GetItemOrNull(uid, SharedReagentGrinder.BeakerSlotId);
+
+ // Do we have anything to grind/juice and a container to put the reagents in?
+ if (inputContainer.ContainedEntities.Count <= 0 || !HasComp(outputContainer))
return;
- }
- component.Busy = true;
-
- var bui = component.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key);
- bui?.SendMessage(new SharedReagentGrinderComponent.ReagentGrinderWorkStartedMessage(program));
+ SoundSpecifier? sound = null;
switch (program)
{
- case SharedReagentGrinderComponent.GrinderProgram.Grind:
- SoundSystem.Play(component.GrindSound.GetSound(), Filter.Pvs(component.Owner), component.Owner, AudioParams.Default);
- // Get each item inside the chamber and get the reagents it contains.
- // Transfer those reagents to the beaker, given we have one in.
- component.Owner.SpawnTimer(component.WorkTime, () =>
- {
- foreach (var item in component.Chamber.ContainedEntities.ToList())
- {
- if (!EntityManager.TryGetComponent(item, out ExtractableComponent? extract)
- || extract.GrindableSolution == null
- || !_solutionsSystem.TryGetSolution(item, extract.GrindableSolution, out var solution)) continue;
-
- var juiceEvent = new ExtractableScalingEvent(); // default of scalar is always 1.0
- RaiseLocalEvent(item, juiceEvent, false);
- if (component.BeakerSolution.CurrentVolume + solution.CurrentVolume * juiceEvent.Scalar >
- component.BeakerSolution.MaxVolume) continue;
- solution.ScaleSolution(juiceEvent.Scalar);
- _solutionsSystem.TryAddSolution(beakerEntity, component.BeakerSolution, solution);
- EntityManager.DeleteEntity(item);
- }
-
- component.Busy = false;
- EnqueueUiUpdate(component);
- bui?.SendMessage(new SharedReagentGrinderComponent.ReagentGrinderWorkCompleteMessage());
- });
+ case GrinderProgram.Grind when inputContainer.ContainedEntities.All(CanGrind):
+ sound = reagentGrinder.GrindSound;
break;
-
- case SharedReagentGrinderComponent.GrinderProgram.Juice:
- SoundSystem.Play(component.JuiceSound.GetSound(), Filter.Pvs(component.Owner), component.Owner, AudioParams.Default);
- component.Owner.SpawnTimer(component.WorkTime, () =>
- {
- foreach (var item in component.Chamber.ContainedEntities.ToList())
- {
- if (!EntityManager.TryGetComponent(item, out var juiceMe)
- || juiceMe.JuiceSolution == null)
- {
- Logger.Warning("Couldn't find a juice solution on entityUid:{0}", item);
- continue;
- }
- var juiceEvent = new ExtractableScalingEvent(); // default of scalar is always 1.0
- if (EntityManager.HasComponent(item))
- {
- RaiseLocalEvent(item, juiceEvent, true);
- }
-
- if (component.BeakerSolution.CurrentVolume + juiceMe.JuiceSolution.TotalVolume * juiceEvent.Scalar > component.BeakerSolution.MaxVolume)
- continue;
- juiceMe.JuiceSolution.ScaleSolution(juiceEvent.Scalar);
- _solutionsSystem.TryAddSolution(beakerEntity, component.BeakerSolution, juiceMe.JuiceSolution);
- EntityManager.DeleteEntity(item);
- }
-
- bui?.SendMessage(new SharedReagentGrinderComponent.ReagentGrinderWorkCompleteMessage());
- component.Busy = false;
- EnqueueUiUpdate(component);
- });
+ case GrinderProgram.Juice when inputContainer.ContainedEntities.All(CanJuice):
+ sound = reagentGrinder.JuiceSound;
break;
+ default:
+ return;
}
+
+ var active = AddComp(uid);
+ active.WorkTimer = (float) reagentGrinder.WorkTime.TotalSeconds;
+ active.Program = program;
+
+ _audioSystem.PlayPvs(sound, uid);
+ _userInterfaceSystem.TrySendUiMessage(uid, ReagentGrinderUiKey.Key,
+ new ReagentGrinderWorkStartedMessage(program));
}
- private void ClickSound(ReagentGrinderComponent component)
+ private void ClickSound(EntityUid uid, ReagentGrinderComponent reagentGrinder)
{
- SoundSystem.Play(component.ClickSound.GetSound(), Filter.Pvs(component.Owner), component.Owner, AudioParams.Default.WithVolume(-2f));
+ _audioSystem.PlayPvs(reagentGrinder.ClickSound, uid, AudioParams.Default.WithVolume(-2f));
+ }
+
+ private Solution? GetGrindSolution(EntityUid uid)
+ {
+ if (TryComp(uid, out var extractable)
+ && extractable.GrindableSolution is not null
+ && _solutionsSystem.TryGetSolution(uid, extractable.GrindableSolution, out var solution))
+ {
+ return solution;
+ }
+ else
+ return null;
+ }
+
+ private bool CanGrind(EntityUid uid)
+ {
+ var solutionName = CompOrNull(uid)?.GrindableSolution;
+
+ return solutionName is not null && _solutionsSystem.TryGetSolution(uid, solutionName, out _);
+ }
+
+ private bool CanJuice(EntityUid uid)
+ {
+ return CompOrNull(uid)?.JuiceSolution is not null;
}
}
}
diff --git a/Content.Server/Kitchen/Events/ExtractableScalingEvent.cs b/Content.Server/Kitchen/Events/ExtractableScalingEvent.cs
deleted file mode 100644
index 875ac02af8..0000000000
--- a/Content.Server/Kitchen/Events/ExtractableScalingEvent.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-namespace Content.Server.Kitchen.Events
-{
- ///
- /// Used in scaling amount of solution to extract in juicing
- ///
- public sealed class ExtractableScalingEvent : EntityEventArgs
- {
-
- public ExtractableScalingEvent()
- {
- Scalar = 1f;
- }
-
- public float Scalar
- {
- get;
- set;
- }
-
- }
-}
diff --git a/Content.Shared/Kitchen/Components/SharedReagentGrinderComponent.cs b/Content.Shared/Kitchen/Components/SharedReagentGrinderComponent.cs
deleted file mode 100644
index eaa5485ad3..0000000000
--- a/Content.Shared/Kitchen/Components/SharedReagentGrinderComponent.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-using Content.Shared.Chemistry.Components;
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Kitchen.Components
-{
-
- public abstract class SharedReagentGrinderComponent : Component
- {
- public static string BeakerSlotId = "ReagentGrinder-reagentContainerContainer";
-
- [Serializable, NetSerializable]
- public sealed class ReagentGrinderGrindStartMessage : BoundUserInterfaceMessage
- {
- public ReagentGrinderGrindStartMessage()
- {
- }
- }
-
- [Serializable, NetSerializable]
- public sealed class ReagentGrinderJuiceStartMessage : BoundUserInterfaceMessage
- {
- public ReagentGrinderJuiceStartMessage()
- {
- }
- }
-
- [Serializable, NetSerializable]
- public sealed class ReagentGrinderEjectChamberAllMessage : BoundUserInterfaceMessage
- {
- public ReagentGrinderEjectChamberAllMessage()
- {
- }
- }
-
- [Serializable, NetSerializable]
- public sealed class ReagentGrinderEjectChamberContentMessage : BoundUserInterfaceMessage
- {
- public EntityUid EntityID;
- public ReagentGrinderEjectChamberContentMessage(EntityUid entityID)
- {
- EntityID = entityID;
- }
- }
-
- [Serializable, NetSerializable]
- public sealed class ReagentGrinderWorkStartedMessage : BoundUserInterfaceMessage
- {
- public GrinderProgram GrinderProgram;
- public ReagentGrinderWorkStartedMessage(GrinderProgram grinderProgram)
- {
- GrinderProgram = grinderProgram;
- }
- }
-
- [Serializable, NetSerializable]
- public sealed class ReagentGrinderWorkCompleteMessage : BoundUserInterfaceMessage
- {
- public ReagentGrinderWorkCompleteMessage()
- {
- }
- }
-
- [Serializable, NetSerializable]
- public enum ReagentGrinderVisualState : byte
- {
- BeakerAttached
- }
-
- [NetSerializable, Serializable]
- public enum ReagentGrinderUiKey : byte
- {
- Key
- }
-
- [Serializable, NetSerializable]
- public enum GrinderProgram : byte
- {
- Grind,
- Juice
- }
- }
-
- [NetSerializable, Serializable]
- public sealed class ReagentGrinderInterfaceState : BoundUserInterfaceState
- {
- public bool IsBusy;
- public bool HasBeakerIn;
- public bool Powered;
- public bool CanJuice;
- public bool CanGrind;
- public EntityUid[] ChamberContents;
- public Solution.ReagentQuantity[]? ReagentQuantities;
- public ReagentGrinderInterfaceState(bool isBusy, bool hasBeaker, bool powered, bool canJuice, bool canGrind, EntityUid[] chamberContents, Solution.ReagentQuantity[]? heldBeakerContents)
- {
- IsBusy = isBusy;
- HasBeakerIn = hasBeaker;
- Powered = powered;
- CanJuice = canJuice;
- CanGrind = canGrind;
- ChamberContents = chamberContents;
- ReagentQuantities = heldBeakerContents;
- }
- }
-}
diff --git a/Content.Shared/Kitchen/SharedReagentGrinder.cs b/Content.Shared/Kitchen/SharedReagentGrinder.cs
new file mode 100644
index 0000000000..a9a3d88bb8
--- /dev/null
+++ b/Content.Shared/Kitchen/SharedReagentGrinder.cs
@@ -0,0 +1,99 @@
+using Content.Shared.Chemistry.Components;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Kitchen
+{
+ public sealed class SharedReagentGrinder
+ {
+ public static string BeakerSlotId = "beakerSlot";
+
+ public static string InputContainerId = "inputContainer";
+ }
+
+ [Serializable, NetSerializable]
+ public sealed class ReagentGrinderStartMessage : BoundUserInterfaceMessage
+ {
+ public readonly GrinderProgram Program;
+ public ReagentGrinderStartMessage(GrinderProgram program)
+ {
+ Program = program;
+ }
+ }
+
+ [Serializable, NetSerializable]
+ public sealed class ReagentGrinderEjectChamberAllMessage : BoundUserInterfaceMessage
+ {
+ public ReagentGrinderEjectChamberAllMessage()
+ {
+ }
+ }
+
+ [Serializable, NetSerializable]
+ public sealed class ReagentGrinderEjectChamberContentMessage : BoundUserInterfaceMessage
+ {
+ public EntityUid EntityId;
+ public ReagentGrinderEjectChamberContentMessage(EntityUid entityId)
+ {
+ EntityId = entityId;
+ }
+ }
+
+ [Serializable, NetSerializable]
+ public sealed class ReagentGrinderWorkStartedMessage : BoundUserInterfaceMessage
+ {
+ public GrinderProgram GrinderProgram;
+ public ReagentGrinderWorkStartedMessage(GrinderProgram grinderProgram)
+ {
+ GrinderProgram = grinderProgram;
+ }
+ }
+
+ [Serializable, NetSerializable]
+ public sealed class ReagentGrinderWorkCompleteMessage : BoundUserInterfaceMessage
+ {
+ public ReagentGrinderWorkCompleteMessage()
+ {
+ }
+ }
+
+ [Serializable, NetSerializable]
+ public enum ReagentGrinderVisualState : byte
+ {
+ BeakerAttached
+ }
+
+ [Serializable, NetSerializable]
+ public enum GrinderProgram : byte
+ {
+ Grind,
+ Juice
+ }
+
+ [NetSerializable, Serializable]
+ public enum ReagentGrinderUiKey : byte
+ {
+ Key
+ }
+
+ [NetSerializable, Serializable]
+ public sealed class ReagentGrinderInterfaceState : BoundUserInterfaceState
+ {
+ public bool IsBusy;
+ public bool HasBeakerIn;
+ public bool Powered;
+ public bool CanJuice;
+ public bool CanGrind;
+ public EntityUid[] ChamberContents;
+ public Solution.ReagentQuantity[]? ReagentQuantities;
+ public ReagentGrinderInterfaceState(bool isBusy, bool hasBeaker, bool powered, bool canJuice, bool canGrind, EntityUid[] chamberContents, Solution.ReagentQuantity[]? heldBeakerContents)
+ {
+ IsBusy = isBusy;
+ HasBeakerIn = hasBeaker;
+ Powered = powered;
+ CanJuice = canJuice;
+ CanGrind = canGrind;
+ ChamberContents = chamberContents;
+ ReagentQuantities = heldBeakerContents;
+ }
+ }
+}
diff --git a/Resources/Locale/en-US/kitchen/components/reagent-grinder-component.ftl b/Resources/Locale/en-US/kitchen/components/reagent-grinder-component.ftl
index ddb8f9417f..30af6e9872 100644
--- a/Resources/Locale/en-US/kitchen/components/reagent-grinder-component.ftl
+++ b/Resources/Locale/en-US/kitchen/components/reagent-grinder-component.ftl
@@ -2,7 +2,8 @@
reagent-grinder-bound-user-interface-instant-button = INSTANT
reagent-grinder-bound-user-interface-cook-time-label = COOK TIME
-
+reagent-grinder-component-cannot-put-entity-message = You can't put this in the reagent grinder!
+
grinder-menu-title = All-In-One Grinder 3000
grinder-menu-grind-button = Grind
grinder-menu-juice-button = Juice
diff --git a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml
index 45c97c457d..a2c1d0ad32 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml
@@ -8,14 +8,6 @@
- type: Transform
anchored: true
- type: ReagentGrinder
- beakerSlot:
- insertSound: /Audio/Machines/machine_switch.ogg
- ejectSound: /Audio/Machines/machine_switch.ogg
- soundOptions:
- volume: -2
- whitelist:
- components:
- - FitsInDispenser
- type: ActivatableUI
key: enum.ReagentGrinderUiKey.Key
- type: UserInterface
@@ -44,11 +36,16 @@
- type: ApcPowerReceiver
powerLoad: 300
- type: ItemSlots
+ slots:
+ beakerSlot:
+ whitelist:
+ components:
+ - FitsInDispenser
- type: Machine
board: ReagentGrinderMachineCircuitboard
- type: ContainerContainer
containers:
- ReagentGrinder-reagentContainerContainer: !type:ContainerSlot
- ReagentGrinder-entityContainerContainer: !type:Container
+ beakerSlot: !type:ContainerSlot
+ inputContainer: !type:Container
machine_board: !type:Container
machine_parts: !type:Container