]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Pulling rework v2 (#24936)
authormetalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Tue, 19 Mar 2024 03:30:56 +0000 (14:30 +1100)
committerGitHub <noreply@github.com>
Tue, 19 Mar 2024 03:30:56 +0000 (14:30 +1100)
* Pulling rework

Fixing up the FOUR systems managing pulling, all the shitcode, and also making it nicer ingame.

* More pulling cleanup

* stats

* More cleanup

* First draft

* More pulling

* weh

* Fix puller

* Pulling working

* Fix merge

* Dunked

* Self-merge time

* Fix hotkey

* Fix container changes

* oop

* Fix multi-pulling

* Move alerts cleanup.

* pulling fixes

53 files changed:
Content.Client/Alerts/ClientAlertsSystem.cs
Content.Client/Physics/Controllers/MoverController.cs
Content.Client/Pulling/PullingSystem.cs [deleted file]
Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs
Content.Client/Throwing/ThrownItemVisualizerSystem.cs
Content.IntegrationTests/Tests/Puller/PullerTest.cs
Content.Server/Alert/Click/StopBeingPulled.cs
Content.Server/Alert/Click/StopPulling.cs
Content.Server/Electrocution/ElectrocutionSystem.cs
Content.Server/Hands/Systems/HandsSystem.cs
Content.Server/NPC/HTN/Preconditions/PulledPrecondition.cs
Content.Server/Objectives/Systems/StealConditionSystem.cs
Content.Server/Physics/Controllers/PullController.cs [deleted file]
Content.Server/Pulling/PullingSystem.cs [deleted file]
Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactInteractionTriggerSystem.cs
Content.Server/Zombies/ZombieSystem.Transform.cs
Content.Shared/Administration/AdminFrozenSystem.cs
Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs
Content.Shared/Buckle/SharedBuckleSystem.cs
Content.Shared/Construction/EntitySystems/AnchorableSystem.cs
Content.Shared/Cuffs/SharedCuffableSystem.cs
Content.Shared/Follower/FollowerSystem.cs
Content.Shared/Friction/TileFrictionController.cs
Content.Shared/Interaction/SharedInteractionSystem.cs
Content.Shared/Movement/Pulling/Components/PullableComponent.cs [new file with mode: 0644]
Content.Shared/Movement/Pulling/Components/PullerComponent.cs [new file with mode: 0644]
Content.Shared/Movement/Pulling/Events/AttemptPullEvent.cs [new file with mode: 0644]
Content.Shared/Movement/Pulling/Events/AttemptStopPullingEvent.cs [new file with mode: 0644]
Content.Shared/Movement/Pulling/Events/BeingPulledAttemptEvent.cs [moved from Content.Shared/Pulling/Events/BeingPulledAttemptEvent.cs with 100% similarity]
Content.Shared/Movement/Pulling/Events/PullMessage.cs [new file with mode: 0644]
Content.Shared/Movement/Pulling/Events/PullStartedMessage.cs [new file with mode: 0644]
Content.Shared/Movement/Pulling/Events/PullStoppedMessage.cs [new file with mode: 0644]
Content.Shared/Movement/Pulling/Events/StartPullAttemptEvent.cs [moved from Content.Shared/Pulling/Events/StartPullAttemptEvent.cs with 100% similarity]
Content.Shared/Movement/Pulling/Systems/PullingSystem.cs [new file with mode: 0644]
Content.Shared/Movement/Systems/SharedMoverController.cs
Content.Shared/Pulling/Components/PullableComponent.cs [deleted file]
Content.Shared/Pulling/Components/SharedPullerComponent.cs [deleted file]
Content.Shared/Pulling/Events/PullAttemptEvent.cs [deleted file]
Content.Shared/Pulling/Events/PullMessage.cs [deleted file]
Content.Shared/Pulling/Events/PullStartedMessage.cs [deleted file]
Content.Shared/Pulling/Events/PullStoppedMessage.cs [deleted file]
Content.Shared/Pulling/PullableMoveMessage.cs [deleted file]
Content.Shared/Pulling/PullableStopMovingMessage.cs [deleted file]
Content.Shared/Pulling/Systems/SharedPullableSystem.cs [deleted file]
Content.Shared/Pulling/Systems/SharedPullerSystem.cs [deleted file]
Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs [deleted file]
Content.Shared/Pulling/Systems/SharedPullingSystem.Actions.cs [deleted file]
Content.Shared/Pulling/Systems/SharedPullingSystem.cs [deleted file]
Content.Shared/Security/Systems/DeployableBarrierSystem.cs
Content.Shared/Teleportation/Systems/SharedPortalSystem.cs
Content.Shared/Throwing/ThrowingSystem.cs
Content.Shared/Throwing/ThrownItemComponent.cs
Content.Shared/Throwing/ThrownItemSystem.cs

index 237f24e3eae96171bd1d33f1c3bba263aba6bddc..9c4ebb9cd2ba62c1bde7f553334f61eb9e6e72c6 100644 (file)
@@ -4,6 +4,7 @@ using JetBrains.Annotations;
 using Robust.Client.Player;
 using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
 
 namespace Content.Client.Alerts;
 
@@ -12,6 +13,7 @@ public sealed class ClientAlertsSystem : AlertsSystem
 {
     public AlertOrderPrototype? AlertOrder { get; set; }
 
+    [Dependency] private readonly IGameTiming _timing = default!;
     [Dependency] private readonly IPlayerManager _playerManager = default!;
     [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
 
@@ -49,24 +51,23 @@ public sealed class ClientAlertsSystem : AlertsSystem
 
     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)
index 7a8a6e39cffc9591c04ceb0dcc728670280de77a..31042854d4a89120b4112baad77820591297450f 100644 (file)
@@ -1,6 +1,7 @@
 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;
@@ -24,7 +25,7 @@ namespace Content.Client.Physics.Controllers
 
             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)
@@ -40,7 +41,7 @@ namespace Content.Client.Physics.Controllers
                 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.
diff --git a/Content.Client/Pulling/PullingSystem.cs b/Content.Client/Pulling/PullingSystem.cs
deleted file mode 100644 (file)
index 556dadd..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-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);
-        }
-    }
-}
index 86d113defb1e52f54203609881f25a34182716be..2fa862f3df7f19067a99ca3a7b2140903f99c39e 100644 (file)
@@ -3,7 +3,7 @@ using Content.Shared.Interaction.Events;
 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;
