From 6e695c404d2c1bd73e7d04f57609ab5bef346988 Mon Sep 17 00:00:00 2001 From: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Date: Wed, 17 Jun 2026 01:27:55 +0200 Subject: [PATCH] Fix handheld grinders (#50) * fix * TEST!!!!!!!!!! * comment * what * im so silly * explosion!!!!! * i hate tests * those can be asserts too * i hate tests --- .../Kitchen/HandheldGrinderInteractionTest.cs | 108 ++++++++++++++++++ .../Components/HandheldGrinderComponent.cs | 2 +- .../EntitySystems/HandheldGrinderSystem.cs | 2 +- 3 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 Content.IntegrationTests/Tests/Kitchen/HandheldGrinderInteractionTest.cs diff --git a/Content.IntegrationTests/Tests/Kitchen/HandheldGrinderInteractionTest.cs b/Content.IntegrationTests/Tests/Kitchen/HandheldGrinderInteractionTest.cs new file mode 100644 index 0000000000..1b693d2802 --- /dev/null +++ b/Content.IntegrationTests/Tests/Kitchen/HandheldGrinderInteractionTest.cs @@ -0,0 +1,108 @@ +using System.Linq; +using Content.IntegrationTests.Tests.Interaction; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Kitchen; +using Content.Shared.Kitchen.Components; +using Content.Shared.Kitchen.EntitySystems; +using Content.Shared.Stacks; +using Robust.Shared.Prototypes; + +namespace Content.IntegrationTests.Tests.Kitchen; + +public sealed partial class HandheldGrinderInteractionTest : InteractionTest +{ + private static readonly EntProtoId Mortar = "MortarAndPestle"; + private static readonly EntProtoId Juicer = "HandheldJuicer"; + + private static readonly EntProtoId SteelSheet = "SheetSteel1"; + private const string Banana = "TestFoodBanana"; + + [TestPrototypes] + private const string Prototypes = @$" +# A modified banana that can only be juiced. +- type: entity + parent: FoodBanana + id: {Banana} + components: + - type: Extractable + grindableSolutionName: null +"; + + /// + /// Spawns a mortar and grinds steel in it, then grinds an ungrindable banana. + /// Does the same with a juicer, but first a banana and then steel. + /// + [Test] + public async Task GrindAndJuiceInHandheldGrindersTest() + { + var grinderSys = SEntMan.System(); + var solutionSys = SEntMan.System(); + var stackSys = SEntMan.System(); + + // Spawn an empty mortar + await SpawnTarget(Mortar); + var grinderComp = Comp(); + + // Spawn steel sheets and get what solution they should grind into. + var sheetsEnt = await PlaceInHands(SteelSheet, 2); + var expectedGrinderSol = grinderSys.GetGrinderSolution(ToServer(sheetsEnt), GrinderProgram.Grind); + Assert.That(expectedGrinderSol, Is.Not.Null); // We expect a solution to exist, would suck if it didn't. + + await Interact(); + + Assert.That(grinderComp.GrinderSolution, Is.Not.Null); // The grinder needs to have its valid solution resolved after interaction. + Assert.That(expectedGrinderSol.Contents.SequenceEqual(grinderComp.GrinderSolution.Value.Comp.Solution.Contents)); // Check if the solution is the one we expected. + Assert.That(stackSys.GetCount(ToServer(sheetsEnt)), Is.EqualTo(1)); // We had 2 steel, now we have one. + + await Interact(); + + Assert.That(SEntMan.EntityExists(ToServer(sheetsEnt)), Is.False); // We use the other sheet, so we used all of our steel. + + + // Spawn a new grinder + await SpawnTarget(Mortar); + grinderComp = Comp(); + + // Manually resolve the solution because the system only resolves it after a VALID interaction, and here we test an invalid one. + solutionSys.ResolveSolution(STarget.Value, grinderComp.SolutionName, ref grinderComp.GrinderSolution); + Assert.That(grinderComp.GrinderSolution, Is.Not.Null); + + var bananaEnt = await PlaceInHands(Banana); + + await Interact(); + + Assert.That(grinderComp.GrinderSolution.Value.Comp.Solution.Volume.Float(), Is.Not.GreaterThan(0f)); // The banana shouldn't have been ground, since it can only be juiced. + Assert.That(SEntMan.EntityExists(ToServer(bananaEnt))); // Banana should exist since the interaction failed. + + + // Now we test the juicer, so we spawn one. + await SpawnTarget(Juicer); + grinderComp = Comp(); + bananaEnt = await PlaceInHands(Banana); + var expectedJuicerSol = grinderSys.GetGrinderSolution(ToServer(bananaEnt), GrinderProgram.Juice); + Assert.That(expectedJuicerSol, Is.Not.Null); // We expect a solution to exist, would suck if it didn't. + + await Interact(); + + Assert.That(grinderComp.GrinderSolution, Is.Not.Null); // Juicer has a valid solution. + Assert.That(expectedJuicerSol.Contents.SequenceEqual(grinderComp.GrinderSolution.Value.Comp.Solution.Contents)); // The banana has been juiced. + Assert.That(SEntMan.EntityExists(ToServer(bananaEnt)), Is.False); // Banana was juiced and therefore should no longer exist. + + + // Spawn a new juicer + await SpawnTarget(Juicer); + grinderComp = Comp(); + + // Manually resolve the solution because the system only resolves it after a VALID interaction, and here we test an invalid one. + solutionSys.ResolveSolution(STarget.Value, grinderComp.SolutionName, ref grinderComp.GrinderSolution); + Assert.That(grinderComp.GrinderSolution, Is.Not.Null); + + sheetsEnt = await PlaceInHands(SteelSheet, 2); + + await Interact(); + + Assert.That(grinderComp.GrinderSolution.Value.Comp.Solution.Volume.Float() , Is.Not.GreaterThan(0f)); // The steel cannot be juiced. + Assert.That(SEntMan.EntityExists(ToServer(sheetsEnt))); // Steel was not used so it should exist. + Assert.That(stackSys.GetCount(ToServer(sheetsEnt)), Is.EqualTo(2)); // Steel was not used, we should have two. + } +} diff --git a/Content.Shared/Kitchen/Components/HandheldGrinderComponent.cs b/Content.Shared/Kitchen/Components/HandheldGrinderComponent.cs index c32d11ddb8..7bc232bff1 100644 --- a/Content.Shared/Kitchen/Components/HandheldGrinderComponent.cs +++ b/Content.Shared/Kitchen/Components/HandheldGrinderComponent.cs @@ -41,7 +41,7 @@ public sealed partial class HandheldGrinderComponent : Component /// The solution into which the output reagents will go. /// [DataField, AutoNetworkedField] - public string SolutionName = "grinderOutput"; + public string SolutionName = "solution"; /// /// Cached solution from the grinder. diff --git a/Content.Shared/Kitchen/EntitySystems/HandheldGrinderSystem.cs b/Content.Shared/Kitchen/EntitySystems/HandheldGrinderSystem.cs index 405083d1ca..c3f853deda 100644 --- a/Content.Shared/Kitchen/EntitySystems/HandheldGrinderSystem.cs +++ b/Content.Shared/Kitchen/EntitySystems/HandheldGrinderSystem.cs @@ -13,7 +13,7 @@ using Robust.Shared.Serialization; namespace Content.Shared.Kitchen.EntitySystems; -internal sealed partial class HandheldGrinderSystem : EntitySystem +public sealed partial class HandheldGrinderSystem : EntitySystem { [Dependency] private SharedReagentGrinderSystem _reagentGrinder = default!; [Dependency] private SharedSolutionContainerSystem _solution = default!;