]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
ECS dragdrop (#12973)
authormetalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Mon, 13 Feb 2023 13:29:34 +0000 (00:29 +1100)
committerGitHub <noreply@github.com>
Mon, 13 Feb 2023 13:29:34 +0000 (13:29 +0000)
* ECS dragdrop

No more excuses.

AAAAAAAAAAAAAA

* kry

* events

aaaaaaaaaa

* HUH

* Fix stripping

* aaaaaa

* spoike

* asease

* fix table vaulting

* ded

* rebiew

aaaaaaaaaaaaa

* drag

* aeaeae

* weh

53 files changed:
Content.Client/DragDrop/DragDropHelper.cs
Content.Client/DragDrop/DragDropSystem.cs
Content.Client/Kitchen/Components/KitchenSpikeComponent.cs
Content.Client/Kitchen/KitchenSpikeSystem.cs [new file with mode: 0644]
Content.Client/MedicalScanner/MedicalScannerComponent.cs
Content.Client/Movement/Systems/ClimbSystem.cs
Content.Client/Strip/StrippableComponent.cs [deleted file]
Content.Client/Strip/StrippableSystem.cs
Content.Server/Buckle/Systems/BuckleSystem.Buckle.cs
Content.Server/Buckle/Systems/BuckleSystem.Strap.cs
Content.Server/Climbing/ClimbSystem.cs
Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs
Content.Server/Interaction/InteractionSystem.cs
Content.Server/Kitchen/Components/KitchenSpikeComponent.cs
Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs
Content.Server/Kitchen/EntitySystems/SharpSystem.cs
Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs
Content.Server/Medical/Components/MedicalScannerComponent.cs
Content.Server/Medical/CryoPodSystem.cs
Content.Server/Medical/MedicalScannerSystem.cs
Content.Server/Strip/StrippableComponent.cs [deleted file]
Content.Server/Strip/StrippableSystem.cs
Content.Shared/ActionBlocker/ActionBlockerSystem.cs
Content.Shared/Body/Components/BodyComponent.cs
Content.Shared/Body/Systems/SharedBodySystem.Body.cs
Content.Shared/Buckle/SharedBuckleSystem.Strap.cs
Content.Shared/Climbing/BonkSystem.cs
Content.Shared/Climbing/SharedClimbSystem.cs
Content.Shared/Cuffs/SharedCuffableSystem.cs
Content.Shared/Disposal/SharedDisposalUnitSystem.cs
Content.Shared/DragDrop/CanDragDropOnEvent.cs [deleted file]
Content.Shared/DragDrop/DragDropRequestEvent.cs
Content.Shared/DragDrop/DraggableEvents.cs [new file with mode: 0644]
Content.Shared/DragDrop/DropAttemptEvent.cs [deleted file]
Content.Shared/DragDrop/IDragDropOn.cs [deleted file]
Content.Shared/DragDrop/IDraggable.cs [deleted file]
Content.Shared/DragDrop/SharedDragDropSystem.cs
Content.Shared/Ghost/SharedGhostSystem.cs
Content.Shared/Hands/HandEvents.cs
Content.Shared/Inventory/InventorySystem.Equip.cs
Content.Shared/Kitchen/Components/SharedKitchenSpikeComponent.cs
Content.Shared/Kitchen/SharedKitchenSpikeSystem.cs [new file with mode: 0644]
Content.Shared/Medical/Cryogenics/SharedCryoPodComponent.cs
Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs
Content.Shared/MedicalScanner/SharedMedicalScannerComponent.cs
Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs
Content.Shared/Nutrition/Components/ButcherableComponent.cs [moved from Content.Shared/Nutrition/Components/SharedButcherableComponent.cs with 51% similarity]
Content.Shared/PAI/SharedPAISystem.cs
Content.Shared/Strip/Components/SharedStrippingComponent.cs [deleted file]
Content.Shared/Strip/Components/StrippableComponent.cs [moved from Content.Shared/Strip/Components/SharedStrippableComponent.cs with 72% similarity]
Content.Shared/Strip/Components/StrippingComponent.cs [new file with mode: 0644]
Content.Shared/Strip/SharedStrippableSystem.cs [new file with mode: 0644]
Content.Shared/Stunnable/SharedStunSystem.cs

index 959ff5e7899a8a1f6e5272285311da7843c687fe..c5d0ccffc0b792bbb889def7792dca9809886f9b 100644 (file)
 using Robust.Client.Input;
 using Robust.Shared.Map;
 
-namespace Content.Client.DragDrop
+namespace Content.Client.DragDrop;
+
+/// <summary>
+/// Helper for implementing drag and drop interactions.
+///
+/// The basic flow for a drag drop interaction as per this helper is:
+/// 1. User presses mouse down on something (using class should communicate this to helper by calling MouseDown()).
+/// 2. User continues to hold the mouse down and moves the mouse outside of the defined
+///    deadzone. OnBeginDrag is invoked to see if a drag should be initiated. If so, initiates a drag.
+///    If user didn't move the mouse beyond the deadzone the drag is not initiated (OnEndDrag invoked).
+/// 3. Every Update/FrameUpdate, OnContinueDrag is invoked.
+/// 4. User lifts mouse up. This is not handled by DragDropHelper. The using class of the helper should
+///     do whatever they want and then end the drag by calling EndDrag() (which invokes OnEndDrag).
+///
+/// If for any reason the drag is ended, OnEndDrag is invoked.
+/// </summary>
+/// <typeparam name="T">thing being dragged and dropped</typeparam>
+public sealed class DragDropHelper<T>
 {
+    private readonly IInputManager _inputManager;
+
+    private readonly OnBeginDrag _onBeginDrag;
+    private readonly OnEndDrag _onEndDrag;
+    private readonly OnContinueDrag _onContinueDrag;
+    public float Deadzone = 2f;
+
     /// <summary>
-    /// Helper for implementing drag and drop interactions.
-    ///
-    /// The basic flow for a drag drop interaction as per this helper is:
-    /// 1. User presses mouse down on something (using class should communicate this to helper by calling MouseDown()).
-    /// 2. User continues to hold the mouse down and moves the mouse outside of the defined
-    ///    deadzone. OnBeginDrag is invoked to see if a drag should be initiated. If so, initiates a drag.
-    ///    If user didn't move the mouse beyond the deadzone the drag is not initiated (OnEndDrag invoked).
-    /// 3. Every Update/FrameUpdate, OnContinueDrag is invoked.
-    /// 4. User lifts mouse up. This is not handled by DragDropHelper. The using class of the helper should
-    ///     do whatever they want and then end the drag by calling EndDrag() (which invokes OnEndDrag).
-    ///
-    /// If for any reason the drag is ended, OnEndDrag is invoked.
+    /// Convenience method, current mouse screen position as provided by inputmanager.
     /// </summary>
-    /// <typeparam name="T">thing being dragged and dropped</typeparam>
-    public sealed class DragDropHelper<T>
+    public ScreenCoordinates MouseScreenPosition => _inputManager.MouseScreenPosition;
+
+    /// <summary>
+    /// True if initiated a drag and currently dragging something.
+    /// I.e. this will be false if we've just had a mousedown over something but the mouse
+    /// has not moved outside of the drag deadzone.
+    /// </summary>
+    public bool IsDragging => _state == DragState.Dragging;
+
+    /// <summary>
+    /// Current thing being dragged or which mouse button is being held down on.
+    /// </summary>
+    public T? Dragged { get; private set; }
+
+    // screen pos where the mouse down began for the drag
+    private ScreenCoordinates _mouseDownScreenPos;
+    private DragState _state = DragState.NotDragging;
+
+    private enum DragState : byte
     {
-        private readonly IInputManager _inputManager;
-
-        private readonly OnBeginDrag _onBeginDrag;
-        private readonly OnEndDrag _onEndDrag;
-        private readonly OnContinueDrag _onContinueDrag;
-        public float Deadzone = 2f;
-
-        /// <summary>
-        /// Convenience method, current mouse screen position as provided by inputmanager.
-        /// </summary>
-        public ScreenCoordinates MouseScreenPosition => _inputManager.MouseScreenPosition;
-
-        /// <summary>
-        /// True if initiated a drag and currently dragging something.
-        /// I.e. this will be false if we've just had a mousedown over something but the mouse
-        /// has not moved outside of the drag deadzone.
-        /// </summary>
-        public bool IsDragging => _state == DragState.Dragging;
-
-        /// <summary>
-        /// Current thing being dragged or which mouse button is being held down on.
-        /// </summary>
-        public T? Dragged { get; private set; }
-
-        // screen pos where the mouse down began for the drag
-        private ScreenCoordinates _mouseDownScreenPos;
-        private DragState _state = DragState.NotDragging;
-
-        private enum DragState : byte
-        {
-            NotDragging,
-            // not dragging yet, waiting to see
-            // if they hold for long enough
-            MouseDown,
-            // currently dragging something
-            Dragging,
-        }
+        NotDragging,
+        // not dragging yet, waiting to see
+        // if they hold for long enough
+        MouseDown,
+        // currently dragging something
+        Dragging,
+    }
 
-        /// <param name="onBeginDrag"><see cref="OnBeginDrag"/></param>
-        /// <param name="onContinueDrag"><see cref="OnContinueDrag"/></param>
-        /// <param name="onEndDrag"><see cref="OnEndDrag"/></param>
-        public DragDropHelper(OnBeginDrag onBeginDrag, OnContinueDrag onContinueDrag, OnEndDrag onEndDrag)
+    /// <param name="onBeginDrag"><see cref="OnBeginDrag"/></param>
+    /// <param name="onContinueDrag"><see cref="OnContinueDrag"/></param>
+    /// <param name="onEndDrag"><see cref="OnEndDrag"/></param>
+    public DragDropHelper(OnBeginDrag onBeginDrag, OnContinueDrag onContinueDrag, OnEndDrag onEndDrag)
+    {
+        _inputManager = IoCManager.Resolve<IInputManager>();
+        _onBeginDrag = onBeginDrag;
+        _onEndDrag = onEndDrag;
+        _onContinueDrag = onContinueDrag;
+    }
+
+    /// <summary>
+    /// Tell the helper that the mouse button was pressed down on
+    /// a target, thus a drag has the possibility to begin for this target.
+    /// Assumes current mouse screen position is the location the mouse was clicked.
+    ///
+    /// EndDrag should be called when the drag is done.
+    /// </summary>
+    public void MouseDown(T target)
+    {
+        if (_state != DragState.NotDragging)
         {
-            _inputManager = IoCManager.Resolve<IInputManager>();
-            _onBeginDrag = onBeginDrag;
-            _onEndDrag = onEndDrag;
-            _onContinueDrag = onContinueDrag;
+            EndDrag();
         }
 
-        /// <summary>
-        /// Tell the helper that the mouse button was pressed down on
-        /// a target, thus a drag has the possibility to begin for this target.
-        /// Assumes current mouse screen position is the location the mouse was clicked.
-        ///
-        /// EndDrag should be called when the drag is done.
-        /// </summary>
-        public void MouseDown(T target)
-        {
-            if (_state != DragState.NotDragging)
-            {
-                EndDrag();
-            }
+        Dragged = target;
+        _state = DragState.MouseDown;
+        _mouseDownScreenPos = _inputManager.MouseScreenPosition;
+    }
 
-            Dragged = target;
-            _state = DragState.MouseDown;
-            _mouseDownScreenPos = _inputManager.MouseScreenPosition;
-        }
+    /// <summary>
+    /// Stop the current drag / drop operation no matter what state it is in.
+    /// </summary>
+    public void EndDrag()
+    {
+        Dragged = default;
+        _state = DragState.NotDragging;
+        _onEndDrag.Invoke();
+    }
 
-        /// <summary>
-        /// Stop the current drag / drop operation no matter what state it is in.
-        /// </summary>
-        public void EndDrag()
+    private void StartDragging()
+    {
+        if (_onBeginDrag.Invoke())
         {
-            Dragged = default;
-            _state = DragState.NotDragging;
-            _onEndDrag.Invoke();
+            _state = DragState.Dragging;
         }
-
-        private void StartDragging()
+        else
         {
-            if (_onBeginDrag.Invoke())
-            {
-                _state = DragState.Dragging;
-            }
-            else
-            {
-                EndDrag();
-            }
+            EndDrag();
         }
+    }
 
-        /// <summary>
-        /// Should be invoked by using class every FrameUpdate or Update.
-        /// </summary>
-        public void Update(float frameTime)
+    /// <summary>
+    /// Should be invoked by using class every FrameUpdate or Update.
+    /// </summary>
+    public void Update(float frameTime)
+    {
+        switch (_state)
         {
-            switch (_state)
+            // check if dragging should begin
+            case DragState.MouseDown:
             {
-                // check if dragging should begin
-                case DragState.MouseDown:
+                var screenPos = _inputManager.MouseScreenPosition;
+                if ((_mouseDownScreenPos.Position - screenPos.Position).Length > Deadzone)
                 {
-                    var screenPos = _inputManager.MouseScreenPosition;
-                    if ((_mouseDownScreenPos.Position - screenPos.Position).Length > Deadzone)
-                    {
-                        StartDragging();
-                    }
-
-                    break;
+                    StartDragging();
                 }
-                case DragState.Dragging:
-                {
-                    if (!_onContinueDrag.Invoke(frameTime))
-                    {
-                        EndDrag();
-                    }
 
-                    break;
+                break;
+            }
+            case DragState.Dragging:
+            {
+                if (!_onContinueDrag.Invoke(frameTime))
+                {
+                    EndDrag();
                 }
+
+                break;
             }
         }
     }
-
-    /// <summary>
-    /// Invoked when a drag is confirmed and going to be initiated. Implementation should
-    /// typically set the drag shadow texture based on the target.
-    /// </summary>
-    /// <returns>true if drag should begin, false to end.</returns>
-    public delegate bool OnBeginDrag();
-
-    /// <summary>
-    /// Invoked every frame when drag is ongoing. Typically implementation should
-    /// make the drag shadow follow the mouse position.
-    /// </summary>
-    /// <returns>true if drag should continue, false to end.</returns>
-    public delegate bool OnContinueDrag(float frameTime);
-
-    /// <summary>
-    /// invoked when
-    /// the drag drop is ending for any reason. This
-    /// should typically just clear the drag shadow.
-    /// </summary>
-    public delegate void OnEndDrag();
-
 }
+
+/// <summary>
+/// Invoked when a drag is confirmed and going to be initiated. Implementation should
+/// typically set the drag shadow texture based on the target.
+/// </summary>
+/// <returns>true if drag should begin, false to end.</returns>
+public delegate bool OnBeginDrag();
+
+/// <summary>
+/// Invoked every frame when drag is ongoing. Typically implementation should
+/// make the drag shadow follow the mouse position.
+/// </summary>
+/// <returns>true if drag should continue, false to end.</returns>
+public delegate bool OnContinueDrag(float frameTime);
+
+/// <summary>
+/// invoked when
+/// the drag drop is ending for any reason. This
+/// should typically just clear the drag shadow.
+/// </summary>
+public delegate void OnEndDrag();
index 1e12d53ba5daab4cd4da66e1109b76ec695d3703..5be800260ddd2e3b4b4c749bb03a2fc756bd8726 100644 (file)
@@ -1,10 +1,8 @@
 using Content.Client.CombatMode;
 using Content.Client.Gameplay;
 using Content.Client.Outline;
-using Content.Client.Viewport;
 using Content.Shared.ActionBlocker;
 using Content.Shared.CCVar;
-using Content.Shared.CombatMode;
 using Content.Shared.DragDrop;
 using Content.Shared.Interaction;
 using Content.Shared.Interaction.Events;
@@ -18,480 +16,538 @@ using Robust.Client.State;
 using Robust.Shared.Configuration;
 using Robust.Shared.Input;
 using Robust.Shared.Input.Binding;
+using Robust.Shared.Map;
+using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Utility;
 using System.Linq;
 using DrawDepth = Content.Shared.DrawDepth.DrawDepth;
 
-namespace Content.Client.DragDrop
+namespace Content.Client.DragDrop;
+
+/// <summary>
+/// Handles clientside drag and drop logic
+/// </summary>
+[UsedImplicitly]
+public sealed class DragDropSystem : SharedDragDropSystem
 {
+    [Dependency] private readonly IStateManager _stateManager = default!;
+    [Dependency] private readonly IInputManager _inputManager = default!;
+    [Dependency] private readonly IEyeManager _eyeManager = default!;
+    [Dependency] private readonly IPlayerManager _playerManager = default!;
+    [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+    [Dependency] private readonly IConfigurationManager _cfgMan = default!;
+    [Dependency] private readonly InteractionOutlineSystem _outline = default!;
+    [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
+    [Dependency] private readonly CombatModeSystem _combatMode = default!;
+    [Dependency] private readonly InputSystem _inputSystem = default!;
+    [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
+    [Dependency] private readonly EntityLookupSystem _lookup = default!;
+    [Dependency] private readonly SharedPopupSystem _popup = default!;
+
+    private ISawmill _sawmill = default!;
+
+    // how often to recheck possible targets (prevents calling expensive
+    // check logic each update)
+    private const float TargetRecheckInterval = 0.25f;
+
+    // if a drag ends up being cancelled and it has been under this
+    // amount of time since the mousedown, we will "replay" the original
+    // mousedown event so it can be treated like a regular click
+    private const float MaxMouseDownTimeForReplayingClick = 0.85f;
+
+    private const string ShaderDropTargetInRange = "SelectionOutlineInrange";
+    private const string ShaderDropTargetOutOfRange = "SelectionOutline";
+
+    /// <summary>
+    /// Current entity being dragged around.
+    /// </summary>
+    private EntityUid? _draggedEntity;
+
+    /// <summary>
+    /// If an entity is being dragged is there a drag shadow.
+    /// </summary>
+    private EntityUid? _dragShadow;
+
+    /// <summary>
+    /// Time since mouse down over the dragged entity
+    /// </summary>
+    private float _mouseDownTime;
+
+    /// <summary>
+    /// how much time since last recheck of all possible targets
+    /// </summary>
+    private float _targetRecheckTime;
+
     /// <summary>
-    /// Handles clientside drag and drop logic
+    /// Reserved initial mousedown event so we can replay it if no drag ends up being performed
     /// </summary>
-    [UsedImplicitly]
-    public sealed class DragDropSystem : SharedDragDropSystem
+    private PointerInputCmdHandler.PointerInputCmdArgs? _savedMouseDown;
+
+    /// <summary>
+    /// Whether we are currently replaying the original mouse down, so we
+    /// can ignore any events sent to this system
+    /// </summary>
+    private bool _isReplaying;
+
+    private float _deadzone;
+
+    private DragState _state = DragState.NotDragging;
+
+    /// <summary>
+    /// screen pos where the mouse down began for the drag
+    /// </summary>
+    private ScreenCoordinates? _mouseDownScreenPos;
+
+    private ShaderInstance? _dropTargetInRangeShader;
+    private ShaderInstance? _dropTargetOutOfRangeShader;
+
+    private readonly List<SpriteComponent> _highlightedSprites = new();
+
+    public override void Initialize()
     {
-        [Dependency] private readonly IStateManager _stateManager = default!;
-        [Dependency] private readonly IInputManager _inputManager = default!;
-        [Dependency] private readonly IEyeManager _eyeManager = default!;
-        [Dependency] private readonly IPlayerManager _playerManager = default!;
-        [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
-        [Dependency] private readonly IConfigurationManager _cfgMan = default!;
-        [Dependency] private readonly InteractionOutlineSystem _outline = default!;
-        [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
-        [Dependency] private readonly CombatModeSystem _combatMode = default!;
-        [Dependency] private readonly InputSystem _inputSystem = default!;
-        [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
-        [Dependency] private readonly EntityLookupSystem _lookup = default!;
-
-        // how often to recheck possible targets (prevents calling expensive
-        // check logic each update)
-        private const float TargetRecheckInterval = 0.25f;
-
-        // if a drag ends up being cancelled and it has been under this
-        // amount of time since the mousedown, we will "replay" the original
-        // mousedown event so it can be treated like a regular click
-        private const float MaxMouseDownTimeForReplayingClick = 0.85f;
-
-        private const string ShaderDropTargetInRange = "SelectionOutlineInrange";
-        private const string ShaderDropTargetOutOfRange = "SelectionOutline";
-
-        // entity performing the drag action
-
-        private EntityUid _dragger;
-        private readonly List<IDraggable> _draggables = new();
-        private EntityUid _dragShadow;
-
-        // time since mouse down over the dragged entity
-        private float _mouseDownTime;
-        // how much time since last recheck of all possible targets
-        private float _targetRecheckTime;
-        // reserved initial mousedown event so we can replay it if no drag ends up being performed
-        private PointerInputCmdHandler.PointerInputCmdArgs? _savedMouseDown;
-        // whether we are currently replaying the original mouse down, so we
-        // can ignore any events sent to this system
-        private bool _isReplaying;
-
-        private DragDropHelper<EntityUid> _dragDropHelper = default!;
-
-        private ShaderInstance? _dropTargetInRangeShader;
-        private ShaderInstance? _dropTargetOutOfRangeShader;
-
-        private readonly List<SpriteComponent> _highlightedSprites = new();
-
-        public override void Initialize()
-        {
-            UpdatesOutsidePrediction = true;
-            UpdatesAfter.Add(typeof(EyeUpdateSystem));
-
-            _dragDropHelper = new DragDropHelper<EntityUid>(OnBeginDrag, OnContinueDrag, OnEndDrag);
-            _cfgMan.OnValueChanged(CCVars.DragDropDeadZone, SetDeadZone, true);
-
-            _dropTargetInRangeShader = _prototypeManager.Index<ShaderPrototype>(ShaderDropTargetInRange).Instance();
-            _dropTargetOutOfRangeShader = _prototypeManager.Index<ShaderPrototype>(ShaderDropTargetOutOfRange).Instance();
-            // needs to fire on mouseup and mousedown so we can detect a drag / drop
-            CommandBinds.Builder
-                .BindBefore(EngineKeyFunctions.Use, new PointerInputCmdHandler(OnUse, false), new[] { typeof(SharedInteractionSystem) })
-                .Register<DragDropSystem>();
-        }
+        base.Initialize();
+        _sawmill = Logger.GetSawmill("drag_drop");
+        UpdatesOutsidePrediction = true;
+        UpdatesAfter.Add(typeof(EyeUpdateSystem));
+
+        _cfgMan.OnValueChanged(CCVars.DragDropDeadZone, SetDeadZone, true);
+
+        _dropTargetInRangeShader = _prototypeManager.Index<ShaderPrototype>(ShaderDropTargetInRange).Instance();
+        _dropTargetOutOfRangeShader = _prototypeManager.Index<ShaderPrototype>(ShaderDropTargetOutOfRange).Instance();
+        // needs to fire on mouseup and mousedown so we can detect a drag / drop
+        CommandBinds.Builder
+            .BindBefore(EngineKeyFunctions.Use, new PointerInputCmdHandler(OnUse, false), new[] { typeof(SharedInteractionSystem) })
+            .Register<DragDropSystem>();
+    }
+
+    private void SetDeadZone(float deadZone)
+    {
+        _deadzone = deadZone;
+    }
+
+    public override void Shutdown()
+    {
+        _cfgMan.UnsubValueChanged(CCVars.DragDropDeadZone, SetDeadZone);
+        CommandBinds.Unregister<DragDropSystem>();
+        base.Shutdown();
+    }
 
-        private void SetDeadZone(float deadZone)
+    private bool OnUse(in PointerInputCmdHandler.PointerInputCmdArgs args)
+    {
+        // not currently predicted
+        if (_inputSystem.Predicted)
+            return false;
+
+        // currently replaying a saved click, don't handle this because
+        // we already decided this click doesn't represent an actual drag attempt
+        if (_isReplaying)
+            return false;
+
+        if (args.State == BoundKeyState.Down)
         {
-            _dragDropHelper.Deadzone = deadZone;
+            return OnUseMouseDown(args);
         }
 
-        public override void Shutdown()
+        if (args.State == BoundKeyState.Up)
         {
-            _cfgMan.UnsubValueChanged(CCVars.DragDropDeadZone, SetDeadZone);
-            _dragDropHelper.EndDrag();
-            CommandBinds.Unregister<DragDropSystem>();
-            base.Shutdown();
+            return OnUseMouseUp(args);
         }
 
-        private bool OnUse(in PointerInputCmdHandler.PointerInputCmdArgs args)
+        return false;
+    }
+
+    private void EndDrag()
+    {
+        if (_state == DragState.NotDragging)
+            return;
+
+        if (_dragShadow != null)
         {
-            // not currently predicted
-            if (_inputSystem.Predicted) return false;
+            Del(_dragShadow.Value);
+            _dragShadow = null;
+        }
 
-            // currently replaying a saved click, don't handle this because
-            // we already decided this click doesn't represent an actual drag attempt
-            if (_isReplaying) return false;
+        _draggedEntity = null;
+        _state = DragState.NotDragging;
+        _mouseDownScreenPos = null;
 
-            if (args.State == BoundKeyState.Down)
-            {
-                return OnUseMouseDown(args);
-            }
-            else if (args.State == BoundKeyState.Up)
-            {
-                return OnUseMouseUp(args);
-            }
+        RemoveHighlights();
+        _outline.SetEnabled(true);
+        _mouseDownTime = 0;
+        _savedMouseDown = null;
+    }
 
+    private bool OnUseMouseDown(in PointerInputCmdHandler.PointerInputCmdArgs args)
+    {
+        if (args.Session?.AttachedEntity is not {Valid: true} dragger ||
+            _combatMode.IsInCombatMode())
+        {
             return false;
         }
 
-        private bool OnUseMouseDown(in PointerInputCmdHandler.PointerInputCmdArgs args)
-        {
-            if (args.Session?.AttachedEntity is not {Valid: true} dragger ||
-                _combatMode.IsInCombatMode())
-            {
-                return false;
-            }
-
-            // cancel any current dragging if there is one (shouldn't be because they would've had to have lifted
-            // the mouse, canceling the drag, but just being cautious)
-            _dragDropHelper.EndDrag();
+        // cancel any current dragging if there is one (shouldn't be because they would've had to have lifted
+        // the mouse, canceling the drag, but just being cautious)
+        EndDrag();
 
-            // possibly initiating a drag
-            // check if the clicked entity is draggable
-            if (!EntityManager.EntityExists(args.EntityUid))
-            {
-                return false;
-            }
+        // possibly initiating a drag
+        // check if the clicked entity is draggable
+        if (!Exists(args.EntityUid))
+        {
+            return false;
+        }
 
-            // check if the entity is reachable
-            if (!_interactionSystem.InRangeUnobstructed(dragger, args.EntityUid))
-            {
-                return false;
-            }
+        // check if the entity is reachable
+        if (!_interactionSystem.InRangeUnobstructed(dragger, args.EntityUid))
+        {
+            return false;
+        }
 
-            var canDrag = false;
-            foreach (var draggable in EntityManager.GetComponents<IDraggable>(args.EntityUid))
-            {
-                var dragEventArgs = new StartDragDropEvent(dragger, args.EntityUid);
+        var ev = new CanDragEvent();
 
-                if (!draggable.CanStartDrag(dragEventArgs))
-                {
-                    continue;
-                }
+        RaiseLocalEvent(args.EntityUid, ref ev);
 
-                _draggables.Add(draggable);
-                canDrag = true;
-            }
+        if (ev.Handled != true)
+            return false;
 
-            if (!canDrag)
-            {
-                return false;
-            }
+        _draggedEntity = args.EntityUid;
+        _state = DragState.MouseDown;
+        _mouseDownScreenPos = _inputManager.MouseScreenPosition;
+        _mouseDownTime = 0;
 
-            // wait to initiate a drag
-            _dragDropHelper.MouseDown(args.EntityUid);
-            _dragger = dragger;
-            _mouseDownTime = 0;
+        // don't want anything else to process the click,
+        // but we will save the event so we can "re-play" it if this drag does
+        // not turn into an actual drag so the click can be handled normally
+        _savedMouseDown = args;
 
-            // don't want anything else to process the click,
-            // but we will save the event so we can "re-play" it if this drag does
-            // not turn into an actual drag so the click can be handled normally
-            _savedMouseDown = args;
+        return true;
 
-            return true;
+    }
 
+    private void StartDrag()
+    {
+        if (!Exists(_draggedEntity))
+        {
+            // something happened to the clicked entity or we moved the mouse off the target so
+            // we shouldn't replay the original click
+            return;
         }
 
-        private bool OnBeginDrag()
+        _state = DragState.Dragging;
+        DebugTools.Assert(_dragShadow == null);
+        _outline.SetEnabled(false);
+        HighlightTargets();
+
+        if (TryComp<SpriteComponent>(_draggedEntity, out var draggedSprite))
         {
-            if (_dragDropHelper.Dragged == default || Deleted(_dragDropHelper.Dragged))
+            // pop up drag shadow under mouse
+            var mousePos = _eyeManager.ScreenToMap(_inputManager.MouseScreenPosition);
+            _dragShadow = EntityManager.SpawnEntity("dragshadow", mousePos);
+            var dragSprite = Comp<SpriteComponent>(_dragShadow.Value);
+            dragSprite.CopyFrom(draggedSprite);
+            dragSprite.RenderOrder = EntityManager.CurrentTick.Value;
+            dragSprite.Color = dragSprite.Color.WithAlpha(0.7f);
+            // keep it on top of everything
+            dragSprite.DrawDepth = (int) DrawDepth.Overlays;
+            if (!dragSprite.NoRotation)
             {
-                // something happened to the clicked entity or we moved the mouse off the target so
-                // we shouldn't replay the original click
-                return false;
+                Transform(_dragShadow.Value).WorldRotation = Transform(_draggedEntity.Value).WorldRotation;
             }
 
-            if (EntityManager.TryGetComponent<SpriteComponent?>(_dragDropHelper.Dragged, out var draggedSprite))
-            {
-                // pop up drag shadow under mouse
-                var mousePos = _eyeManager.ScreenToMap(_dragDropHelper.MouseScreenPosition);
-                _dragShadow = EntityManager.SpawnEntity("dragshadow", mousePos);
-                var dragSprite = EntityManager.GetComponent<SpriteComponent>(_dragShadow);
-                dragSprite.CopyFrom(draggedSprite);
-                dragSprite.RenderOrder = EntityManager.CurrentTick.Value;
-                dragSprite.Color = dragSprite.Color.WithAlpha(0.7f);
-                // keep it on top of everything
-                dragSprite.DrawDepth = (int) DrawDepth.Overlays;
-                if (!dragSprite.NoRotation)
-                {
-                    EntityManager.GetComponent<TransformComponent>(_dragShadow).WorldRotation = EntityManager.GetComponent<TransformComponent>(_dragDropHelper.Dragged).WorldRotation;
-                }
-
-                HighlightTargets();
-                _outline.SetEnabled(false);
+            // drag initiated
+            return;
+        }
 
-                // drag initiated
-                return true;
-            }
+        _sawmill.Warning($"Unable to display drag shadow for {ToPrettyString(_draggedEntity.Value)} because it has no sprite component.");
+    }
 
-            Logger.Warning("Unable to display drag shadow for {0} because it" +
-                           " has no sprite component.", EntityManager.GetComponent<MetaDataComponent>(_dragDropHelper.Dragged).EntityName);
+    private bool UpdateDrag(float frameTime)
+    {
+        if (!Exists(_draggedEntity) || _combatMode.IsInCombatMode())
+        {
+            EndDrag();
             return false;
         }
 
-        private bool OnContinueDrag(float frameTime)
-        {
-            if (_dragDropHelper.Dragged == default || Deleted(_dragDropHelper.Dragged) ||
-                _combatMode.IsInCombatMode())
-            {
-                return false;
-            }
-
-            DebugTools.AssertNotNull(_dragger);
-
-            // still in range of the thing we are dragging?
-            if (!_interactionSystem.InRangeUnobstructed(_dragger, _dragDropHelper.Dragged))
-            {
-                return false;
-            }
+        var player = _playerManager.LocalPlayer?.ControlledEntity;
 
-            // TODO: would use MapPosition instead if it had a setter, but it has no setter.
-            // is that intentional, or should we add a setter for Transform.MapPosition?
-            if (_dragShadow == default)
-                return false;
+        // still in range of the thing we are dragging?
+        if (player == null || !_interactionSystem.InRangeUnobstructed(player.Value, _draggedEntity.Value))
+        {
+            return false;
+        }
 
-            _targetRecheckTime += frameTime;
-            if (_targetRecheckTime > TargetRecheckInterval)
-            {
-                HighlightTargets();
-                _targetRecheckTime -= TargetRecheckInterval;
-            }
+        if (_dragShadow == null)
+            return false;
 
-            return true;
-        }
+        _targetRecheckTime += frameTime;
 
-        private void OnEndDrag()
+        if (_targetRecheckTime > TargetRecheckInterval)
         {
-            RemoveHighlights();
-            if (_dragShadow != default)
-            {
-                EntityManager.DeleteEntity(_dragShadow);
-            }
-
-            _outline.SetEnabled(true);
-            _dragShadow = default;
-            _draggables.Clear();
-            _dragger = default;
-            _mouseDownTime = 0;
-            _savedMouseDown = null;
+            HighlightTargets();
+            _targetRecheckTime -= TargetRecheckInterval;
         }
 
-        private bool OnUseMouseUp(in PointerInputCmdHandler.PointerInputCmdArgs args)
+        return true;
+    }
+
+    private bool OnUseMouseUp(in PointerInputCmdHandler.PointerInputCmdArgs args)
+    {
+        if (_state == DragState.MouseDown)
         {
-            if (_dragDropHelper.IsDragging == false || _dragDropHelper.Dragged == default)
+            // haven't started the drag yet, quick mouseup, definitely treat it as a normal click by
+            // replaying the original cmd
+            try
             {
-                // haven't started the drag yet, quick mouseup, definitely treat it as a normal click by
-                // replaying the original cmd
                 if (_savedMouseDown.HasValue && _mouseDownTime < MaxMouseDownTimeForReplayingClick)
                 {
                     var savedValue = _savedMouseDown.Value;
                     _isReplaying = true;
                     // adjust the timing info based on the current tick so it appears as if it happened now
                     var replayMsg = savedValue.OriginalMessage;
-                    var adjustedInputMsg = new FullInputCmdMessage(args.OriginalMessage.Tick, args.OriginalMessage.SubTick,
-                        replayMsg.InputFunctionId, replayMsg.State, replayMsg.Coordinates, replayMsg.ScreenCoordinates, replayMsg.Uid);
+
+                    var adjustedInputMsg = new FullInputCmdMessage(args.OriginalMessage.Tick,
+                        args.OriginalMessage.SubTick,
+                        replayMsg.InputFunctionId, replayMsg.State, replayMsg.Coordinates, replayMsg.ScreenCoordinates,
+                        replayMsg.Uid);
 
                     if (savedValue.Session != null)
                     {
-                        _inputSystem.HandleInputCommand(savedValue.Session, EngineKeyFunctions.Use, adjustedInputMsg, true);
+                        _inputSystem.HandleInputCommand(savedValue.Session, EngineKeyFunctions.Use, adjustedInputMsg,
+                            true);
                     }
 
                     _isReplaying = false;
                 }
-                _dragDropHelper.EndDrag();
-                return false;
             }
-
-            if (_dragger == default)
+            finally
             {
-                _dragDropHelper.EndDrag();
-                return false;
+                EndDrag();
             }
 
-            IList<EntityUid> entities;
-
-            if (_stateManager.CurrentState is GameplayState screen)
-            {
-                entities = screen.GetClickableEntities(args.Coordinates).ToList();
-            }
-            else
-            {
-                entities = Array.Empty<EntityUid>();
-            }
+            return false;
+        }
 
-            var outOfRange = false;
+        var localPlayer = _playerManager.LocalPlayer?.ControlledEntity;
 
-            foreach (var entity in entities)
-            {
-                if (entity == _dragDropHelper.Dragged) continue;
-
-                // check if it's able to be dropped on by current dragged entity
-                var dropArgs = new DragDropEvent(_dragger, args.Coordinates, _dragDropHelper.Dragged, entity);
+        if (localPlayer == null || !Exists(_draggedEntity))
+        {
+            EndDrag();
+            return false;
+        }
 
-                // TODO: Cache valid CanDragDrops
-                if (ValidDragDrop(dropArgs) != true) continue;
+        IEnumerable<EntityUid> entities;
 
-                if (!_interactionSystem.InRangeUnobstructed(dropArgs.User, dropArgs.Target)
-                    || !_interactionSystem.InRangeUnobstructed(dropArgs.User, dropArgs.Dragged))
-                {
-                    outOfRange = true;
-                    continue;
-                }
+        if (_stateManager.CurrentState is GameplayState screen)
+        {
+            entities = screen.GetClickableEntities(args.Coordinates);
+        }
+        else
+        {
+            entities = Array.Empty<EntityUid>();
+        }
 
-                foreach (var draggable in _draggables)
-                {
-                    if (!draggable.CanDrop(dropArgs)) continue;
+        var outOfRange = false;
+        var user = localPlayer.Value;
 
-                    // tell the server about the drop attempt
-                    RaiseNetworkEvent(new DragDropRequestEvent(args.Coordinates, _dragDropHelper.Dragged,
-                        entity));
+        foreach (var entity in entities)
+        {
+            if (entity == _draggedEntity)
+                continue;
 
-                    draggable.Drop(dropArgs);
+            // check if it's able to be dropped on by current dragged entity
+            var valid = ValidDragDrop(user, _draggedEntity.Value, entity);
 
-                    _dragDropHelper.EndDrag();
-                    return true;
-                }
-            }
+            if (valid != true) continue;
 
-            if (outOfRange &&
-                _playerManager.LocalPlayer?.ControlledEntity is { } player &&
-                player.IsValid())
+            if (!_interactionSystem.InRangeUnobstructed(user, entity)
+                || !_interactionSystem.InRangeUnobstructed(user, _draggedEntity.Value))
             {
-                player.PopupMessage(Loc.GetString("drag-drop-system-out-of-range-text"));
+                outOfRange = true;
+                continue;
             }
 
-            _dragDropHelper.EndDrag();
-            return false;
+            // tell the server about the drop attempt
+            RaiseNetworkEvent(new DragDropRequestEvent(_draggedEntity.Value, entity));
+            EndDrag();
+            return true;
         }
 
-        // TODO make this just use TargetOutlineSystem
-        private void HighlightTargets()
+        if (outOfRange)
         {
-            if (_dragDropHelper.Dragged == default || Deleted(_dragDropHelper.Dragged) ||
-                _dragShadow == default || Deleted(_dragShadow))
-            {
-                Logger.Warning("Programming error. Can't highlight drag and drop targets, not currently " +
-                               "dragging anything or dragged entity / shadow was deleted.");
-                return;
-            }
+            _popup.PopupEntity(Loc.GetString("drag-drop-system-out-of-range-text"), _draggedEntity.Value, Filter.Local(), true);
+        }
 
-            // highlights the possible targets which are visible
-            // and able to be dropped on by the current dragged entity
+        EndDrag();
+        return false;
+    }
 
-            // remove current highlights
-            RemoveHighlights();
+    // TODO make this just use TargetOutlineSystem
+    private void HighlightTargets()
+    {
+        if (!Exists(_draggedEntity) ||
+            !Exists(_dragShadow))
+        {
+            return;
+        }
 
-            // find possible targets on screen even if not reachable
-            // TODO: Duplicated in SpriteSystem and TargetOutlineSystem. Should probably be cached somewhere for a frame?
-            var mousePos = _eyeManager.ScreenToMap(_inputManager.MouseScreenPosition).Position;
-            var bounds = new Box2(mousePos - 1.5f, mousePos + 1.5f);
-            var pvsEntities = _lookup.GetEntitiesIntersecting(_eyeManager.CurrentMap, bounds, LookupFlags.Approximate | LookupFlags.Static);
-            foreach (var pvsEntity in pvsEntities)
-            {
-                if (!EntityManager.TryGetComponent(pvsEntity, out SpriteComponent? inRangeSprite) ||
-                    !inRangeSprite.Visible ||
-                    pvsEntity == _dragDropHelper.Dragged) continue;
+        var user = _playerManager.LocalPlayer?.ControlledEntity;
 
-                // check if it's able to be dropped on by current dragged entity
-                var dropArgs = new DragDropEvent(_dragger, EntityManager.GetComponent<TransformComponent>(pvsEntity).Coordinates, _dragDropHelper.Dragged, pvsEntity);
+        if (user == null)
+            return;
 
-                var valid = ValidDragDrop(dropArgs);
-                if (valid == null) continue;
+        // highlights the possible targets which are visible
+        // and able to be dropped on by the current dragged entity
 
-                // We'll do a final check given server-side does this before any dragdrop can take place.
-                if (valid.Value)
-                {
-                    valid = _interactionSystem.InRangeUnobstructed(dropArgs.Target, dropArgs.Dragged)
-                        && _interactionSystem.InRangeUnobstructed(dropArgs.Target, dropArgs.Target);
-                }
+        // remove current highlights
+        RemoveHighlights();
 
-                if (inRangeSprite.PostShader != null &&
-                    inRangeSprite.PostShader != _dropTargetInRangeShader &&
-                    inRangeSprite.PostShader != _dropTargetOutOfRangeShader)
-                    return;
+        // find possible targets on screen even if not reachable
+        // TODO: Duplicated in SpriteSystem and TargetOutlineSystem. Should probably be cached somewhere for a frame?
+        var mousePos = _eyeManager.ScreenToMap(_inputManager.MouseScreenPosition);
+        var bounds = new Box2(mousePos.Position - 1.5f, mousePos.Position + 1.5f);
+        var pvsEntities = _lookup.GetEntitiesIntersecting(mousePos.MapId, bounds);
 
-                // highlight depending on whether its in or out of range
-                inRangeSprite.PostShader = valid.Value ? _dropTargetInRangeShader : _dropTargetOutOfRangeShader;
-                inRangeSprite.RenderOrder = EntityManager.CurrentTick.Value;
-                _highlightedSprites.Add(inRangeSprite);
-            }
-        }
+        var spriteQuery = GetEntityQuery<SpriteComponent>();
 
-        private void RemoveHighlights()
+        foreach (var entity in pvsEntities)
         {
-            foreach (var highlightedSprite in _highlightedSprites)
+            if (!spriteQuery.TryGetComponent(entity, out var inRangeSprite) ||
+                !inRangeSprite.Visible ||
+                entity == _draggedEntity)
+            {
+                continue;
+            }
+
+            var valid = ValidDragDrop(user.Value, _draggedEntity.Value, entity);
+
+            // check if it's able to be dropped on by current dragged entity
+            if (valid == null)
+                continue;
+
+            // We'll do a final check given server-side does this before any dragdrop can take place.
+            if (valid.Value)
             {
-                if (highlightedSprite.PostShader != _dropTargetInRangeShader && highlightedSprite.PostShader != _dropTargetOutOfRangeShader)
-                    continue;
+                valid = _interactionSystem.InRangeUnobstructed(user.Value, _draggedEntity.Value)
+                        && _interactionSystem.InRangeUnobstructed(user.Value, entity);
+            }
 
-                highlightedSprite.PostShader = null;
-                highlightedSprite.RenderOrder = 0;
+            if (inRangeSprite.PostShader != null &&
+                inRangeSprite.PostShader != _dropTargetInRangeShader &&
+                inRangeSprite.PostShader != _dropTargetOutOfRangeShader)
+            {
+                continue;
             }
 
-            _highlightedSprites.Clear();
+            // highlight depending on whether its in or out of range
+            inRangeSprite.PostShader = valid.Value ? _dropTargetInRangeShader : _dropTargetOutOfRangeShader;
+            inRangeSprite.RenderOrder = EntityManager.CurrentTick.Value;
+            _highlightedSprites.Add(inRangeSprite);
         }
+    }
 
-        /// <summary>
-        ///     Are these args valid for drag-drop?
-        /// </summary>
-        /// <param name="eventArgs"></param>
-        /// <returns>null if the target doesn't support IDragDropOn</returns>
-        private bool? ValidDragDrop(DragDropEvent eventArgs)
+    private void RemoveHighlights()
+    {
+        foreach (var highlightedSprite in _highlightedSprites)
         {
-            if (!_actionBlockerSystem.CanInteract(eventArgs.User, eventArgs.Target))
-            {
-                return false;
-            }
+            if (highlightedSprite.PostShader != _dropTargetInRangeShader && highlightedSprite.PostShader != _dropTargetOutOfRangeShader)
+                continue;
 
-            // CanInteract() doesn't support checking a second "target" entity.
-            // Doing so manually:
-            var ev = new GettingInteractedWithAttemptEvent(eventArgs.User, eventArgs.Dragged);
-            RaiseLocalEvent(eventArgs.Dragged, ev, true);
-            if (ev.Cancelled)
-                return false;
+            highlightedSprite.PostShader = null;
+            highlightedSprite.RenderOrder = 0;
+        }
 
-            var valid = CheckDragDropOn(eventArgs);
+        _highlightedSprites.Clear();
+    }
 
-            foreach (var comp in EntityManager.GetComponents<IDragDropOn>(eventArgs.Target))
-            {
-                if (!comp.CanDragDropOn(eventArgs))
-                {
-                    valid = false;
-                    // dragDropOn.Add(comp);
-                    continue;
-                }
+    /// <summary>
+    ///     Are these args valid for drag-drop?
+    /// </summary>
+    /// <returns>
+    /// Returns null if no interactions are available or the user / target cannot interact with each other.
+    /// Returns false if interactions exist but are not available currently.
+    /// </returns>
+    private bool? ValidDragDrop(EntityUid user, EntityUid dragged, EntityUid target)
+    {
+        if (!_actionBlockerSystem.CanInteract(user, target))
+            return null;
 
-                valid = true;
-                break;
-            }
+        // CanInteract() doesn't support checking a second "target" entity.
+        // Doing so manually:
+        var ev = new GettingInteractedWithAttemptEvent(user, dragged);
+        RaiseLocalEvent(dragged, ev, true);
 
-            if (valid != true) return valid;
+        if (ev.Cancelled)
+            return false;
 
-            // Need at least one IDraggable to return true or else we can't do shit
-            valid = false;
+        var dropEv = new CanDropDraggedEvent(user, target);
 
-            foreach (var comp in EntityManager.GetComponents<IDraggable>(eventArgs.User))
-            {
-                if (!comp.CanDrop(eventArgs)) continue;
-                valid = true;
-                break;
-            }
+        RaiseLocalEvent(dragged, ref dropEv);
 
-            return valid;
+        if (dropEv.Handled)
+        {
+            if (!dropEv.CanDrop)
+                return false;
         }
 
-        public override void Update(float frameTime)
-        {
-            base.Update(frameTime);
+        var dropEv2 = new CanDropTargetEvent(user, dragged);
 
-            _dragDropHelper.Update(frameTime);
-        }
+        RaiseLocalEvent(target, ref dropEv2);
 
-        public override void FrameUpdate(float frameTime)
-        {
-            base.FrameUpdate(frameTime);
+        if (dropEv2.Handled)
+            return dropEv2.CanDrop;
+
+        return null;
+    }
 
-            // Update position every frame to make it smooth.
-            if (_dragDropHelper.IsDragging)
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
+
+        switch (_state)
+        {
+            // check if dragging should begin
+            case DragState.MouseDown:
             {
-                var mousePos = _eyeManager.ScreenToMap(_inputManager.MouseScreenPosition);
-                Transform(_dragShadow).WorldPosition = mousePos.Position;
+                var screenPos = _inputManager.MouseScreenPosition;
+                if ((_mouseDownScreenPos!.Value.Position - screenPos.Position).Length > _deadzone)
+                {
+                    StartDrag();
+                }
+
+                break;
             }
+            case DragState.Dragging:
+                UpdateDrag(frameTime);
+                break;
+        }
+    }
+
+    public override void FrameUpdate(float frameTime)
+    {
+        base.FrameUpdate(frameTime);
+
+        // Update position every frame to make it smooth.
+        if (Exists(_dragShadow))
+        {
+            var mousePos = _eyeManager.ScreenToMap(_inputManager.MouseScreenPosition);
+            Transform(_dragShadow.Value).WorldPosition = mousePos.Position;
         }
     }
 }
+
+public enum DragState : byte
+{
+    NotDragging,
+
+    /// <summary>
+    /// Not dragging yet, waiting to see
+    /// if they hold for long enough
+    /// </summary>
+    MouseDown,
+
+    /// <summary>
+    /// Currently dragging something
+    /// </summary>
+    Dragging,
+}
index 58f271d63f1cecc605f513fddc13b97709ff06bb..375677ef608b541802f36fcda13b188ba634ac6f 100644 (file)
@@ -1,15 +1,9 @@
-using Content.Shared.DragDrop;
 using Content.Shared.Kitchen.Components;
-using Robust.Shared.GameObjects;
 
 namespace Content.Client.Kitchen.Components
 {
-    [RegisterComponent]
-    internal sealed class KitchenSpikeComponent : SharedKitchenSpikeComponent
+    [RegisterComponent, ComponentReference(typeof(SharedKitchenSpikeComponent))]
+    public sealed class KitchenSpikeComponent : SharedKitchenSpikeComponent
     {
-        public override bool DragDropOn(DragDropEvent eventArgs)
-        {
-            return true;
-        }
     }
 }
diff --git a/Content.Client/Kitchen/KitchenSpikeSystem.cs b/Content.Client/Kitchen/KitchenSpikeSystem.cs
new file mode 100644 (file)
index 0000000..3627a29
--- /dev/null
@@ -0,0 +1,8 @@
+using Content.Shared.Kitchen;
+
+namespace Content.Client.Kitchen;
+
+public sealed class KitchenSpikeSystem : SharedKitchenSpikeSystem
+{
+
+}
index 49bcbc63a97ac2282422df62ab4a2b570d7a6415..1369544cd68d165b5bdca6a85400a5e7e5b07fae 100644 (file)
@@ -1,16 +1,9 @@
-using Content.Shared.DragDrop;
-using Content.Shared.MedicalScanner;
-using Robust.Shared.GameObjects;
+using Content.Shared.MedicalScanner;
 
 namespace Content.Client.MedicalScanner
 {
     [RegisterComponent]
-    [ComponentReference(typeof(SharedMedicalScannerComponent))]
     public sealed class MedicalScannerComponent : SharedMedicalScannerComponent
     {
-        public override bool DragDropOn(DragDropEvent eventArgs)
-        {
-            return false;
-        }
     }
 }
index 1f7b757ba446c61e80de0391bac7c74a9444f314..826c3b0a52d61c22e1f8058435af1f39d367c7e7 100644 (file)
@@ -13,6 +13,7 @@ public sealed class ClimbSystem : SharedClimbSystem
     {
         base.Initialize();
         SubscribeLocalEvent<ClimbingComponent, ComponentHandleState>(OnClimbingState);
+        SubscribeLocalEvent<ClimbableComponent, CanDropTargetEvent>(OnCanDragDropOn);
     }
 
     private static void OnClimbingState(EntityUid uid, ClimbingComponent component, ref ComponentHandleState args)
@@ -24,15 +25,15 @@ public sealed class ClimbSystem : SharedClimbSystem
         component.OwnerIsTransitioning = climbModeState.IsTransitioning;
     }
 
-    protected override void OnCanDragDropOn(EntityUid uid, ClimbableComponent component, CanDragDropOnEvent args)
+    protected override void OnCanDragDropOn(EntityUid uid, ClimbableComponent component, ref CanDropTargetEvent args)
     {
-        base.OnCanDragDropOn(uid, component, args);
+        base.OnCanDragDropOn(uid, component, ref args);
 
         if (!args.CanDrop)
             return;
 
         var user = args.User;
-        var target = args.Target;
+        var target = uid;
         var dragged = args.Dragged;
         bool Ignored(EntityUid entity) => entity == target || entity == user || entity == dragged;
 
diff --git a/Content.Client/Strip/StrippableComponent.cs b/Content.Client/Strip/StrippableComponent.cs
deleted file mode 100644 (file)
index daad176..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-using Content.Shared.DragDrop;
-using Content.Shared.Strip.Components;
-using Robust.Shared.GameObjects;
-
-namespace Content.Client.Strip
-{
-    [RegisterComponent]
-    [ComponentReference(typeof(SharedStrippableComponent))]
-    public sealed class StrippableComponent : SharedStrippableComponent
-    {
-        public override bool Drop(DragDropEvent args)
-        {
-            // TODO: Prediction
-            return false;
-        }
-    }
-}
index 59083c45154c3fa199f8bfbab4406c0f8762c160..bc7e3255508e0a105b4741615e80fbf467006725 100644 (file)
@@ -3,14 +3,16 @@ using Content.Shared.Cuffs.Components;
 using Content.Shared.Ensnaring.Components;
 using Content.Shared.Hands;
 using Content.Shared.Inventory.Events;
+using Content.Shared.Strip;
+using Content.Shared.Strip.Components;
 using Robust.Client.GameObjects;
 
 namespace Content.Client.Strip;
 
 /// <summary>
-///     This is the client-side stripping system, which just triggers UI updates on events. 
+///     This is the client-side stripping system, which just triggers UI updates on events.
 /// </summary>
-public sealed class StrippableSystem : EntitySystem
+public sealed class StrippableSystem : SharedStrippableSystem
 {
     public override void Initialize()
     {
index 4ab9439f949a3e1237062b9f1232e44eb4c88411..48ec12f9f9bb32f353d59edbc9b3de7a5f0c11e9 100644 (file)
@@ -31,8 +31,8 @@ public sealed partial class BuckleSystem
         SubscribeLocalEvent<BuckleComponent, InteractHandEvent>(HandleInteractHand);
         SubscribeLocalEvent<BuckleComponent, GetVerbsEvent<InteractionVerb>>(AddUnbuckleVerb);
         SubscribeLocalEvent<BuckleComponent, InsertIntoEntityStorageAttemptEvent>(OnEntityStorageInsertAttempt);
-        SubscribeLocalEvent<BuckleComponent, CanDropEvent>(OnBuckleCanDrop);
-        SubscribeLocalEvent<BuckleComponent, DragDropEvent>(OnBuckleDragDrop);
+        SubscribeLocalEvent<BuckleComponent, CanDropDraggedEvent>(OnBuckleCanDrop);
+        SubscribeLocalEvent<BuckleComponent, DragDropDraggedEvent>(OnBuckleDragDrop);
     }
 
     private void AddUnbuckleVerb(EntityUid uid, BuckleComponent component, GetVerbsEvent<InteractionVerb> args)
@@ -104,12 +104,12 @@ public sealed partial class BuckleSystem
             args.Cancelled = true;
     }
 
-    private void OnBuckleCanDrop(EntityUid uid, BuckleComponent component, CanDropEvent args)
+    private void OnBuckleCanDrop(EntityUid uid, BuckleComponent component, ref CanDropDraggedEvent args)
     {
         args.Handled = HasComp<StrapComponent>(args.Target);
     }
 
-    private void OnBuckleDragDrop(EntityUid uid, BuckleComponent component, DragDropEvent args)
+    private void OnBuckleDragDrop(EntityUid uid, BuckleComponent component, ref DragDropDraggedEvent args)
     {
         args.Handled = TryBuckle(uid, args.User, args.Target, component);
     }
index 65e15f0fc20ba93e72cf0e0c1fdb84b233f46727..1ca2acdef3b9649c5548b19624993dc9b65d2962 100644 (file)
@@ -27,7 +27,7 @@ public sealed partial class BuckleSystem
         SubscribeLocalEvent<StrapComponent, DestructionEventArgs>((_,c,_) => StrapRemoveAll(c));
         SubscribeLocalEvent<StrapComponent, BreakageEventArgs>((_, c, _) => StrapRemoveAll(c));
         SubscribeLocalEvent<StrapComponent, ConstructionBeforeDeleteEvent>((_, c, _) => StrapRemoveAll(c));
-        SubscribeLocalEvent<StrapComponent, DragDropEvent>(OnStrapDragDrop);
+        SubscribeLocalEvent<StrapComponent, DragDropTargetEvent>(OnStrapDragDrop);
     }
 
     private void OnStrapGetState(EntityUid uid, StrapComponent component, ref ComponentGetState args)
@@ -185,9 +185,9 @@ public sealed partial class BuckleSystem
         Dirty(strap);
     }
 
-    private void OnStrapDragDrop(EntityUid uid, StrapComponent component, DragDropEvent args)
+    private void OnStrapDragDrop(EntityUid uid, StrapComponent component, ref DragDropTargetEvent args)
     {
-        if (!StrapCanDragDropOn(uid, args.User, args.Target, args.Dragged, component))
+        if (!StrapCanDragDropOn(uid, args.User, uid, args.Dragged, component))
             return;
 
         args.Handled = TryBuckle(args.Dragged, args.User, uid);
index f0c953c99a64b2f6b4473a89e774c834571dc400..6f11cb0351033d141ffaa9ca2004fbf5fa0c34c0 100644 (file)
@@ -58,7 +58,7 @@ public sealed class ClimbSystem : SharedClimbSystem
 
         SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
         SubscribeLocalEvent<ClimbableComponent, GetVerbsEvent<AlternativeVerb>>(AddClimbableVerb);
-        SubscribeLocalEvent<ClimbableComponent, DragDropEvent>(OnClimbableDragDrop);
+        SubscribeLocalEvent<ClimbableComponent, DragDropTargetEvent>(OnClimbableDragDrop);
 
         SubscribeLocalEvent<ClimbingComponent, ClimbFinishedEvent>(OnClimbFinished);
         SubscribeLocalEvent<ClimbingComponent, EndCollideEvent>(OnClimbEndCollide);
@@ -68,17 +68,17 @@ public sealed class ClimbSystem : SharedClimbSystem
         SubscribeLocalEvent<GlassTableComponent, ClimbedOnEvent>(OnGlassClimbed);
     }
 
-    protected override void OnCanDragDropOn(EntityUid uid, ClimbableComponent component, CanDragDropOnEvent args)
+    protected override void OnCanDragDropOn(EntityUid uid, ClimbableComponent component, ref CanDropTargetEvent args)
     {
-        base.OnCanDragDropOn(uid, component, args);
+        base.OnCanDragDropOn(uid, component, ref args);
 
         if (!args.CanDrop)
             return;
 
         string reason;
         var canVault = args.User == args.Dragged
-            ? CanVault(component, args.User, args.Target, out reason)
-            : CanVault(component, args.User, args.Dragged, args.Target, out reason);
+            ? CanVault(component, args.User, uid, out reason)
+            : CanVault(component, args.User, args.Dragged, uid, out reason);
 
         if (!canVault)
             _popupSystem.PopupEntity(reason, args.User, args.User);
@@ -103,9 +103,9 @@ public sealed class ClimbSystem : SharedClimbSystem
         });
     }
 
-    private void OnClimbableDragDrop(EntityUid uid, ClimbableComponent component, DragDropEvent args)
+    private void OnClimbableDragDrop(EntityUid uid, ClimbableComponent component, ref DragDropTargetEvent args)
     {
-        TryMoveEntity(component, args.User, args.Dragged, args.Target);
+        TryMoveEntity(component, args.User, args.Dragged, uid);
     }
 
     private void TryMoveEntity(ClimbableComponent component, EntityUid user, EntityUid entityToMove,
index 8439295484b250b4741daf886b48cb08cd4627ff..0e7ed86331a8f0c2e5605dd58a0867e457271371 100644 (file)
@@ -69,7 +69,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
             // Interactions
             SubscribeLocalEvent<DisposalUnitComponent, ActivateInWorldEvent>(HandleActivate);
             SubscribeLocalEvent<DisposalUnitComponent, AfterInteractUsingEvent>(HandleAfterInteractUsing);
-            SubscribeLocalEvent<DisposalUnitComponent, DragDropEvent>(HandleDragDropOn);
+            SubscribeLocalEvent<DisposalUnitComponent, DragDropTargetEvent>(HandleDragDropOn);
             SubscribeLocalEvent<DisposalUnitComponent, DestructionEventArgs>(HandleDestruction);
 
             // Verbs
@@ -391,7 +391,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
             TryEjectContents(uid, component);
         }
 
-        private void HandleDragDropOn(EntityUid uid, DisposalUnitComponent component, DragDropEvent args)
+        private void HandleDragDropOn(EntityUid uid, DisposalUnitComponent component, ref DragDropTargetEvent args)
         {
             args.Handled = TryInsert(uid, args.Dragged, args.User);
         }
index f4fbafcafc9df2b540786b9e4441f5d6edbb77d3..2433332afc7bb116037e8fcea2c02228cac860ea 100644 (file)
@@ -60,58 +60,38 @@ namespace Content.Server.Interaction
         }
 
         #region Drag drop
+
         private void HandleDragDropRequestEvent(DragDropRequestEvent msg, EntitySessionEventArgs args)
         {
-            if (!ValidateClientInput(args.SenderSession, msg.DropLocation, msg.Target, out var userEntity))
-            {
-                Logger.InfoS("system.interaction", $"DragDropRequestEvent input validation failed");
+            if (Deleted(msg.Dragged) || Deleted(msg.Target))
                 return;
-            }
 
-            if (Deleted(msg.Dropped) || Deleted(msg.Target))
-                return;
+            var user = args.SenderSession.AttachedEntity;
 
-            if (!_actionBlockerSystem.CanInteract(userEntity.Value, msg.Target))
+            if (user == null || !_actionBlockerSystem.CanInteract(user.Value, msg.Target))
                 return;
 
-            var interactionArgs = new DragDropEvent(userEntity.Value, msg.DropLocation, msg.Dropped, msg.Target);
-
             // must be in range of both the target and the object they are drag / dropping
             // Client also does this check but ya know we gotta validate it.
-            if (!InRangeUnobstructed(interactionArgs.User, interactionArgs.Dragged, popup: true)
-                || !InRangeUnobstructed(interactionArgs.User, interactionArgs.Target, popup: true))
+            if (!InRangeUnobstructed(user.Value, msg.Dragged, popup: true)
+                || !InRangeUnobstructed(user.Value, msg.Target, popup: true))
+            {
                 return;
+            }
+
+            var dragArgs = new DragDropDraggedEvent(user.Value, msg.Target);
 
             // trigger dragdrops on the dropped entity
-            RaiseLocalEvent(msg.Dropped, interactionArgs, true);
+            RaiseLocalEvent(msg.Dragged, ref dragArgs);
 
-            if (interactionArgs.Handled)
+            if (dragArgs.Handled)
                 return;
 
-            foreach (var dragDrop in AllComps<IDraggable>(msg.Dropped))
-            {
-                if (dragDrop.CanDrop(interactionArgs) &&
-                    dragDrop.Drop(interactionArgs))
-                {
-                    return;
-                }
-            }
-
-            // trigger dragdropons on the targeted entity
-            RaiseLocalEvent(msg.Target, interactionArgs, false);
-
-            if (interactionArgs.Handled)
-                return;
+            var dropArgs = new DragDropTargetEvent(user.Value, msg.Dragged);
 
-            foreach (var dragDropOn in AllComps<IDragDropOn>(msg.Target))
-            {
-                if (dragDropOn.CanDragDropOn(interactionArgs) &&
-                    dragDropOn.DragDropOn(interactionArgs))
-                {
-                    return;
-                }
-            }
+            RaiseLocalEvent(msg.Target, ref dropArgs);
         }
+
         #endregion
     }
 }
index 6abea76ec026108858e9db725e202c9aae81f271..6333ca74e221845cad78417d6ced29fe83499282 100644 (file)
@@ -1,10 +1,9 @@
 using Content.Server.Kitchen.EntitySystems;
-using Content.Shared.DragDrop;
 using Content.Shared.Kitchen.Components;
 
 namespace Content.Server.Kitchen.Components
 {
-    [RegisterComponent, Access(typeof(KitchenSpikeSystem))]
+    [RegisterComponent, Access(typeof(KitchenSpikeSystem)), ComponentReference(typeof(SharedKitchenSpikeComponent))]
     public sealed class KitchenSpikeComponent : SharedKitchenSpikeComponent
     {
         public List<string?>? PrototypesToSpawn;
@@ -16,11 +15,5 @@ namespace Content.Server.Kitchen.Components
 
         // Prevents simultaneous spiking of two bodies (could be replaced with CancellationToken, but I don't see any situation where Cancel could be called)
         public bool InUse;
-
-        // ECS this out!, when DragDropSystem and InteractionSystem refactored
-        public override bool DragDropOn(DragDropEvent eventArgs)
-        {
-            return true;
-        }
     }
 }
index 08ae86cef8bc7072f17e1f4754c1176a59ceb9fd..78d4a01debd0b5e553c29fadcdc1b3c33a9c5795 100644 (file)
@@ -3,7 +3,6 @@ using Content.Server.DoAfter;
 using Content.Server.Kitchen.Components;
 using Content.Server.Nutrition.Components;
 using Content.Server.Popups;
-using Content.Shared.Administration.Logs;
 using Content.Shared.Database;
 using Content.Shared.DragDrop;
 using Content.Shared.IdentityManagement;
@@ -15,6 +14,7 @@ using Content.Shared.Storage;
 using Robust.Shared.Random;
 using static Content.Shared.Kitchen.Components.SharedKitchenSpikeComponent;
 using Content.Shared.Interaction.Events;
+using Content.Shared.Kitchen;
 using Content.Shared.Mobs.Components;
 using Content.Shared.Mobs.Systems;
 using Content.Shared.Popups;
@@ -22,7 +22,7 @@ using Robust.Server.GameObjects;
 
 namespace Content.Server.Kitchen.EntitySystems
 {
-    internal sealed class KitchenSpikeSystem : EntitySystem
+    public sealed class KitchenSpikeSystem : SharedKitchenSpikeSystem
     {
         [Dependency] private readonly PopupSystem _popupSystem = default!;
         [Dependency] private readonly DoAfterSystem _doAfter = default!;
@@ -37,13 +37,21 @@ namespace Content.Server.Kitchen.EntitySystems
 
             SubscribeLocalEvent<KitchenSpikeComponent, InteractUsingEvent>(OnInteractUsing);
             SubscribeLocalEvent<KitchenSpikeComponent, InteractHandEvent>(OnInteractHand);
-            SubscribeLocalEvent<KitchenSpikeComponent, DragDropEvent>(OnDragDrop);
+            SubscribeLocalEvent<KitchenSpikeComponent, DragDropTargetEvent>(OnDragDrop);
 
             //DoAfter
             SubscribeLocalEvent<KitchenSpikeComponent, SpikingFinishedEvent>(OnSpikingFinished);
             SubscribeLocalEvent<KitchenSpikeComponent, SpikingFailEvent>(OnSpikingFail);
 
             SubscribeLocalEvent<KitchenSpikeComponent, SuicideEvent>(OnSuicide);
+
+            SubscribeLocalEvent<ButcherableComponent, CanDropDraggedEvent>(OnButcherableCanDrop);
+        }
+
+        private void OnButcherableCanDrop(EntityUid uid, ButcherableComponent component, ref CanDropDraggedEvent args)
+        {
+            args.Handled = true;
+            args.CanDrop |= component.Type != ButcheringType.Knife;
         }
 
         private void OnSuicide(EntityUid uid, KitchenSpikeComponent component, SuicideEvent args)
@@ -62,7 +70,7 @@ namespace Content.Server.Kitchen.EntitySystems
         {
             component.InUse = false;
 
-            if (EntityManager.TryGetComponent<SharedButcherableComponent>(args.VictimUid, out var butcherable))
+            if (EntityManager.TryGetComponent<ButcherableComponent>(args.VictimUid, out var butcherable))
                 butcherable.BeingButchered = false;
         }
 
@@ -70,7 +78,7 @@ namespace Content.Server.Kitchen.EntitySystems
         {
             component.InUse = false;
 
-            if (EntityManager.TryGetComponent<SharedButcherableComponent>(args.VictimUid, out var butcherable))
+            if (EntityManager.TryGetComponent<ButcherableComponent>(args.VictimUid, out var butcherable))
                 butcherable.BeingButchered = false;
 
             if (Spikeable(uid, args.UserUid, args.VictimUid, component, butcherable))
@@ -79,9 +87,9 @@ namespace Content.Server.Kitchen.EntitySystems
             }
         }
 
-        private void OnDragDrop(EntityUid uid, KitchenSpikeComponent component, DragDropEvent args)
+        private void OnDragDrop(EntityUid uid, KitchenSpikeComponent component, ref DragDropTargetEvent args)
         {
-            if(args.Handled)
+            if (args.Handled)
                 return;
 
             args.Handled = true;
@@ -111,7 +119,7 @@ namespace Content.Server.Kitchen.EntitySystems
         }
 
         private void Spike(EntityUid uid, EntityUid userUid, EntityUid victimUid,
-            KitchenSpikeComponent? component = null, SharedButcherableComponent? butcherable = null)
+            KitchenSpikeComponent? component = null, ButcherableComponent? butcherable = null)
         {
             if (!Resolve(uid, ref component) || !Resolve(victimUid, ref butcherable))
                 return;
@@ -177,7 +185,7 @@ namespace Content.Server.Kitchen.EntitySystems
         }
 
         private bool Spikeable(EntityUid uid, EntityUid userUid, EntityUid victimUid,
-            KitchenSpikeComponent? component = null, SharedButcherableComponent? butcherable = null)
+            KitchenSpikeComponent? component = null, ButcherableComponent? butcherable = null)
         {
             if (!Resolve(uid, ref component))
                 return false;
@@ -208,7 +216,7 @@ namespace Content.Server.Kitchen.EntitySystems
         }
 
         public bool TrySpike(EntityUid uid, EntityUid userUid, EntityUid victimUid, KitchenSpikeComponent? component = null,
-            SharedButcherableComponent? butcherable = null, MobStateComponent? mobState = null)
+            ButcherableComponent? butcherable = null, MobStateComponent? mobState = null)
         {
             if (!Resolve(uid, ref component) || component.InUse ||
                 !Resolve(victimUid, ref butcherable) || butcherable.BeingButchered)
index 21022bb6291a78df4c372795d0269fa2a9ca14e7..4aceecd419707b97ae5914b7a2a6e585e157e6a6 100644 (file)
@@ -34,7 +34,7 @@ public sealed class SharpSystem : EntitySystem
         SubscribeLocalEvent<SharpButcherDoafterComplete>(OnDoafterComplete);
         SubscribeLocalEvent<SharpButcherDoafterCancelled>(OnDoafterCancelled);
 
-        SubscribeLocalEvent<SharedButcherableComponent, GetVerbsEvent<InteractionVerb>>(OnGetInteractionVerbs);
+        SubscribeLocalEvent<ButcherableComponent, GetVerbsEvent<InteractionVerb>>(OnGetInteractionVerbs);
     }
 
     private void OnAfterInteract(EntityUid uid, SharpComponent component, AfterInteractEvent args)