index bbd36731104bac72a05a02c153073527ca0da30a..b25b4fbb7deffc3f87705056755f0ac43e384701 100644 (file)
@@ -24,7 +24,7 @@ public sealed class ThrownItemVisualizerSystem : EntitySystem
 
     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);
index ba91f54ff7751207376d74efb80ee73e376203e8..87d174f7272272694466fd57d2b76885ccdd66ff 100644 (file)
@@ -1,6 +1,6 @@
 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;
 
@@ -29,7 +29,7 @@ public sealed class PullerTest
             {
                 foreach (var proto in protoManager.EnumeratePrototypes<EntityPrototype>())
                 {
-                    if (!proto.TryGetComponent(out SharedPullerComponent? puller))
+                    if (!proto.TryGetComponent(out PullerComponent? puller))
                         continue;
 
                     if (!puller.NeedsHands)
index 2cf076fbeed236de5d0b635dcbf09ac7478d6fd4..b02da38ecfa718799da6a3ebedc31c4fd2be38a8 100644 (file)
@@ -1,7 +1,7 @@
 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
@@ -20,9 +20,9 @@ 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);
             }
         }
     }
index 00a4149598517e18da65ee1de6b7bb5bd35ff75f..76f9569429f987974aae86075bd8c6578a00b327 100644 (file)
@@ -1,6 +1,6 @@
 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
@@ -15,12 +15,12 @@ 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);
             }
         }
     }
index f71f8b69742eeec6cc35a216eaf97d29716e4ca0..d967013f6525a859c45cff16c44a8f89fa0ea5f5 100644 (file)
@@ -19,7 +19,6 @@ using Content.Shared.Jittering;
 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;
@@ -32,6 +31,8 @@ using Robust.Shared.Physics.Events;
 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;
 
@@ -475,14 +476,14 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem
         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))
         {
index 62278064b685a2478cf09eedd5bd11a71e6961fd..99958db2728b2162596d02758884bede58a843bd 100644 (file)
@@ -1,6 +1,5 @@
 using System.Numerics;
 using Content.Server.Inventory;
-using Content.Server.Pulling;
 using Content.Server.Stack;
 using Content.Server.Stunnable;
 using Content.Shared.ActionBlocker;
@@ -13,9 +12,9 @@ using Content.Shared.Hands.EntitySystems;
 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;
@@ -88,9 +87,8 @@ namespace Content.Server.Hands.Systems
                 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;
@@ -130,13 +128,13 @@ namespace Content.Server.Hands.Systems
 
         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??");
             }
