// Maybe someday we'll have clientside prediction for entity spawning, but not today.
// Until then, these methods do nothing on the client.
/// <inheritdoc/>
- public override bool TrySplashSpillAt(EntityUid uid, EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true, EntityUid? user = null)
+ public override bool TrySplashSpillAt(Entity<SpillableComponent?> entity, EntityCoordinates coordinates, out EntityUid puddleUid, out Solution solution, bool sound = true, EntityUid? user = null)
+ {
+ puddleUid = EntityUid.Invalid;
+ solution = new Solution();
+ return false;
+ }
+
+ public override bool TrySplashSpillAt(EntityUid entity,
+ EntityCoordinates coordinates,
+ Solution spilled,
+ out EntityUid puddleUid,
+ bool sound = true,
+ EntityUid? user = null)
{
puddleUid = EntityUid.Invalid;
return false;
-using Content.Shared.Chemistry.EntitySystems;
using Content.Server.Fluids.EntitySystems;
-using Content.Shared.Fluids.Components;
+using Content.Shared.Chemistry.EntitySystems;
using JetBrains.Annotations;
-namespace Content.Server.Destructible.Thresholds.Behaviors
-{
- [UsedImplicitly]
- [DataDefinition]
- public sealed partial class SpillBehavior : IThresholdBehavior
- {
- [DataField]
- public string? Solution;
+namespace Content.Server.Destructible.Thresholds.Behaviors;
- /// <summary>
- /// If there is a SpillableComponent on EntityUidowner use it to create a puddle/smear.
- /// Or whatever solution is specified in the behavior itself.
- /// If none are available do nothing.
- /// </summary>
- /// <param name="owner">Entity on which behavior is executed</param>
- /// <param name="system">system calling the behavior</param>
- /// <param name="cause"></param>
- public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null)
- {
- var solutionContainerSystem = system.EntityManager.System<SharedSolutionContainerSystem>();
- var spillableSystem = system.EntityManager.System<PuddleSystem>();
+[UsedImplicitly]
+[DataDefinition]
+public sealed partial class SpillBehavior : IThresholdBehavior
+{
+ /// <summary>
+ /// Optional fallback solution name if SpillableComponent is not present.
+ /// </summary>
+ [DataField]
+ public string? Solution;
- var coordinates = system.EntityManager.GetComponent<TransformComponent>(owner).Coordinates;
+ /// <summary>
+ /// When triggered, spills the entity's solution onto the ground.
+ /// Will first try to use the solution from a SpillableComponent if present,
+ /// otherwise falls back to the solution specified in the behavior's data fields.
+ /// The solution is properly drained/split before spilling to prevent double-spilling with other behaviors.
+ /// </summary>
+ /// <param name="owner">Entity whose solution will be spilled</param>
+ /// <param name="system">System calling this behavior</param>
+ /// <param name="cause">Optional entity that caused this behavior to trigger</param>
+ public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null)
+ {
+ var puddleSystem = system.EntityManager.System<PuddleSystem>();
+ var solutionContainer = system.EntityManager.System<SharedSolutionContainerSystem>();
+ var coordinates = system.EntityManager.GetComponent<TransformComponent>(owner).Coordinates;
- if (system.EntityManager.TryGetComponent(owner, out SpillableComponent? spillableComponent) &&
- solutionContainerSystem.TryGetSolution(owner, spillableComponent.SolutionName, out _, out var compSolution))
- {
- spillableSystem.TrySplashSpillAt(owner, coordinates, compSolution, out _, false, user: cause);
- }
- else if (Solution != null &&
- solutionContainerSystem.TryGetSolution(owner, Solution, out _, out var behaviorSolution))
- {
- spillableSystem.TrySplashSpillAt(owner, coordinates, behaviorSolution, out _, user: cause);
- }
- }
+ // Spill the solution that was drained/split
+ if (solutionContainer.TryGetSolution(owner, Solution, out _, out var solution))
+ puddleSystem.TrySplashSpillAt(owner, coordinates, solution, out _, false, cause);
+ else
+ puddleSystem.TrySplashSpillAt(owner, coordinates, out _, out _, false, cause);
}
}
private void SpillOnLand(Entity<SpillableComponent> entity, ref LandEvent args)
{
- if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln, out var solution))
+ if (!entity.Comp.SpillWhenThrown || Openable.IsClosed(entity.Owner))
return;
- if (Openable.IsClosed(entity.Owner))
- return;
-
- if (!entity.Comp.SpillWhenThrown)
- return;
-
- if (args.User != null)
+ if (TrySplashSpillAt(entity.Owner, Transform(entity).Coordinates, out _, out var solution) && args.User != null)
{
AdminLogger.Add(LogType.Landed,
$"{ToPrettyString(entity.Owner):entity} spilled a solution {SharedSolutionContainerSystem.ToPrettyString(solution):solution} on landing");
}
-
- var drainedSolution = _solutionContainerSystem.Drain(entity.Owner, soln.Value, solution.Volume);
- TrySplashSpillAt(entity.Owner, Transform(entity).Coordinates, drainedSolution, out _);
}
private void OnDoAfter(Entity<SpillableComponent> entity, ref SpillDoAfterEvent args)
// TODO: This can be predicted once https://github.com/space-wizards/RobustToolbox/pull/5849 is merged
/// <inheritdoc/>
- public override bool TrySplashSpillAt(EntityUid uid,
+ public override bool TrySplashSpillAt(Entity<SpillableComponent?> entity,
EntityCoordinates coordinates,
- Solution solution,
out EntityUid puddleUid,
+ out Solution spilled,
bool sound = true,
EntityUid? user = null)
{
puddleUid = EntityUid.Invalid;
+ spilled = new Solution();
- if (solution.Volume == 0)
+ if (!Resolve(entity, ref entity.Comp))
+ return false;
+
+ if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var solution))
+ return false;
+
+ spilled = solution.Value.Comp.Solution;
+
+ return TrySplashSpillAt(entity, coordinates, spilled, out puddleUid, sound, user);
+ }
+
+ public override bool TrySplashSpillAt(EntityUid entity,
+ EntityCoordinates coordinates,
+ Solution spilled,
+ out EntityUid puddleUid,
+ bool sound = true,
+ EntityUid? user = null)
+ {
+ puddleUid = EntityUid.Invalid;
+
+ if (spilled.Volume == 0)
return false;
var targets = new List<EntityUid>();
var owner = ent.Owner;
// between 5 and 30%
- var splitAmount = solution.Volume * _random.NextFloat(0.05f, 0.30f);
- var splitSolution = solution.SplitSolution(splitAmount);
+ var splitAmount = spilled.Volume * _random.NextFloat(0.05f, 0.30f);
+ var splitSolution = spilled.SplitSolution(splitAmount);
if (user != null)
{
AdminLogger.Add(LogType.Landed,
- $"{ToPrettyString(user.Value):user} threw {ToPrettyString(uid):entity} which splashed a solution {SharedSolutionContainerSystem.ToPrettyString(solution):solution} onto {ToPrettyString(owner):target}");
+ $"{ToPrettyString(user.Value):user} threw {ToPrettyString(entity):entity} which splashed a solution {SharedSolutionContainerSystem.ToPrettyString(spilled):solution} onto {ToPrettyString(owner):target}");
}
targets.Add(owner);
Reactive.DoEntityReaction(owner, splitSolution, ReactionMethod.Touch);
- Popups.PopupEntity(
- Loc.GetString("spill-land-spilled-on-other", ("spillable", uid),
- ("target", Identity.Entity(owner, EntityManager))), owner, PopupType.SmallCaution);
+ Popups.PopupEntity(Loc.GetString("spill-land-spilled-on-other",
+ ("spillable", entity),
+ ("target", Identity.Entity(owner, EntityManager))),
+ owner,
+ PopupType.SmallCaution);
}
- _color.RaiseEffect(solution.GetColor(_prototypeManager), targets,
- Filter.Pvs(uid, entityManager: EntityManager));
+ _color.RaiseEffect(spilled.GetColor(_prototypeManager), targets,
+ Filter.Pvs(entity, entityManager: EntityManager));
- return TrySpillAt(coordinates, solution, out puddleUid, sound);
+ return TrySpillAt(coordinates, spilled, out puddleUid, sound);
}
/// <inheritdoc/>
}
#region Spill
+
+ /// <inheritdoc cref="TrySplashSpillAt(EntityUid,EntityCoordinates,Solution,out EntityUid,bool,EntityUid?)"/>
+ public abstract bool TrySplashSpillAt(Entity<SpillableComponent?> entity,
+ EntityCoordinates coordinates,
+ out EntityUid puddleUid,
+ out Solution spilled,
+ bool sound = true,
+ EntityUid? user = null);
+
// These methods are in Shared to make it easier to interact with PuddleSystem in Shared code.
// Note that they always fail when run on the client, not creating a puddle and returning false.
// Adding proper prediction to this system would require spawning temporary puddle entities on the
/// <remarks>
/// On the client, this will always set <paramref name="puddleUid"/> to <see cref="EntityUid.Invalid"/> and return false.
/// </remarks>
- public abstract bool TrySplashSpillAt(EntityUid uid,
+ public abstract bool TrySplashSpillAt(EntityUid entity,
EntityCoordinates coordinates,
- Solution solution,
+ Solution spilled,
out EntityUid puddleUid,
bool sound = true,
EntityUid? user = null);
var solution = _solutionContainer.SplitSolution(soln.Value, interactions.Volume);
// Spray the solution onto the ground and anyone nearby
- if (TryComp(entity, out TransformComponent? transform))
- _puddle.TrySplashSpillAt(entity, transform.Coordinates, solution, out _, sound: false);
+ var coordinates = Transform(entity).Coordinates;
+ _puddle.TrySplashSpillAt(entity.Owner, coordinates, out _, out _, sound: false);
var drinkName = Identity.Entity(entity, EntityManager);