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();
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;
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,
+}
-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;
- }
}
}
--- /dev/null
+using Content.Shared.Kitchen;
+
+namespace Content.Client.Kitchen;
+
+public sealed class KitchenSpikeSystem : SharedKitchenSpikeSystem
+{
+
+}
-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;
- }
}
}
{
base.Initialize();
SubscribeLocalEvent<ClimbingComponent, ComponentHandleState>(OnClimbingState);
+ SubscribeLocalEvent<ClimbableComponent, CanDropTargetEvent>(OnCanDragDropOn);
}
private static void OnClimbingState(EntityUid uid, ClimbingComponent component, ref ComponentHandleState args)
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;
+++ /dev/null
-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;
- }
- }
-}
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()
{
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)
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);
}
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)
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);
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
SubscribeLocalEvent<ClimbableComponent, GetVerbsEvent<AlternativeVerb>>(AddClimbableVerb);
- SubscribeLocalEvent<ClimbableComponent, DragDropEvent>(OnClimbableDragDrop);
+ SubscribeLocalEvent<ClimbableComponent, DragDropTargetEvent>(OnClimbableDragDrop);
SubscribeLocalEvent<ClimbingComponent, ClimbFinishedEvent>(OnClimbFinished);
SubscribeLocalEvent<ClimbingComponent, EndCollideEvent>(OnClimbEndCollide);
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);
});
}
- 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,
// Interactions
SubscribeLocalEvent<DisposalUnitComponent, ActivateInWorldEvent>(HandleActivate);
SubscribeLocalEvent<DisposalUnitComponent, AfterInteractUsingEvent>(HandleAfterInteractUsing);
- SubscribeLocalEvent<DisposalUnitComponent, DragDropEvent>(HandleDragDropOn);
+ SubscribeLocalEvent<DisposalUnitComponent, DragDropTargetEvent>(HandleDragDropOn);
SubscribeLocalEvent<DisposalUnitComponent, DestructionEventArgs>(HandleDestruction);
// Verbs
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);
}
}
#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
}
}
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;
// 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;
- }
}
}
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;
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;
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!;
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)
{
component.InUse = false;
- if (EntityManager.TryGetComponent<SharedButcherableComponent>(args.VictimUid, out var butcherable))
+ if (EntityManager.TryGetComponent<ButcherableComponent>(args.VictimUid, out var butcherable))
butcherable.BeingButchered = false;
}
{
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))
}
}
- 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;
}
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;
}
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;
}
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)
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)
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))
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))
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;
{
component.BloodReagent = stream.BloodReagent;
}
- if (TryComp<SharedButcherableComponent>(toProcess, out var butcherableComponent))
+ if (TryComp<ButcherableComponent>(toProcess, out var butcherableComponent))
{
component.SpawnedEntities = butcherableComponent.SpawnedEntities;
}
[DataField("partRatingCloningFailChanceMultiplier")]
public float PartRatingFailMultiplier = 0.75f;
-
- // ECS this out!, when DragDropSystem and InteractionSystem refactored
- public override bool DragDropOn(DragDropEvent eventArgs)
- {
- return true;
- }
}
}
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;
{
base.Initialize();
+ SubscribeLocalEvent<CryoPodComponent, CanDropTargetEvent>(OnCryoPodCanDropOn);
SubscribeLocalEvent<CryoPodComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<CryoPodComponent, GetVerbsEvent<AlternativeVerb>>(AddAlternativeVerbs);
SubscribeLocalEvent<CryoPodComponent, GotEmaggedEvent>(OnEmagged);
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);
#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)
{
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;
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)
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);
!args.CanAccess ||
!args.CanInteract ||
IsOccupied(component) ||
- !component.CanInsert(args.Using.Value))
+ !CanScannerInsert(uid, args.Using.Value, component))
return;
string name = "Unknown";
InteractionVerb verb = new()
{
- Act = () => InsertBody(component.Owner, args.Target, component),
+ Act = () => InsertBody(uid, args.Target, component),
Category = VerbCategory.Insert,
Text = name
};
// 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);
}
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);
}
}
_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)
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);
}
}
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)
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)
+++ /dev/null
-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();
- }
-}
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!;
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;
// 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;
- }
- }
}
}
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;
public bool CanDrop(EntityUid uid)
{
- var ev = new DropAttemptEvent(uid);
+ var ev = new DropAttemptEvent();
RaiseLocalEvent(uid, ev);
return !ev.Cancelled;
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;
[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;
/// </summary>
[DataField("requiredLegs")]
public int RequiredLegs;
-
- bool IDraggable.CanStartDrag(StartDragDropEvent args)
- {
- return true;
- }
-
- bool IDraggable.CanDrop(CanDropEvent args)
- {
- return true;
- }
}
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;
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)
{
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)
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;
}
}
public override void Initialize()
{
base.Initialize();
- SubscribeLocalEvent<BonkableComponent, DragDropEvent>(OnDragDrop);
+ SubscribeLocalEvent<BonkableComponent, DragDropTargetEvent>(OnDragDrop);
}
public bool TryBonk(EntityUid user, EntityUid bonkableUid, BonkableComponent? bonkableComponent = null)
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);
}
}
using Content.Shared.DragDrop;
-using Content.Shared.Movement;
using Content.Shared.Movement.Events;
namespace Content.Shared.Climbing;
{
base.Initialize();
SubscribeLocalEvent<ClimbingComponent, UpdateCanMoveEvent>(HandleMoveAttempt);
- SubscribeLocalEvent<ClimbableComponent, CanDragDropOnEvent>(OnCanDragDropOn);
}
private static void HandleMoveAttempt(EntityUid uid, ClimbingComponent component, UpdateCanMoveEvent args)
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);
}
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;
{
base.Initialize();
SubscribeLocalEvent<SharedDisposalUnitComponent, PreventCollideEvent>(OnPreventCollide);
- SubscribeLocalEvent<SharedDisposalUnitComponent, CanDragDropOnEvent>(OnCanDragDropOn);
+ SubscribeLocalEvent<SharedDisposalUnitComponent, CanDropTargetEvent>(OnCanDragDropOn);
}
private void OnPreventCollide(EntityUid uid, SharedDisposalUnitComponent component, ref PreventCollideEvent args)
}
}
- 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;
+++ /dev/null
-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;
- }
-}
-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;
}
}
--- /dev/null
+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;
+}
+++ /dev/null
-namespace Content.Shared.DragDrop
-{
- public sealed class DropAttemptEvent : CancellableEntityEventArgs
- {
- public DropAttemptEvent(EntityUid uid)
- {
- Uid = uid;
- }
-
- public EntityUid Uid { get; }
- }
-}
+++ /dev/null
-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);
- }
-}
+++ /dev/null
-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;
- }
- }
-}
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;
- }
}
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;
DisplayName = displayName;
IsWarpPoint = isWarpPoint;
}
-
+
/// <summary>
/// The entity representing the warp point.
/// This is passed back to the server in <see cref="GhostWarpToTargetRequestEvent"/>
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>
// 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);
}
-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;
[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
{
--- /dev/null
+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;
+ }
+}
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.
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;
- }
}
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;
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");
-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,
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);
}
}
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;
-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
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;
+++ /dev/null
-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;
- }
- }
-}
-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]
--- /dev/null
+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 {}
+}
--- /dev/null
+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;
+ }
+}
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;