@@ -144,7 +142,7 @@ namespace Content.Server.Hands.Systems
 
         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.
@@ -153,8 +151,10 @@ namespace Content.Server.Hands.Systems
             {
                 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;
index 64a72b13cfae75d54f3c385e5c0b113d00249f40..d276be721870f45ab31ad4b246456511c03c2ce0 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Shared.Pulling;
+using PullingSystem = Content.Shared.Movement.Pulling.Systems.PullingSystem;
 
 namespace Content.Server.NPC.HTN.Preconditions;
 
@@ -7,14 +8,14 @@ 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)
index 02d4ee010b5883aef9bb85205f5028a2a2259661..0fe6f0947c876bf2973f80526f23005d002f8448 100644 (file)
@@ -6,11 +6,10 @@ using Content.Shared.Objectives.Systems;
 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;
 
@@ -100,19 +99,19 @@ public sealed class StealConditionSystem : EntitySystem
         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);
                 }
             }
diff --git a/Content.Server/Physics/Controllers/PullController.cs b/Content.Server/Physics/Controllers/PullController.cs
deleted file mode 100644 (file)
index 8f58f80..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-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);
-                }
-            }
-        }
-    }
-}
diff --git a/Content.Server/Pulling/PullingSystem.cs b/Content.Server/Pulling/PullingSystem.cs
deleted file mode 100644 (file)
index 69bb7c9..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-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);
-        }
-    }
-}
index 239b6741608171b18a361c01b818345fc64c1a1d..9976d56da0b2943a92ba1674848608aa47a00a44 100644 (file)
@@ -1,6 +1,6 @@
 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;
@@ -22,7 +22,7 @@ public sealed class ArtifactInteractionTriggerSystem : EntitySystem
         if (!component.PullActivation)
             return;
 
-        _artifactSystem.TryActivateArtifact(uid, args.Puller.Owner);
+        _artifactSystem.TryActivateArtifact(uid, args.PullerUid);
     }
 
     private void OnAttack(EntityUid uid, ArtifactInteractionTriggerComponent component, AttackedEvent args)
index 8ae10d7383449d07fad075d36b46b42ed83e146c..63cc87308670d0d0b5f9a544bf2ae2e32e3c88d2 100644 (file)
@@ -24,6 +24,7 @@ using Content.Shared.Humanoid;
 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;
@@ -31,7 +32,6 @@ using Content.Shared.Nutrition.AnimalHusbandry;
 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;
@@ -59,7 +59,6 @@ namespace Content.Server.Zombies
         [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>
@@ -264,7 +263,9 @@ namespace Content.Server.Zombies
                 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.
index 14438cc59120cc76e418ae73f55980d7383b3968..4ec9600b0bdce2b987eb852dac1b2cd4eb4c70fe 100644 (file)
@@ -1,13 +1,10 @@
 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;
@@ -15,7 +12,7 @@ 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()
     {
@@ -45,9 +42,9 @@ public sealed class AdminFrozenSystem : EntitySystem
 
     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);
index b58bdf83e4929a919668325822330cc353b69978..2527f7455308ecadcf150cff5973674958548576 100644 (file)
@@ -10,7 +10,6 @@ using Content.Shared.Interaction;
 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;
@@ -19,6 +18,7 @@ using Content.Shared.Verbs;
 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;
 
@@ -348,11 +348,11 @@ public abstract partial class SharedBuckleSystem
         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);
             }
         }
 
@@ -361,12 +361,12 @@ public abstract partial class SharedBuckleSystem
             _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);
             }
         }
 
index 8f6833566374dad3a993d9fbf8fc2c63dbfbd0cf..67218657e52e994c6d4f6d6a390cdb14712ee5cc 100644 (file)
@@ -15,6 +15,7 @@ using Robust.Shared.Network;
 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;
 
@@ -35,7 +36,7 @@ public abstract partial class SharedBuckleSystem : EntitySystem
     [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!;
index 70bcfbab43f4964b4d1e4c47e8d097ebdcd517fe..f06278c6d73e30ef5504fac0b027f813fe355f67 100644 (file)
@@ -6,16 +6,15 @@ using Content.Shared.Coordinates.Helpers;
 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;
@@ -27,7 +26,7 @@ public sealed partial class AnchorableSystem : EntitySystem
     [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!;
@@ -132,9 +131,9 @@ public sealed partial class AnchorableSystem : EntitySystem
         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!
@@ -175,7 +174,7 @@ public sealed partial class AnchorableSystem : EntitySystem
     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))
