diff --git a/Content.IntegrationTests/Tests/Chemistry/SolutionPurgeRegenerationTests.cs b/Content.IntegrationTests/Tests/Chemistry/SolutionPurgeRegenerationTests.cs new file mode 100644 index 0000000000..2f69f0eb67 --- /dev/null +++ b/Content.IntegrationTests/Tests/Chemistry/SolutionPurgeRegenerationTests.cs @@ -0,0 +1,48 @@ +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.FixedPoint; +using Robust.Shared.GameObjects; +using Robust.Shared.Prototypes; + +namespace Content.IntegrationTests.Tests.Chemistry; + +[TestFixture] +[TestOf(typeof(SolutionRegenerationSystem))] +[TestOf(typeof(SolutionPurgeSystem))] +public sealed class SolutionPurgeRegenerationTests : GameTest +{ + private static readonly EntProtoId AdvancedMop = "AdvMopItem"; + private static readonly ProtoId Water = "Water"; + private static readonly ProtoId NotWater = "DexalinPlus"; + + [SidedDependency(Side.Server)] private readonly SharedSolutionContainerSystem _solutionContainer = default!; + + [Test] + public async Task TestMop() + { + var testMap = await Pair.CreateTestMap(); + + EntityUid mop = default!; + Entity solution = default!; + await Server.WaitPost(() => + { + mop = SSpawnAtPosition(AdvancedMop, testMap.GridCoords); + + var generated = SComp(mop).Generated; + var purge = SComp(mop); + Assume.That(generated.ContainsPrototype(Water)); + Assume.That(purge.Preserve, Does.Not.Contain(NotWater)); + + + Assert.That(_solutionContainer.TryGetSolution(mop, "absorbed", out var mopSolution, out _)); + solution = mopSolution!.Value; + Assert.That(_solutionContainer.AddSolution(solution, new Solution(NotWater, 50)), Is.EqualTo(FixedPoint2.New(50))); + }); + + await PoolManager.WaitUntil(Server, () => !solution.Comp.Solution.ContainsPrototype(NotWater)); + await PoolManager.WaitUntil(Server, () => solution.Comp.Solution.Volume == solution.Comp.Solution.MaxVolume); + } +} diff --git a/Content.IntegrationTests/Tests/Chemistry/SprayVaporTests.cs b/Content.IntegrationTests/Tests/Chemistry/SprayVaporTests.cs new file mode 100644 index 0000000000..501e11886a --- /dev/null +++ b/Content.IntegrationTests/Tests/Chemistry/SprayVaporTests.cs @@ -0,0 +1,80 @@ +using System.Numerics; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; +using Content.IntegrationTests.NUnit.Constraints; +using Content.Server.Chemistry.Components; +using Content.Server.Chemistry.EntitySystems; +using Content.Server.Decals; +using Content.Server.Fluids.EntitySystems; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Decals; +using Content.Shared.Fluids.Components; +using Content.Shared.Fluids.EntitySystems; +using Robust.Shared.GameObjects; +using Robust.Shared.Prototypes; + +namespace Content.IntegrationTests.Tests.Chemistry; + +[TestFixture] +[TestOf(typeof(SharedSpraySystem))] +[TestOf(typeof(VaporSystem))] +public sealed class SprayVaporTests : GameTest +{ + private static readonly ProtoId Blood = "Blood"; + private static readonly EntProtoId SprayBottleSpaceCleaner = "SprayBottleSpaceCleaner"; + private const string BloodPuddle = "SprayVaporTestBloodPuddle"; + private const int BloodVolume = 5; + + [TestPrototypes] + private static readonly string Prototypes = @$" +- type: entity + parent: Puddle + id: {BloodPuddle} + suffix: Blood + components: + - type: Solution + id: puddle + solution: + maxVol: 1000 + reagents: + - ReagentId: {Blood} + Quantity: {BloodVolume} +"; + + [SidedDependency(Side.Server)] private readonly SpraySystem _spray = default!; + [SidedDependency(Side.Server)] private readonly SolutionContainerSystem _solutionContainer = default!; + [SidedDependency(Side.Server)] private readonly SharedTransformSystem _transform = default!; + + [Test] + public async Task TestSprayingSpaceCleaner() + { + var testMap = await Pair.CreateTestMap(); + + Entity puddle = default!; + + await Server.WaitAssertion(() => + { + var sprayCleaner = SSpawnAtPosition(SprayBottleSpaceCleaner, testMap.GridCoords); + Assume.That(sprayCleaner, Has.Comp(Server)); + _transform.SetLocalPositionNoLerp(sprayCleaner, SComp(sprayCleaner).LocalPosition + new Vector2(1, 1)); + + var puddleUid = SSpawnAtPosition(BloodPuddle, testMap.GridCoords); + Assume.That(puddleUid, Has.Comp(Server)); + Assume.That(_solutionContainer.TryGetSolution(puddleUid, "puddle", out var puddleSolution, out _)); + puddle = puddleSolution!.Value; + Assume.That(puddle.Comp.Solution.ContainsPrototype(Blood)); + + _spray.Spray((sprayCleaner, SComp(sprayCleaner)), _transform.GetMapCoordinates(puddleUid)); + var vaporEnum = SEntMan.EntityQueryEnumerator(); + Assume.That(vaporEnum.MoveNext(out _)); + }); + + await PoolManager.WaitUntil(Server, () => !SEntMan.EntityQueryEnumerator().MoveNext(out _)); + + await Server.WaitAssertion(() => + { + Assert.That(!puddle.Comp.Solution.ContainsPrototype(Blood)); + }); + } +} diff --git a/Content.IntegrationTests/Tests/Tools/WelderTests.cs b/Content.IntegrationTests/Tests/Tools/WelderTests.cs new file mode 100644 index 0000000000..e504153f5a --- /dev/null +++ b/Content.IntegrationTests/Tests/Tools/WelderTests.cs @@ -0,0 +1,59 @@ +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Tools.Components; +using Content.Shared.Tools.Systems; +using Robust.Shared.GameObjects; +using Robust.Shared.Prototypes; + +namespace Content.IntegrationTests.Tests.Tools; + +[TestFixture] +[TestOf(typeof(SharedToolSystem))] +public sealed class WelderTests : GameTest +{ + private const string Welder = "TestTinyWelder"; + + [TestPrototypes] + private const string Prototypes = $@" +- type: entity + parent: [SolutionToolWelderMiniEmergency, Welder] + id: {Welder} + components: + - type: Solution + solution: + maxVol: 5 + reagents: + - ReagentId: WeldingFuel + Quantity: 5 +"; + + [SidedDependency(Side.Server)] private readonly SharedToolSystem _tool = default!; + [SidedDependency(Side.Server)] private readonly SharedSolutionContainerSystem _solutionContainer = default!; + + [Test] + public async Task FuelDepletion() + { + Entity welder = default!; + Entity fuel = default!; + + await Server.WaitPost(() => + { + var uid = SSpawn(Welder); + welder = (uid, SComp(uid)); + Assume.That(_solutionContainer.TryGetSolution(uid, welder.Comp.FuelSolutionName, out var solutionEnt, out _)); + fuel = solutionEnt!.Value; + + _tool.TurnOn(welder, null); + }); + + await PoolManager.WaitUntil(Server, () => fuel.Comp.Solution.Volume <= 0); + + await Server.WaitPost(() => + { + Assert.That(SComp(welder).Activated, Is.False); + }); + } +} diff --git a/Content.Server/Chemistry/Components/VaporComponent.cs b/Content.Server/Chemistry/Components/VaporComponent.cs index 1bc3881b1d..e07283b09c 100644 --- a/Content.Server/Chemistry/Components/VaporComponent.cs +++ b/Content.Server/Chemistry/Components/VaporComponent.cs @@ -5,8 +5,6 @@ namespace Content.Server.Chemistry.Components [RegisterComponent] public sealed partial class VaporComponent : Component { - public const string SolutionName = "vapor"; - /// /// Stores data on the previously reacted tile. We only want to do reaction checks once per tile. /// diff --git a/Content.Server/Chemistry/EntitySystems/VaporSystem.cs b/Content.Server/Chemistry/EntitySystems/VaporSystem.cs index b6b3433bf5..0dabd076e1 100644 --- a/Content.Server/Chemistry/EntitySystems/VaporSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/VaporSystem.cs @@ -1,7 +1,6 @@ using Content.Server.Chemistry.Components; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components; -using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; using Content.Shared.Physics; @@ -16,6 +15,7 @@ using Robust.Shared.Physics.Systems; using Robust.Shared.Prototypes; using Robust.Shared.Spawners; using System.Numerics; +using Content.Shared.Vapor; namespace Content.Server.Chemistry.EntitySystems { @@ -23,11 +23,12 @@ namespace Content.Server.Chemistry.EntitySystems internal sealed class VaporSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _protoManager = default!; + [Dependency] private readonly ReactiveSystem _reactive = default!; + [Dependency] private readonly ThrowingSystem _throwing = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; - [Dependency] private readonly ThrowingSystem _throwing = default!; - [Dependency] private readonly ReactiveSystem _reactive = default!; + [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; public override void Initialize() @@ -39,11 +40,8 @@ namespace Content.Server.Chemistry.EntitySystems private void HandleCollide(Entity entity, ref StartCollideEvent args) { - foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions(entity.Owner)) - { - var solution = soln.Comp.Solution; - _reactive.DoEntityReaction(args.OtherEntity, solution, ReactionMethod.Touch); - } + var solution = Comp(entity).Solution; + _reactive.DoEntityReaction(args.OtherEntity, solution, ReactionMethod.Touch); // Check for collision with a impassable object (e.g. wall) and stop if ((args.OtherFixture.CollisionLayer & (int)CollisionGroup.Impassable) != 0 && args.OtherFixture.Hard) @@ -52,7 +50,7 @@ namespace Content.Server.Chemistry.EntitySystems } } - public void Start(Entity vapor, + public void Start(Entity vapor, TransformComponent vaporXform, Vector2 dir, float speed, @@ -60,6 +58,9 @@ namespace Content.Server.Chemistry.EntitySystems float aliveTime, EntityUid? user = null) { + if (!Resolve(vapor, ref vapor.Comp)) + return; + vapor.Comp.Active = true; var despawn = EnsureComp(vapor); despawn.Lifetime = aliveTime; @@ -78,21 +79,20 @@ namespace Content.Server.Chemistry.EntitySystems } } - internal bool TryAddSolution(Entity vapor, Solution solution) + internal bool TryAddSolution(Entity vapor, Entity solution, FixedPoint2 split) { - if (solution.Volume == 0) - { + if (solution.Comp.Solution.Volume <= 0 || split <= 0 || !Resolve(vapor, ref vapor.Comp)) return false; + + var newSolution = _solutionContainer.SplitSolution(solution, split); + + if (TryComp(vapor, out var appearance)) + { + _appearance.SetData(vapor, VaporVisuals.Color, newSolution.GetColor(_protoManager).WithAlpha(1f), appearance); + _appearance.SetData(vapor, VaporVisuals.State, true, appearance); } - if (!_solutionContainerSystem.TryGetSolution(vapor.Owner, - VaporComponent.SolutionName, - out var vaporSolution)) - { - return false; - } - - return _solutionContainerSystem.TryAddSolution(vaporSolution.Value, solution); + return _solutionContainer.TryAddSolution((vapor, vapor.Comp), newSolution); } public override void Update(float frameTime) @@ -101,8 +101,8 @@ namespace Content.Server.Chemistry.EntitySystems // Enumerate over all VaporComponents // TODO: Vapor should just use SolutionComponent and not be capable of having multiple solutions. - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var vaporComp, out var container, out var xform)) + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var vaporComp, out var solution, out var xform)) { // Return early if we're not active if (!vaporComp.Active) @@ -118,47 +118,43 @@ namespace Content.Server.Chemistry.EntitySystems if (vaporComp.PreviousTileRef != null && tile == vaporComp.PreviousTileRef) continue; - // Enumerate over all the reagents in the vapor entity solution - foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((uid, container))) + // Iterate over the reagents in the solution + // Reason: Each reagent in our solution may have a unique TileReaction + // In this instance, we check individually for each reagent's TileReaction + // This is not doing chemical reactions! + var contents = solution.Solution; + foreach (var reagentQuantity in contents.Contents.ToArray()) { - // Iterate over the reagents in the solution - // Reason: Each reagent in our solution may have a unique TileReaction - // In this instance, we check individually for each reagent's TileReaction - // This is not doing chemical reactions! - var contents = soln.Comp.Solution; - foreach (var reagentQuantity in contents.Contents.ToArray()) - { - // Check if the reagent is empty - if (reagentQuantity.Quantity == FixedPoint2.Zero) - continue; + // Check if the reagent is empty + if (reagentQuantity.Quantity == FixedPoint2.Zero) + continue; - var reagent = _protoManager.Index(reagentQuantity.Reagent.Prototype); + var reagent = _protoManager.Index(reagentQuantity.Reagent.Prototype); - // Limit the reaction amount to a minimum value to ensure no floating point funnies. - // Ex: A solution with a low percentage transfer amount will slowly approach 0.01... and never get deleted - var clampedAmount = Math.Max( - (float)reagentQuantity.Quantity * vaporComp.TransferAmountPercentage, - vaporComp.MinimumTransferAmount); + // Limit the reaction amount to a minimum value to ensure no floating point funnies. + // Ex: A solution with a low percentage transfer amount will slowly approach 0.01... and never get deleted + var clampedAmount = Math.Max( + (float)reagentQuantity.Quantity * vaporComp.TransferAmountPercentage, + vaporComp.MinimumTransferAmount); - // Preform the reagent's TileReaction - var reaction = - reagent.ReactionTile(tile, - clampedAmount, - EntityManager, - reagentQuantity.Reagent.Data); + // Preform the reagent's TileReaction + var reaction = + reagent.ReactionTile(tile, + clampedAmount, + EntityManager, + reagentQuantity.Reagent.Data); - if (reaction > reagentQuantity.Quantity) - reaction = reagentQuantity.Quantity; - - _solutionContainerSystem.RemoveReagent(soln, reagentQuantity.Reagent, reaction); - } - - // Delete the vapor entity if it has no contents - if (contents.Volume == 0) - QueueDel(uid); + if (reaction > reagentQuantity.Quantity) + reaction = reagentQuantity.Quantity; + _solutionContainer.RemoveReagent((uid, solution), reagentQuantity.Reagent, reaction); } + // Delete the vapor entity if it has no contents + if (contents.Volume == 0) + QueueDel(uid); + + // Set the previous tile reference to the current tile vaporComp.PreviousTileRef = tile; } diff --git a/Content.Server/Fluids/EntitySystems/SpraySystem.cs b/Content.Server/Fluids/EntitySystems/SpraySystem.cs index 3b7b4327c4..ebede89a12 100644 --- a/Content.Server/Fluids/EntitySystems/SpraySystem.cs +++ b/Content.Server/Fluids/EntitySystems/SpraySystem.cs @@ -1,19 +1,15 @@ -using Content.Server.Chemistry.Components; using Content.Server.Chemistry.EntitySystems; using Content.Server.Gravity; using Content.Server.Popups; using Content.Shared.CCVar; using Content.Shared.Chemistry.EntitySystems; -using Content.Shared.FixedPoint; using Content.Shared.Fluids; using Content.Shared.Interaction; using Content.Shared.Timing; -using Content.Shared.Vapor; using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; using Robust.Shared.Configuration; using Robust.Shared.Physics.Components; -using Robust.Shared.Prototypes; using System.Numerics; using Content.Shared.Fluids.EntitySystems; using Content.Shared.Fluids.Components; @@ -24,7 +20,6 @@ namespace Content.Server.Fluids.EntitySystems; public sealed class SpraySystem : SharedSpraySystem { - [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly GravitySystem _gravity = default!; [Dependency] private readonly PhysicsSystem _physics = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; @@ -32,7 +27,6 @@ public sealed class SpraySystem : SharedSpraySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; [Dependency] private readonly VaporSystem _vapor = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly ContainerSystem _container = default!; @@ -151,10 +145,6 @@ public sealed class SpraySystem : SharedSpraySystem target = sprayerMapPos.Offset(diffNorm * entity.Comp.SprayDistance); var adjustedSolutionAmount = entity.Comp.TransferAmount / entity.Comp.VaporAmount; - var newSolution = _solutionContainer.SplitSolution(soln.Value, adjustedSolutionAmount); - - if (newSolution.Volume <= FixedPoint2.Zero) - break; // Spawn the vapor cloud onto the grid/map the user is present on. Offset the start position based on how far the target destination is. var vaporPos = sprayerMapPos.Offset(distance < 1 ? quarter : threeQuarters); @@ -163,22 +153,13 @@ public sealed class SpraySystem : SharedSpraySystem _transform.SetWorldRotation(vaporXform, rotation); - if (TryComp(vapor, out AppearanceComponent? appearance)) - { - _appearance.SetData(vapor, VaporVisuals.Color, solution.GetColor(_proto).WithAlpha(1f), appearance); - _appearance.SetData(vapor, VaporVisuals.State, true, appearance); - } - - // Add the solution to the vapor and actually send the thing - var vaporComponent = Comp(vapor); - var ent = (vapor, vaporComponent); - _vapor.TryAddSolution(ent, newSolution); + _vapor.TryAddSolution(vapor, soln.Value, adjustedSolutionAmount); // impulse direction is defined in world-coordinates, not local coordinates var impulseDirection = rotation.ToVec(); var time = diffLength / entity.Comp.SprayVelocity; - _vapor.Start(ent, vaporXform, impulseDirection * diffLength, entity.Comp.SprayVelocity, target, time, user); + _vapor.Start(vapor, vaporXform, impulseDirection * diffLength, entity.Comp.SprayVelocity, target, time, user); var thingGettingPushed = entity.Owner; if (_container.TryGetOuterContainer(entity, sprayerXform, out var container)) diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.Solution.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.Solution.cs index 166f52b920..66addc75e7 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.Solution.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.Solution.cs @@ -1,4 +1,5 @@ using Content.Server.Chemistry.Components; +using Content.Server.Chemistry.EntitySystems; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.FixedPoint; @@ -12,6 +13,7 @@ namespace Content.Server.Weapons.Ranged.Systems; public sealed partial class GunSystem { [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; + [Dependency] private readonly VaporSystem _vapor = default!; protected override void InitializeSolution() { @@ -61,25 +63,10 @@ public sealed partial class GunSystem { var (shot, shootable) = base.GetSolutionShot(ent, position); - if (!_solutionContainer.TryGetSolution(ent.Owner, ent.Comp.SolutionId, out var solution, out _)) + if (!_solutionContainer.TryGetSolution(ent.Owner, ent.Comp.SolutionId, out var solution)) return (shot, shootable); - var newSolution = _solutionContainer.SplitSolution(solution.Value, ent.Comp.FireCost); - - if (newSolution.Volume <= FixedPoint2.Zero) - return (shot, shootable); - - if (TryComp(shot, out var appearance)) - { - Appearance.SetData(shot, VaporVisuals.Color, newSolution.GetColor(ProtoManager).WithAlpha(1f), appearance); - Appearance.SetData(shot, VaporVisuals.State, true, appearance); - } - - // Add the solution to the vapor and actually send the thing - if (_solutionContainer.TryGetSolution(shot, VaporComponent.SolutionName, out var vaporSolution, out _)) - { - _solutionContainer.TryAddSolution(vaporSolution.Value, newSolution); - } + _vapor.TryAddSolution(shot, solution.Value, ent.Comp.FireCost); return (shot, shootable); } } diff --git a/Content.Shared/Chemistry/Components/SolutionPurgeComponent.cs b/Content.Shared/Chemistry/Components/SolutionPurgeComponent.cs index acaebb7ef8..c10e6d2283 100644 --- a/Content.Shared/Chemistry/Components/SolutionPurgeComponent.cs +++ b/Content.Shared/Chemistry/Components/SolutionPurgeComponent.cs @@ -15,12 +15,6 @@ namespace Content.Shared.Chemistry.Components; [Access(typeof(SolutionPurgeSystem))] public sealed partial class SolutionPurgeComponent : Component { - /// - /// The name of the solution to detract from. - /// - [DataField(required: true)] - public string Solution = string.Empty; - /// /// The reagent(s) to be ignored when purging the solution /// diff --git a/Content.Shared/Chemistry/Components/SolutionRegenerationComponent.cs b/Content.Shared/Chemistry/Components/SolutionRegenerationComponent.cs index be41ace321..6fb44175a7 100644 --- a/Content.Shared/Chemistry/Components/SolutionRegenerationComponent.cs +++ b/Content.Shared/Chemistry/Components/SolutionRegenerationComponent.cs @@ -11,18 +11,6 @@ namespace Content.Shared.Chemistry.Components; [Access(typeof(SolutionRegenerationSystem))] public sealed partial class SolutionRegenerationComponent : Component { - /// - /// The name of the solution to add to. - /// - [DataField("solution", required: true)] - public string SolutionName = string.Empty; - - /// - /// The solution to add reagents to. - /// - [ViewVariables] - public Entity? SolutionRef = null; - /// /// The reagent(s) to be regenerated in the solution. /// diff --git a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs index 0b75a05e91..1ce3b4b15a 100644 --- a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs @@ -656,21 +656,18 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem /// /// Adds a solution to the container, if it can fully fit. /// - /// entity holding targetSolution - /// entity holding targetSolution + /// Solution we are adding to /// solution being added /// If the solution could be added. - public bool TryAddSolution(Entity soln, Solution toAdd) + public bool TryAddSolution(Entity solution, Solution toAdd) { - var (uid, comp) = soln; - var solution = comp.Solution; - if (toAdd.Volume == FixedPoint2.Zero) return true; - if (toAdd.Volume > solution.AvailableVolume) + + if (toAdd.Volume > solution.Comp.Solution.AvailableVolume) return false; - ForceAddSolution(soln, toAdd); + ForceAddSolution((solution, solution.Comp), toAdd); return true; } diff --git a/Content.Shared/Chemistry/EntitySystems/SolutionPurgeSystem.cs b/Content.Shared/Chemistry/EntitySystems/SolutionPurgeSystem.cs index b31320e5ac..ed7aedc447 100644 --- a/Content.Shared/Chemistry/EntitySystems/SolutionPurgeSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SolutionPurgeSystem.cs @@ -27,9 +27,8 @@ public sealed class SolutionPurgeSystem : EntitySystem { base.Update(frameTime); - // TODO: SolutionPurgeComponent on Solution Entities! - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var purge, out var manager)) + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var purge, out var solution)) { if (_timing.CurTime < purge.NextPurgeTime) continue; @@ -39,12 +38,9 @@ public sealed class SolutionPurgeSystem : EntitySystem // Needs to be networked and dirtied so that the client can reroll it during prediction Dirty(uid, purge); - if (_solutionContainer.TryGetSolution((uid, manager), purge.Solution, out var solution)) - { - _solutionContainer.SplitSolutionWithout(solution.Value, - purge.Quantity, - purge.Preserve.Select(proto => proto.Id).ToArray()); - } + _solutionContainer.SplitSolutionWithout((uid, solution), + purge.Quantity, + purge.Preserve.ToArray()); } } } diff --git a/Content.Shared/Chemistry/EntitySystems/SolutionRegenerationSystem.cs b/Content.Shared/Chemistry/EntitySystems/SolutionRegenerationSystem.cs index 6aeaa7fd6f..8be2c03221 100644 --- a/Content.Shared/Chemistry/EntitySystems/SolutionRegenerationSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SolutionRegenerationSystem.cs @@ -16,7 +16,6 @@ public sealed class SolutionRegenerationSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnEntRemoved); } private void OnMapInit(Entity ent, ref MapInitEvent args) @@ -26,21 +25,13 @@ public sealed class SolutionRegenerationSystem : EntitySystem Dirty(ent); } - // Workaround for https://github.com/space-wizards/space-station-14/pull/35314 - private void OnEntRemoved(Entity ent, ref EntRemovedFromContainerMessage args) - { - // Make sure the removed entity was our contained solution and clear our cached reference - if (args.Entity == ent.Comp.SolutionRef?.Owner) - ent.Comp.SolutionRef = null; - } - public override void Update(float frameTime) { base.Update(frameTime); // TODO: SolutionRegenerationComponent on Solution Entities! - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var regen, out var manager)) + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var regen, out var solution)) { if (_timing.CurTime < regen.NextRegenTime) continue; @@ -49,13 +40,7 @@ public sealed class SolutionRegenerationSystem : EntitySystem regen.NextRegenTime += regen.Duration; // Needs to be networked and dirtied so that the client can reroll it during prediction Dirty(uid, regen); - if (!_solutionContainer.ResolveSolution((uid, manager), - regen.SolutionName, - ref regen.SolutionRef, - out var solution)) - continue; - - var amount = FixedPoint2.Min(solution.AvailableVolume, regen.Generated.Volume); + var amount = FixedPoint2.Min(solution.Solution.AvailableVolume, regen.Generated.Volume); if (amount <= FixedPoint2.Zero) continue; @@ -64,7 +49,7 @@ public sealed class SolutionRegenerationSystem : EntitySystem ? regen.Generated : regen.Generated.Clone().SplitSolution(amount); - _solutionContainer.TryAddSolution(regen.SolutionRef.Value, generated); + _solutionContainer.TryAddSolution((uid, solution), generated); } } } diff --git a/Content.Shared/EntityEffects/Effects/Solution/AddReagentToSolutionEntityEffectSystem.cs b/Content.Shared/EntityEffects/Effects/Solution/AddReagentToSolutionEntityEffectSystem.cs index e6c9090339..39f416e62b 100644 --- a/Content.Shared/EntityEffects/Effects/Solution/AddReagentToSolutionEntityEffectSystem.cs +++ b/Content.Shared/EntityEffects/Effects/Solution/AddReagentToSolutionEntityEffectSystem.cs @@ -1,4 +1,5 @@ -using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Robust.Shared.Prototypes; @@ -13,6 +14,12 @@ namespace Content.Shared.EntityEffects.Effects.Solution; /// public sealed class AddReagentToSolutionEntityEffectSystem : EntityEffectSystem { + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent>(Effect); + } + [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; protected override void Effect(Entity entity, ref EntityEffectEvent args) @@ -25,6 +32,14 @@ public sealed class AddReagentToSolutionEntityEffectSystem : EntityEffectSystem< _solutionContainer.TryAddReagent(solutionContainer.Value, reagent, args.Scale * args.Effect.StrengthModifier); } + + private void Effect(Entity entity, ref EntityEffectEvent args) + { + if (entity.Comp.Id != args.Effect.Solution) + return; + + _solutionContainer.TryAddReagent(entity, args.Effect.Reagent, args.Scale * args.Effect.StrengthModifier); + } } /// diff --git a/Content.Shared/Fluids/AbsorbentComponent.cs b/Content.Shared/Fluids/AbsorbentComponent.cs index c499a2d2ee..9931a04a55 100644 --- a/Content.Shared/Fluids/AbsorbentComponent.cs +++ b/Content.Shared/Fluids/AbsorbentComponent.cs @@ -30,7 +30,7 @@ public sealed partial class AbsorbentComponent : Component /// How much solution we can transfer in one interaction. /// [DataField] - public FixedPoint2 PickupAmount = FixedPoint2.New(100); + public FixedPoint2 PickupAmount = FixedPoint2.New(120); /// /// The effect spawned when the puddle fully evaporates. diff --git a/Content.Shared/Tools/Systems/SharedToolSystem.Welder.cs b/Content.Shared/Tools/Systems/SharedToolSystem.Welder.cs index b0da547a27..b657b99170 100644 --- a/Content.Shared/Tools/Systems/SharedToolSystem.Welder.cs +++ b/Content.Shared/Tools/Systems/SharedToolSystem.Welder.cs @@ -117,13 +117,13 @@ public abstract partial class SharedToolSystem if (TryComp(target, out ReagentTankComponent? tank) && tank.TankType == ReagentTankType.Fuel && SolutionContainerSystem.TryGetDrainableSolution(target, out var targetSoln, out var targetSolution) - && SolutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.FuelSolutionName, out var solutionComp, out var welderSolution)) + && SolutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.FuelSolutionName, out var solution, out var welderSolution)) { var trans = FixedPoint2.Min(welderSolution.AvailableVolume, targetSolution.Volume); if (trans > 0) { var drained = SolutionContainerSystem.Drain(target, targetSoln.Value, trans); - SolutionContainerSystem.TryAddSolution(solutionComp.Value, drained); + SolutionContainerSystem.TryAddSolution(solution.Value, drained); _audioSystem.PlayPredicted(entity.Comp.WelderRefill, entity, user: args.User); _popup.PopupClient(Loc.GetString("welder-component-after-interact-refueled-message"), entity, args.User); } @@ -210,9 +210,10 @@ public abstract partial class SharedToolSystem private void UpdateWelders() { // TODO: Same as the other EntityQueryEnumerators... - var query = EntityQueryEnumerator(); + // TODO: ActiveWelderComponent + var query = EntityQueryEnumerator(); var curTime = _timing.CurTime; - while (query.MoveNext(out var uid, out var welder, out var solutionContainer)) + while (query.MoveNext(out var uid, out var welder)) { if (curTime < welder.NextUpdate) continue; @@ -223,7 +224,8 @@ public abstract partial class SharedToolSystem if (!welder.Enabled) continue; - if (!SolutionContainerSystem.TryGetSolution((uid, solutionContainer), welder.FuelSolutionName, out var solutionComp, out var solution)) + // TODO: Relations + if (!SolutionContainerSystem.TryGetSolution(uid, welder.FuelSolutionName, out var solutionComp, out var solution)) continue; SolutionContainerSystem.RemoveReagent(solutionComp.Value, welder.FuelReagent, welder.FuelConsumption * welder.WelderUpdateTimer.TotalSeconds); diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index ac87be68d7..fba5bbce03 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -2523,7 +2523,6 @@ solutions: - SolutionVenomSpider - type: SolutionRegeneration - solution: melee generated: reagents: - ReagentId: Mechanotoxin diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/moproach.yml b/Resources/Prototypes/Entities/Mobs/NPCs/moproach.yml index 60f8c0f138..06d0965c0e 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/moproach.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/moproach.yml @@ -30,13 +30,11 @@ - type: UseDelay delay: 0.5 # quick feet - type: SolutionRegeneration - solution: absorbed generated: reagents: - ReagentId: Water Quantity: 10 - type: SolutionPurge - solution: absorbed preserve: - Water quantity: 10 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml index a615e5ebab..ad5d9a452f 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml @@ -127,7 +127,6 @@ rootTask: task: FirebotCompound - type: SolutionRegeneration - solution: spray generated: reagents: - ReagentId: Water @@ -273,13 +272,11 @@ - type: UseDelay delay: 2 - type: SolutionRegeneration - solution: absorbed generated: reagents: - ReagentId: Water Quantity: 10 - type: SolutionPurge - solution: absorbed preserve: - Water quantity: 10 diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_fun.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_fun.yml index d7b87aeb24..0e8e12265d 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_fun.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_fun.yml @@ -63,7 +63,6 @@ - ReagentId: SpaceLube Quantity: 960 - type: SolutionRegeneration - solution: beaker generated: reagents: - ReagentId: SpaceLube diff --git a/Resources/Prototypes/Entities/Objects/Misc/arabianlamp.yml b/Resources/Prototypes/Entities/Objects/Misc/arabianlamp.yml index 363ad4d95f..e280552578 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/arabianlamp.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/arabianlamp.yml @@ -107,7 +107,6 @@ heldOnly: true exactVolume: true - type: SolutionRegeneration - solution: welder generated: reagents: - ReagentId: WeldingFuel diff --git a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml index c86f97eaab..942474e871 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml @@ -165,7 +165,6 @@ description: It extinguishes fires. it slowly refills with water. components: - type: SolutionRegeneration - solution: spray generated: reagents: - ReagentId: Water diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml index 757260aa7a..a9c62705c4 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml @@ -89,13 +89,11 @@ - type: Absorbent pickupAmount: 100 - type: SolutionRegeneration - solution: absorbed generated: reagents: - ReagentId: Water Quantity: 5 - type: SolutionPurge - solution: absorbed preserve: - Water quantity: 10 diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml index 1df2c803cf..9079ee1cb6 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml @@ -149,7 +149,6 @@ - type: SolutionTransfer canSend: false # No giving away infinite space cleaner! - type: SolutionRegeneration - solution: drink generated: reagents: - ReagentId: SpaceCleaner diff --git a/Resources/Prototypes/Entities/Objects/Specific/Xenoborg/nocturine_hypo.yml b/Resources/Prototypes/Entities/Objects/Specific/Xenoborg/nocturine_hypo.yml index 58aa84b927..f53ce62a0c 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Xenoborg/nocturine_hypo.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Xenoborg/nocturine_hypo.yml @@ -11,7 +11,6 @@ - ReagentId: Nocturine Quantity: 12 - type: SolutionRegeneration - solution: hypospray generated: reagents: - ReagentId: Nocturine diff --git a/Resources/Prototypes/Entities/Objects/Tools/lighters.yml b/Resources/Prototypes/Entities/Objects/Tools/lighters.yml index 159da3c5f4..8d6446287b 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/lighters.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/lighters.yml @@ -397,7 +397,7 @@ Quantity: 15 - type: entity - parent: [SolutionGinormous, BaseBrandedLighter, BaseCentcommContraband] + parent: [SolutionToolWelderExperimental, BaseBrandedLighter, BaseCentcommContraband] id: CentCommFlippo name: Gilded CentComm Flippo description: "An Ornate, jade embossed and gilded flippo frame containing a bluespace powered jet. The latch is secured by a miniature access reader that only responds to CentComm officials. The nicest lighter known to man." @@ -421,21 +421,10 @@ color: Gold - type: RefillableSolution solution: Welder - - type: Solution - solution: - reagents: - - ReagentId: Plasma - Quantity: 480 - type: Tool useSound: collection: Welder qualities: Welding - - type: SolutionRegeneration - solution: Welder - generated: - reagents: - - ReagentId: Plasma - Quantity: 0.1 - type: Lock - type: AccessReader access: [["CentralCommand"]] diff --git a/Resources/Prototypes/Entities/Objects/Tools/welders.yml b/Resources/Prototypes/Entities/Objects/Tools/welders.yml index 6162f25a66..6c415e6599 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/welders.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/welders.yml @@ -168,12 +168,8 @@ enabled: false radius: 1.5 color: lightblue - - type: SolutionRegeneration - solution: welder - generated: - reagents: - - ReagentId: WeldingFuel - Quantity: 1 + - type: Welder + fuelReagent: Plasma - type: RequiresEyeProtection statusEffectTime: 5 # less harmful; sunglasses can block it @@ -185,8 +181,13 @@ - type: Solution solution: reagents: - - ReagentId: WeldingFuel + - ReagentId: Plasma Quantity: 480 # Literally infinite so this number basically doesn't matter, also this should be a battery tbqh. + - type: SolutionRegeneration + generated: + reagents: + - ReagentId: Plasma + Quantity: 1 - type: entity name: emergency welding tool @@ -243,7 +244,6 @@ fuelConsumption: 2 fuelLitCost: 1 - type: SolutionRegeneration - solution: welder generated: reagents: - ReagentId: WeldingFuel diff --git a/Resources/Prototypes/Entities/Structures/Furniture/sink.yml b/Resources/Prototypes/Entities/Structures/Furniture/sink.yml index 19fb548789..4654c70374 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/sink.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/sink.yml @@ -25,7 +25,6 @@ solutions: - SolutionDrainNormal - type: SolutionRegeneration - solution: tank generated: reagents: - ReagentId: Water diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml index c2853701c4..8fc64bcb08 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml @@ -88,7 +88,6 @@ solutions: - SolutionDrainNormal - type: SolutionRegeneration - solution: tank generated: reagents: - ReagentId: Water diff --git a/Resources/Prototypes/Entities/Tiles/water.yml b/Resources/Prototypes/Entities/Tiles/water.yml index d4f2616bf0..bfabe3129d 100644 --- a/Resources/Prototypes/Entities/Tiles/water.yml +++ b/Resources/Prototypes/Entities/Tiles/water.yml @@ -25,7 +25,6 @@ - ReagentId: Water Quantity: 7680 - type: SolutionRegeneration - solution: pool generated: reagents: - ReagentId: Water