@@ -47,7 +47,7 @@ public sealed class SharpSystem : EntitySystem
 
     private void TryStartButcherDoafter(EntityUid knife, EntityUid target, EntityUid user)
     {
-        if (!TryComp<SharedButcherableComponent>(target, out var butcher))
+        if (!TryComp<ButcherableComponent>(target, out var butcher))
             return;
 
         if (!TryComp<SharpComponent>(knife, out var sharp))
@@ -79,7 +79,7 @@ public sealed class SharpSystem : EntitySystem
 
     private void OnDoafterComplete(SharpButcherDoafterComplete ev)
     {
-        if (!TryComp<SharedButcherableComponent>(ev.Entity, out var butcher))
+        if (!TryComp<ButcherableComponent>(ev.Entity, out var butcher))
             return;
 
         if (!TryComp<SharpComponent>(ev.Sharp, out var sharp))
@@ -123,7 +123,7 @@ public sealed class SharpSystem : EntitySystem
         sharp.Butchering.Remove(ev.Entity);
     }
 
-    private void OnGetInteractionVerbs(EntityUid uid, SharedButcherableComponent component, GetVerbsEvent<InteractionVerb> args)
+    private void OnGetInteractionVerbs(EntityUid uid, ButcherableComponent component, GetVerbsEvent<InteractionVerb> args)
     {
         if (component.Type != ButcheringType.Knife || args.Hands == null)
             return;
index 9ec1b3a290dfd56e0a9e4e7cfe833e68408f68bd..7d351059300529ce91568251f0b0b0bf48e5a956 100644 (file)
@@ -228,7 +228,7 @@ namespace Content.Server.Medical.BiomassReclaimer
             {
                 component.BloodReagent = stream.BloodReagent;
             }
-            if (TryComp<SharedButcherableComponent>(toProcess, out var butcherableComponent))
+            if (TryComp<ButcherableComponent>(toProcess, out var butcherableComponent))
             {
                 component.SpawnedEntities = butcherableComponent.SpawnedEntities;
             }
index 1c6606ea847d0ecb004785fda25fd716f11d5028..165eb17d20a65d22f01042def188f42b34d1e86f 100644 (file)
@@ -21,11 +21,5 @@ namespace Content.Server.Medical.Components
 
         [DataField("partRatingCloningFailChanceMultiplier")]
         public float PartRatingFailMultiplier = 0.75f;
-
-        // ECS this out!, when DragDropSystem and InteractionSystem refactored
-        public override bool DragDropOn(DragDropEvent eventArgs)
-        {
-            return true;
-        }
     }
 }
