using Robust.Client.Player;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
namespace Content.Client.Alerts;
{
public AlertOrderPrototype? AlertOrder { get; set; }
+ [Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
protected override void AfterShowAlert(Entity<AlertsComponent> alerts)
{
- if (_playerManager.LocalEntity != alerts.Owner)
- return;
-
- SyncAlerts?.Invoke(this, alerts.Comp.Alerts);
+ UpdateHud(alerts);
}
- protected override void AfterClearAlert(Entity<AlertsComponent> alertsComponent)
+ protected override void AfterClearAlert(Entity<AlertsComponent> alerts)
{
- if (_playerManager.LocalEntity != alertsComponent.Owner)
- return;
+ UpdateHud(alerts);
+ }
- SyncAlerts?.Invoke(this, alertsComponent.Comp.Alerts);
+ private void ClientAlertsHandleState(Entity<AlertsComponent> alerts, ref AfterAutoHandleStateEvent args)
+ {
+ UpdateHud(alerts);
}
- private void ClientAlertsHandleState(EntityUid uid, AlertsComponent component, ref AfterAutoHandleStateEvent args)
+ private void UpdateHud(Entity<AlertsComponent> entity)
{
- if (_playerManager.LocalEntity == uid)
- SyncAlerts?.Invoke(this, component.Alerts);
+ if (_playerManager.LocalEntity == entity.Owner)
+ SyncAlerts?.Invoke(this, entity.Comp.Alerts);
}
private void OnPlayerAttached(EntityUid uid, AlertsComponent component, LocalPlayerAttachedEvent args)
using Content.Shared.Movement.Components;
+using Content.Shared.Movement.Pulling.Components;
using Content.Shared.Movement.Systems;
-using Content.Shared.Pulling.Components;
+using Robust.Client.GameObjects;
using Robust.Client.Physics;
using Robust.Client.Player;
using Robust.Shared.Physics.Components;
SubscribeLocalEvent<InputMoverComponent, UpdateIsPredictedEvent>(OnUpdatePredicted);
SubscribeLocalEvent<MovementRelayTargetComponent, UpdateIsPredictedEvent>(OnUpdateRelayTargetPredicted);
- SubscribeLocalEvent<SharedPullableComponent, UpdateIsPredictedEvent>(OnUpdatePullablePredicted);
+ SubscribeLocalEvent<PullableComponent, UpdateIsPredictedEvent>(OnUpdatePullablePredicted);
}
private void OnUpdatePredicted(EntityUid uid, InputMoverComponent component, ref UpdateIsPredictedEvent args)
args.IsPredicted = true;
}
- private void OnUpdatePullablePredicted(EntityUid uid, SharedPullableComponent component, ref UpdateIsPredictedEvent args)
+ private void OnUpdatePullablePredicted(EntityUid uid, PullableComponent component, ref UpdateIsPredictedEvent args)
{
// Enable prediction if an entity is being pulled by the player.
// Disable prediction if an entity is being pulled by some non-player entity.
+++ /dev/null
-using Content.Shared.Pulling;
-using Content.Shared.Pulling.Components;
-using JetBrains.Annotations;
-using Robust.Client.Physics;
-
-namespace Content.Client.Pulling
-{
- [UsedImplicitly]
- public sealed class PullingSystem : SharedPullingSystem
- {
- public override void Initialize()
- {
- base.Initialize();
-
- UpdatesAfter.Add(typeof(PhysicsSystem));
-
- SubscribeLocalEvent<SharedPullableComponent, PullableMoveMessage>(OnPullableMove);
- SubscribeLocalEvent<SharedPullableComponent, PullableStopMovingMessage>(OnPullableStopMove);
- }
- }
-}
using Content.Shared.Inventory.Events;
using Content.Shared.Item;
using Content.Shared.Movement.Events;
-using Content.Shared.Physics.Pull;
+using Content.Shared.Movement.Pulling.Events;
using Content.Shared.Throwing;
namespace Content.Client.Replay.Spectator;
private void OnAutoHandleState(EntityUid uid, ThrownItemComponent component, ref AfterAutoHandleStateEvent args)
{
- if (!TryComp<SpriteComponent>(uid, out var sprite))
+ if (!TryComp<SpriteComponent>(uid, out var sprite) || !component.Animate)
return;
var animationPlayer = EnsureComp<AnimationPlayerComponent>(uid);
using Content.Shared.Hands.Components;
+using Content.Shared.Movement.Pulling.Components;
using Content.Shared.Prototypes;
-using Content.Shared.Pulling.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;
{
foreach (var proto in protoManager.EnumeratePrototypes<EntityPrototype>())
{
- if (!proto.TryGetComponent(out SharedPullerComponent? puller))
+ if (!proto.TryGetComponent(out PullerComponent? puller))
continue;
if (!puller.NeedsHands)
using Content.Shared.ActionBlocker;
using Content.Shared.Alert;
-using Content.Shared.Pulling.Components;
-using Content.Shared.Pulling;
+using Content.Shared.Movement.Pulling.Components;
+using Content.Shared.Movement.Pulling.Systems;
using JetBrains.Annotations;
namespace Content.Server.Alert.Click
if (!entityManager.System<ActionBlockerSystem>().CanInteract(player, null))
return;
- if (entityManager.TryGetComponent(player, out SharedPullableComponent? playerPullable))
+ if (entityManager.TryGetComponent(player, out PullableComponent? playerPullable))
{
- entityManager.System<SharedPullingSystem>().TryStopPull(playerPullable);
+ entityManager.System<PullingSystem>().TryStopPull(player, playerPullable, user: player);
}
}
}
using Content.Shared.Alert;
-using Content.Shared.Pulling;
-using Content.Shared.Pulling.Components;
+using Content.Shared.Movement.Pulling.Components;
+using Content.Shared.Movement.Pulling.Systems;
using JetBrains.Annotations;
namespace Content.Server.Alert.Click
public void AlertClicked(EntityUid player)
{
var entManager = IoCManager.Resolve<IEntityManager>();
+ var ps = entManager.System<PullingSystem>();
- var ps = entManager.System<SharedPullingSystem>();
- var playerTarget = ps.GetPulled(player);
- if (playerTarget != default && entManager.TryGetComponent(playerTarget, out SharedPullableComponent? playerPullable))
+ if (entManager.TryGetComponent(player, out PullerComponent? puller) &&
+ entManager.TryGetComponent(puller.Pulling, out PullableComponent? pullableComp))
{
- ps.TryStopPull(playerPullable);
+ ps.TryStopPull(puller.Pulling.Value, pullableComp, user: player);
}
}
}
using Content.Shared.Maps;
using Content.Shared.Mobs;
using Content.Shared.Popups;
-using Content.Shared.Pulling.Components;
using Content.Shared.Speech.EntitySystems;
using Content.Shared.StatusEffect;
using Content.Shared.Stunnable;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
+using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent;
+using PullerComponent = Content.Shared.Movement.Pulling.Components.PullerComponent;
namespace Content.Server.Electrocution;
all.Add((entity, depth));
visited.Add(entity);
- if (TryComp<SharedPullableComponent>(entity, out var pullable) &&
+ if (TryComp<PullableComponent>(entity, out var pullable) &&
pullable.Puller is { Valid: true } pullerId &&
!visited.Contains(pullerId))
{
GetChainedElectrocutionTargetsRecurse(pullerId, depth + 1, visited, all);
}
- if (TryComp<SharedPullerComponent>(entity, out var puller) &&
+ if (TryComp<PullerComponent>(entity, out var puller) &&
puller.Pulling is { Valid: true } pullingId &&
!visited.Contains(pullingId))
{
using System.Numerics;
using Content.Server.Inventory;
-using Content.Server.Pulling;
using Content.Server.Stack;
using Content.Server.Stunnable;
using Content.Shared.ActionBlocker;
using Content.Shared.IdentityManagement;
using Content.Shared.Input;
using Content.Shared.Inventory.VirtualItem;
-using Content.Shared.Physics.Pull;
-using Content.Shared.Popups;
-using Content.Shared.Pulling.Components;
+using Content.Shared.Movement.Pulling.Components;
+using Content.Shared.Movement.Pulling.Events;
+using Content.Shared.Movement.Pulling.Systems;
using Content.Shared.Stacks;
using Content.Shared.Throwing;
using Robust.Shared.Audio;
return;
// Break any pulls
- if (TryComp(uid, out SharedPullerComponent? puller) && puller.Pulling is EntityUid pulled &&
- TryComp(pulled, out SharedPullableComponent? pullable))
- _pullingSystem.TryStopPull(pullable);
+ if (TryComp(uid, out PullerComponent? puller) && TryComp(puller.Pulling, out PullableComponent? pullable))
+ _pullingSystem.TryStopPull(puller.Pulling.Value, pullable);
if (!_handsSystem.TryDrop(uid, component.ActiveHand!, null, checkActionBlocker: false))
return;
private void HandlePullStarted(EntityUid uid, HandsComponent component, PullStartedMessage args)
{
- if (args.Puller.Owner != uid)
+ if (args.PullerUid != uid)
return;
- if (TryComp<SharedPullerComponent>(args.Puller.Owner, out var pullerComp) && !pullerComp.NeedsHands)
+ if (TryComp<PullerComponent>(args.PullerUid, out var pullerComp) && !pullerComp.NeedsHands)
return;
- if (!_virtualItemSystem.TrySpawnVirtualItemInHand(args.Pulled.Owner, uid))
+ if (!_virtualItemSystem.TrySpawnVirtualItemInHand(args.PulledUid, uid))
{
DebugTools.Assert("Unable to find available hand when starting pulling??");
}
private void HandlePullStopped(EntityUid uid, HandsComponent component, PullStoppedMessage args)
{
- if (args.Puller.Owner != uid)
+ if (args.PullerUid != uid)
return;
// Try find hand that is doing this pull.
{
if (hand.HeldEntity == null
|| !TryComp(hand.HeldEntity, out VirtualItemComponent? virtualItem)
- || virtualItem.BlockingEntity != args.Pulled.Owner)
+ || virtualItem.BlockingEntity != args.PulledUid)
+ {
continue;
+ }
QueueDel(hand.HeldEntity.Value);
break;
using Content.Shared.Pulling;
+using PullingSystem = Content.Shared.Movement.Pulling.Systems.PullingSystem;
namespace Content.Server.NPC.HTN.Preconditions;
/// </summary>
public sealed partial class PulledPrecondition : HTNPrecondition
{
- private SharedPullingSystem _pulling = default!;
+ private PullingSystem _pulling = default!;
[ViewVariables(VVAccess.ReadWrite)] [DataField("isPulled")] public bool IsPulled = true;
public override void Initialize(IEntitySystemManager sysManager)
{
base.Initialize(sysManager);
- _pulling = sysManager.GetEntitySystem<SharedPullingSystem>();
+ _pulling = sysManager.GetEntitySystem<PullingSystem>();
}
public override bool IsMet(NPCBlackboard blackboard)
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
-using Content.Shared.Pulling.Components;
-using Content.Shared.Objectives;
using Content.Shared.Mind.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Mobs.Components;
+using Content.Shared.Movement.Pulling.Components;
namespace Content.Server.Objectives.Systems;
var count = 0;
//check pulling object
- if (TryComp<SharedPullerComponent>(mind.OwnedEntity, out var pull)) //TO DO: to make the code prettier? don't like the repetition
+ if (TryComp<PullerComponent>(mind.OwnedEntity, out var pull)) //TO DO: to make the code prettier? don't like the repetition
{
- var pullid = pull.Pulling;
- if (pullid != null)
+ var pulledEntity = pull.Pulling;
+ if (pulledEntity != null)
{
// check if this is the item
- if (CheckStealTarget(pullid.Value, condition)) count++;
+ if (CheckStealTarget(pulledEntity.Value, condition)) count++;
//we don't check the inventories of sentient entity
- if (!TryComp<MindContainerComponent>(pullid, out var pullMind))
+ if (!HasComp<MindContainerComponent>(pulledEntity))
{
// if it is a container check its contents
- if (_containerQuery.TryGetComponent(pullid, out var containerManager))
+ if (_containerQuery.TryGetComponent(pulledEntity, out var containerManager))
stack.Push(containerManager);
}
}
+++ /dev/null
-using System.Numerics;
-using Content.Shared.ActionBlocker;
-using Content.Shared.Gravity;
-using Content.Shared.Pulling;
-using Content.Shared.Pulling.Components;
-using Content.Shared.Rotatable;
-using Robust.Shared.Physics;
-using Robust.Shared.Physics.Components;
-using Robust.Shared.Physics.Controllers;
-
-namespace Content.Server.Physics.Controllers
-{
- public sealed class PullController : VirtualController
- {
- // Parameterization for pulling:
- // Speeds. Note that the speed is mass-independent (multiplied by mass).
- // Instead, tuning to mass is done via the mass values below.
- // Note that setting the speed too high results in overshoots (stabilized by drag, but bad)
- private const float AccelModifierHigh = 15f;
- private const float AccelModifierLow = 60.0f;
- // High/low-mass marks. Curve is constant-lerp-constant, i.e. if you can even pull an item,
- // you'll always get at least AccelModifierLow and no more than AccelModifierHigh.
- private const float AccelModifierHighMass = 70.0f; // roundstart saltern emergency closet
- private const float AccelModifierLowMass = 5.0f; // roundstart saltern emergency crowbar
- // Used to control settling (turns off pulling).
- private const float MaximumSettleVelocity = 0.1f;
- private const float MaximumSettleDistance = 0.1f;
- // Settle shutdown control.
- // Mustn't be too massive, as that causes severe mispredicts *and can prevent it ever resolving*.
- // Exists to bleed off "I pulled my crowbar" overshoots.
- // Minimum velocity for shutdown to be necessary. This prevents stuff getting stuck b/c too much shutdown.
- private const float SettleMinimumShutdownVelocity = 0.25f;
- // Distance in which settle shutdown multiplier is at 0. It then scales upwards linearly with closer distances.
- private const float SettleShutdownDistance = 1.0f;
- // Velocity change of -LinearVelocity * frameTime * this
- private const float SettleShutdownMultiplier = 20.0f;
-
- // How much you must move for the puller movement check to actually hit.
- private const float MinimumMovementDistance = 0.005f;
-
- [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
- [Dependency] private readonly SharedPullingSystem _pullableSystem = default!;
- [Dependency] private readonly SharedGravitySystem _gravity = default!;
- [Dependency] private readonly SharedTransformSystem _transform = default!;
-
- // TODO: Move this stuff to pullingsystem
- /// <summary>
- /// If distance between puller and pulled entity lower that this threshold,
- /// pulled entity will not change its rotation.
- /// Helps with small distance jittering
- /// </summary>
- private const float ThresholdRotDistance = 1;
-
- /// <summary>
- /// If difference between puller and pulled angle lower that this threshold,
- /// pulled entity will not change its rotation.
- /// Helps with diagonal movement jittering
- /// As of further adjustments, should divide cleanly into 90 degrees
- /// </summary>
- private const float ThresholdRotAngle = 22.5f;
-
- public override void Initialize()
- {
- UpdatesAfter.Add(typeof(MoverController));
- SubscribeLocalEvent<SharedPullerComponent, MoveEvent>(OnPullerMove);
-
- base.Initialize();
- }
-
- private void OnPullerMove(EntityUid uid, SharedPullerComponent component, ref MoveEvent args)
- {
- if (component.Pulling is not { } pullable || !TryComp<SharedPullableComponent>(pullable, out var pullableComponent))
- return;
-
- UpdatePulledRotation(uid, pullable);
-
- if (args.NewPosition.EntityId == args.OldPosition.EntityId &&
- (args.NewPosition.Position - args.OldPosition.Position).LengthSquared() < MinimumMovementDistance * MinimumMovementDistance)
- return;
-
- if (TryComp<PhysicsComponent>(pullable, out var physics))
- PhysicsSystem.WakeBody(pullable, body: physics);
-
- _pullableSystem.StopMoveTo(pullableComponent);
- }
-
- private void UpdatePulledRotation(EntityUid puller, EntityUid pulled)
- {
- // TODO: update once ComponentReference works with directed event bus.
- if (!TryComp(pulled, out RotatableComponent? rotatable))
- return;
-
- if (!rotatable.RotateWhilePulling)
- return;
-
- var xforms = GetEntityQuery<TransformComponent>();
- var pulledXform = xforms.GetComponent(pulled);
- var pullerXform = xforms.GetComponent(puller);
-
- var pullerData = TransformSystem.GetWorldPositionRotation(pullerXform, xforms);
- var pulledData = TransformSystem.GetWorldPositionRotation(pulledXform, xforms);
-
- var dir = pullerData.WorldPosition - pulledData.WorldPosition;
- if (dir.LengthSquared() > ThresholdRotDistance * ThresholdRotDistance)
- {
- var oldAngle = pulledData.WorldRotation;
- var newAngle = Angle.FromWorldVec(dir);
-
- var diff = newAngle - oldAngle;
- if (Math.Abs(diff.Degrees) > ThresholdRotAngle / 2f)
- {
- // Ok, so this bit is difficult because ideally it would look like it's snapping to sane angles.
- // Otherwise PIANO DOOR STUCK! happens.
- // But it also needs to work with station rotation / align to the local parent.
- // So...
- var baseRotation = pulledData.WorldRotation - pulledXform.LocalRotation;
- var localRotation = newAngle - baseRotation;
- var localRotationSnapped = Angle.FromDegrees(Math.Floor((localRotation.Degrees / ThresholdRotAngle) + 0.5f) * ThresholdRotAngle);
- TransformSystem.SetLocalRotation(pulledXform, localRotationSnapped);
- }
- }
- }
-
- public override void UpdateBeforeSolve(bool prediction, float frameTime)
- {
- base.UpdateBeforeSolve(prediction, frameTime);
-
- foreach (var pullable in _pullableSystem.Moving)
- {
- // There's a 1-frame delay between stopping moving something and it leaving the Moving set.
- // This can include if leaving the Moving set due to not being pulled anymore,
- // or due to being deleted.
-
- if (pullable.Deleted)
- continue;
-
- if (pullable.MovingTo == null)
- continue;
-
- if (pullable.Puller is not {Valid: true} puller)
- continue;
-
- var pullableEnt = pullable.Owner;
- var pullableXform = Transform(pullableEnt);
- var pullerXform = Transform(puller);
-
- // Now that's over with...
-
- var pullerPosition = pullerXform.MapPosition;
- var movingTo = pullable.MovingTo.Value.ToMap(EntityManager, _transform);
- if (movingTo.MapId != pullerPosition.MapId)
- {
- _pullableSystem.StopMoveTo(pullable);
- continue;
- }
-
- if (!TryComp<PhysicsComponent>(pullableEnt, out var physics) ||
- physics.BodyType == BodyType.Static ||
- movingTo.MapId != pullableXform.MapID)
- {
- _pullableSystem.StopMoveTo(pullable);
- continue;
- }
-
- var movingPosition = movingTo.Position;
- var ownerPosition = pullableXform.MapPosition.Position;
-
- var diff = movingPosition - ownerPosition;
- var diffLength = diff.Length();
-
- if (diffLength < MaximumSettleDistance && physics.LinearVelocity.Length() < MaximumSettleVelocity)
- {
- PhysicsSystem.SetLinearVelocity(pullableEnt, Vector2.Zero, body: physics);
- _pullableSystem.StopMoveTo(pullable);
- continue;
- }
-
- var impulseModifierLerp = Math.Min(1.0f, Math.Max(0.0f, (physics.Mass - AccelModifierLowMass) / (AccelModifierHighMass - AccelModifierLowMass)));
- var impulseModifier = MathHelper.Lerp(AccelModifierLow, AccelModifierHigh, impulseModifierLerp);
- var multiplier = diffLength < 1 ? impulseModifier * diffLength : impulseModifier;
- // Note the implication that the real rules of physics don't apply to pulling control.
- var accel = diff.Normalized() * multiplier;
- // Now for the part where velocity gets shutdown...
- if (diffLength < SettleShutdownDistance && physics.LinearVelocity.Length() >= SettleMinimumShutdownVelocity)
- {
- // Shutdown velocity increases as we get closer to centre
- var scaling = (SettleShutdownDistance - diffLength) / SettleShutdownDistance;
- accel -= physics.LinearVelocity * SettleShutdownMultiplier * scaling;
- }
-
- PhysicsSystem.WakeBody(pullableEnt, body: physics);
-
- var impulse = accel * physics.Mass * frameTime;
- PhysicsSystem.ApplyLinearImpulse(pullableEnt, impulse, body: physics);
-
- // if the puller is weightless or can't move, then we apply the inverse impulse (Newton's third law).
- // doing it under gravity produces an unsatisfying wiggling when pulling.
- // If player can't move, assume they are on a chair and we need to prevent pull-moving.
- if ((_gravity.IsWeightless(puller) && pullerXform.GridUid == null) || !_actionBlockerSystem.CanMove(puller))
- {
- PhysicsSystem.WakeBody(puller);
- PhysicsSystem.ApplyLinearImpulse(puller, -impulse);
- }
- }
- }
- }
-}
+++ /dev/null
-using Content.Shared.Input;
-using Content.Shared.Pulling;
-using Content.Shared.Pulling.Components;
-using JetBrains.Annotations;
-using Robust.Server.GameObjects;
-using Robust.Shared.Input.Binding;
-using Robust.Shared.Player;
-
-namespace Content.Server.Pulling
-{
- [UsedImplicitly]
- public sealed class PullingSystem : SharedPullingSystem
- {
- public override void Initialize()
- {
- base.Initialize();
-
- UpdatesAfter.Add(typeof(PhysicsSystem));
-
- SubscribeLocalEvent<SharedPullableComponent, PullableMoveMessage>(OnPullableMove);
- SubscribeLocalEvent<SharedPullableComponent, PullableStopMovingMessage>(OnPullableStopMove);
-
- CommandBinds.Builder
- .Bind(ContentKeyFunctions.ReleasePulledObject, InputCmdHandler.FromDelegate(HandleReleasePulledObject))
- .Register<PullingSystem>();
- }
-
- private void HandleReleasePulledObject(ICommonSession? session)
- {
- if (session?.AttachedEntity is not {Valid: true} player)
- {
- return;
- }
-
- if (!TryGetPulled(player, out var pulled))
- {
- return;
- }
-
- if (!EntityManager.TryGetComponent(pulled.Value, out SharedPullableComponent? pullable))
- {
- return;
- }
-
- TryStopPull(pullable);
- }
- }
-}
using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components;
using Content.Shared.Interaction;
-using Content.Shared.Physics.Pull;
+using Content.Shared.Movement.Pulling.Events;
using Content.Shared.Weapons.Melee.Events;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems;
if (!component.PullActivation)
return;
- _artifactSystem.TryActivateArtifact(uid, args.Puller.Owner);
+ _artifactSystem.TryActivateArtifact(uid, args.PullerUid);
}
private void OnAttack(EntityUid uid, ArtifactInteractionTriggerComponent component, AttackedEvent args)
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
+using Content.Shared.Movement.Pulling.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.NPC.Components;
using Content.Shared.NPC.Systems;
using Content.Shared.Nutrition.Components;
using Content.Shared.Popups;
using Content.Shared.Roles;
-using Content.Shared.Pulling.Components;
using Content.Shared.Weapons.Melee;
using Content.Shared.Zombies;
using Content.Shared.Prying.Components;
[Dependency] private readonly IChatManager _chatMan = default!;
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly SharedRoleSystem _roles = default!;
- [Dependency] private readonly MobThresholdSystem _mobThreshold = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
/// <summary>
RemComp(target, handsComp);
}
- RemComp<SharedPullerComponent>(target);
+ // Sloth: What the fuck?
+ // How long until compregistry lmao.
+ RemComp<PullerComponent>(target);
// No longer waiting to become a zombie:
// Requires deferral because this is (probably) the event which called ZombifyEntity in the first place.
using Content.Shared.ActionBlocker;
using Content.Shared.Interaction.Events;
using Content.Shared.Item;
-using Content.Shared.Movement;
using Content.Shared.Movement.Events;
-using Content.Shared.Physics.Pull;
-using Content.Shared.Pulling;
-using Content.Shared.Pulling.Components;
-using Content.Shared.Pulling.Events;
-using Content.Shared.Stunnable;
+using Content.Shared.Movement.Pulling.Components;
+using Content.Shared.Movement.Pulling.Events;
+using Content.Shared.Movement.Pulling.Systems;
using Content.Shared.Throwing;
namespace Content.Shared.Administration;
public sealed class AdminFrozenSystem : EntitySystem
{
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
- [Dependency] private readonly SharedPullingSystem _pulling = default!;
+ [Dependency] private readonly PullingSystem _pulling = default!;
public override void Initialize()
{
private void OnStartup(EntityUid uid, AdminFrozenComponent component, ComponentStartup args)
{
- if (TryComp<SharedPullableComponent>(uid, out var pullable))
+ if (TryComp<PullableComponent>(uid, out var pullable))
{
- _pulling.TryStopPull(pullable);
+ _pulling.TryStopPull(uid, pullable);
}
UpdateCanMove(uid, component, args);
using Content.Shared.Mobs.Components;
using Content.Shared.Movement.Events;
using Content.Shared.Popups;
-using Content.Shared.Pulling.Components;
using Content.Shared.Standing;
using Content.Shared.Storage.Components;
using Content.Shared.Stunnable;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Events;
using Robust.Shared.Utility;
+using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent;
namespace Content.Shared.Buckle;
RaiseLocalEvent(ev.BuckledEntity, ref ev);
RaiseLocalEvent(ev.StrapEntity, ref ev);
- if (TryComp<SharedPullableComponent>(buckleUid, out var ownerPullable))
+ if (TryComp<PullableComponent>(buckleUid, out var ownerPullable))
{
if (ownerPullable.Puller != null)
{
- _pulling.TryStopPull(ownerPullable);
+ _pulling.TryStopPull(buckleUid, ownerPullable);
}
}
_physics.ResetDynamics(physics);
}
- if (!buckleComp.PullStrap && TryComp<SharedPullableComponent>(strapUid, out var toPullable))
+ if (!buckleComp.PullStrap && TryComp<PullableComponent>(strapUid, out var toPullable))
{
if (toPullable.Puller == buckleUid)
{
// can't pull it and buckle to it at the same time
- _pulling.TryStopPull(toPullable);
+ _pulling.TryStopPull(strapUid, toPullable);
}
}
using Robust.Shared.Physics.Systems;
using Robust.Shared.Player;
using Robust.Shared.Timing;
+using PullingSystem = Content.Shared.Movement.Pulling.Systems.PullingSystem;
namespace Content.Shared.Buckle;
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
[Dependency] private readonly SharedJointSystem _joints = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
- [Dependency] private readonly SharedPullingSystem _pulling = default!;
+ [Dependency] private readonly PullingSystem _pulling = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly StandingStateSystem _standing = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
using Content.Shared.Database;
using Content.Shared.DoAfter;
using Content.Shared.Interaction;
+using Content.Shared.Movement.Pulling.Components;
+using Content.Shared.Movement.Pulling.Systems;
using Content.Shared.Popups;
-using Content.Shared.Pulling;
-using Content.Shared.Pulling.Components;
using Content.Shared.Tools;
using Content.Shared.Tools.Components;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Components;
using Content.Shared.Tag;
-using Robust.Shared.Player;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
- [Dependency] private readonly SharedPullingSystem _pulling = default!;
+ [Dependency] private readonly PullingSystem _pulling = default!;
[Dependency] private readonly SharedToolSystem _tool = default!;
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
var rot = xform.LocalRotation;
xform.LocalRotation = Math.Round(rot / (Math.PI / 2)) * (Math.PI / 2);
- if (TryComp<SharedPullableComponent>(uid, out var pullable) && pullable.Puller != null)
+ if (TryComp<PullableComponent>(uid, out var pullable) && pullable.Puller != null)
{
- _pulling.TryStopPull(pullable);
+ _pulling.TryStopPull(uid, pullable);
}
// TODO: Anchoring snaps rn anyway!
public void TryToggleAnchor(EntityUid uid, EntityUid userUid, EntityUid usingUid,
AnchorableComponent? anchorable = null,
TransformComponent? transform = null,
- SharedPullableComponent? pullable = null,
+ PullableComponent? pullable = null,
ToolComponent? usingTool = null)
{
if (!Resolve(uid, ref transform))
private void TryAnchor(EntityUid uid, EntityUid userUid, EntityUid usingUid,
AnchorableComponent? anchorable = null,
TransformComponent? transform = null,
- SharedPullableComponent? pullable = null,
+ PullableComponent? pullable = null,
ToolComponent? usingTool = null)
{
if (!Resolve(uid, ref anchorable, ref transform))
using Content.Shared.Item;
using Content.Shared.Mobs.Systems;
using Content.Shared.Movement.Events;
-using Content.Shared.Physics.Pull;
+using Content.Shared.Movement.Pulling.Events;
using Content.Shared.Popups;
-using Content.Shared.Pulling.Components;
using Content.Shared.Pulling.Events;
using Content.Shared.Rejuvenate;
using Content.Shared.Stunnable;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Serialization;
+using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent;
namespace Content.Shared.Cuffs
{
SubscribeLocalEvent<CuffableComponent, EntInsertedIntoContainerMessage>(OnCuffsInsertedIntoContainer);
SubscribeLocalEvent<CuffableComponent, RejuvenateEvent>(OnRejuvenate);
SubscribeLocalEvent<CuffableComponent, ComponentInit>(OnStartup);
- SubscribeLocalEvent<CuffableComponent, StopPullingEvent>(HandleStopPull);
+ SubscribeLocalEvent<CuffableComponent, AttemptStopPullingEvent>(HandleStopPull);
SubscribeLocalEvent<CuffableComponent, UpdateCanMoveEvent>(HandleMoveAttempt);
SubscribeLocalEvent<CuffableComponent, IsEquippingAttemptEvent>(OnEquipAttempt);
SubscribeLocalEvent<CuffableComponent, IsUnequippingAttemptEvent>(OnUnequipAttempt);
private void OnBeingPulledAttempt(EntityUid uid, CuffableComponent component, BeingPulledAttemptEvent args)
{
- if (!TryComp<SharedPullableComponent>(uid, out var pullable))
+ if (!TryComp<PullableComponent>(uid, out var pullable))
return;
if (pullable.Puller != null && !component.CanStillInteract) // If we are being pulled already and cuffed, we can't get pulled again.
private void HandleMoveAttempt(EntityUid uid, CuffableComponent component, UpdateCanMoveEvent args)
{
- if (component.CanStillInteract || !EntityManager.TryGetComponent(uid, out SharedPullableComponent? pullable) || !pullable.BeingPulled)
+ if (component.CanStillInteract || !EntityManager.TryGetComponent(uid, out PullableComponent? pullable) || !pullable.BeingPulled)
return;
args.Cancel();
}
- private void HandleStopPull(EntityUid uid, CuffableComponent component, StopPullingEvent args)
+ private void HandleStopPull(EntityUid uid, CuffableComponent component, AttemptStopPullingEvent args)
{
if (args.User == null || !Exists(args.User.Value))
return;
if (args.User.Value == uid && !component.CanStillInteract)
- args.Cancel();
+ args.Cancelled = true;
}
private void AddUncuffVerb(EntityUid uid, CuffableComponent component, GetVerbsEvent<Verb> args)
using Content.Shared.Ghost;
using Content.Shared.Hands;
using Content.Shared.Movement.Events;
+using Content.Shared.Movement.Pulling.Events;
using Content.Shared.Movement.Systems;
-using Content.Shared.Physics.Pull;
using Content.Shared.Tag;
using Content.Shared.Verbs;
using Robust.Shared.Containers;
using Content.Shared.CCVar;
using Content.Shared.Gravity;
using Content.Shared.Movement.Events;
+using Content.Shared.Movement.Pulling.Components;
using Content.Shared.Movement.Systems;
-using Content.Shared.Pulling.Components;
using JetBrains.Annotations;
using Robust.Shared.Configuration;
using Robust.Shared.Map;
[Dependency] private readonly SharedMoverController _mover = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
+ private EntityQuery<TileFrictionModifierComponent> _frictionQuery;
+ private EntityQuery<TransformComponent> _xformQuery;
+ private EntityQuery<PullerComponent> _pullerQuery;
+ private EntityQuery<PullableComponent> _pullableQuery;
+ private EntityQuery<MapGridComponent> _gridQuery;
+
private float _stopSpeed;
private float _frictionModifier;
public const float DefaultFriction = 0.3f;
Subs.CVar(_configManager, CCVars.TileFrictionModifier, value => _frictionModifier = value, true);
Subs.CVar(_configManager, CCVars.StopSpeed, value => _stopSpeed = value, true);
+ _frictionQuery = GetEntityQuery<TileFrictionModifierComponent>();
+ _xformQuery = GetEntityQuery<TransformComponent>();
+ _pullerQuery = GetEntityQuery<PullerComponent>();
+ _pullableQuery = GetEntityQuery<PullableComponent>();
+ _gridQuery = GetEntityQuery<MapGridComponent>();
}
public override void UpdateBeforeMapSolve(bool prediction, PhysicsMapComponent mapComponent, float frameTime)
{
base.UpdateBeforeMapSolve(prediction, mapComponent, frameTime);
- var frictionQuery = GetEntityQuery<TileFrictionModifierComponent>();
- var xformQuery = GetEntityQuery<TransformComponent>();
- var pullerQuery = GetEntityQuery<SharedPullerComponent>();
- var pullableQuery = GetEntityQuery<SharedPullableComponent>();
- var gridQuery = GetEntityQuery<MapGridComponent>();
-
foreach (var body in mapComponent.AwakeBodies)
{
var uid = body.Owner;
if (body.LinearVelocity.Equals(Vector2.Zero) && body.AngularVelocity.Equals(0f))
continue;
- if (!xformQuery.TryGetComponent(uid, out var xform))
+ if (!_xformQuery.TryGetComponent(uid, out var xform))
{
Log.Error($"Unable to get transform for {ToPrettyString(uid)} in tilefrictioncontroller");
continue;
}
- var surfaceFriction = GetTileFriction(uid, body, xform, gridQuery, frictionQuery);
+ var surfaceFriction = GetTileFriction(uid, body, xform);
var bodyModifier = 1f;
- if (frictionQuery.TryGetComponent(uid, out var frictionComp))
+ if (_frictionQuery.TryGetComponent(uid, out var frictionComp))
{
bodyModifier = frictionComp.Modifier;
}
// If we're sandwiched between 2 pullers reduce friction
// Might be better to make this dynamic and check how many are in the pull chain?
// Either way should be much faster for now.
- if (pullerQuery.TryGetComponent(uid, out var puller) && puller.Pulling != null &&
- pullableQuery.TryGetComponent(uid, out var pullable) && pullable.BeingPulled)
+ if (_pullerQuery.TryGetComponent(uid, out var puller) && puller.Pulling != null &&
+ _pullableQuery.TryGetComponent(uid, out var pullable) && pullable.BeingPulled)
{
bodyModifier *= 0.2f;
}
private float GetTileFriction(
EntityUid uid,
PhysicsComponent body,
- TransformComponent xform,
- EntityQuery<MapGridComponent> gridQuery,
- EntityQuery<TileFrictionModifierComponent> frictionQuery)
+ TransformComponent xform)
{
// TODO: Make IsWeightless event-based; we already have grid traversals tracked so just raise events
if (_gravity.IsWeightless(uid, body, xform))
return 0.0f;
// If not on a grid then return the map's friction.
- if (!gridQuery.TryGetComponent(xform.GridUid, out var grid))
+ if (!_gridQuery.TryGetComponent(xform.GridUid, out var grid))
{
- return frictionQuery.TryGetComponent(xform.MapUid, out var friction)
+ return _frictionQuery.TryGetComponent(xform.MapUid, out var friction)
? friction.Modifier
: DefaultFriction;
}
while (anc.MoveNext(out var tileEnt))
{
- if (frictionQuery.TryGetComponent(tileEnt, out var friction))
+ if (_frictionQuery.TryGetComponent(tileEnt, out var friction))
return friction.Modifier;
}
using Content.Shared.Inventory.Events;
using Content.Shared.Item;
using Content.Shared.Movement.Components;
+using Content.Shared.Movement.Pulling.Components;
+using Content.Shared.Movement.Pulling.Systems;
using Content.Shared.Physics;
using Content.Shared.Popups;
-using Content.Shared.Pulling;
-using Content.Shared.Pulling.Components;
using Content.Shared.Tag;
using Content.Shared.Timing;
using Content.Shared.Verbs;
[Dependency] private readonly SharedVerbSystem _verbSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly UseDelaySystem _useDelay = default!;
- [Dependency] private readonly SharedPullingSystem _pullSystem = default!;
+ [Dependency] private readonly PullingSystem _pullSystem = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
if (!InRangeUnobstructed(userEntity.Value, uid, popup: true))
return false;
- if (!TryComp(uid, out SharedPullableComponent? pull))
+ if (!TryComp(uid, out PullableComponent? pull))
return false;
- _pullSystem.TogglePull(userEntity.Value, pull);
+ _pullSystem.TogglePull(uid, userEntity.Value, pull);
return false;
}
--- /dev/null
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Movement.Pulling.Components;
+
+/// <summary>
+/// Specifies an entity as being pullable by an entity with <see cref="PullerComponent"/>
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(Systems.PullingSystem))]
+public sealed partial class PullableComponent : Component
+{
+ /// <summary>
+ /// The current entity pulling this component.
+ /// </summary>
+ [AutoNetworkedField, DataField]
+ public EntityUid? Puller;
+
+ /// <summary>
+ /// The pull joint.
+ /// </summary>
+ [AutoNetworkedField, DataField]
+ public string? PullJointId;
+
+ public bool BeingPulled => Puller != null;
+
+ /// <summary>
+ /// If the physics component has FixedRotation should we keep it upon being pulled
+ /// </summary>
+ [Access(typeof(Systems.PullingSystem), Other = AccessPermissions.ReadExecute)]
+ [ViewVariables(VVAccess.ReadWrite), DataField("fixedRotation")]
+ public bool FixedRotationOnPull;
+
+ /// <summary>
+ /// What the pullable's fixedrotation was set to before being pulled.
+ /// </summary>
+ [Access(typeof(Systems.PullingSystem), Other = AccessPermissions.ReadExecute)]
+ [AutoNetworkedField, DataField]
+ public bool PrevFixedRotation;
+}
--- /dev/null
+using Content.Shared.Movement.Pulling.Systems;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Shared.Movement.Pulling.Components;
+
+/// <summary>
+/// Specifies an entity as being able to pull another entity with <see cref="PullableComponent"/>
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(PullingSystem))]
+public sealed partial class PullerComponent : Component
+{
+ // My raiding guild
+ /// <summary>
+ /// Next time the puller can throw what is being pulled.
+ /// Used to avoid spamming it for infinite spin + velocity.
+ /// </summary>
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField]
+ public TimeSpan NextThrow;
+
+ [DataField]
+ public TimeSpan ThrowCooldown = TimeSpan.FromSeconds(1);
+
+ // Before changing how this is updated, please see SharedPullerSystem.RefreshMovementSpeed
+ public float WalkSpeedModifier => Pulling == default ? 1.0f : 0.95f;
+
+ public float SprintSpeedModifier => Pulling == default ? 1.0f : 0.95f;
+
+ /// <summary>
+ /// Entity currently being pulled if applicable.
+ /// </summary>
+ [AutoNetworkedField, DataField]
+ public EntityUid? Pulling;
+
+ /// <summary>
+ /// Does this entity need hands to be able to pull something?
+ /// </summary>
+ [DataField]
+ public bool NeedsHands = true;
+}
--- /dev/null
+using Robust.Shared.Physics.Components;
+
+namespace Content.Shared.Movement.Pulling.Events;
+
+/// <summary>
+/// Raised directed on puller and pullable to determine if it can be pulled.
+/// </summary>
+public sealed class PullAttemptEvent : PullMessage
+{
+ public PullAttemptEvent(EntityUid pullerUid, EntityUid pullableUid) : base(pullerUid, pullableUid) { }
+
+ public bool Cancelled { get; set; }
+}
--- /dev/null
+namespace Content.Shared.Pulling.Events;
+
+/// <summary>
+/// Raised when a request is made to stop pulling an entity.
+/// </summary>
+public record struct AttemptStopPullingEvent(EntityUid? User = null)
+{
+ public readonly EntityUid? User = User;
+ public bool Cancelled;
+}
\ No newline at end of file
--- /dev/null
+namespace Content.Shared.Movement.Pulling.Events;
+
+public abstract class PullMessage : EntityEventArgs
+{
+ public readonly EntityUid PullerUid;
+ public readonly EntityUid PulledUid;
+
+ protected PullMessage(EntityUid pullerUid, EntityUid pulledUid)
+ {
+ PullerUid = pullerUid;
+ PulledUid = pulledUid;
+ }
+}
--- /dev/null
+namespace Content.Shared.Movement.Pulling.Events;
+
+public sealed class PullStartedMessage : PullMessage
+{
+ public PullStartedMessage(EntityUid pullerUid, EntityUid pullableUid) :
+ base(pullerUid, pullableUid)
+ {
+ }
+}
--- /dev/null
+using Robust.Shared.Physics.Components;
+
+namespace Content.Shared.Movement.Pulling.Events;
+
+/// <summary>
+/// Raised directed on both puller and pullable.
+/// </summary>
+public sealed class PullStoppedMessage : PullMessage
+{
+ public PullStoppedMessage(EntityUid pullerUid, EntityUid pulledUid) : base(pullerUid, pulledUid)
+ {
+ }
+}
--- /dev/null
+using System.Numerics;
+using Content.Shared.ActionBlocker;
+using Content.Shared.Administration.Logs;
+using Content.Shared.Alert;
+using Content.Shared.Buckle.Components;
+using Content.Shared.Database;
+using Content.Shared.Hands;
+using Content.Shared.Hands.EntitySystems;
+using Content.Shared.Input;
+using Content.Shared.Interaction;
+using Content.Shared.Movement.Events;
+using Content.Shared.Movement.Pulling.Components;
+using Content.Shared.Movement.Pulling.Events;
+using Content.Shared.Movement.Systems;
+using Content.Shared.Pulling.Events;
+using Content.Shared.Throwing;
+using Content.Shared.Verbs;
+using Robust.Shared.Containers;
+using Robust.Shared.Input.Binding;
+using Robust.Shared.Map;
+using Robust.Shared.Physics;
+using Robust.Shared.Physics.Components;
+using Robust.Shared.Physics.Events;
+using Robust.Shared.Physics.Systems;
+using Robust.Shared.Player;
+using Robust.Shared.Timing;
+
+namespace Content.Shared.Movement.Pulling.Systems;
+
+/// <summary>
+/// Allows one entity to pull another behind them via a physics distance joint.
+/// </summary>
+public sealed class PullingSystem : EntitySystem
+{
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
+ [Dependency] private readonly ActionBlockerSystem _blocker = default!;
+ [Dependency] private readonly AlertsSystem _alertsSystem = default!;
+ [Dependency] private readonly MovementSpeedModifierSystem _modifierSystem = default!;
+ [Dependency] private readonly SharedJointSystem _joints = default!;
+ [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
+ [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
+ [Dependency] private readonly SharedInteractionSystem _interaction = default!;
+ [Dependency] private readonly SharedPhysicsSystem _physics = default!;
+ [Dependency] private readonly SharedTransformSystem _xformSys = default!;
+ [Dependency] private readonly ThrowingSystem _throwing = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ UpdatesAfter.Add(typeof(SharedPhysicsSystem));
+ UpdatesOutsidePrediction = true;
+
+ SubscribeLocalEvent<PullableComponent, MoveInputEvent>(OnPullableMoveInput);
+ SubscribeLocalEvent<PullableComponent, CollisionChangeEvent>(OnPullableCollisionChange);
+ SubscribeLocalEvent<PullableComponent, JointRemovedEvent>(OnJointRemoved);
+ SubscribeLocalEvent<PullableComponent, GetVerbsEvent<Verb>>(AddPullVerbs);
+ SubscribeLocalEvent<PullableComponent, EntGotInsertedIntoContainerMessage>(OnPullableContainerInsert);
+
+ SubscribeLocalEvent<PullerComponent, EntGotInsertedIntoContainerMessage>(OnPullerContainerInsert);
+ SubscribeLocalEvent<PullerComponent, EntityUnpausedEvent>(OnPullerUnpaused);
+ SubscribeLocalEvent<PullerComponent, VirtualItemDeletedEvent>(OnVirtualItemDeleted);
+ SubscribeLocalEvent<PullerComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovespeed);
+
+ CommandBinds.Builder
+ .Bind(ContentKeyFunctions.MovePulledObject, new PointerInputCmdHandler(OnRequestMovePulledObject))
+ .Bind(ContentKeyFunctions.ReleasePulledObject, InputCmdHandler.FromDelegate(OnReleasePulledObject, handle: false))
+ .Register<PullingSystem>();
+ }
+
+ private void OnPullerContainerInsert(Entity<PullerComponent> ent, ref EntGotInsertedIntoContainerMessage args)
+ {
+ if (ent.Comp.Pulling == null) return;
+
+ if (!TryComp(ent.Comp.Pulling.Value, out PullableComponent? pulling))
+ return;
+
+ TryStopPull(ent.Comp.Pulling.Value, pulling, ent.Owner);
+ }
+
+ private void OnPullableContainerInsert(Entity<PullableComponent> ent, ref EntGotInsertedIntoContainerMessage args)
+ {
+ TryStopPull(ent.Owner, ent.Comp);
+ }
+
+ public override void Shutdown()
+ {
+ base.Shutdown();
+ CommandBinds.Unregister<PullingSystem>();
+ }
+
+ private void OnPullerUnpaused(EntityUid uid, PullerComponent component, ref EntityUnpausedEvent args)
+ {
+ component.NextThrow += args.PausedTime;
+ }
+
+ private void OnVirtualItemDeleted(EntityUid uid, PullerComponent component, VirtualItemDeletedEvent args)
+ {
+ // If client deletes the virtual hand then stop the pull.
+ if (component.Pulling == null)
+ return;
+
+ if (component.Pulling != args.BlockingEntity)
+ return;
+
+ if (EntityManager.TryGetComponent(args.BlockingEntity, out PullableComponent? comp))
+ {
+ TryStopPull(args.BlockingEntity, comp, uid);
+ }
+ }
+
+ private void AddPullVerbs(EntityUid uid, PullableComponent component, GetVerbsEvent<Verb> args)
+ {
+ if (!args.CanAccess || !args.CanInteract)
+ return;
+
+ // Are they trying to pull themselves up by their bootstraps?
+ if (args.User == args.Target)
+ return;
+
+ //TODO VERB ICONS add pulling icon
+ if (component.Puller == args.User)
+ {
+ Verb verb = new()
+ {
+ Text = Loc.GetString("pulling-verb-get-data-text-stop-pulling"),
+ Act = () => TryStopPull(uid, component, user: args.User),
+ DoContactInteraction = false // pulling handle its own contact interaction.
+ };
+ args.Verbs.Add(verb);
+ }
+ else if (CanPull(args.User, args.Target))
+ {
+ Verb verb = new()
+ {
+ Text = Loc.GetString("pulling-verb-get-data-text"),
+ Act = () => TryStartPull(args.User, args.Target),
+ DoContactInteraction = false // pulling handle its own contact interaction.
+ };
+ args.Verbs.Add(verb);
+ }
+ }
+
+ private void OnRefreshMovespeed(EntityUid uid, PullerComponent component, RefreshMovementSpeedModifiersEvent args)
+ {
+ args.ModifySpeed(component.WalkSpeedModifier, component.SprintSpeedModifier);
+ }
+
+ private void OnPullableMoveInput(EntityUid uid, PullableComponent component, ref MoveInputEvent args)
+ {
+ // If someone moves then break their pulling.
+ if (!component.BeingPulled)
+ return;
+
+ var entity = args.Entity;
+
+ if (!_blocker.CanMove(entity))
+ return;
+
+ TryStopPull(uid, component, user: uid);
+ }
+
+ private void OnPullableCollisionChange(EntityUid uid, PullableComponent component, ref CollisionChangeEvent args)
+ {
+ // IDK what this is supposed to be.
+ if (!_timing.ApplyingState && component.PullJointId != null && !args.CanCollide)
+ {
+ _joints.RemoveJoint(uid, component.PullJointId);
+ }
+ }
+
+ private void OnJointRemoved(EntityUid uid, PullableComponent component, JointRemovedEvent args)
+ {
+ // Just handles the joint getting nuked without going through pulling system (valid behavior).
+
+ // Not relevant / pullable state handle it.
+ if (component.Puller != args.OtherEntity ||
+ args.Joint.ID != component.PullJointId ||
+ _timing.ApplyingState)
+ {
+ return;
+ }
+
+ if (args.Joint.ID != component.PullJointId || component.Puller == null)
+ return;
+
+ StopPulling(uid, component);
+ }
+
+ /// <summary>
+ /// Forces pulling to stop and handles cleanup.
+ /// </summary>
+ private void StopPulling(EntityUid pullableUid, PullableComponent pullableComp)
+ {
+ if (!_timing.ApplyingState)
+ {
+ if (TryComp<PhysicsComponent>(pullableUid, out var pullablePhysics))
+ {
+ _physics.SetFixedRotation(pullableUid, pullableComp.PrevFixedRotation, body: pullablePhysics);
+ }
+ }
+
+ // No more joints with puller -> force stop pull.
+ if (TryComp<PullerComponent>(pullableComp.Puller, out var pullerComp))
+ {
+ var pullerUid = pullableComp.Puller.Value;
+ _alertsSystem.ClearAlert(pullerUid, AlertType.Pulling);
+ pullerComp.Pulling = null;
+ Dirty(pullableComp.Puller.Value, pullerComp);
+
+ // Messaging
+ var message = new PullStoppedMessage(pullerUid, pullableUid);
+ _modifierSystem.RefreshMovementSpeedModifiers(pullerUid);
+ _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(pullerUid):user} stopped pulling {ToPrettyString(pullableUid):target}");
+
+ RaiseLocalEvent(pullerUid, message);
+ RaiseLocalEvent(pullableUid, message);
+ }
+
+ pullableComp.PullJointId = null;
+ pullableComp.Puller = null;
+ Dirty(pullableUid, pullableComp);
+
+ _alertsSystem.ClearAlert(pullableUid, AlertType.Pulled);
+ }
+
+ public bool IsPulled(EntityUid uid, PullableComponent? component = null)
+ {
+ return Resolve(uid, ref component, false) && component.BeingPulled;
+ }
+
+ private bool OnRequestMovePulledObject(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
+ {
+ if (session?.AttachedEntity is not { } player ||
+ !player.IsValid())
+ {
+ return false;
+ }
+
+ if (!TryComp<PullerComponent>(player, out var pullerComp))
+ return false;
+
+ var pulled = pullerComp.Pulling;
+
+ if (!HasComp<PullableComponent>(pulled))
+ return false;
+
+ if (_containerSystem.IsEntityInContainer(player))
+ return false;
+
+ // Cooldown buddy
+ if (_timing.CurTime < pullerComp.NextThrow)
+ return false;
+
+ pullerComp.NextThrow = _timing.CurTime + pullerComp.ThrowCooldown;
+
+ // Cap the distance
+ const float range = 2f;
+ var fromUserCoords = coords.WithEntityId(player, EntityManager);
+ var userCoords = new EntityCoordinates(player, Vector2.Zero);
+
+ if (!userCoords.InRange(EntityManager, _xformSys, fromUserCoords, range))
+ {
+ var userDirection = fromUserCoords.Position - userCoords.Position;
+ fromUserCoords = userCoords.Offset(userDirection.Normalized() * range);
+ }
+
+ Dirty(player, pullerComp);
+ _throwing.TryThrow(pulled.Value, fromUserCoords, user: player, strength: 4f, animated: false, recoil: false, playSound: false);
+ return false;
+ }
+
+ public bool IsPulling(EntityUid puller, PullerComponent? component = null)
+ {
+ return Resolve(puller, ref component, false) && component.Pulling != null;
+ }
+
+ private void OnReleasePulledObject(ICommonSession? session)
+ {
+ if (session?.AttachedEntity is not {Valid: true} player)
+ {
+ return;
+ }
+
+ if (!TryComp(player, out PullerComponent? pullerComp) ||
+ !TryComp(pullerComp.Pulling, out PullableComponent? pullableComp))
+ {
+ return;
+ }
+
+ TryStopPull(pullerComp.Pulling.Value, pullableComp, user: player);
+ }
+
+ public bool CanPull(EntityUid puller, EntityUid pullableUid, PullerComponent? pullerComp = null)
+ {
+ if (!Resolve(puller, ref pullerComp, false))
+ {
+ return false;
+ }
+
+ if (pullerComp.NeedsHands && !_handsSystem.TryGetEmptyHand(puller, out _))
+ {
+ return false;
+ }
+
+ if (!_blocker.CanInteract(puller, pullableUid))
+ {
+ return false;
+ }
+
+ if (!EntityManager.TryGetComponent<PhysicsComponent>(pullableUid, out var physics))
+ {
+ return false;
+ }
+
+ if (physics.BodyType == BodyType.Static)
+ {
+ return false;
+ }
+
+ if (puller == pullableUid)
+ {
+ return false;
+ }
+
+ if (!_containerSystem.IsInSameOrNoContainer(puller, pullableUid))
+ {
+ return false;
+ }
+
+ if (EntityManager.TryGetComponent(puller, out BuckleComponent? buckle))
+ {
+ // Prevent people pulling the chair they're on, etc.
+ if (buckle is { PullStrap: false, Buckled: true } && (buckle.LastEntityBuckledTo == pullableUid))
+ {
+ return false;
+ }
+ }
+
+ var getPulled = new BeingPulledAttemptEvent(puller, pullableUid);
+ RaiseLocalEvent(pullableUid, getPulled, true);
+ var startPull = new StartPullAttemptEvent(puller, pullableUid);
+ RaiseLocalEvent(puller, startPull, true);
+ return !startPull.Cancelled && !getPulled.Cancelled;
+ }
+
+ public bool TogglePull(EntityUid pullableUid, EntityUid pullerUid, PullableComponent pullable)
+ {
+ if (pullable.Puller == pullerUid)
+ {
+ return TryStopPull(pullableUid, pullable);
+ }
+
+ return TryStartPull(pullerUid, pullableUid, pullableComp: pullable);
+ }
+
+ public bool TogglePull(EntityUid pullerUid, PullerComponent puller)
+ {
+ if (!TryComp<PullableComponent>(puller.Pulling, out var pullable))
+ return false;
+
+ return TogglePull(puller.Pulling.Value, pullerUid, pullable);
+ }
+
+ public bool TryStartPull(EntityUid pullerUid, EntityUid pullableUid, EntityUid? user = null,
+ PullerComponent? pullerComp = null, PullableComponent? pullableComp = null)
+ {
+ if (!Resolve(pullerUid, ref pullerComp, false) ||
+ !Resolve(pullableUid, ref pullableComp, false))
+ {
+ return false;
+ }
+
+ if (pullerComp.Pulling == pullableUid)
+ return true;
+
+ if (!CanPull(pullerUid, pullableUid))
+ return false;
+
+ if (!EntityManager.TryGetComponent<PhysicsComponent>(pullerUid, out var pullerPhysics) ||
+ !EntityManager.TryGetComponent<PhysicsComponent>(pullableUid, out var pullablePhysics))
+ {
+ return false;
+ }
+
+ // Ensure that the puller is not currently pulling anything.
+ var oldPullable = pullerComp.Pulling;
+
+ if (oldPullable != null)
+ {
+ // Well couldn't stop the old one.
+ if (!TryStopPull(oldPullable.Value, pullableComp, user))
+ return false;
+ }
+
+ // Is the pullable currently being pulled by something else?
+ if (pullableComp.Puller != null)
+ {
+ // Uhhh
+ if (pullableComp.Puller == pullerUid)
+ return false;
+
+ if (!TryStopPull(pullableUid, pullableComp, pullerUid))
+ return false;
+ }
+
+ var pullAttempt = new PullAttemptEvent(pullerUid, pullableUid);
+ RaiseLocalEvent(pullerUid, pullAttempt);
+
+ if (pullAttempt.Cancelled)
+ return false;
+
+ RaiseLocalEvent(pullableUid, pullAttempt);
+
+ if (pullAttempt.Cancelled)
+ return false;
+
+ // Pulling confirmed
+
+ _interaction.DoContactInteraction(pullableUid, pullerUid);
+
+ // Use net entity so it's consistent across client and server.
+ pullableComp.PullJointId = $"pull-joint-{GetNetEntity(pullableUid)}";
+
+ pullerComp.Pulling = pullableUid;
+ pullableComp.Puller = pullerUid;
+
+ // joint state handling will manage its own state
+ if (!_timing.ApplyingState)
+ {
+ // Joint startup
+ var union = _physics.GetHardAABB(pullerUid).Union(_physics.GetHardAABB(pullableUid, body: pullablePhysics));
+ var length = Math.Max((float) union.Size.X, (float) union.Size.Y) * 0.75f;
+
+ var joint = _joints.CreateDistanceJoint(pullableUid, pullerUid, id: pullableComp.PullJointId);
+ joint.CollideConnected = false;
+ // This maximum has to be there because if the object is constrained too closely, the clamping goes backwards and asserts.
+ joint.MaxLength = Math.Max(1.0f, length);
+ joint.Length = length * 0.75f;
+ joint.MinLength = 0f;
+ joint.Stiffness = 1f;
+
+ _physics.SetFixedRotation(pullableUid, pullableComp.FixedRotationOnPull, body: pullablePhysics);
+ }
+
+ pullableComp.PrevFixedRotation = pullablePhysics.FixedRotation;
+
+ // Messaging
+ var message = new PullStartedMessage(pullerUid, pullableUid);
+ _alertsSystem.ShowAlert(pullerUid, AlertType.Pulling);
+ _alertsSystem.ShowAlert(pullableUid, AlertType.Pulled);
+
+ RaiseLocalEvent(pullerUid, message);
+ RaiseLocalEvent(pullableUid, message);
+
+ Dirty(pullerUid, pullerComp);
+ Dirty(pullableUid, pullableComp);
+
+ _adminLogger.Add(LogType.Action, LogImpact.Low,
+ $"{ToPrettyString(pullerUid):user} started pulling {ToPrettyString(pullableUid):target}");
+ return true;
+ }
+
+ public bool TryStopPull(EntityUid pullableUid, PullableComponent pullable, EntityUid? user = null)
+ {
+ var pullerUidNull = pullable.Puller;
+
+ if (pullerUidNull == null)
+ return false;
+
+ var msg = new AttemptStopPullingEvent(user);
+ RaiseLocalEvent(pullableUid, msg, true);
+
+ if (msg.Cancelled)
+ return false;
+
+ // Stop pulling confirmed!
+ if (!_timing.ApplyingState)
+ {
+ // Joint shutdown
+ if (pullable.PullJointId != null)
+ {
+ _joints.RemoveJoint(pullableUid, pullable.PullJointId);
+ pullable.PullJointId = null;
+ }
+ }
+
+ StopPulling(pullableUid, pullable);
+ return true;
+ }
+}
using Content.Shared.Mobs.Systems;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Events;
-using Content.Shared.Pulling.Components;
using Content.Shared.Tag;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
+using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent;
namespace Content.Shared.Movement.Systems
{
protected EntityQuery<MovementSpeedModifierComponent> ModifierQuery;
protected EntityQuery<PhysicsComponent> PhysicsQuery;
protected EntityQuery<RelayInputMoverComponent> RelayQuery;
- protected EntityQuery<SharedPullableComponent> PullableQuery;
+ protected EntityQuery<PullableComponent> PullableQuery;
protected EntityQuery<TransformComponent> XformQuery;
protected EntityQuery<CanMoveInAirComponent> CanMoveInAirQuery;
protected EntityQuery<NoRotateOnMoveComponent> NoRotateQuery;
RelayTargetQuery = GetEntityQuery<MovementRelayTargetComponent>();
PhysicsQuery = GetEntityQuery<PhysicsComponent>();
RelayQuery = GetEntityQuery<RelayInputMoverComponent>();
- PullableQuery = GetEntityQuery<SharedPullableComponent>();
+ PullableQuery = GetEntityQuery<PullableComponent>();
XformQuery = GetEntityQuery<TransformComponent>();
NoRotateQuery = GetEntityQuery<NoRotateOnMoveComponent>();
CanMoveInAirQuery = GetEntityQuery<CanMoveInAirComponent>();
!otherCollider.CanCollide ||
((collider.CollisionMask & otherCollider.CollisionLayer) == 0 &&
(otherCollider.CollisionMask & collider.CollisionLayer) == 0) ||
- (TryComp(otherCollider.Owner, out SharedPullableComponent? pullable) && pullable.BeingPulled))
+ (TryComp(otherCollider.Owner, out PullableComponent? pullable) && pullable.BeingPulled))
{
continue;
}
+++ /dev/null
-using Robust.Shared.GameStates;
-using Robust.Shared.Map;
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Pulling.Components
-{
- // Before you try to add another type than SharedPullingStateManagementSystem, consider the can of worms you may be opening!
- [NetworkedComponent, AutoGenerateComponentState]
- [Access(typeof(SharedPullingStateManagementSystem))]
- [RegisterComponent]
- public sealed partial class SharedPullableComponent : Component
- {
- /// <summary>
- /// The current entity pulling this component.
- /// </summary>
- [DataField, AutoNetworkedField]
- public EntityUid? Puller { get; set; }
-
- /// <summary>
- /// The pull joint.
- /// </summary>
- [DataField, AutoNetworkedField]
- public string? PullJointId { get; set; }
-
- public bool BeingPulled => Puller != null;
-
- [Access(typeof(SharedPullingStateManagementSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
- public EntityCoordinates? MovingTo { get; set; }
-
- /// <summary>
- /// If the physics component has FixedRotation should we keep it upon being pulled
- /// </summary>
- [Access(typeof(SharedPullingSystem), Other = AccessPermissions.ReadExecute)]
- [ViewVariables(VVAccess.ReadWrite), DataField("fixedRotation")]
- public bool FixedRotationOnPull { get; set; }
-
- /// <summary>
- /// What the pullable's fixedrotation was set to before being pulled.
- /// </summary>
- [Access(typeof(SharedPullingSystem), Other = AccessPermissions.ReadExecute)]
- [ViewVariables]
- public bool PrevFixedRotation;
- }
-
- /// <summary>
- /// Raised when a request is made to stop pulling an entity.
- /// </summary>
- public sealed class StopPullingEvent : CancellableEntityEventArgs
- {
- public EntityUid? User { get; }
-
- public StopPullingEvent(EntityUid? uid = null)
- {
- User = uid;
- }
- }
-}
+++ /dev/null
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Pulling.Components
-{
- [RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
- [Access(typeof(SharedPullingStateManagementSystem))]
- public sealed partial class SharedPullerComponent : Component
- {
- // Before changing how this is updated, please see SharedPullerSystem.RefreshMovementSpeed
- public float WalkSpeedModifier => Pulling == default ? 1.0f : 0.95f;
-
- public float SprintSpeedModifier => Pulling == default ? 1.0f : 0.95f;
-
- [DataField, AutoNetworkedField]
- public EntityUid? Pulling { get; set; }
-
- /// <summary>
- /// Does this entity need hands to be able to pull something?
- /// </summary>
- [DataField("needsHands")]
- public bool NeedsHands = true;
- }
-}
+++ /dev/null
-using Robust.Shared.Physics.Components;
-
-namespace Content.Shared.Physics.Pull
-{
- public sealed class PullAttemptEvent : PullMessage
- {
- public PullAttemptEvent(PhysicsComponent puller, PhysicsComponent pulled) : base(puller, pulled) { }
-
- public bool Cancelled { get; set; }
- }
-}
+++ /dev/null
-using Robust.Shared.Physics.Components;
-
-namespace Content.Shared.Physics.Pull
-{
- public abstract class PullMessage : EntityEventArgs
- {
- public readonly PhysicsComponent Puller;
- public readonly PhysicsComponent Pulled;
-
- protected PullMessage(PhysicsComponent puller, PhysicsComponent pulled)
- {
- Puller = puller;
- Pulled = pulled;
- }
- }
-}
+++ /dev/null
-using Robust.Shared.Physics.Components;
-
-namespace Content.Shared.Physics.Pull
-{
- public sealed class PullStartedMessage : PullMessage
- {
- public PullStartedMessage(PhysicsComponent puller, PhysicsComponent pulled) :
- base(puller, pulled)
- {
- }
- }
-}
+++ /dev/null
-using Robust.Shared.Physics.Components;
-
-namespace Content.Shared.Physics.Pull
-{
- public sealed class PullStoppedMessage : PullMessage
- {
- public PullStoppedMessage(PhysicsComponent puller, PhysicsComponent pulled) : base(puller, pulled)
- {
- }
- }
-}
+++ /dev/null
-namespace Content.Shared.Pulling
-{
- public sealed class PullableMoveMessage : EntityEventArgs
- {
- }
-}
+++ /dev/null
-namespace Content.Shared.Pulling
-{
- public sealed class PullableStopMovingMessage : EntityEventArgs
- {
- }
-}
+++ /dev/null
-using Content.Shared.ActionBlocker;
-using Content.Shared.Mobs.Systems;
-using Content.Shared.Pulling.Components;
-using Content.Shared.Movement.Events;
-
-namespace Content.Shared.Pulling.Systems
-{
- public sealed class SharedPullableSystem : EntitySystem
- {
- [Dependency] private readonly ActionBlockerSystem _blocker = default!;
- [Dependency] private readonly MobStateSystem _mobState = default!;
- [Dependency] private readonly SharedPullingSystem _pullSystem = default!;
-
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<SharedPullableComponent, MoveInputEvent>(OnRelayMoveInput);
- }
-
- private void OnRelayMoveInput(EntityUid uid, SharedPullableComponent component, ref MoveInputEvent args)
- {
- var entity = args.Entity;
- if (_mobState.IsIncapacitated(entity) || !_blocker.CanMove(entity)) return;
-
- _pullSystem.TryStopPull(component);
- }
- }
-}
+++ /dev/null
-using Content.Shared.Administration.Logs;
-using Content.Shared.Alert;
-using Content.Shared.Database;
-using Content.Shared.Hands;
-using Content.Shared.Movement.Systems;
-using Content.Shared.Physics.Pull;
-using Content.Shared.Pulling.Components;
-using JetBrains.Annotations;
-
-namespace Content.Shared.Pulling.Systems
-{
- [UsedImplicitly]
- public sealed class SharedPullerSystem : EntitySystem
- {
- [Dependency] private readonly SharedPullingStateManagementSystem _why = default!;
- [Dependency] private readonly SharedPullingSystem _pullSystem = default!;
- [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifierSystem = default!;
- [Dependency] private readonly AlertsSystem _alertsSystem = default!;
- [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
-
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent<SharedPullerComponent, PullStartedMessage>(PullerHandlePullStarted);
- SubscribeLocalEvent<SharedPullerComponent, PullStoppedMessage>(PullerHandlePullStopped);
- SubscribeLocalEvent<SharedPullerComponent, VirtualItemDeletedEvent>(OnVirtualItemDeleted);
- SubscribeLocalEvent<SharedPullerComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovespeed);
- SubscribeLocalEvent<SharedPullerComponent, ComponentShutdown>(OnPullerShutdown);
- }
-
- private void OnPullerShutdown(EntityUid uid, SharedPullerComponent component, ComponentShutdown args)
- {
- _why.ForceDisconnectPuller(component);
- }
-
- private void OnVirtualItemDeleted(EntityUid uid, SharedPullerComponent component, VirtualItemDeletedEvent args)
- {
- if (component.Pulling == null)
- return;
-
- if (component.Pulling == args.BlockingEntity)
- {
- if (EntityManager.TryGetComponent<SharedPullableComponent>(args.BlockingEntity, out var comp))
- {
- _pullSystem.TryStopPull(comp, uid);
- }
- }
- }
-
- private void PullerHandlePullStarted(
- EntityUid uid,
- SharedPullerComponent component,
- PullStartedMessage args)
- {
- if (args.Puller.Owner != uid)
- return;
-
- _alertsSystem.ShowAlert(component.Owner, AlertType.Pulling);
-
- RefreshMovementSpeed(component);
- }
-
- private void PullerHandlePullStopped(
- EntityUid uid,
- SharedPullerComponent component,
- PullStoppedMessage args)
- {
- if (args.Puller.Owner != uid)
- return;
-
- var euid = component.Owner;
- if (_alertsSystem.IsShowingAlert(euid, AlertType.Pulling))
- _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(euid):user} stopped pulling {ToPrettyString(args.Pulled.Owner):target}");
- _alertsSystem.ClearAlert(euid, AlertType.Pulling);
-
- RefreshMovementSpeed(component);
- }
-
- private void OnRefreshMovespeed(EntityUid uid, SharedPullerComponent component, RefreshMovementSpeedModifiersEvent args)
- {
- args.ModifySpeed(component.WalkSpeedModifier, component.SprintSpeedModifier);
- }
-
- private void RefreshMovementSpeed(SharedPullerComponent component)
- {
- _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(component.Owner);
- }
- }
-}
+++ /dev/null
-using Content.Shared.Physics.Pull;
-using Content.Shared.Pulling.Components;
-using JetBrains.Annotations;
-using Robust.Shared.Map;
-using Robust.Shared.Physics;
-using Robust.Shared.Physics.Components;
-using Robust.Shared.Physics.Systems;
-using Robust.Shared.Timing;
-
-namespace Content.Shared.Pulling
-{
- /// <summary>
- /// This is the core of pulling state management.
- /// Because pulling state is such a mess to get right, all writes to pulling state must go through this class.
- /// </summary>
- [UsedImplicitly]
- public sealed class SharedPullingStateManagementSystem : EntitySystem
- {
- [Dependency] private readonly SharedJointSystem _jointSystem = default!;
- [Dependency] private readonly SharedPhysicsSystem _physics = default!;
- [Dependency] private readonly IGameTiming _timing = default!;
-
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent<SharedPullableComponent, ComponentShutdown>(OnShutdown);
- }
-
- private void OnShutdown(EntityUid uid, SharedPullableComponent component, ComponentShutdown args)
- {
- if (component.Puller != null)
- ForceRelationship(null, component);
- }
-
- // A WARNING:
- // The following 2 functions are the most internal part of the pulling system's relationship management.
- // They do not expect to be cancellable.
- private void ForceDisconnect(SharedPullerComponent puller, SharedPullableComponent pullable)
- {
- var pullerPhysics = EntityManager.GetComponent<PhysicsComponent>(puller.Owner);
- var pullablePhysics = EntityManager.GetComponent<PhysicsComponent>(pullable.Owner);
-
- // MovingTo shutdown
- ForceSetMovingTo(pullable, null);
-
- // Joint shutdown
- if (!_timing.ApplyingState && // During state-handling, joint component will handle its own state.
- pullable.PullJointId != null &&
- TryComp(puller.Owner, out JointComponent? jointComp))
- {
- if (jointComp.GetJoints.TryGetValue(pullable.PullJointId, out var j))
- _jointSystem.RemoveJoint(j);
- }
- pullable.PullJointId = null;
-
- // State shutdown
- puller.Pulling = null;
- pullable.Puller = null;
-
- // Messaging
- var message = new PullStoppedMessage(pullerPhysics, pullablePhysics);
-
- RaiseLocalEvent(puller.Owner, message, broadcast: false);
-
- if (Initialized(pullable.Owner))
- RaiseLocalEvent(pullable.Owner, message, true);
-
- // Networking
- Dirty(puller);
- Dirty(pullable);
- }
-
- public void ForceRelationship(SharedPullerComponent? puller, SharedPullableComponent? pullable)
- {
- if (_timing.ApplyingState)
- return;
- ;
- if (pullable != null && puller != null && (puller.Pulling == pullable.Owner))
- {
- // Already done
- return;
- }
-
- // Start by disconnecting the pullable from whatever it is currently connected to.
- var pullableOldPullerE = pullable?.Puller;
- if (pullableOldPullerE != null)
- {
- ForceDisconnect(EntityManager.GetComponent<SharedPullerComponent>(pullableOldPullerE.Value), pullable!);
- }
-
- // Continue with the puller.
- var pullerOldPullableE = puller?.Pulling;
- if (pullerOldPullableE != null)
- {
- ForceDisconnect(puller!, EntityManager.GetComponent<SharedPullableComponent>(pullerOldPullableE.Value));
- }
-
- // And now for the actual connection (if any).
-
- if (puller != null && pullable != null)
- {
- var pullerPhysics = EntityManager.GetComponent<PhysicsComponent>(puller.Owner);
- var pullablePhysics = EntityManager.GetComponent<PhysicsComponent>(pullable.Owner);
- pullable.PullJointId = $"pull-joint-{pullable.Owner}";
-
- // State startup
- puller.Pulling = pullable.Owner;
- pullable.Puller = puller.Owner;
-
- // joint state handling will manage its own state
- if (!_timing.ApplyingState)
- {
- // Joint startup
- var union = _physics.GetHardAABB(puller.Owner).Union(_physics.GetHardAABB(pullable.Owner, body: pullablePhysics));
- var length = Math.Max(union.Size.X, union.Size.Y) * 0.75f;
-
- var joint = _jointSystem.CreateDistanceJoint(pullablePhysics.Owner, pullerPhysics.Owner, id: pullable.PullJointId);
- joint.CollideConnected = false;
- // This maximum has to be there because if the object is constrained too closely, the clamping goes backwards and asserts.
- joint.MaxLength = Math.Max(1.0f, length);
- joint.Length = length * 0.75f;
- joint.MinLength = 0f;
- joint.Stiffness = 1f;
- }
-
- // Messaging
- var message = new PullStartedMessage(pullerPhysics, pullablePhysics);
-
- RaiseLocalEvent(puller.Owner, message, broadcast: false);
- RaiseLocalEvent(pullable.Owner, message, true);
-
- // Networking
- Dirty(puller);
- Dirty(pullable);
- }
- }
-
- // For OnRemove use only.
- public void ForceDisconnectPuller(SharedPullerComponent puller)
- {
- // DO NOT ADD ADDITIONAL LOGIC IN THIS FUNCTION. Do it in ForceRelationship.
- ForceRelationship(puller, null);
- }
-
- // For OnRemove use only.
- public void ForceDisconnectPullable(SharedPullableComponent pullable)
- {
- // DO NOT ADD ADDITIONAL LOGIC IN THIS FUNCTION. Do it in ForceRelationship.
- ForceRelationship(null, pullable);
- }
-
- public void ForceSetMovingTo(SharedPullableComponent pullable, EntityCoordinates? movingTo)
- {
- if (_timing.ApplyingState)
- return;
-
- if (pullable.MovingTo == movingTo)
- {
- return;
- }
-
- // Don't allow setting a MovingTo if there's no puller.
- // The other half of this guarantee (shutting down a MovingTo if the puller goes away) is enforced in ForceRelationship.
- if (pullable.Puller == null && movingTo != null)
- {
- return;
- }
-
- pullable.MovingTo = movingTo;
- Dirty(pullable);
-
- if (movingTo == null)
- {
- RaiseLocalEvent(pullable.Owner, new PullableStopMovingMessage(), true);
- }
- else
- {
- RaiseLocalEvent(pullable.Owner, new PullableMoveMessage(), true);
- }
- }
-
- /// <summary>
- /// Changes if the entity needs a hand in order to be able to pull objects.
- /// </summary>
- public void ChangeHandRequirement(EntityUid uid, bool needsHands, SharedPullerComponent? comp)
- {
- if (!Resolve(uid, ref comp, false))
- return;
-
- comp.NeedsHands = needsHands;
-
- Dirty(uid, comp);
- }
- }
-}
+++ /dev/null
-using Content.Shared.ActionBlocker;
-using Content.Shared.Administration.Logs;
-using Content.Shared.Buckle.Components;
-using Content.Shared.Database;
-using Content.Shared.Hands.EntitySystems;
-using Content.Shared.Interaction;
-using Content.Shared.Physics.Pull;
-using Content.Shared.Pulling.Components;
-using Content.Shared.Pulling.Events;
-using Robust.Shared.Containers;
-using Robust.Shared.Map;
-using Robust.Shared.Physics;
-using Robust.Shared.Physics.Components;
-using Robust.Shared.Physics.Systems;
-using Robust.Shared.Timing;
-using Robust.Shared.Utility;
-
-namespace Content.Shared.Pulling
-{
- public abstract partial class SharedPullingSystem
- {
- [Dependency] private readonly ActionBlockerSystem _blocker = default!;
- [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
- [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
- [Dependency] private readonly SharedInteractionSystem _interaction = default!;
- [Dependency] private readonly SharedPhysicsSystem _physics = default!;
- [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
- [Dependency] private readonly IGameTiming _timing = default!;
-
- public bool CanPull(EntityUid puller, EntityUid pulled)
- {
- if (!EntityManager.TryGetComponent<SharedPullerComponent>(puller, out var comp))
- {
- return false;
- }
-
- if (comp.NeedsHands && !_handsSystem.TryGetEmptyHand(puller, out _))
- {
- return false;
- }
-
- if (!_blocker.CanInteract(puller, pulled))
- {
- return false;
- }
-
- if (!EntityManager.TryGetComponent<PhysicsComponent>(pulled, out var physics))
- {
- return false;
- }
-
- if (physics.BodyType == BodyType.Static)
- {
- return false;
- }
-
- if (puller == pulled)
- {
- return false;
- }
-
- if(_containerSystem.IsEntityInContainer(puller) || _containerSystem.IsEntityInContainer(pulled))
- {
- return false;
- }
-
- if (EntityManager.TryGetComponent(puller, out BuckleComponent? buckle))
- {
- // Prevent people pulling the chair they're on, etc.
- if (buckle is { PullStrap: false, Buckled: true } && (buckle.LastEntityBuckledTo == pulled))
- {
- return false;
- }
- }
-
- var getPulled = new BeingPulledAttemptEvent(puller, pulled);
- RaiseLocalEvent(pulled, getPulled, true);
- var startPull = new StartPullAttemptEvent(puller, pulled);
- RaiseLocalEvent(puller, startPull, true);
- return (!startPull.Cancelled && !getPulled.Cancelled);
- }
-
- public bool TogglePull(EntityUid puller, SharedPullableComponent pullable)
- {
- if (pullable.Puller == puller)
- {
- return TryStopPull(pullable);
- }
- return TryStartPull(puller, pullable.Owner);
- }
-
- // -- Core attempted actions --
-
- public bool TryStopPull(SharedPullableComponent pullable, EntityUid? user = null)
- {
- if (_timing.ApplyingState)
- return false;
-
- if (!pullable.BeingPulled)
- {
- return false;
- }
-
- var msg = new StopPullingEvent(user);
- RaiseLocalEvent(pullable.Owner, msg, true);
-
- if (msg.Cancelled) return false;
-
- // Stop pulling confirmed!
-
- if (TryComp<PhysicsComponent>(pullable.Owner, out var pullablePhysics))
- {
- _physics.SetFixedRotation(pullable.Owner, pullable.PrevFixedRotation, body: pullablePhysics);
- }
-
- _pullSm.ForceRelationship(null, pullable);
- return true;
- }
-
- public bool TryStartPull(EntityUid puller, EntityUid pullable)
- {
- if (!EntityManager.TryGetComponent(puller, out SharedPullerComponent? pullerComp))
- {
- return false;
- }
- if (!EntityManager.TryGetComponent(pullable, out SharedPullableComponent? pullableComp))
- {
- return false;
- }
- return TryStartPull(pullerComp, pullableComp);
- }
-
- // The main "start pulling" function.
- public bool TryStartPull(SharedPullerComponent puller, SharedPullableComponent pullable)
- {
- if (_timing.ApplyingState)
- return false;
-
- if (puller.Pulling == pullable.Owner)
- return true;
-
- // Pulling a new object : Perform sanity checks.
-
- if (!CanPull(puller.Owner, pullable.Owner))
- {
- return false;
- }
-
- if (!EntityManager.TryGetComponent<PhysicsComponent>(puller.Owner, out var pullerPhysics))
- {
- return false;
- }
-
- if (!EntityManager.TryGetComponent<PhysicsComponent>(pullable.Owner, out var pullablePhysics))
- {
- return false;
- }
-
- // Ensure that the puller is not currently pulling anything.
- // If this isn't done, then it happens too late, and the start/stop messages go out of order,
- // and next thing you know it thinks it's not pulling anything even though it is!
-
- var oldPullable = puller.Pulling;
- if (oldPullable != null)
- {
- if (EntityManager.TryGetComponent(oldPullable.Value, out SharedPullableComponent? oldPullableComp))
- {
- if (!TryStopPull(oldPullableComp))
- {
- return false;
- }
- }
- else
- {
- Log.Warning("Well now you've done it, haven't you? Someone transferred pulling (onto {0}) while presently pulling something that has no Pullable component (on {1})!", pullable.Owner, oldPullable);
- return false;
- }
- }
-
- // Ensure that the pullable is not currently being pulled.
- // Same sort of reasons as before.
-
- var oldPuller = pullable.Puller;
- if (oldPuller != null)
- {
- if (!TryStopPull(pullable))
- {
- return false;
- }
- }
-
- // Continue with pulling process.
-
- var pullAttempt = new PullAttemptEvent(pullerPhysics, pullablePhysics);
-
- RaiseLocalEvent(puller.Owner, pullAttempt, broadcast: false);
-
- if (pullAttempt.Cancelled)
- {
- return false;
- }
-
- RaiseLocalEvent(pullable.Owner, pullAttempt, true);
-
- if (pullAttempt.Cancelled)
- return false;
-
- _interaction.DoContactInteraction(pullable.Owner, puller.Owner);
-
- _pullSm.ForceRelationship(puller, pullable);
- pullable.PrevFixedRotation = pullablePhysics.FixedRotation;
- _physics.SetFixedRotation(pullable.Owner, pullable.FixedRotationOnPull, body: pullablePhysics);
- _adminLogger.Add(LogType.Action, LogImpact.Low,
- $"{ToPrettyString(puller.Owner):user} started pulling {ToPrettyString(pullable.Owner):target}");
- return true;
- }
-
- public bool TryMoveTo(SharedPullableComponent pullable, EntityCoordinates to)
- {
- if (pullable.Puller == null)
- {
- return false;
- }
-
- if (!EntityManager.HasComponent<PhysicsComponent>(pullable.Owner))
- {
- return false;
- }
-
- _pullSm.ForceSetMovingTo(pullable, to);
- return true;
- }
-
- public void StopMoveTo(SharedPullableComponent pullable)
- {
- _pullSm.ForceSetMovingTo(pullable, null);
- }
- }
-}
+++ /dev/null
-using System.Diagnostics.CodeAnalysis;
-using Content.Shared.Alert;
-using Content.Shared.GameTicking;
-using Content.Shared.Input;
-using Content.Shared.Physics.Pull;
-using Content.Shared.Pulling.Components;
-using Content.Shared.Verbs;
-using JetBrains.Annotations;
-using Robust.Shared.Containers;
-using Robust.Shared.Input.Binding;
-using Robust.Shared.Map;
-using Robust.Shared.Physics;
-using Robust.Shared.Physics.Events;
-using Robust.Shared.Physics.Systems;
-using Robust.Shared.Player;
-
-namespace Content.Shared.Pulling
-{
- [UsedImplicitly]
- public abstract partial class SharedPullingSystem : EntitySystem
- {
- [Dependency] private readonly SharedPullingStateManagementSystem _pullSm = default!;
- [Dependency] private readonly AlertsSystem _alertsSystem = default!;
- [Dependency] private readonly SharedJointSystem _joints = default!;
-
- /// <summary>
- /// A mapping of pullers to the entity that they are pulling.
- /// </summary>
- private readonly Dictionary<EntityUid, EntityUid> _pullers =
- new();
-
- private readonly HashSet<SharedPullableComponent> _moving = new();
- private readonly HashSet<SharedPullableComponent> _stoppedMoving = new();
-
- public IReadOnlySet<SharedPullableComponent> Moving => _moving;
-
- public override void Initialize()
- {
- base.Initialize();
-
- UpdatesOutsidePrediction = true;
-
- SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
- SubscribeLocalEvent<PullStartedMessage>(OnPullStarted);
- SubscribeLocalEvent<PullStoppedMessage>(OnPullStopped);
- SubscribeLocalEvent<EntInsertedIntoContainerMessage>(HandleContainerInsert);
- SubscribeLocalEvent<SharedPullableComponent, JointRemovedEvent>(OnJointRemoved);
- SubscribeLocalEvent<SharedPullableComponent, CollisionChangeEvent>(OnPullableCollisionChange);
-
- SubscribeLocalEvent<SharedPullableComponent, PullStartedMessage>(PullableHandlePullStarted);
- SubscribeLocalEvent<SharedPullableComponent, PullStoppedMessage>(PullableHandlePullStopped);
-
- SubscribeLocalEvent<SharedPullableComponent, GetVerbsEvent<Verb>>(AddPullVerbs);
-
- CommandBinds.Builder
- .Bind(ContentKeyFunctions.MovePulledObject, new PointerInputCmdHandler(HandleMovePulledObject))
- .Register<SharedPullingSystem>();
- }
-
- private void OnPullableCollisionChange(EntityUid uid, SharedPullableComponent component, ref CollisionChangeEvent args)
- {
- if (component.PullJointId != null && !args.CanCollide)
- {
- _joints.RemoveJoint(uid, component.PullJointId);
- }
- }
-
- private void OnJointRemoved(EntityUid uid, SharedPullableComponent component, JointRemovedEvent args)
- {
- if (component.Puller != args.OtherEntity)
- return;
-
- // Do we have some other join with our Puller?
- // or alternatively:
- // TODO track the relevant joint.
-
- if (TryComp(uid, out JointComponent? joints))
- {
- foreach (var jt in joints.GetJoints.Values)
- {
- if (jt.BodyAUid == component.Puller || jt.BodyBUid == component.Puller)
- return;
- }
- }
-
- // No more joints with puller -> force stop pull.
- _pullSm.ForceDisconnectPullable(component);
- }
-
- private void AddPullVerbs(EntityUid uid, SharedPullableComponent component, GetVerbsEvent<Verb> args)
- {
- if (!args.CanAccess || !args.CanInteract)
- return;
-
- // Are they trying to pull themselves up by their bootstraps?
- if (args.User == args.Target)
- return;
-
- //TODO VERB ICONS add pulling icon
- if (component.Puller == args.User)
- {
- Verb verb = new()
- {
- Text = Loc.GetString("pulling-verb-get-data-text-stop-pulling"),
- Act = () => TryStopPull(component, args.User),
- DoContactInteraction = false // pulling handle its own contact interaction.
- };
- args.Verbs.Add(verb);
- }
- else if (CanPull(args.User, args.Target))
- {
- Verb verb = new()
- {
- Text = Loc.GetString("pulling-verb-get-data-text"),
- Act = () => TryStartPull(args.User, args.Target),
- DoContactInteraction = false // pulling handle its own contact interaction.
- };
- args.Verbs.Add(verb);
- }
- }
-
- // Raise a "you are being pulled" alert if the pulled entity has alerts.
- private void PullableHandlePullStarted(EntityUid uid, SharedPullableComponent component, PullStartedMessage args)
- {
- if (args.Pulled.Owner != uid)
- return;
-
- _alertsSystem.ShowAlert(uid, AlertType.Pulled);
- }
-
- private void PullableHandlePullStopped(EntityUid uid, SharedPullableComponent component, PullStoppedMessage args)
- {
- if (args.Pulled.Owner != uid)
- return;
-
- _alertsSystem.ClearAlert(uid, AlertType.Pulled);
- }
-
- public bool IsPulled(EntityUid uid, SharedPullableComponent? component = null)
- {
- return Resolve(uid, ref component, false) && component.BeingPulled;
- }
-
- public override void Update(float frameTime)
- {
- base.Update(frameTime);
-
- _moving.ExceptWith(_stoppedMoving);
- _stoppedMoving.Clear();
- }
-
- public void Reset(RoundRestartCleanupEvent ev)
- {
- _pullers.Clear();
- _moving.Clear();
- _stoppedMoving.Clear();
- }
-
- private void OnPullStarted(PullStartedMessage message)
- {
- SetPuller(message.Puller.Owner, message.Pulled.Owner);
- }
-
- private void OnPullStopped(PullStoppedMessage message)
- {
- RemovePuller(message.Puller.Owner);
- }
-
- protected void OnPullableMove(EntityUid uid, SharedPullableComponent component, PullableMoveMessage args)
- {
- _moving.Add(component);
- }
-
- protected void OnPullableStopMove(EntityUid uid, SharedPullableComponent component, PullableStopMovingMessage args)
- {
- _stoppedMoving.Add(component);
- }
-
- // TODO: When Joint networking is less shitcodey fix this to use a dedicated joints message.
- private void HandleContainerInsert(EntInsertedIntoContainerMessage message)
- {
- if (TryComp(message.Entity, out SharedPullableComponent? pullable))
- {
- TryStopPull(pullable);
- }
-
- if (TryComp(message.Entity, out SharedPullerComponent? puller))
- {
- if (puller.Pulling == null) return;
-
- if (!TryComp(puller.Pulling.Value, out SharedPullableComponent? pulling))
- return;
-
- TryStopPull(pulling);
- }
- }
-
- private bool HandleMovePulledObject(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
- {
- if (session?.AttachedEntity is not { } player ||
- !player.IsValid())
- return false;
-
- if (!TryGetPulled(player, out var pulled))
- return false;
-
- if (!TryComp(pulled.Value, out SharedPullableComponent? pullable))
- return false;
-
- if (_containerSystem.IsEntityInContainer(player))
- return false;
-
- TryMoveTo(pullable, coords);
-
- return false;
- }
-
- private void SetPuller(EntityUid puller, EntityUid pulled)
- {
- _pullers[puller] = pulled;
- }
-
- private bool RemovePuller(EntityUid puller)
- {
- return _pullers.Remove(puller);
- }
-
- public EntityUid GetPulled(EntityUid by)
- {
- return _pullers.GetValueOrDefault(by);
- }
-
- public bool TryGetPulled(EntityUid by, [NotNullWhen(true)] out EntityUid? pulled)
- {
- return (pulled = GetPulled(by)) != null;
- }
-
- public bool IsPulling(EntityUid puller)
- {
- return _pullers.ContainsKey(puller);
- }
- }
-}
using Content.Shared.Lock;
-using Content.Shared.Pulling;
-using Content.Shared.Pulling.Components;
+using Content.Shared.Movement.Pulling.Components;
+using Content.Shared.Movement.Pulling.Systems;
using Content.Shared.Security.Components;
using Robust.Shared.Physics.Systems;
[Dependency] private readonly FixtureSystem _fixtures = default!;
[Dependency] private readonly SharedPointLightSystem _pointLight = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
- [Dependency] private readonly SharedPullingSystem _pulling = default!;
+ [Dependency] private readonly PullingSystem _pulling = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
public override void Initialize()
_physics.SetHard(uid, fixture, false);
}
- if (TryComp(uid, out SharedPullableComponent? pullable))
- _pulling.TryStopPull(pullable);
+ if (TryComp(uid, out PullableComponent? pullable))
+ _pulling.TryStopPull(uid, pullable);
SharedPointLightComponent? pointLight = null;
if (_pointLight.ResolveLight(uid, ref pointLight))
using System.Linq;
using Content.Shared.Ghost;
+using Content.Shared.Movement.Pulling.Components;
+using Content.Shared.Movement.Pulling.Systems;
using Content.Shared.Popups;
using Content.Shared.Projectiles;
-using Content.Shared.Pulling;
-using Content.Shared.Pulling.Components;
using Content.Shared.Teleportation.Components;
using Content.Shared.Verbs;
using Robust.Shared.Audio;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
- [Dependency] private readonly SharedPullingSystem _pulling = default!;
+ [Dependency] private readonly PullingSystem _pulling = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
private const string PortalFixture = "portalFixture";
return;
// break pulls before portal enter so we dont break shit
- if (TryComp<SharedPullableComponent>(subject, out var pullable) && pullable.BeingPulled)
+ if (TryComp<PullableComponent>(subject, out var pullable) && pullable.BeingPulled)
{
- _pulling.TryStopPull(pullable);
+ _pulling.TryStopPull(subject, pullable);
}
- if (TryComp<SharedPullerComponent>(subject, out var pulling)
- && pulling.Pulling != null && TryComp<SharedPullableComponent>(pulling.Pulling.Value, out var subjectPulling))
+ if (TryComp<PullerComponent>(subject, out var pullerComp)
+ && TryComp<PullableComponent>(pullerComp.Pulling, out var subjectPulling))
{
- _pulling.TryStopPull(subjectPulling);
+ _pulling.TryStopPull(subject, subjectPulling);
}
// if they came from another portal, just return and wait for them to exit the portal
{
public const float ThrowAngularImpulse = 5f;
+ /// <summary>
+ /// Speed cap on rotation in case of click-spam.
+ /// </summary>
+ public const float ThrowAngularCap = 3f * MathF.PI;
+
public const float PushbackDefault = 2f;
/// <summary>
float strength = 1.0f,
EntityUid? user = null,
float pushbackRatio = PushbackDefault,
+ bool recoil = true,
+ bool animated = true,
bool playSound = true)
{
- var thrownPos = Transform(uid).MapPosition;
- var mapPos = coordinates.ToMap(EntityManager, _transform);
+ var thrownPos = _transform.GetMapCoordinates(uid);
+ var mapPos = _transform.ToMapCoordinates(coordinates);
if (mapPos.MapId != thrownPos.MapId)
return;
- TryThrow(uid, mapPos.Position - thrownPos.Position, strength, user, pushbackRatio, playSound);
+ TryThrow(uid, mapPos.Position - thrownPos.Position, strength, user, pushbackRatio, recoil: recoil, animated: animated, playSound: playSound);
}
/// <summary>
float strength = 1.0f,
EntityUid? user = null,
float pushbackRatio = PushbackDefault,
+ bool recoil = true,
+ bool animated = true,
bool playSound = true)
{
var physicsQuery = GetEntityQuery<PhysicsComponent>();
return;
var projectileQuery = GetEntityQuery<ProjectileComponent>();
- var tagQuery = GetEntityQuery<TagComponent>();
TryThrow(
uid,
projectileQuery,
strength,
user,
- pushbackRatio,
- playSound);
+ pushbackRatio, recoil: recoil, animated: animated, playSound: playSound);
}
/// <summary>
float strength = 1.0f,
EntityUid? user = null,
float pushbackRatio = PushbackDefault,
+ bool recoil = true,
+ bool animated = true,
bool playSound = true)
{
if (strength <= 0 || direction == Vector2Helpers.Infinity || direction == Vector2Helpers.NaN || direction == Vector2.Zero)
if (projectileQuery.TryGetComponent(uid, out var proj) && !proj.OnlyCollideWhenShot)
return;
- var comp = new ThrownItemComponent();
- comp.Thrower = user;
+ var comp = new ThrownItemComponent
+ {
+ Thrower = user,
+ Animate = animated,
+ };
// Estimate time to arrival so we can apply OnGround status and slow it much faster.
var time = direction.Length() / strength;
comp.ThrownTime = _gameTiming.CurTime;
+ // TODO: This is a bandaid, don't do this.
+ // if you want to force landtime have the caller handle it or add a new method.
// did we launch this with something stronger than our hands?
if (TryComp<HandsComponent>(comp.Thrower, out var hands) && strength > hands.ThrowForceMultiplier)
comp.LandTime = comp.ThrownTime + TimeSpan.FromSeconds(time);
if (user == null)
return;
- _recoil.KickCamera(user.Value, -direction * 0.04f);
+ if (recoil)
+ _recoil.KickCamera(user.Value, -direction * 0.04f);
// Give thrower an impulse in the other direction
if (pushbackRatio != 0.0f &&
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), AutoGenerateComponentPause]
public sealed partial class ThrownItemComponent : Component
{
+ /// <summary>
+ /// Should the in-air throwing animation play.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public bool Animate = true;
+
/// <summary>
/// The entity that threw this entity.
/// </summary>
using Content.Shared.Database;
using Content.Shared.Gravity;
using Content.Shared.Physics;
-using Content.Shared.Physics.Pull;
-using Robust.Shared.GameStates;
+using Content.Shared.Movement.Pulling.Events;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Events;
private void HandlePullStarted(PullStartedMessage message)
{
// TODO: this isn't directed so things have to be done the bad way
- if (EntityManager.TryGetComponent(message.Pulled.Owner, out ThrownItemComponent? thrownItemComponent))
- StopThrow(message.Pulled.Owner, thrownItemComponent);
+ if (EntityManager.TryGetComponent(message.PulledUid, out ThrownItemComponent? thrownItemComponent))
+ StopThrow(message.PulledUid, thrownItemComponent);
}
public void StopThrow(EntityUid uid, ThrownItemComponent thrownItemComponent)