using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.EntitySystems;
-using Content.Server.Fluids.Components;
using Content.Server.Gravity;
using Content.Server.Popups;
using Content.Shared.CCVar;
using Robust.Shared.Physics.Components;
using Robust.Shared.Prototypes;
using System.Numerics;
+using Content.Shared.Fluids.EntitySystems;
+using Content.Shared.Fluids.Components;
+using Robust.Server.Containers;
using Robust.Shared.Map;
namespace Content.Server.Fluids.EntitySystems;
-public sealed class SpraySystem : EntitySystem
+public sealed class SpraySystem : SharedSpraySystem
{
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly GravitySystem _gravity = 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!;
private float _gridImpulseMultiplier;
var targetMapPos = _transform.GetMapCoordinates(GetEntityQuery<TransformComponent>().GetComponent(args.Target));
- Spray(entity, args.User, targetMapPos);
+ Spray(entity, targetMapPos, args.User);
}
private void UpdateGridMassMultiplier(float value)
var clickPos = _transform.ToMapCoordinates(args.ClickLocation);
- Spray(entity, args.User, clickPos);
+ Spray(entity, clickPos, args.User);
}
- public void Spray(Entity<SprayComponent> entity, EntityUid user, MapCoordinates mapcoord)
+ public override void Spray(Entity<SprayComponent> entity, EntityUid? user = null)
+ {
+ var xform = Transform(entity);
+ var throwing = xform.LocalRotation.ToWorldVec() * entity.Comp.SprayDistance;
+ var direction = xform.Coordinates.Offset(throwing);
+
+ Spray(entity, _transform.ToMapCoordinates(direction), user);
+ }
+
+ public override void Spray(Entity<SprayComponent> entity, MapCoordinates mapcoord, EntityUid? user = null)
{
if (!_solutionContainer.TryGetSolution(entity.Owner, SprayComponent.SolutionName, out var soln, out var solution))
return;
var ev = new SprayAttemptEvent(user);
RaiseLocalEvent(entity, ref ev);
if (ev.Cancelled)
+ {
+ if (ev.CancelPopupMessage != null && user != null)
+ _popupSystem.PopupEntity(Loc.GetString(ev.CancelPopupMessage), entity.Owner, user.Value);
return;
+ }
- if (TryComp<UseDelayComponent>(entity, out var useDelay)
- && _useDelay.IsDelayed((entity, useDelay)))
+ if (_useDelay.IsDelayed((entity, null)))
return;
if (solution.Volume <= 0)
{
- _popupSystem.PopupEntity(Loc.GetString("spray-component-is-empty-message"), entity.Owner, user);
+ if (user != null)
+ _popupSystem.PopupEntity(Loc.GetString(entity.Comp.SprayEmptyPopupMessage, ("entity", entity)), entity.Owner, user.Value);
return;
}
var xformQuery = GetEntityQuery<TransformComponent>();
- var userXform = xformQuery.GetComponent(user);
+ var sprayerXform = xformQuery.GetComponent(entity);
- var userMapPos = _transform.GetMapCoordinates(userXform);
+ var sprayerMapPos = _transform.GetMapCoordinates(sprayerXform);
var clickMapPos = mapcoord;
- var diffPos = clickMapPos.Position - userMapPos.Position;
+ var diffPos = clickMapPos.Position - sprayerMapPos.Position;
if (diffPos == Vector2.Zero || diffPos == Vector2Helpers.NaN)
return;
Angle.FromDegrees(spread * (amount - 1) / 2));
// Calculate the destination for the vapor cloud. Limit to the maximum spray distance.
- var target = userMapPos
+ var target = sprayerMapPos
.Offset((diffNorm + rotation.ToVec()).Normalized() * diffLength + quarter);
- var distance = (target.Position - userMapPos.Position).Length();
+ var distance = (target.Position - sprayerMapPos.Position).Length();
if (distance > entity.Comp.SprayDistance)
- target = userMapPos.Offset(diffNorm * entity.Comp.SprayDistance);
+ target = sprayerMapPos.Offset(diffNorm * entity.Comp.SprayDistance);
var adjustedSolutionAmount = entity.Comp.TransferAmount / entity.Comp.VaporAmount;
var newSolution = _solutionContainer.SplitSolution(soln.Value, adjustedSolutionAmount);
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 = userMapPos.Offset(distance < 1 ? quarter : threeQuarters);
+ var vaporPos = sprayerMapPos.Offset(distance < 1 ? quarter : threeQuarters);
var vapor = Spawn(entity.Comp.SprayedPrototype, vaporPos);
var vaporXform = xformQuery.GetComponent(vapor);
_vapor.Start(ent, vaporXform, impulseDirection * diffLength, entity.Comp.SprayVelocity, target, time, user);
- if (TryComp<PhysicsComponent>(user, out var body))
+ var thingGettingPushed = entity.Owner;
+ if (_container.TryGetOuterContainer(entity, sprayerXform, out var container))
+ thingGettingPushed = container.Owner;
+
+ if (TryComp<PhysicsComponent>(thingGettingPushed, out var body))
{
- if (_gravity.IsWeightless(user))
+ if (_gravity.IsWeightless(thingGettingPushed))
{
// push back the player
- _physics.ApplyLinearImpulse(user, -impulseDirection * entity.Comp.PushbackAmount, body: body);
+ _physics.ApplyLinearImpulse(thingGettingPushed, -impulseDirection * entity.Comp.PushbackAmount, body: body);
}
else
{
// push back the grid the player is standing on
- var userTransform = Transform(user);
+ var userTransform = Transform(thingGettingPushed);
if (userTransform.GridUid == userTransform.ParentUid)
{
// apply both linear and angular momentum depending on the player position
_audio.PlayPvs(entity.Comp.SpraySound, entity, entity.Comp.SpraySound.Params.WithVariation(0.125f));
- if (useDelay != null)
- _useDelay.TryResetDelay((entity, useDelay));
+ _useDelay.TryResetDelay(entity);
}
}
--- /dev/null
+using Content.Shared.Actions;
+using Content.Shared.Fluids.Components;
+using Content.Shared.Verbs;
+using Robust.Shared.Map;
+
+namespace Content.Shared.Fluids.EntitySystems;
+
+public abstract class SharedSpraySystem : EntitySystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<EquipSprayComponent, GetVerbsEvent<EquipmentVerb>>(OnGetVerb);
+ SubscribeLocalEvent<SprayLiquidEvent>(SprayLiquid);
+ }
+
+ private void SprayLiquid(SprayLiquidEvent ev)
+ {
+ var equipSprayEnt = ev.Action.Comp.Container;
+
+ if (equipSprayEnt == null)
+ {
+ Log.Warning($"{ev.Action.Comp.AttachedEntity} tried to use the SprayLiquidEvent but the entity was null.");
+ return;
+ }
+
+ if (!TryComp<SprayComponent>(equipSprayEnt, out var sprayComponent))
+ {
+ Log.Warning($"{ev.Action.Comp.AttachedEntity} tried to use the SprayLiquidEvent on {equipSprayEnt} but the SprayComponent did not exist.");
+ return;
+ }
+
+ Spray((equipSprayEnt.Value, sprayComponent), ev.Performer);
+ }
+
+ private void OnGetVerb(Entity<EquipSprayComponent> entity, ref GetVerbsEvent<EquipmentVerb> args)
+ {
+ if (entity.Comp.VerbLocId == null || !args.CanAccess || !args.CanInteract)
+ return;
+
+ var sprayComponent = Comp<SprayComponent>(entity);
+ var user = args.User;
+
+ var verb = new EquipmentVerb
+ {
+ Act = () =>
+ {
+ Spray((entity, sprayComponent), user);
+ },
+ Text = Loc.GetString(entity.Comp.VerbLocId),
+ };
+ args.Verbs.Add(verb);
+ }
+
+ /// <summary>
+ /// Spray starting from the entity, to the given coordinates. If the user is supplied, will give them failure
+ /// popups and will also push them in space.
+ /// </summary>
+ /// <param name="entity">Entity that is spraying.</param>
+ /// <param name="mapcoord">The coordinates being aimed at.</param>
+ /// <param name="user">The user that is using the spraying device.</param>
+ public virtual void Spray(Entity<SprayComponent> entity, MapCoordinates mapcoord, EntityUid? user = null)
+ {
+ // do nothing!
+ }
+
+ /// <summary>
+ /// Spray starting from the entity and facing the direction its pointing.
+ /// </summary>
+ /// <param name="entity">Entity that is spraying.</param>
+ /// <param name="user">User that is using the spraying device.</param>
+ public virtual void Spray(Entity<SprayComponent> entity, EntityUid? user = null)
+ {
+ // do nothing!
+ }
+}
+
+public sealed partial class SprayLiquidEvent : InstantActionEvent;
+