index 694ff77b592aa152ceb25d6a7c6246568b5392ba..b7890310446ec709a6c533546708f4daa36731da 100644 (file)
@@ -20,7 +20,6 @@ using Content.Shared.Chemistry;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Containers.ItemSlots;
-using Content.Shared.Destructible;
 using Content.Shared.DragDrop;
 using Content.Shared.Emag.Systems;
 using Content.Shared.Examine;
@@ -54,6 +53,7 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
     {
         base.Initialize();
 
+        SubscribeLocalEvent<CryoPodComponent, CanDropTargetEvent>(OnCryoPodCanDropOn);
         SubscribeLocalEvent<CryoPodComponent, ComponentInit>(OnComponentInit);
         SubscribeLocalEvent<CryoPodComponent, GetVerbsEvent<AlternativeVerb>>(AddAlternativeVerbs);
         SubscribeLocalEvent<CryoPodComponent, GotEmaggedEvent>(OnEmagged);
@@ -63,7 +63,7 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
         SubscribeLocalEvent<CryoPodComponent, CryoPodPryInterrupted>(OnCryoPodPryInterrupted);
 
         SubscribeLocalEvent<CryoPodComponent, AtmosDeviceUpdateEvent>(OnCryoPodUpdateAtmosphere);
-        SubscribeLocalEvent<CryoPodComponent, DragDropEvent>(HandleDragDropOn);
+        SubscribeLocalEvent<CryoPodComponent, DragDropTargetEvent>(HandleDragDropOn);
         SubscribeLocalEvent<CryoPodComponent, InteractUsingEvent>(OnInteractUsing);
         SubscribeLocalEvent<CryoPodComponent, ExaminedEvent>(OnExamined);
         SubscribeLocalEvent<CryoPodComponent, PowerChangedEvent>(OnPowerChanged);
@@ -127,7 +127,7 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem
 
     #region Interaction
 
-    private void HandleDragDropOn(EntityUid uid, CryoPodComponent cryoPodComponent, DragDropEvent args)
+    private void HandleDragDropOn(EntityUid uid, CryoPodComponent cryoPodComponent, ref DragDropTargetEvent args)
     {
         if (cryoPodComponent.BodyContainer.ContainedEntity != null)
         {
index 979ce061f13041c4717aaaf141cffc07cbb06561..d468cb0417a454baf2ce647fc9dd01da2d9277af 100644 (file)
@@ -12,6 +12,8 @@ using Content.Server.MachineLinking.System;
 using Content.Server.MachineLinking.Events;
 using Content.Server.Cloning.Components;
 using Content.Server.Construction;
+using Content.Server.Power.EntitySystems;
+using Content.Shared.Body.Components;
 using Content.Shared.Mobs.Components;
 using Content.Shared.Mobs.Systems;
 using Robust.Server.Containers;
@@ -42,11 +44,26 @@ namespace Content.Server.Medical
             SubscribeLocalEvent<MedicalScannerComponent, GetVerbsEvent<InteractionVerb>>(AddInsertOtherVerb);
             SubscribeLocalEvent<MedicalScannerComponent, GetVerbsEvent<AlternativeVerb>>(AddAlternativeVerbs);
             SubscribeLocalEvent<MedicalScannerComponent, DestructionEventArgs>(OnDestroyed);
-            SubscribeLocalEvent<MedicalScannerComponent, DragDropEvent>(HandleDragDropOn);
+            SubscribeLocalEvent<MedicalScannerComponent, DragDropTargetEvent>(OnDragDropOn);
             SubscribeLocalEvent<MedicalScannerComponent, PortDisconnectedEvent>(OnPortDisconnected);
             SubscribeLocalEvent<MedicalScannerComponent, AnchorStateChangedEvent>(OnAnchorChanged);
             SubscribeLocalEvent<MedicalScannerComponent, RefreshPartsEvent>(OnRefreshParts);
             SubscribeLocalEvent<MedicalScannerComponent, UpgradeExamineEvent>(OnUpgradeExamine);
+            SubscribeLocalEvent<MedicalScannerComponent, CanDropTargetEvent>(OnCanDragDropOn);
+        }
+
+        private void OnCanDragDropOn(EntityUid uid, MedicalScannerComponent component, ref CanDropTargetEvent args)
+        {
+            args.Handled = true;
+            args.CanDrop |= CanScannerInsert(uid, args.Dragged, component);
+        }
+
+        public bool CanScannerInsert(EntityUid uid, EntityUid target, MedicalScannerComponent? component = null)
+        {
+            if (!Resolve(uid, ref component))
+                return false;
+
+            return HasComp<BodyComponent>(target);
         }
 
         private void OnComponentInit(EntityUid uid, MedicalScannerComponent scannerComponent, ComponentInit args)
@@ -58,7 +75,7 @@ namespace Content.Server.Medical
 
         private void OnRelayMovement(EntityUid uid, MedicalScannerComponent scannerComponent, ref ContainerRelayMovementEntityEvent args)
         {
-            if (!_blocker.CanInteract(args.Entity, scannerComponent.Owner))
+            if (!_blocker.CanInteract(args.Entity, uid))
                 return;
 
             EjectBody(uid, scannerComponent);
@@ -70,7 +87,7 @@ namespace Content.Server.Medical
                 !args.CanAccess ||
                 !args.CanInteract ||
                 IsOccupied(component) ||
-                !component.CanInsert(args.Using.Value))
+                !CanScannerInsert(uid, args.Using.Value, component))
                 return;
 
             string name = "Unknown";
@@ -79,7 +96,7 @@ namespace Content.Server.Medical
 
             InteractionVerb verb = new()
             {
-                Act = () => InsertBody(component.Owner, args.Target, component),
+                Act = () => InsertBody(uid, args.Target, component),
                 Category = VerbCategory.Insert,
                 Text = name
             };
@@ -104,11 +121,11 @@ namespace Content.Server.Medical
 
             // Self-insert verb
             if (!IsOccupied(component) &&
-                component.CanInsert(args.User) &&
+                CanScannerInsert(uid, args.User, component) &&
                 _blocker.CanMove(args.User))
             {
                 AlternativeVerb verb = new();
-                verb.Act = () => InsertBody(component.Owner, args.User, component);
+                verb.Act = () => InsertBody(uid, args.User, component);
                 verb.Text = Loc.GetString("medical-scanner-verb-enter");
                 args.Verbs.Add(verb);
             }
@@ -119,7 +136,7 @@ namespace Content.Server.Medical
             EjectBody(uid, scannerComponent);
         }
 
