mirror of
https://github.com/corvax-team/ss14-wl.git
synced 2026-06-09 10:06:46 +02:00
Fix Broken Solution Enumerators (#43789)
* burge * bruegr * YAML * two misc fixes * snoutta here * add tests --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> Co-authored-by: Janet Blackquill <uhhadd@gmail.com>
This commit is contained in:
committed by
GitHub
parent
12e45305de
commit
4d1325bccf
@@ -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<ReagentPrototype> Water = "Water";
|
||||
private static readonly ProtoId<ReagentPrototype> NotWater = "DexalinPlus";
|
||||
|
||||
[SidedDependency(Side.Server)] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
|
||||
|
||||
[Test]
|
||||
public async Task TestMop()
|
||||
{
|
||||
var testMap = await Pair.CreateTestMap();
|
||||
|
||||
EntityUid mop = default!;
|
||||
Entity<SolutionComponent> solution = default!;
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
mop = SSpawnAtPosition(AdvancedMop, testMap.GridCoords);
|
||||
|
||||
var generated = SComp<SolutionRegenerationComponent>(mop).Generated;
|
||||
var purge = SComp<SolutionPurgeComponent>(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);
|
||||
}
|
||||
}
|
||||
@@ -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<ReagentPrototype> 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<SolutionComponent> puddle = default!;
|
||||
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
var sprayCleaner = SSpawnAtPosition(SprayBottleSpaceCleaner, testMap.GridCoords);
|
||||
Assume.That(sprayCleaner, Has.Comp<SprayComponent>(Server));
|
||||
_transform.SetLocalPositionNoLerp(sprayCleaner, SComp<TransformComponent>(sprayCleaner).LocalPosition + new Vector2(1, 1));
|
||||
|
||||
var puddleUid = SSpawnAtPosition(BloodPuddle, testMap.GridCoords);
|
||||
Assume.That(puddleUid, Has.Comp<PuddleComponent>(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<SprayComponent>(sprayCleaner)), _transform.GetMapCoordinates(puddleUid));
|
||||
var vaporEnum = SEntMan.EntityQueryEnumerator<VaporComponent>();
|
||||
Assume.That(vaporEnum.MoveNext(out _));
|
||||
});
|
||||
|
||||
await PoolManager.WaitUntil(Server, () => !SEntMan.EntityQueryEnumerator<VaporComponent>().MoveNext(out _));
|
||||
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(!puddle.Comp.Solution.ContainsPrototype(Blood));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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<WelderComponent> welder = default!;
|
||||
Entity<SolutionComponent> fuel = default!;
|
||||
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
var uid = SSpawn(Welder);
|
||||
welder = (uid, SComp<WelderComponent>(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<ItemToggleComponent>(welder).Activated, Is.False);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,6 @@ namespace Content.Server.Chemistry.Components
|
||||
[RegisterComponent]
|
||||
public sealed partial class VaporComponent : Component
|
||||
{
|
||||
public const string SolutionName = "vapor";
|
||||
|
||||
/// <summary>
|
||||
/// Stores data on the previously reacted tile. We only want to do reaction checks once per tile.
|
||||
/// </summary>
|
||||
|
||||
@@ -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<VaporComponent> 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<SolutionComponent>(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<VaporComponent> vapor,
|
||||
public void Start(Entity<VaporComponent?> 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<TimedDespawnComponent>(vapor);
|
||||
despawn.Lifetime = aliveTime;
|
||||
@@ -78,21 +79,20 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
}
|
||||
}
|
||||
|
||||
internal bool TryAddSolution(Entity<VaporComponent> vapor, Solution solution)
|
||||
internal bool TryAddSolution(Entity<SolutionComponent?> vapor, Entity<SolutionComponent> 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<AppearanceComponent>(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<VaporComponent, SolutionManagerComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var uid, out var vaporComp, out var container, out var xform))
|
||||
var query = EntityQueryEnumerator<VaporComponent, SolutionComponent, TransformComponent>();
|
||||
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<ReagentPrototype>(reagentQuantity.Reagent.Prototype);
|
||||
var reagent = _protoManager.Index<ReagentPrototype>(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;
|
||||
}
|
||||
|
||||
@@ -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<VaporComponent>(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))
|
||||
|
||||
@@ -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<AppearanceComponent>(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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,6 @@ namespace Content.Shared.Chemistry.Components;
|
||||
[Access(typeof(SolutionPurgeSystem))]
|
||||
public sealed partial class SolutionPurgeComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the solution to detract from.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public string Solution = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The reagent(s) to be ignored when purging the solution
|
||||
/// </summary>
|
||||
|
||||
@@ -11,18 +11,6 @@ namespace Content.Shared.Chemistry.Components;
|
||||
[Access(typeof(SolutionRegenerationSystem))]
|
||||
public sealed partial class SolutionRegenerationComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the solution to add to.
|
||||
/// </summary>
|
||||
[DataField("solution", required: true)]
|
||||
public string SolutionName = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The solution to add reagents to.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public Entity<SolutionComponent>? SolutionRef = null;
|
||||
|
||||
/// <summary>
|
||||
/// The reagent(s) to be regenerated in the solution.
|
||||
/// </summary>
|
||||
|
||||
@@ -656,21 +656,18 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
||||
/// <summary>
|
||||
/// Adds a solution to the container, if it can fully fit.
|
||||
/// </summary>
|
||||
/// <param name="targetUid">entity holding targetSolution</param>
|
||||
/// <param name="targetSolution">entity holding targetSolution</param>
|
||||
/// <param name="solution">Solution we are adding to</param>
|
||||
/// <param name="toAdd">solution being added</param>
|
||||
/// <returns>If the solution could be added.</returns>
|
||||
public bool TryAddSolution(Entity<SolutionComponent> soln, Solution toAdd)
|
||||
public bool TryAddSolution(Entity<SolutionComponent> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,9 +27,8 @@ public sealed class SolutionPurgeSystem : EntitySystem
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
// TODO: SolutionPurgeComponent on Solution Entities!
|
||||
var query = EntityQueryEnumerator<SolutionPurgeComponent, SolutionManagerComponent>();
|
||||
while (query.MoveNext(out var uid, out var purge, out var manager))
|
||||
var query = EntityQueryEnumerator<SolutionPurgeComponent, SolutionComponent>();
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ public sealed class SolutionRegenerationSystem : EntitySystem
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SolutionRegenerationComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<SolutionRegenerationComponent, EntRemovedFromContainerMessage>(OnEntRemoved);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<SolutionRegenerationComponent> 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<SolutionRegenerationComponent> 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<SolutionRegenerationComponent, SolutionManagerComponent>();
|
||||
while (query.MoveNext(out var uid, out var regen, out var manager))
|
||||
var query = EntityQueryEnumerator<SolutionRegenerationComponent, SolutionComponent>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+16
-1
@@ -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;
|
||||
/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
|
||||
public sealed class AddReagentToSolutionEntityEffectSystem : EntityEffectSystem<SolutionManagerComponent, AddReagentToSolution>
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<SolutionComponent, EntityEffectEvent<AddReagentToSolution>>(Effect);
|
||||
}
|
||||
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
|
||||
|
||||
protected override void Effect(Entity<SolutionManagerComponent> entity, ref EntityEffectEvent<AddReagentToSolution> args)
|
||||
@@ -25,6 +32,14 @@ public sealed class AddReagentToSolutionEntityEffectSystem : EntityEffectSystem<
|
||||
|
||||
_solutionContainer.TryAddReagent(solutionContainer.Value, reagent, args.Scale * args.Effect.StrengthModifier);
|
||||
}
|
||||
|
||||
private void Effect(Entity<SolutionComponent> entity, ref EntityEffectEvent<AddReagentToSolution> args)
|
||||
{
|
||||
if (entity.Comp.Id != args.Effect.Solution)
|
||||
return;
|
||||
|
||||
_solutionContainer.TryAddReagent(entity, args.Effect.Reagent, args.Scale * args.Effect.StrengthModifier);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="EntityEffect"/>
|
||||
|
||||
@@ -30,7 +30,7 @@ public sealed partial class AbsorbentComponent : Component
|
||||
/// How much solution we can transfer in one interaction.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public FixedPoint2 PickupAmount = FixedPoint2.New(100);
|
||||
public FixedPoint2 PickupAmount = FixedPoint2.New(120);
|
||||
|
||||
/// <summary>
|
||||
/// The effect spawned when the puddle fully evaporates.
|
||||
|
||||
@@ -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<WelderComponent, SolutionManagerComponent>();
|
||||
// TODO: ActiveWelderComponent
|
||||
var query = EntityQueryEnumerator<WelderComponent>();
|
||||
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);
|
||||
|
||||
@@ -2523,7 +2523,6 @@
|
||||
solutions:
|
||||
- SolutionVenomSpider
|
||||
- type: SolutionRegeneration
|
||||
solution: melee
|
||||
generated:
|
||||
reagents:
|
||||
- ReagentId: Mechanotoxin
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -63,7 +63,6 @@
|
||||
- ReagentId: SpaceLube
|
||||
Quantity: 960
|
||||
- type: SolutionRegeneration
|
||||
solution: beaker
|
||||
generated:
|
||||
reagents:
|
||||
- ReagentId: SpaceLube
|
||||
|
||||
@@ -107,7 +107,6 @@
|
||||
heldOnly: true
|
||||
exactVolume: true
|
||||
- type: SolutionRegeneration
|
||||
solution: welder
|
||||
generated:
|
||||
reagents:
|
||||
- ReagentId: WeldingFuel
|
||||
|
||||
@@ -165,7 +165,6 @@
|
||||
description: It extinguishes fires. it slowly refills with water.
|
||||
components:
|
||||
- type: SolutionRegeneration
|
||||
solution: spray
|
||||
generated:
|
||||
reagents:
|
||||
- ReagentId: Water
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -149,7 +149,6 @@
|
||||
- type: SolutionTransfer
|
||||
canSend: false # No giving away infinite space cleaner!
|
||||
- type: SolutionRegeneration
|
||||
solution: drink
|
||||
generated:
|
||||
reagents:
|
||||
- ReagentId: SpaceCleaner
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
- ReagentId: Nocturine
|
||||
Quantity: 12
|
||||
- type: SolutionRegeneration
|
||||
solution: hypospray
|
||||
generated:
|
||||
reagents:
|
||||
- ReagentId: Nocturine
|
||||
|
||||
@@ -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"]]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
solutions:
|
||||
- SolutionDrainNormal
|
||||
- type: SolutionRegeneration
|
||||
solution: tank
|
||||
generated:
|
||||
reagents:
|
||||
- ReagentId: Water
|
||||
|
||||
@@ -88,7 +88,6 @@
|
||||
solutions:
|
||||
- SolutionDrainNormal
|
||||
- type: SolutionRegeneration
|
||||
solution: tank
|
||||
generated:
|
||||
reagents:
|
||||
- ReagentId: Water
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
- ReagentId: Water
|
||||
Quantity: 7680
|
||||
- type: SolutionRegeneration
|
||||
solution: pool
|
||||
generated:
|
||||
reagents:
|
||||
- ReagentId: Water
|
||||
|
||||
Reference in New Issue
Block a user