@@ -198,7 +197,7 @@ public sealed partial class AnchorableSystem : EntitySystem
     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))
index fc005fd30fa12aa6c123f8674356d0cfd3529e2f..5cade56aca1b4e1483b885ceade1a3691c6ad1c8 100644 (file)
@@ -21,9 +21,8 @@ using Content.Shared.Inventory.VirtualItem;
 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;
@@ -36,6 +35,7 @@ using Robust.Shared.Containers;
 using Robust.Shared.Network;
 using Robust.Shared.Player;
 using Robust.Shared.Serialization;
+using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent;
 
 namespace Content.Shared.Cuffs
 {
@@ -70,7 +70,7 @@ 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);
@@ -182,7 +182,7 @@ namespace Content.Shared.Cuffs
 
         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.
@@ -214,19 +214,19 @@ namespace Content.Shared.Cuffs
 
         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)
index 5656778a3f9448aefec13fbfe604fa971a285b08..fc7cccf9bd6ade208709867daa49886826208410 100644 (file)
@@ -4,8 +4,8 @@ using Content.Shared.Follower.Components;
 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;
index ba4d9fc24f8b40486910734cc4826655a1dd3194..3583947ee360274cf9b3ea7fb228d45b16c40e5a 100644 (file)
@@ -2,8 +2,8 @@ using System.Numerics;
 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;
@@ -23,6 +23,12 @@ namespace Content.Shared.Friction
         [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;
@@ -33,18 +39,17 @@ namespace Content.Shared.Friction
 
             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;
@@ -60,16 +65,16 @@ namespace Content.Shared.Friction
                 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;
                 }
@@ -82,8 +87,8 @@ namespace Content.Shared.Friction
                 // 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;
                 }
@@ -163,9 +168,7 @@ namespace Content.Shared.Friction
         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))
@@ -175,9 +178,9 @@ namespace Content.Shared.Friction
                 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;
             }
@@ -197,7 +200,7 @@ namespace Content.Shared.Friction
 
             while (anc.MoveNext(out var tileEnt))
             {
-                if (frictionQuery.TryGetComponent(tileEnt, out var friction))
+                if (_frictionQuery.TryGetComponent(tileEnt, out var friction))
                     return friction.Modifier;
             }
 
index 7d5e390b6f01ef625a7150ac9ac3cb4fd6fc6302..7c85ede9b0718768915a56b600c7598cbad8a8c5 100644 (file)
@@ -15,10 +15,10 @@ using Content.Shared.Inventory;
 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;
@@ -60,7 +60,7 @@ namespace Content.Shared.Interaction
         [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!;
@@ -185,10 +185,10 @@ namespace Content.Shared.Interaction
             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;
         }
 
diff --git a/Content.Shared/Movement/Pulling/Components/PullableComponent.cs b/Content.Shared/Movement/Pulling/Components/PullableComponent.cs
new file mode 100644 (file)
index 0000000..db889e7
--- /dev/null
@@ -0,0 +1,39 @@
+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;
+}
diff --git a/Content.Shared/Movement/Pulling/Components/PullerComponent.cs b/Content.Shared/Movement/Pulling/Components/PullerComponent.cs
new file mode 100644 (file)
index 0000000..1fc9b73
--- /dev/null
@@ -0,0 +1,41 @@
+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;
+}
diff --git a/Content.Shared/Movement/Pulling/Events/AttemptPullEvent.cs b/Content.Shared/Movement/Pulling/Events/AttemptPullEvent.cs
new file mode 100644 (file)
index 0000000..b0101c4
--- /dev/null
@@ -0,0 +1,13 @@
+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; }
+}
diff --git a/Content.Shared/Movement/Pulling/Events/AttemptStopPullingEvent.cs b/Content.Shared/Movement/Pulling/Events/AttemptStopPullingEvent.cs
new file mode 100644 (file)
index 0000000..cd7edc5
--- /dev/null
@@ -0,0 +1,10 @@
+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
diff --git a/Content.Shared/Movement/Pulling/Events/PullMessage.cs b/Content.Shared/Movement/Pulling/Events/PullMessage.cs
new file mode 100644 (file)
index 0000000..a427e44
--- /dev/null
@@ -0,0 +1,13 @@
+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;
+    }
+}
diff --git a/Content.Shared/Movement/Pulling/Events/PullStartedMessage.cs b/Content.Shared/Movement/Pulling/Events/PullStartedMessage.cs
new file mode 100644 (file)
index 0000000..29460e1
--- /dev/null
@@ -0,0 +1,9 @@
+namespace Content.Shared.Movement.Pulling.Events;
+
+public sealed class PullStartedMessage : PullMessage
+{
+    public PullStartedMessage(EntityUid pullerUid, EntityUid pullableUid) :
+        base(pullerUid, pullableUid)
+    {
+    }
+}
diff --git a/Content.Shared/Movement/Pulling/Events/PullStoppedMessage.cs b/Content.Shared/Movement/Pulling/Events/PullStoppedMessage.cs
new file mode 100644 (file)
index 0000000..47aa345
--- /dev/null
@@ -0,0 +1,13 @@
+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)
+    {
+    }
+}
diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs
new file mode 100644 (file)
index 0000000..33794ba
--- /dev/null
@@ -0,0 +1,492 @@
+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;
+    }
+}
index 2c03507f6174a4067e443de1292610de007ffa30..98b898615dba2b85f95c386ae58eb48dd38b4a27 100644 (file)
@@ -9,7 +9,6 @@ using Content.Shared.Maps;
 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;