-        private void HandleDragDropOn(EntityUid uid, MedicalScannerComponent scannerComponent, DragDropEvent args)
+        private void OnDragDropOn(EntityUid uid, MedicalScannerComponent scannerComponent, ref DragDropTargetEvent args)
         {
             InsertBody(uid, args.Dragged, scannerComponent);
         }
@@ -141,9 +158,9 @@ namespace Content.Server.Medical
             }
             _cloningConsoleSystem.UpdateUserInterface(console);
         }
-        private MedicalScannerStatus GetStatus(MedicalScannerComponent scannerComponent)
+        private MedicalScannerStatus GetStatus(EntityUid uid, MedicalScannerComponent scannerComponent)
         {
-            if (TryComp<ApcPowerReceiverComponent>(scannerComponent.Owner, out var power) && power.Powered)
+            if (this.IsPowered(uid, EntityManager))
             {
                 var body = scannerComponent.BodyContainer.ContainedEntity;
                 if (body == null)
@@ -180,9 +197,9 @@ namespace Content.Server.Medical
 
         private void UpdateAppearance(EntityUid uid, MedicalScannerComponent scannerComponent)
         {
-            if (TryComp<AppearanceComponent>(scannerComponent.Owner, out var appearance))
+            if (TryComp<AppearanceComponent>(uid, out var appearance))
             {
-                _appearance.SetData(uid, MedicalScannerVisuals.Status, GetStatus(scannerComponent), appearance);
+                _appearance.SetData(uid, MedicalScannerVisuals.Status, GetStatus(uid, scannerComponent), appearance);
             }
         }
 
@@ -210,11 +227,11 @@ namespace Content.Server.Medical
             if (scannerComponent.BodyContainer.ContainedEntity != null)
                 return;
 
-            if (!TryComp<MobStateComponent>(user, out var comp))
+            if (!HasComp<MobStateComponent>(user))
                 return;
 
             scannerComponent.BodyContainer.Insert(user);
-            UpdateAppearance(scannerComponent.Owner, scannerComponent);
+            UpdateAppearance(uid, scannerComponent);
         }
 
         public void EjectBody(EntityUid uid, MedicalScannerComponent? scannerComponent)
@@ -222,11 +239,12 @@ namespace Content.Server.Medical
             if (!Resolve(uid, ref scannerComponent))
                 return;
 
-            if (scannerComponent.BodyContainer.ContainedEntity is not {Valid: true} contained) return;
+            if (scannerComponent.BodyContainer.ContainedEntity is not {Valid: true} contained)
+                return;
 
             scannerComponent.BodyContainer.Remove(contained);
             _climbSystem.ForciblySetClimbing(contained, uid);
-            UpdateAppearance(scannerComponent.Owner, scannerComponent);
+            UpdateAppearance(uid, scannerComponent);
         }
 
         private void OnRefreshParts(EntityUid uid, MedicalScannerComponent component, RefreshPartsEvent args)
