--- /dev/null
+using Content.IntegrationTests.Tests.Interaction;
+using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.FixedPoint;
+using Content.Shared.Fluids.Components;
+using Content.Shared.Nutrition.Components;
+using Robust.Shared.Prototypes;
+
+namespace Content.IntegrationTests.Tests.Chemistry;
+
+public sealed class DrainTest : InteractionTest
+{
+ private static readonly EntProtoId PizzaPrototype = "FoodPizzaMargherita";
+ private static readonly EntProtoId DrainPrototype = "FloorDrain";
+ private static readonly EntProtoId BucketPrototype = "Bucket";
+ private static readonly ProtoId<ReagentPrototype> BloodReagent = "Blood";
+ private static readonly ProtoId<ReagentPrototype> WaterReagent = "Water";
+ private static readonly FixedPoint2 WaterVolume = 50; // 50u
+ private static readonly FixedPoint2 PuddleVolume = 30; // 30u
+
+ [TestPrototypes]
+ private static readonly string Prototypes = @$"
+- type: entity
+ parent: Puddle
+ id: PuddleBloodTest
+ suffix: Blood (30u)
+ components:
+ - type: SolutionContainerManager
+ solutions:
+ puddle:
+ maxVol: 1000
+ reagents:
+ - ReagentId: {BloodReagent}
+ Quantity: {PuddleVolume}
+";
+
+
+ /// <summary>
+ /// Tests that drag drop interactions with drains are working as intended.
+ /// </summary>
+ [Test]
+ public async Task DragDropOntoDrainTest()
+ {
+ var solutionContainerSys = SEntMan.System<SharedSolutionContainerSystem>();
+
+ // Spawn a drain one tile away.
+ var drain = await Spawn(DrainPrototype);
+
+ // Spawn a bucket at the player's coordinates.
+ var bucket = await Spawn(BucketPrototype, PlayerCoords);
+
+ // Add water to the bucket.
+ Assert.That(solutionContainerSys.TryGetDrainableSolution(ToServer(bucket), out var solutionEnt, out var solution), "Bucket had no drainable solution.");
+ await Server.WaitAssertion(() =>
+ {
+ Assert.That(solutionContainerSys.TryAddReagent(solutionEnt.Value, WaterReagent, WaterVolume), "Could not add water to the bucket.");
+ });
+
+ // Check that the bucket was filled.
+ Assert.That(solutionContainerSys.TryGetDrainableSolution(ToServer(bucket), out solutionEnt, out solution), "Bucket had no drainable solution after filling it.");
+ Assert.That(solution.Volume, Is.EqualTo(WaterVolume));
+
+ // Drag drop the bucket onto the drain.
+ await DragDrop(bucket, drain);
+
+ // Check that the bucket is empty.
+ Assert.That(solutionContainerSys.TryGetDrainableSolution(ToServer(bucket), out solutionEnt, out solution), "Bucket had no drainable solution after draining it.");
+ Assert.That(solution.Volume, Is.EqualTo(FixedPoint2.Zero), "Bucket was not empty after draining it.");
+
+ await Delete(bucket);
+
+ // Spawn a pizza at the player's coordinates.
+ var pizza = await Spawn(PizzaPrototype, PlayerCoords);
+
+ // Check that the pizza is not empty.
+ var edibleSolutionId = Comp<EdibleComponent>(pizza).Solution;
+ Assert.That(solutionContainerSys.TryGetSolution(ToServer(pizza), edibleSolutionId, out solutionEnt, out solution), "Pizza had no edible solution.");
+ var pizzaVolume = solution.Volume;
+ Assert.That(pizzaVolume, Is.GreaterThan(FixedPoint2.Zero), "Pizza had no reagents inside its edible solution.");
+
+ // Drag drop the pizza onto the drain.
+ // Yes, this was a bug that existed before.
+ await DragDrop(pizza, drain);
+
+ // Check that the pizza did not get deleted or had its reagents drained.
+ AssertExists(pizza);
+ Assert.That(solutionContainerSys.TryGetSolution(ToServer(pizza), edibleSolutionId, out solutionEnt, out solution), "Pizza had no edible solution.");
+ Assert.That(solution.Volume, Is.EqualTo(pizzaVolume), "Pizza lost reagents when drag dropped onto a drain.");
+ }
+
+ /// <summary>
+ /// Tests that drains make puddles next to them disappear.
+ /// </summary>
+ [Test]
+ public async Task DrainPuddleTest()
+ {
+ var solutionContainerSys = SEntMan.System<SharedSolutionContainerSystem>();
+
+ // Spawn a puddle at the player coordinates;
+ var puddle = await Spawn("PuddleBloodTest", PlayerCoords);
+
+ // Make sure the reagent chosen for this test does not evaporate on its own.
+ // If you are a fork that made more reagents evaporate, just change BloodReagent ProtoId above to something else.
+ Assert.That(HasComp<EvaporationComponent>(puddle), Is.False, "The chosen reagent is evaporating on its own and we cannot use it for the drain test.");
+
+ var puddleSolutionId = Comp<PuddleComponent>(puddle).SolutionName;
+ Assert.That(solutionContainerSys.TryGetSolution(ToServer(puddle), puddleSolutionId, out _, out var solution), "Puddle had no solution.");
+ Assert.That(solution.Volume, Is.EqualTo(PuddleVolume), "Puddle had the wrong amount of reagents after spawning.");
+
+ // Wait a few seconds and check that the puddle did not disappear on its own.
+ await RunSeconds(10);
+ Assert.That(solutionContainerSys.TryGetSolution(ToServer(puddle), puddleSolutionId, out _, out solution), "Puddle had no solution.");
+ Assert.That(solution.Volume, Is.EqualTo(PuddleVolume), "Puddle had the wrong amount of reagents after spawning.");
+
+ // Spawn a drain one tile away.
+ await Spawn(DrainPrototype);
+
+ // Wait a few seconds.
+ await RunSeconds(10);
+
+ // Make sure the puddle was deleted by the drain.
+ AssertDeleted(puddle);
+ }
+}
}
/// <summary>
- /// Spawn an entity at the target coordinates and set it as the target.
+ /// Spawn an entity at the given coordinates and set it as the target.
+ /// If no coordinates are given it will default to <see cref="TargetCoords"/>
/// </summary>
[MemberNotNull(nameof(Target), nameof(STarget), nameof(CTarget))]
#pragma warning disable CS8774 // Member must have a non-null value when exiting.
- protected async Task<NetEntity> SpawnTarget(string prototype)
+ protected async Task<NetEntity> SpawnTarget(string prototype, NetCoordinates? coords = null)
{
+ coords ??= TargetCoords;
Target = NetEntity.Invalid;
await Server.WaitPost(() =>
{
- Target = SEntMan.GetNetEntity(SEntMan.SpawnAtPosition(prototype, SEntMan.GetCoordinates(TargetCoords)));
+ Target = SEntMan.GetNetEntity(SEntMan.SpawnAtPosition(prototype, SEntMan.GetCoordinates(coords.Value)));
});
await RunTicks(5);
#pragma warning restore CS8774 // Member must have a non-null value when exiting.
/// <summary>
- /// Spawn an entity entity at the target coordinates without setting it as the target.
+ /// Spawn an entity entity at the given coordinates without setting it as the target.
+ /// If no coordinates are given it will default to <see cref="TargetCoords"/>
/// </summary>
- protected async Task<NetEntity> Spawn(string prototype)
+ protected async Task<NetEntity> Spawn(string prototype, NetCoordinates? coords = null)
{
+ coords ??= TargetCoords;
var entity = NetEntity.Invalid;
await Server.WaitPost(() =>
{
- entity = SEntMan.GetNetEntity(SEntMan.SpawnAtPosition(prototype, SEntMan.GetCoordinates(TargetCoords)));
+ entity = SEntMan.GetNetEntity(SEntMan.SpawnAtPosition(prototype, SEntMan.GetCoordinates(coords.Value)));
});
await RunTicks(5);
}
}
+ /// <summary>
+ /// Simulates a drag and drop mouse interaction from one entity to another.
+ /// </summary>
+ protected async Task DragDrop(NetEntity source, NetEntity target)
+ {
+ // ScreenCoordinates diff needs to be larger than DragDropSystem.Deadzone for the drag drop to initiate
+ var screenX = CDragDropSys.Deadzone + 1f;
+
+ // Start drag
+ await SetKey(EngineKeyFunctions.Use,
+ BoundKeyState.Down,
+ NetPosition(source),
+ source,
+ screenCoordinates: new ScreenCoordinates(screenX, 0f, WindowId.Main));
+
+ await RunTicks(3);
+
+ // End drag
+ await SetKey(EngineKeyFunctions.Use,
+ BoundKeyState.Up,
+ NetPosition(target),
+ target,
+ screenCoordinates: new ScreenCoordinates(0f, 0f, WindowId.Main));
+
+ await RunTicks(3);
+ }
+
/// <summary>
/// Throw the currently held entity. Defaults to targeting the current <see cref="TargetCoords"/>
/// </summary>
protected EntityCoordinates Position(NetEntity uid) => Position(ToServer(uid));
protected EntityCoordinates Position(EntityUid uid) => Xform(uid).Coordinates;
+ protected NetCoordinates NetPosition(NetEntity uid) => FromServer(Position(uid));
#endregion
}
-using Content.Client.Interaction;
-using Content.IntegrationTests.Tests.Interaction;
+using Content.IntegrationTests.Tests.Interaction;
+using Content.Shared.Strip.Components;
using Robust.Shared.GameObjects;
-using Robust.Shared.Input;
-using Robust.Shared.Map;
namespace Content.IntegrationTests.Tests.Strip;
{
protected override string PlayerPrototype => "MobHuman";
+ /// <summary>
+ /// Tests that the stripping UI is opened when drag dropping from another mob onto the player.
+ /// </summary>
[Test]
public async Task DragDropOpensStrip()
{
- // Spawn one tile away
- TargetCoords = SEntMan.GetNetCoordinates(new EntityCoordinates(MapData.MapUid, 1, 0));
await SpawnTarget("MobHuman");
var userInterface = Comp<UserInterfaceComponent>(Target);
- Assert.That(userInterface.Actors.Count == 0);
+ Assert.That(userInterface.Actors, Is.Empty);
- // screenCoordinates diff needs to be larger than DragDropSystem._deadzone
- var screenX = CEntMan.System<DragDropSystem>().Deadzone + 1f;
+ await DragDrop(Target.Value, Player);
- // Start drag
- await SetKey(EngineKeyFunctions.Use,
- BoundKeyState.Down,
- TargetCoords,
- Target,
- screenCoordinates: new ScreenCoordinates(screenX, 0f, WindowId.Main));
+ Assert.That(userInterface.Actors, Is.Not.Empty);
- await RunTicks(5);
-
- // End drag
- await SetKey(EngineKeyFunctions.Use,
- BoundKeyState.Up,
- PlayerCoords,
- Player,
- screenCoordinates: new ScreenCoordinates(0f, 0f, WindowId.Main));
-
- await RunTicks(5);
-
- Assert.That(userInterface.Actors.Count > 0);
+ Assert.That(CUiSys.IsUiOpen(CTarget.Value, StrippingUiKey.Key));
+ Assert.That(SUiSys.IsUiOpen(STarget.Value, StrippingUiKey.Key));
}
}