@@ -23,6 +22,7 @@ using Robust.Shared.Physics.Controllers;
 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
 {
@@ -53,7 +53,7 @@ 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;
@@ -85,7 +85,7 @@ namespace Content.Shared.Movement.Systems
             RelayTargetQuery = GetEntityQuery<MovementRelayTargetComponent>();
             PhysicsQuery = GetEntityQuery<PhysicsComponent>();
             RelayQuery = GetEntityQuery<RelayInputMoverComponent>();
-            PullableQuery = GetEntityQuery<SharedPullableComponent>();
+            PullableQuery = GetEntityQuery<PullableComponent>();
             XformQuery = GetEntityQuery<TransformComponent>();
             NoRotateQuery = GetEntityQuery<NoRotateOnMoveComponent>();
             CanMoveInAirQuery = GetEntityQuery<CanMoveInAirComponent>();
@@ -376,7 +376,7 @@ namespace Content.Shared.Movement.Systems
                     !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;
                 }
diff --git a/Content.Shared/Pulling/Components/PullableComponent.cs b/Content.Shared/Pulling/Components/PullableComponent.cs
deleted file mode 100644 (file)
index c5c3068..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-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;
-        }
-    }
-}
diff --git a/Content.Shared/Pulling/Components/SharedPullerComponent.cs b/Content.Shared/Pulling/Components/SharedPullerComponent.cs
deleted file mode 100644 (file)
index 57a86e7..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-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;
-    }
-}
diff --git a/Content.Shared/Pulling/Events/PullAttemptEvent.cs b/Content.Shared/Pulling/Events/PullAttemptEvent.cs
deleted file mode 100644 (file)
index 6296dc2..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-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; }
-    }
-}
diff --git a/Content.Shared/Pulling/Events/PullMessage.cs b/Content.Shared/Pulling/Events/PullMessage.cs
deleted file mode 100644 (file)
index b11de7c..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-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;
-        }
-    }
-}
diff --git a/Content.Shared/Pulling/Events/PullStartedMessage.cs b/Content.Shared/Pulling/Events/PullStartedMessage.cs
deleted file mode 100644 (file)
index 0ede284..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-using Robust.Shared.Physics.Components;
-
-namespace Content.Shared.Physics.Pull
-{
-    public sealed class PullStartedMessage : PullMessage
-    {
-        public PullStartedMessage(PhysicsComponent puller, PhysicsComponent pulled) :
-            base(puller, pulled)
-        {
-        }
-    }
-}
diff --git a/Content.Shared/Pulling/Events/PullStoppedMessage.cs b/Content.Shared/Pulling/Events/PullStoppedMessage.cs
deleted file mode 100644 (file)
index afcbcb7..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-using Robust.Shared.Physics.Components;
-
-namespace Content.Shared.Physics.Pull
-{
-    public sealed class PullStoppedMessage : PullMessage
-    {
-        public PullStoppedMessage(PhysicsComponent puller, PhysicsComponent pulled) : base(puller, pulled)
-        {
-        }
-    }
-}
diff --git a/Content.Shared/Pulling/PullableMoveMessage.cs b/Content.Shared/Pulling/PullableMoveMessage.cs
deleted file mode 100644 (file)
index 46c6b12..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Content.Shared.Pulling
-{
-    public sealed class PullableMoveMessage : EntityEventArgs
-    {
-    }
-}
diff --git a/Content.Shared/Pulling/PullableStopMovingMessage.cs b/Content.Shared/Pulling/PullableStopMovingMessage.cs
deleted file mode 100644 (file)
index 0233e32..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Content.Shared.Pulling
-{
-    public sealed class PullableStopMovingMessage : EntityEventArgs
-    {
-    }
-}
diff --git a/Content.Shared/Pulling/Systems/SharedPullableSystem.cs b/Content.Shared/Pulling/Systems/SharedPullableSystem.cs
deleted file mode 100644 (file)
index 3dab476..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-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);
-        }
-    }
-}
diff --git a/Content.Shared/Pulling/Systems/SharedPullerSystem.cs b/Content.Shared/Pulling/Systems/SharedPullerSystem.cs
deleted file mode 100644 (file)
index e388d7a..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-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);
-        }
-    }
-}
diff --git a/Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs b/Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs
deleted file mode 100644 (file)
index 38ed899..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-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);
-        }
-    }
-}
diff --git a/Content.Shared/Pulling/Systems/SharedPullingSystem.Actions.cs b/Content.Shared/Pulling/Systems/SharedPullingSystem.Actions.cs
deleted file mode 100644 (file)
index 1e2bb90..0000000
+++ /dev/null
@@ -1,239 +0,0 @@
-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);
-        }
-    }
-}
diff --git a/Content.Shared/Pulling/Systems/SharedPullingSystem.cs b/Content.Shared/Pulling/Systems/SharedPullingSystem.cs
deleted file mode 100644 (file)
index 0c139ee..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-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);
-        }
-    }
-}
index 7b9ce841a9939b1813ee959f1d1038c32420a280..622edc4b62ecd4595856ab5e55111d4cc23d57ec 100644 (file)
@@ -1,6 +1,6 @@
 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;
 