diff --git a/Content.Server/Strip/StrippableComponent.cs b/Content.Server/Strip/StrippableComponent.cs
deleted file mode 100644 (file)
index 141ddde..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-using System.Threading;
-using Content.Shared.DragDrop;
-using Content.Shared.Strip.Components;
-
-namespace Content.Server.Strip
-{
-    [RegisterComponent]
-    [ComponentReference(typeof(SharedStrippableComponent))]
-    [Access(typeof(StrippableSystem))]
-    public sealed class StrippableComponent : SharedStrippableComponent
-    {
-        /// <summary>
-        /// The strip delay for hands.
-        /// </summary>
-        [DataField("handDelay")]
-        public float HandStripDelay = 4f;
-
-        public override bool Drop(DragDropEvent args)
-        {
-            IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<StrippableSystem>().StartOpeningStripper(args.User, this);
-            return true;
-        }
-
-        public Dictionary<EntityUid, CancellationTokenSource> CancelTokens = new();
-    }
-}
index 4acc65fba270c193d3a9db19a2c7e5e6f094c055..d70d374f69fb726ce78b143a00e5e025b96a4f4c 100644 (file)
@@ -17,10 +17,11 @@ using Content.Server.Administration.Logs;
 using Content.Shared.Database;
 using Content.Shared.Ensnaring.Components;
 using Content.Shared.Interaction;
+using Content.Shared.Strip;
 
 namespace Content.Server.Strip
 {
-    public sealed class StrippableSystem : EntitySystem
+    public sealed class StrippableSystem : SharedStrippableSystem
     {
         [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
         [Dependency] private readonly InventorySystem _inventorySystem = default!;
@@ -104,8 +105,10 @@ namespace Content.Server.Strip
                 TakeItemFromHands(user, handId, component);
         }
 
-        public void StartOpeningStripper(EntityUid user, StrippableComponent component, bool openInCombat = false)
+        public override void StartOpeningStripper(EntityUid user, StrippableComponent component, bool openInCombat = false)
         {
+            base.StartOpeningStripper(user, component, openInCombat);
+
             if (TryComp<SharedCombatModeComponent>(user, out var mode) && mode.IsInCombatMode && !openInCombat)
                 return;
 
@@ -446,25 +449,5 @@ namespace Content.Server.Strip
             // hand update will trigger strippable update
             _adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):user} has stripped the item {ToPrettyString(held):item} from {ToPrettyString(component.Owner):target}");
         }
-
-        private sealed class OpenStrippingCompleteEvent
-        {
-            public readonly EntityUid User;
-
-            public OpenStrippingCompleteEvent(EntityUid user)
-            {
-                User = user;
-            }
-        }
-
-        private sealed class OpenStrippingCancelledEvent
-        {
-            public readonly EntityUid User;
-
-            public OpenStrippingCancelledEvent(EntityUid user)
-            {
-                User = user;
-            }
-        }
     }
 }
index 12310ce0f56db8e19aaddcf669d03e28a5adbf0c..e0d772c718dd154ab922a7b2c657c83b2d5c7337 100644 (file)
@@ -1,6 +1,7 @@
 using Content.Shared.Body.Events;
 using Content.Shared.DragDrop;
 using Content.Shared.Emoting;
+using Content.Shared.Hands;
 using Content.Shared.Interaction;
 using Content.Shared.Interaction.Events;
 using Content.Shared.Item;
@@ -115,7 +116,7 @@ namespace Content.Shared.ActionBlocker
 
         public bool CanDrop(EntityUid uid)
         {
-            var ev = new DropAttemptEvent(uid);
+            var ev = new DropAttemptEvent();
             RaiseLocalEvent(uid, ev);
 
             return !ev.Cancelled;
index 022d3a7799f695e1e97b392d6dde42e855b24c31..312f37f251d49c6945b6c0213e34f7f7f478bd87 100644 (file)
@@ -1,7 +1,6 @@
 using Content.Shared.Body.Part;
 using Content.Shared.Body.Prototypes;
 using Content.Shared.Body.Systems;
-using Content.Shared.DragDrop;
 using Robust.Shared.Audio;
 using Robust.Shared.GameStates;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
@@ -10,7 +9,7 @@ namespace Content.Shared.Body.Components;
 
 [RegisterComponent, NetworkedComponent]
 [Access(typeof(SharedBodySystem))]
-public sealed class BodyComponent : Component, IDraggable
+public sealed class BodyComponent : Component
 {
     [DataField("prototype", customTypeSerializer: typeof(PrototypeIdSerializer<BodyPrototype>))]
     public readonly string? Prototype;
@@ -27,14 +26,4 @@ public sealed class BodyComponent : Component, IDraggable
     /// </summary>
     [DataField("requiredLegs")]
     public int RequiredLegs;
-
-    bool IDraggable.CanStartDrag(StartDragDropEvent args)
-    {
-        return true;
-    }
-
-    bool IDraggable.CanDrop(CanDropEvent args)
-    {
-        return true;
-    }
 }
index 92a0c88a2a299a7654f73ec44b54c7e4c416897f..3b464ca115ce4f78ddb8b24406796186026d1a9a 100644 (file)
@@ -5,6 +5,7 @@ using Content.Shared.Body.Organ;
 using Content.Shared.Body.Part;
 using Content.Shared.Body.Prototypes;
 using Content.Shared.Coordinates;
+using Content.Shared.DragDrop;
 using Robust.Shared.Containers;
 using Robust.Shared.GameStates;
 
@@ -18,6 +19,12 @@ public partial class SharedBodySystem
 
         SubscribeLocalEvent<BodyComponent, ComponentGetState>(OnBodyGetState);
         SubscribeLocalEvent<BodyComponent, ComponentHandleState>(OnBodyHandleState);
+        SubscribeLocalEvent<BodyComponent, CanDragEvent>(OnBodyCanDrag);
+    }
+
+    private void OnBodyCanDrag(EntityUid uid, BodyComponent component, ref CanDragEvent args)
+    {
+        args.Handled = true;
     }
 
     private void OnBodyInit(EntityUid bodyId, BodyComponent body, ComponentInit args)
index 8e54bd384ee0f747494c6904146ad0f398151405..9e9ffa15838a7649a5bf4ab1a6c6b63aef894949 100644 (file)
@@ -13,7 +13,7 @@ public abstract partial class SharedBuckleSystem
     {
         SubscribeLocalEvent<StrapComponent, MoveEvent>(OnStrapRotate);
         SubscribeLocalEvent<StrapComponent, ComponentHandleState>(OnStrapHandleState);
-        SubscribeLocalEvent<StrapComponent, CanDragDropOnEvent>(OnStrapCanDragDropOn);
+        SubscribeLocalEvent<StrapComponent, CanDropTargetEvent>(OnStrapCanDropOn);
     }
 
     private void OnStrapHandleState(EntityUid uid, StrapComponent component, ref ComponentHandleState args)
@@ -85,9 +85,9 @@ public abstract partial class SharedBuckleSystem
         return _interactions.InRangeUnobstructed(target, buckleId, buckle.Range, predicate: Ignored);
     }
 
-    private void OnStrapCanDragDropOn(EntityUid uid, StrapComponent strap, CanDragDropOnEvent args)
+    private void OnStrapCanDropOn(EntityUid uid, StrapComponent strap, ref CanDropTargetEvent args)
     {
-        args.CanDrop = StrapCanDragDropOn(args.Target, args.User, args.Target, args.Dragged, strap);
+        args.CanDrop = StrapCanDragDropOn(uid, args.User, uid, args.Dragged, strap);
         args.Handled = true;
     }
 }
index 9774f59751e6ed2cd88fa7850f16421ae1da6c38..dcf534058d31ddce9aa653795e4237ea13847d18 100644 (file)
@@ -22,7 +22,7 @@ public sealed class BonkSystem : EntitySystem
     public override void Initialize()
     {
         base.Initialize();
-        SubscribeLocalEvent<BonkableComponent, DragDropEvent>(OnDragDrop);
+        SubscribeLocalEvent<BonkableComponent, DragDropTargetEvent>(OnDragDrop);
     }
 
     public bool TryBonk(EntityUid user, EntityUid bonkableUid, BonkableComponent? bonkableComponent = null)
@@ -56,8 +56,8 @@ public sealed class BonkSystem : EntitySystem
         return false;
     }
 
-    private void OnDragDrop(EntityUid user, BonkableComponent bonkableComponent, DragDropEvent args)
+    private void OnDragDrop(EntityUid uid, BonkableComponent bonkableComponent, ref DragDropTargetEvent args)
     {
-        TryBonk(args.Dragged, args.Target);
+        TryBonk(args.Dragged, uid);
     }
 }
index 1d9ff5f606dbdeaa7b63e396a4bd2141f94ca7e2..6521599f541a7644f1d951a79ab553b76723bcda 100644 (file)
@@ -1,5 +1,4 @@
 using Content.Shared.DragDrop;
-using Content.Shared.Movement;
 using Content.Shared.Movement.Events;
 
 namespace Content.Shared.Climbing;
@@ -10,7 +9,6 @@ public abstract class SharedClimbSystem : EntitySystem
     {
         base.Initialize();
         SubscribeLocalEvent<ClimbingComponent, UpdateCanMoveEvent>(HandleMoveAttempt);
-        SubscribeLocalEvent<ClimbableComponent, CanDragDropOnEvent>(OnCanDragDropOn);
     }
 
     private static void HandleMoveAttempt(EntityUid uid, ClimbingComponent component, UpdateCanMoveEvent args)
@@ -22,7 +20,7 @@ public abstract class SharedClimbSystem : EntitySystem
             args.Cancel();
     }
 