@@ -11,7 +11,7 @@ public sealed class DeployableBarrierSystem : EntitySystem
     [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()
@@ -54,8 +54,8 @@ public sealed class DeployableBarrierSystem : EntitySystem
                 _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))
index ebd83624114a0d3b5802f1118544808acd395708..8d67aec518a6832b569e6b517989cfc0abb003aa 100644 (file)
@@ -1,9 +1,9 @@
 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;
@@ -28,7 +28,7 @@ public abstract class SharedPortalSystem : EntitySystem
     [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";
@@ -93,15 +93,15 @@ public abstract class SharedPortalSystem : EntitySystem
             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
index 54294318315053a3caee9017d861e95d76d5dfdd..7c861a85adb0f7046b2af1478baa084a5366040d 100644 (file)
@@ -20,6 +20,11 @@ public sealed class ThrowingSystem : EntitySystem
 {
     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>
@@ -42,15 +47,17 @@ public sealed class ThrowingSystem : EntitySystem
         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>
@@ -65,6 +72,8 @@ public sealed class ThrowingSystem : EntitySystem
         float strength = 1.0f,
         EntityUid? user = null,
         float pushbackRatio = PushbackDefault,
+        bool recoil = true,
+        bool animated = true,
         bool playSound = true)
     {
         var physicsQuery = GetEntityQuery<PhysicsComponent>();
@@ -72,7 +81,6 @@ public sealed class ThrowingSystem : EntitySystem
             return;
 
         var projectileQuery = GetEntityQuery<ProjectileComponent>();
-        var tagQuery = GetEntityQuery<TagComponent>();
 
         TryThrow(
             uid,
@@ -82,8 +90,7 @@ public sealed class ThrowingSystem : EntitySystem
             projectileQuery,
             strength,
             user,
-            pushbackRatio,
-            playSound);
+            pushbackRatio, recoil: recoil, animated: animated, playSound: playSound);
     }
 
     /// <summary>
@@ -101,6 +108,8 @@ public sealed class ThrowingSystem : EntitySystem
         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)
@@ -116,12 +125,17 @@ public sealed class ThrowingSystem : EntitySystem
         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);
@@ -166,7 +180,8 @@ public sealed class ThrowingSystem : EntitySystem
         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 &&
index 16c9b13254cf099e54cc27f350454a66d95bb7b9..f7defaa4aabe3f4b0c3efc25ebcdad1c62f536b1 100644 (file)
@@ -8,6 +8,12 @@ namespace Content.Shared.Throwing
     [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>
index 0f38c4d753383b3661eb37c4650ecce346c9aefe..8645d9b72339739855e97d060e1b932a49284bd4 100644 (file)
@@ -3,8 +3,7 @@ using Content.Shared.Administration.Logs;
 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;
@@ -85,8 +84,8 @@ namespace Content.Shared.Throwing
         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)