-    protected virtual void OnCanDragDropOn(EntityUid uid, ClimbableComponent component, CanDragDropOnEvent args)
+    protected virtual void OnCanDragDropOn(EntityUid uid, ClimbableComponent component, ref CanDropTargetEvent args)
     {
         args.CanDrop = HasComp<ClimbingComponent>(args.Dragged);
     }
index db17972ac8ccedf077deda385a460478f1e2f40c..b5cf8a5868f5f06a8961253d3b9d1ad993d15975 100644 (file)
@@ -2,6 +2,7 @@ using Content.Shared.ActionBlocker;
 using Content.Shared.Alert;
 using Content.Shared.Cuffs.Components;
 using Content.Shared.DragDrop;
+using Content.Shared.Hands;
 using Content.Shared.Hands.Components;
 using Content.Shared.Interaction.Events;
 using Content.Shared.Inventory.Events;
index e0312a7af3d8d361d753c429420341b800515e5a..e0f0faf1a21559d952c08be149f3fd67ef386de2 100644 (file)
@@ -27,7 +27,7 @@ namespace Content.Shared.Disposal
         {
             base.Initialize();
             SubscribeLocalEvent<SharedDisposalUnitComponent, PreventCollideEvent>(OnPreventCollide);
-            SubscribeLocalEvent<SharedDisposalUnitComponent, CanDragDropOnEvent>(OnCanDragDropOn);
+            SubscribeLocalEvent<SharedDisposalUnitComponent, CanDropTargetEvent>(OnCanDragDropOn);
         }
 
         private void OnPreventCollide(EntityUid uid, SharedDisposalUnitComponent component, ref PreventCollideEvent args)
@@ -48,9 +48,10 @@ namespace Content.Shared.Disposal
             }
         }
 
-        private void OnCanDragDropOn(EntityUid uid, SharedDisposalUnitComponent component, CanDragDropOnEvent args)
+        private void OnCanDragDropOn(EntityUid uid, SharedDisposalUnitComponent component, ref CanDropTargetEvent args)
         {
-            if (args.Handled) return;
+            if (args.Handled)
+                return;
 
             args.CanDrop = CanInsert(component, args.Dragged);
             args.Handled = true;
diff --git a/Content.Shared/DragDrop/CanDragDropOnEvent.cs b/Content.Shared/DragDrop/CanDragDropOnEvent.cs
deleted file mode 100644 (file)
index 9667bb7..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-namespace Content.Shared.DragDrop;
-
-/// <summary>
-/// Event that gets send to the target of a drag drop action
-/// Mark this event as handled to specify that the entity can be dropped on
-/// and set CanDrop to true or false, depending on whether dropping the entity onto the target is actually possible.
-/// </summary>
-public sealed class CanDragDropOnEvent : HandledEntityEventArgs
-{
-    /// <summary>
-    ///     Entity doing the drag and drop.
-    /// </summary>
-    public EntityUid User { get; }
-
-    /// <summary>
-    ///     Entity that is being dragged.
-    /// </summary>
-    public EntityUid Dragged { get; }
-
-    /// <summary>
-    ///     Entity that is being dropped on.
-    /// </summary>
-    public EntityUid Target { get; }
-
-    /// <summary>
-    ///     If the dragged entity can be dropped on the target.
-    /// </summary>
-    public bool CanDrop { get; set; } = false;
-
-    public CanDragDropOnEvent(EntityUid user, EntityUid dragged, EntityUid target)
-    {
-        User = user;
-        Dragged = dragged;
-        Target = target;
-    }
-}
index b48e3889e68ec1adf725e33414babaef593bdac6..af32997942b788ab7c4e4590ce049a694805075a 100644 (file)
@@ -1,33 +1,26 @@
-using Robust.Shared.Map;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.DragDrop
 {
     /// <summary>
-    /// Requests a drag / drop interaction to be performed
+    /// Raised on the client to the server requesting a drag-drop.
     /// </summary>
     [Serializable, NetSerializable]
     public sealed class DragDropRequestEvent : EntityEventArgs
     {
-        /// <summary>
-        ///     Location that the entity was dropped.
-        /// </summary>
-        public EntityCoordinates DropLocation { get; }
-
         /// <summary>
         ///     Entity that was dragged and dropped.
         /// </summary>
-        public EntityUid Dropped { get; }
+        public EntityUid Dragged { get; }
 
         /// <summary>
         ///     Entity that was drag dropped on.
         /// </summary>
         public EntityUid Target { get; }
 
-        public DragDropRequestEvent(EntityCoordinates dropLocation, EntityUid dropped, EntityUid target)
+        public DragDropRequestEvent(EntityUid dragged, EntityUid target)
         {
-            DropLocation = dropLocation;
-            Dropped = dropped;
+            Dragged = dragged;
             Target = target;
         }
     }
diff --git a/Content.Shared/DragDrop/DraggableEvents.cs b/Content.Shared/DragDrop/DraggableEvents.cs
new file mode 100644 (file)
index 0000000..288a269
--- /dev/null
@@ -0,0 +1,67 @@
+namespace Content.Shared.DragDrop;
+
+/// <summary>
+/// Raised directed on an entity when attempting to start a drag.
+/// </summary>
+[ByRefEvent]
+public record struct CanDragEvent
+{
+    /// <summary>
+    /// False if we are unable to drag this entity.
+    /// </summary>
+    public bool Handled;
+}
+
+/// <summary>
+/// Raised directed on a dragged entity to indicate whether it has interactions with the target entity.
+/// </summary>
+[ByRefEvent]
+public record struct CanDropDraggedEvent(EntityUid User, EntityUid Target)
+{
+    public readonly EntityUid User = User;
+    public readonly EntityUid Target = Target;
+    public bool Handled = false;
+
+    /// <summary>
+    /// Can we drop the entity onto the target? If the event is not handled then there is no supported interactions.
+    /// </summary>
+    public bool CanDrop = false;
+}
+
+/// <summary>
+/// Raised directed on the target entity to indicate whether it has interactions with the dragged entity.
+/// </summary>
+[ByRefEvent]
+public record struct CanDropTargetEvent(EntityUid User, EntityUid Dragged)
+{
+    public readonly EntityUid User = User;
+    public readonly EntityUid Dragged = Dragged;
+    public bool Handled = false;
+
+    /// <summary>
+    /// <see cref="CanDropDraggedEvent"/>
+    /// </summary>
+    public bool CanDrop = false;
+}
+
+/// <summary>
+/// Raised directed on a dragged entity when it is dropped on a target entity.
+/// </summary>
+[ByRefEvent]
+public record struct DragDropDraggedEvent(EntityUid User, EntityUid Target)
+{
+    public readonly EntityUid User = User;
+    public readonly EntityUid Target = Target;
+    public bool Handled = false;
+}
+
+/// <summary>
+/// Raised directed on the target entity when a dragged entity is dragged onto it.
+/// </summary>
+[ByRefEvent]
+public record struct DragDropTargetEvent(EntityUid User, EntityUid Dragged)
+{
+    public readonly EntityUid User = User;
+    public readonly EntityUid Dragged = Dragged;
+    public bool Handled = false;
+}
diff --git a/Content.Shared/DragDrop/DropAttemptEvent.cs b/Content.Shared/DragDrop/DropAttemptEvent.cs
deleted file mode 100644 (file)
index 033a142..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace Content.Shared.DragDrop
-{
-    public sealed class DropAttemptEvent : CancellableEntityEventArgs
-    {
-        public DropAttemptEvent(EntityUid uid)
-        {
-            Uid = uid;
-        }
-
-        public EntityUid Uid { get; }
-    }
-}
diff --git a/Content.Shared/DragDrop/IDragDropOn.cs b/Content.Shared/DragDrop/IDragDropOn.cs
deleted file mode 100644 (file)
index 0fca993..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-namespace Content.Shared.DragDrop
-{
-    /// <summary>
-    ///     This interface allows the component's entity to be dragged and dropped
-    ///     onto by another entity and gives it behavior when that occurs.
-    /// </summary>
-    [RequiresExplicitImplementation]
-    public interface IDragDropOn
-    {
-        /// <summary>
-        ///     Invoked when another entity is being dragged and dropped
-        ///     onto this one before invoking <see cref="DragDropOn"/>.
-        ///     Note that other drag and drop interactions may be attempted if
-        ///     this one fails.
-        /// </summary>
-        /// <param name="eventArgs"></param>
-        /// <returns>true if <see cref="eventArgs"/> is valid, false otherwise.</returns>
-        bool CanDragDropOn(DragDropEvent eventArgs);
-
-        /// <summary>
-        ///     Invoked server-side when another entity is being dragged and dropped
-        ///     onto this one before invoking <see cref="DragDropOn"/>
-        ///     Note that other drag and drop interactions may be attempted if
-        ///     this one fails.
-        /// </summary>
-        /// <returns>
-        ///     true if an interaction occurred and no further interaction should
-        ///     be processed for this drop.
-        /// </returns>
-        bool DragDropOn(DragDropEvent eventArgs);
-    }
-}
diff --git a/Content.Shared/DragDrop/IDraggable.cs b/Content.Shared/DragDrop/IDraggable.cs
deleted file mode 100644 (file)
index f494042..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-using Robust.Shared.Map;
-
-namespace Content.Shared.DragDrop
-{
-    /// <summary>
-    ///     This interface allows a local client to initiate dragging of the component's
-    ///     entity by mouse, for drag and drop interactions.
-    /// </summary>
-    [RequiresExplicitImplementation]
-    public interface IDraggable
-    {
-        /// <summary>
-        ///     Invoked when an user is attempting to initiate a drag with
-        ///     this component's entity in range. It's fine to return true even if there
-        ///     wouldn't be any valid targets - just return true if this entity is in a
-        ///     "draggable" state.
-        /// </summary>
-        /// <param name="args">
-        ///     The information about the drag, such as who is doing it.
-        /// </param>
-        /// <returns>True if the drag should be initiated, false otherwise.</returns>
-        bool CanStartDrag(StartDragDropEvent args)
-        {
-            return true;
-        }
-
-        /// <summary>
-        ///     Invoked on entities visible to the user to check if this component's
-        ///     entity can be dropped on the indicated target entity.
-        ///     No need to check range / reachability in here.
-        ///     Returning true will cause the target entity to be highlighted as
-        ///     a potential target and allow dropping when in range.
-        /// </summary>
-        /// <returns>
-        ///     True if target is a valid target to be dropped on by this component's
-        ///     entity, false otherwise.
-        /// </returns>
-        bool CanDrop(CanDropEvent args);
-
-        /// <summary>
-        ///     Invoked when this component's entity is being dropped on another.
-        ///     Other drag and drop interactions may be attempted if this one fails.
-        /// </summary>
-        /// <param name="args">
-        ///     The information about the drag, such as who is doing it.
-        /// </param>
-        /// <returns>
-        ///     True if an interaction occurred and no further interaction should
-        ///     be processed for this drop, false otherwise.
-        /// </returns>
-        bool Drop(DragDropEvent args)
-        {
-            return false;
-        }
-    }
-
-    [Virtual]
-    public class StartDragDropEvent : HandledEntityEventArgs
-    {
-        /// <summary>
-        ///     Entity doing the drag and drop.
-        /// </summary>
-        public EntityUid User { get; }
-
-        /// <summary>
-        ///     Entity that is being dragged.
-        /// </summary>
-        public EntityUid Dragged { get; }
-
-        /// <summary>
-        ///     Creates a new instance of <see cref="StartDragDropEvent"/>.
-        /// </summary>
-        /// <param name="user">The entity doing the drag and drop.</param>
-        /// <param name="dragged">The entity that is being dragged and dropped.</param>
-        public StartDragDropEvent(EntityUid user, EntityUid dragged)
-        {
-            User = user;
-            Dragged = dragged;
-        }
-    }
-
-    [Virtual]
-    public class CanDropEvent : StartDragDropEvent
-    {
-        /// <summary>
-        ///     The entity uid that <see cref="StartDragDropEvent.Dragged"/>
-        ///     is being dropped onto.
-        /// </summary>
-        public EntityUid Target { get; }
-
-        /// <summary>
-        ///     Creates a new instance of <see cref="CanDropEvent"/>.
-        /// </summary>
-        /// <param name="user">The entity doing the drag and drop.</param>
-        /// <param name="dragged">The entity that is being dragged and dropped.</param>
-        /// <param name="target">The entity that <see cref="dragged"/> is being dropped onto.</param>
-        public CanDropEvent(EntityUid user, EntityUid dragged, EntityUid target) : base(user, dragged)
-        {
-            Target = target;
-        }
-    }
-
-    [Virtual]
-    public class DragDropEvent : CanDropEvent
-    {
-        /// <summary>
-        ///     The location where <see cref="StartDragDropEvent.Dragged"/>
-        ///     is being dropped.
-        /// </summary>
-        public EntityCoordinates DropLocation { get; }
-
-        /// <summary>
-        ///     Creates a new instance of <see cref="DragDropEvent"/>.
-        /// </summary>
-        /// <param name="user">The entity doing the drag and drop.</param>
-        /// <param name="dropLocation">The location where <see cref="dropped"/> is being dropped.</param>
-        /// <param name="dragged">The entity that is being dragged and dropped.</param>
-        /// <param name="target">The entity that <see cref="dropped"/> is being dropped onto.</param>
-        public DragDropEvent(EntityUid user, EntityCoordinates dropLocation, EntityUid dragged, EntityUid target) : base(user, dragged, target)
-        {
-            DropLocation = dropLocation;
-        }
-    }
-}
index d6fab4b6ffbc0d718e9fcea1a8f9f80750fd6aa0..7f1f6c23f73ddb0798ee185fc3147796b3bce204 100644 (file)
@@ -2,12 +2,5 @@
 
 public abstract class SharedDragDropSystem : EntitySystem
 {
-    protected bool? CheckDragDropOn(DragDropEvent eventArgs)
-    {
-        var canDragDropOnEvent = new CanDragDropOnEvent(eventArgs.User, eventArgs.Dragged, eventArgs.Target);
 
-        RaiseLocalEvent(eventArgs.Target, canDragDropOnEvent, false);
-
-        return canDragDropOnEvent.Handled ? canDragDropOnEvent.CanDrop : null;
-    }
 }
index b480906f97613e045daa56636bfb9cf51e8508e1..fa91d5e2ff381319263a52cabb19a428a2d1b1b4 100644 (file)
@@ -1,5 +1,6 @@
 using Content.Shared.DragDrop;
 using Content.Shared.Emoting;
+using Content.Shared.Hands;
 using Content.Shared.Interaction.Events;
 using Content.Shared.Item;
 using Robust.Shared.Serialization;
@@ -56,7 +57,7 @@ namespace Content.Shared.Ghost
             DisplayName = displayName;
             IsWarpPoint = isWarpPoint;
         }
-        
+
         /// <summary>
         /// The entity representing the warp point.
         /// This is passed back to the server in <see cref="GhostWarpToTargetRequestEvent"/>
index 3d5f64986a691357b0c85308256504b177fbf7c1..29f4b2cad858c4973e25b1259076a3e579e79d6c 100644 (file)
@@ -7,6 +7,14 @@ using static Robust.Shared.GameObjects.SharedSpriteComponent;
 
 namespace Content.Shared.Hands
 {
+    /// <summary>
+    /// Raised directed on an entity when attempting to drop its hand items.
+    /// </summary>
+    public sealed class DropAttemptEvent : CancellableEntityEventArgs
+    {
+        public readonly EntityUid Uid;
+    }
+
     /// <summary>
     ///     Raised directed at an item that needs to update its in-hand sprites/layers.
     /// </summary>
index e545ef84a8788d54a7781db7e820fd02331455b0..cd305bbf968864874ebab303db33e9fe655764c9 100644 (file)
@@ -243,8 +243,8 @@ public abstract partial class InventorySystem
         // that requires server/client specific code.
         // Uhhh TODO, fix this. This doesn't even fucking check if the target item is IN the targets inventory.
         return actor != target &&
-            HasComp<SharedStrippableComponent>(target) &&
-            HasComp<SharedStrippingComponent>(actor) &&
+            HasComp<StrippableComponent>(target) &&
+            HasComp<StrippingComponent>(actor) &&
             HasComp<SharedHandsComponent>(actor);
     }
 
index cee52320737d8fbd099d9a5d72024cc02e637803..1428ef8bb65e89445b0f2374e7808efef7b4044a 100644 (file)
@@ -1,11 +1,11 @@
-using Content.Shared.DragDrop;
-using Content.Shared.Nutrition.Components;
 using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Kitchen.Components
 {
-    public abstract class SharedKitchenSpikeComponent : Component, IDragDropOn
+    [NetworkedComponent]
+    public abstract class SharedKitchenSpikeComponent : Component
     {
         [DataField("delay")]
         public float SpikeDelay = 7.0f;
@@ -14,19 +14,6 @@ namespace Content.Shared.Kitchen.Components
         [DataField("sound")]
         public SoundSpecifier SpikeSound = new SoundPathSpecifier("/Audio/Effects/Fluids/splat.ogg");
 
-        bool IDragDropOn.CanDragDropOn(DragDropEvent eventArgs)
-        {
-            if (!IoCManager.Resolve<IEntityManager>().HasComponent<SharedButcherableComponent>(eventArgs.Dragged))
-            {
-                return false;
-            }
-
-            // TODO: Once we get silicons need to check organic
-            return true;
-        }
-
-        public abstract bool DragDropOn(DragDropEvent eventArgs);
-
         [Serializable, NetSerializable]
         public enum KitchenSpikeVisuals : byte
         {
diff --git a/Content.Shared/Kitchen/SharedKitchenSpikeSystem.cs b/Content.Shared/Kitchen/SharedKitchenSpikeSystem.cs
new file mode 100644 (file)
index 0000000..fa8d7a9
--- /dev/null
@@ -0,0 +1,31 @@
+using Content.Shared.DragDrop;
+using Content.Shared.Kitchen.Components;
+using Content.Shared.Nutrition.Components;
+
+namespace Content.Shared.Kitchen;
+
+public abstract class SharedKitchenSpikeSystem : EntitySystem
+{
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<SharedKitchenSpikeComponent, CanDropTargetEvent>(OnCanDrop);
+    }
+
+    private void OnCanDrop(EntityUid uid, SharedKitchenSpikeComponent component, ref CanDropTargetEvent args)
+    {
+        if (args.Handled)
+            return;
+
+        args.Handled = true;
+
+        if (!HasComp<ButcherableComponent>(args.Dragged))
+        {
+            args.CanDrop = false;
+            return;
+        }
+
+        // TODO: Once we get silicons need to check organic
+        args.CanDrop = true;
+    }
+}
index b10a9298794cca0eb642e5fe6f2c9a05bdb444f8..3a9c76111ca25ada10836fc9918abfe70e5f9e00 100644 (file)
@@ -9,7 +9,7 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
 namespace Content.Shared.Medical.Cryogenics;
 
 [NetworkedComponent]
-public abstract class SharedCryoPodComponent: Component, IDragDropOn
+public abstract class SharedCryoPodComponent: Component
 {
     /// <summary>
     /// Specifies the name of the atmospherics port to draw gas from.
@@ -87,19 +87,4 @@ public abstract class SharedCryoPodComponent: Component, IDragDropOn
         ContainsEntity,
         IsOn
     }
-
-    public bool CanInsert(EntityUid entity)
-    {
-        return IoCManager.Resolve<IEntityManager>().HasComponent<BodyComponent>(entity);
-    }
-
-    bool IDragDropOn.CanDragDropOn(DragDropEvent eventArgs)
-    {
-        return CanInsert(eventArgs.Dragged);
-    }
-
-    bool IDragDropOn.DragDropOn(DragDropEvent eventArgs)
-    {
-        return false;
-    }
 }
index 3c41cec3cd6ff94487a77ecd8b5792c9ed2df703..502669dc5cddf4d3bb33b07f1c227e8e8533fbc4 100644 (file)
@@ -1,4 +1,6 @@
 using Content.Server.Medical.Components;
+using Content.Shared.Body.Components;
+using Content.Shared.DragDrop;
 using Content.Shared.Emag.Systems;
 using Content.Shared.Mobs.Components;
 using Content.Shared.Mobs.Systems;
@@ -26,6 +28,12 @@ public abstract partial class SharedCryoPodSystem: EntitySystem
         InitializeInsideCryoPod();
     }
 
+    protected void OnCryoPodCanDropOn(EntityUid uid, SharedCryoPodComponent component, ref CanDropTargetEvent args)
+    {
+        args.CanDrop = args.CanDrop && HasComp<BodyComponent>(args.Dragged);
+        args.Handled = true;
+    }
+
     protected void OnComponentInit(EntityUid uid, SharedCryoPodComponent cryoPodComponent, ComponentInit args)
     {
         cryoPodComponent.BodyContainer = _containerSystem.EnsureContainer<ContainerSlot>(uid, "scanner-body");
index 1b1805a1f1addf55ffe96fd22cd50280fe9c939e..848370c1d0349e4abb7e8db8ac05057acb6f3f58 100644 (file)
@@ -1,19 +1,18 @@
-using Content.Shared.Body.Components;
 using Content.Shared.DragDrop;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.MedicalScanner
 {
-    public abstract class SharedMedicalScannerComponent : Component, IDragDropOn
+    public abstract class SharedMedicalScannerComponent : Component
     {
         [Serializable, NetSerializable]
-        public enum MedicalScannerVisuals
+        public enum MedicalScannerVisuals : byte
         {
             Status
         }
 
         [Serializable, NetSerializable]
-        public enum MedicalScannerStatus
+        public enum MedicalScannerStatus : byte
         {
             Off,
             Open,
@@ -22,17 +21,5 @@ namespace Content.Shared.MedicalScanner
             Green,
             Yellow,
         }
-
-        public bool CanInsert(EntityUid entity)
-        {
-            return IoCManager.Resolve<IEntityManager>().HasComponent<BodyComponent>(entity);
-        }
-
-        bool IDragDropOn.CanDragDropOn(DragDropEvent eventArgs)
-        {
-            return CanInsert(eventArgs.Dragged);
-        }
-
-        public abstract bool DragDropOn(DragDropEvent eventArgs);
     }
 }
index 82b183e519000aa9e4be098a4cf682598c2da25a..6515ba41a8195a2cbaafa0ca49932496a7c11562 100644 (file)
@@ -2,6 +2,7 @@
 using Content.Shared.Disease.Events;
 using Content.Shared.DragDrop;
 using Content.Shared.Emoting;
+using Content.Shared.Hands;
 using Content.Shared.Interaction.Events;
 using Content.Shared.Inventory.Events;
 using Content.Shared.Item;
similarity index 51%
rename from Content.Shared/Nutrition/Components/SharedButcherableComponent.cs
rename to Content.Shared/Nutrition/Components/ButcherableComponent.cs
index 9f7794b30a3f34c2e432052dc951dd89a50ecf6d..ab8e40b4d45db171704bf52fd3568e7b0bd6282a 100644 (file)
@@ -1,38 +1,31 @@
-using Content.Shared.DragDrop;
 using Content.Shared.Storage;
+using Robust.Shared.GameStates;
 
 namespace Content.Shared.Nutrition.Components
 {
     /// <summary>
     /// Indicates that the entity can be thrown on a kitchen spike for butchering.
     /// </summary>
-    [RegisterComponent]
-    public sealed class SharedButcherableComponent : Component, IDraggable
+    [RegisterComponent, NetworkedComponent]
+    public sealed class ButcherableComponent : Component
     {
         [DataField("spawned", required: true)]
         public List<EntitySpawnEntry> SpawnedEntities = new();
 
-        [DataField("butcherDelay")]
+        [ViewVariables(VVAccess.ReadWrite), DataField("butcherDelay")]
         public float ButcherDelay = 8.0f;
 
-        [DataField("butcheringType")]
+        [ViewVariables(VVAccess.ReadWrite), DataField("butcheringType")]
         public ButcheringType Type = ButcheringType.Knife;
 
         /// <summary>
         /// Prevents butchering same entity on two and more spikes simultaneously and multiple doAfters on the same Spike
         /// </summary>
+        [ViewVariables]
         public bool BeingButchered;
-
-        // TODO: ECS this out!, my guess CanDropEvent should be client side only and then "ValidDragDrop" in the DragDropSystem needs a little touch
-        // But this may lead to creating client-side systems for every Draggable component subbed to CanDrop. Actually those systems could control
-        // CanDropOn behaviors as well (IDragDropOn)
-        bool IDraggable.CanDrop(CanDropEvent args)
-        {
-            return Type != ButcheringType.Knife;
-        }
     }
 
-    public enum ButcheringType
+    public enum ButcheringType : byte
     {
         Knife, // e.g. goliaths
         Spike, // e.g. monkeys
index 0e25d83824a9949591baa326599a2376763c7c4f..a29c7bba017a7bccb0c13e397f029a594cee1002 100644 (file)
@@ -1,6 +1,7 @@
 using Content.Shared.ActionBlocker;
 using Content.Shared.Actions;
 using Content.Shared.DragDrop;
+using Content.Shared.Hands;
 using Content.Shared.Interaction.Events;
 using Content.Shared.Item;
 using Content.Shared.Movement;
diff --git a/Content.Shared/Strip/Components/SharedStrippingComponent.cs b/Content.Shared/Strip/Components/SharedStrippingComponent.cs
deleted file mode 100644 (file)
index ef4c8b2..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-using Content.Shared.ActionBlocker;
-using Content.Shared.DragDrop;
-using Content.Shared.Hands.Components;
-
-namespace Content.Shared.Strip.Components
-{
-    /// <summary>
-    ///     Give to an entity to say they can strip another entity.
-    /// </summary>
-    [RegisterComponent]
-    public sealed class SharedStrippingComponent : Component, IDragDropOn
-    {
-        bool IDragDropOn.CanDragDropOn(DragDropEvent eventArgs)
-        {
-            var ent = IoCManager.Resolve<IEntityManager>();
-            return eventArgs.Target != eventArgs.Dragged &&
-                eventArgs.Target == eventArgs.User &&
-                ent.HasComponent<SharedStrippableComponent>(eventArgs.Dragged) &&
-                ent.HasComponent<SharedHandsComponent>(eventArgs.User) &&
-                ent.EntitySysManager.GetEntitySystem<ActionBlockerSystem>().CanInteract(eventArgs.User, eventArgs.Dragged);
-        }
-
-        bool IDragDropOn.DragDropOn(DragDropEvent eventArgs)
-        {
-            // Handled by StrippableComponent
-            return true;
-        }
-    }
-}
similarity index 72%
rename from Content.Shared/Strip/Components/SharedStrippableComponent.cs
rename to Content.Shared/Strip/Components/StrippableComponent.cs
index ad8f83b5d6ff0200fc183e5b82eb4365675816c5..597795473e5b6cbd04388fb2e0cd72e07416bd31 100644 (file)
@@ -1,24 +1,17 @@
-using Content.Shared.ActionBlocker;
-using Content.Shared.DragDrop;
-using Content.Shared.Hands.Components;
 using Content.Shared.Inventory;
+using Robust.Shared.GameStates;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Strip.Components
 {
-    public abstract class SharedStrippableComponent : Component, IDraggable
+    [RegisterComponent, NetworkedComponent]
+    public sealed class StrippableComponent : Component
     {
-        bool IDraggable.CanDrop(CanDropEvent args)
-        {
-            var ent = IoCManager.Resolve<IEntityManager>();
-            return args.Target != args.Dragged &&
-                args.Target == args.User &&
-                ent.HasComponent<SharedStrippingComponent>(args.User) &&
-                ent.HasComponent<SharedHandsComponent>(args.User) &&
-                ent.EntitySysManager.GetEntitySystem<ActionBlockerSystem>().CanInteract(args.User, args.Dragged);
-        }
-
-        public abstract bool Drop(DragDropEvent args);
+        /// <summary>
+        /// The strip delay for hands.
+        /// </summary>
+        [ViewVariables(VVAccess.ReadWrite), DataField("handDelay")]
+        public float HandStripDelay = 4f;
     }
 
     [NetSerializable, Serializable]
diff --git a/Content.Shared/Strip/Components/StrippingComponent.cs b/Content.Shared/Strip/Components/StrippingComponent.cs
new file mode 100644 (file)
index 0000000..6893e28
--- /dev/null
@@ -0,0 +1,10 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Strip.Components
+{
+    /// <summary>
+    ///     Give to an entity to say they can strip another entity.
+    /// </summary>
+    [RegisterComponent, NetworkedComponent]
+    public sealed class StrippingComponent : Component {}
+}
diff --git a/Content.Shared/Strip/SharedStrippableSystem.cs b/Content.Shared/Strip/SharedStrippableSystem.cs
new file mode 100644 (file)
index 0000000..ec707b5
--- /dev/null
@@ -0,0 +1,49 @@
+using Content.Shared.DragDrop;
+using Content.Shared.Hands.Components;
+using Content.Shared.Strip.Components;
+
+namespace Content.Shared.Strip;
+
+public abstract class SharedStrippableSystem : EntitySystem
+{
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<StrippingComponent, CanDropTargetEvent>(OnCanDropOn);
+        SubscribeLocalEvent<StrippableComponent, CanDropDraggedEvent>(OnCanDrop);
+        SubscribeLocalEvent<StrippableComponent, DragDropDraggedEvent>(OnDragDrop);
+    }
+
+    private void OnDragDrop(EntityUid uid, StrippableComponent component, ref DragDropDraggedEvent args)
+    {
+        // If the user drags a strippable thing onto themselves.
+        if (args.Handled || args.Target != args.User)
+            return;
+
+        StartOpeningStripper(args.User, component);
+        args.Handled = true;
+    }
+
+    public virtual void StartOpeningStripper(EntityUid user, StrippableComponent component, bool openInCombat = false)
+    {
+
+    }
+
+    private void OnCanDropOn(EntityUid uid, StrippingComponent component, ref CanDropTargetEvent args)
+    {
+        args.Handled = true;
+        args.CanDrop |= uid == args.User &&
+                        HasComp<StrippableComponent>(args.Dragged) &&
+                        HasComp<SharedHandsComponent>(args.User);
+    }
+
+    private void OnCanDrop(EntityUid uid, StrippableComponent component, ref CanDropDraggedEvent args)
+    {
+        args.CanDrop |= args.Target == args.User &&
+                        HasComp<StrippingComponent>(args.User) &&
+                        HasComp<SharedHandsComponent>(args.User);
+
+        if (args.CanDrop)
+            args.Handled = true;
+    }
+}
index 530b0252be071690e57b054c0ba49d02986ee44a..458d28dbf99df17ae11e41aba60515e9230293ec 100644 (file)
@@ -8,6 +8,7 @@ using Content.Shared.Inventory.Events;
 using Content.Shared.Item;
 using Content.Shared.Bed.Sleep;
 using Content.Shared.Database;
+using Content.Shared.Hands;
 using Content.Shared.Mobs;
 using Content.Shared.Mobs.Components;
 using Content.Shared.Mobs.Systems;