diff --git a/Content.Client/DragDrop/DragDropHelper.cs b/Content.Client/DragDrop/DragDropHelper.cs index 959ff5e789..c5d0ccffc0 100644 --- a/Content.Client/DragDrop/DragDropHelper.cs +++ b/Content.Client/DragDrop/DragDropHelper.cs @@ -1,165 +1,163 @@ using Robust.Client.Input; using Robust.Shared.Map; -namespace Content.Client.DragDrop +namespace Content.Client.DragDrop; + +/// +/// 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. +/// +/// thing being dragged and dropped +public sealed class DragDropHelper { + private readonly IInputManager _inputManager; + + private readonly OnBeginDrag _onBeginDrag; + private readonly OnEndDrag _onEndDrag; + private readonly OnContinueDrag _onContinueDrag; + public float Deadzone = 2f; + /// - /// 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. /// - /// thing being dragged and dropped - public sealed class DragDropHelper + public ScreenCoordinates MouseScreenPosition => _inputManager.MouseScreenPosition; + + /// + /// 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. + /// + public bool IsDragging => _state == DragState.Dragging; + + /// + /// Current thing being dragged or which mouse button is being held down on. + /// + 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; + NotDragging, + // not dragging yet, waiting to see + // if they hold for long enough + MouseDown, + // currently dragging something + Dragging, + } - private readonly OnBeginDrag _onBeginDrag; - private readonly OnEndDrag _onEndDrag; - private readonly OnContinueDrag _onContinueDrag; - public float Deadzone = 2f; + /// + /// + /// + public DragDropHelper(OnBeginDrag onBeginDrag, OnContinueDrag onContinueDrag, OnEndDrag onEndDrag) + { + _inputManager = IoCManager.Resolve(); + _onBeginDrag = onBeginDrag; + _onEndDrag = onEndDrag; + _onContinueDrag = onContinueDrag; + } - /// - /// Convenience method, current mouse screen position as provided by inputmanager. - /// - public ScreenCoordinates MouseScreenPosition => _inputManager.MouseScreenPosition; - - /// - /// 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. - /// - public bool IsDragging => _state == DragState.Dragging; - - /// - /// Current thing being dragged or which mouse button is being held down on. - /// - 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 + /// + /// 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. + /// + public void MouseDown(T target) + { + if (_state != DragState.NotDragging) { - NotDragging, - // not dragging yet, waiting to see - // if they hold for long enough - MouseDown, - // currently dragging something - Dragging, + EndDrag(); } - /// - /// - /// - public DragDropHelper(OnBeginDrag onBeginDrag, OnContinueDrag onContinueDrag, OnEndDrag onEndDrag) + Dragged = target; + _state = DragState.MouseDown; + _mouseDownScreenPos = _inputManager.MouseScreenPosition; + } + + /// + /// Stop the current drag / drop operation no matter what state it is in. + /// + public void EndDrag() + { + Dragged = default; + _state = DragState.NotDragging; + _onEndDrag.Invoke(); + } + + private void StartDragging() + { + if (_onBeginDrag.Invoke()) { - _inputManager = IoCManager.Resolve(); - _onBeginDrag = onBeginDrag; - _onEndDrag = onEndDrag; - _onContinueDrag = onContinueDrag; + _state = DragState.Dragging; } - - /// - /// 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. - /// - public void MouseDown(T target) + else { - if (_state != DragState.NotDragging) - { - EndDrag(); - } - - Dragged = target; - _state = DragState.MouseDown; - _mouseDownScreenPos = _inputManager.MouseScreenPosition; - } - - /// - /// Stop the current drag / drop operation no matter what state it is in. - /// - public void EndDrag() - { - Dragged = default; - _state = DragState.NotDragging; - _onEndDrag.Invoke(); - } - - private void StartDragging() - { - if (_onBeginDrag.Invoke()) - { - _state = DragState.Dragging; - } - else - { - EndDrag(); - } - } - - /// - /// Should be invoked by using class every FrameUpdate or Update. - /// - public void Update(float frameTime) - { - switch (_state) - { - // check if dragging should begin - case DragState.MouseDown: - { - var screenPos = _inputManager.MouseScreenPosition; - if ((_mouseDownScreenPos.Position - screenPos.Position).Length > Deadzone) - { - StartDragging(); - } - - break; - } - case DragState.Dragging: - { - if (!_onContinueDrag.Invoke(frameTime)) - { - EndDrag(); - } - - break; - } - } + EndDrag(); } } /// - /// Invoked when a drag is confirmed and going to be initiated. Implementation should - /// typically set the drag shadow texture based on the target. + /// Should be invoked by using class every FrameUpdate or Update. /// - /// true if drag should begin, false to end. - public delegate bool OnBeginDrag(); + public void Update(float frameTime) + { + switch (_state) + { + // check if dragging should begin + case DragState.MouseDown: + { + var screenPos = _inputManager.MouseScreenPosition; + if ((_mouseDownScreenPos.Position - screenPos.Position).Length > Deadzone) + { + StartDragging(); + } - /// - /// Invoked every frame when drag is ongoing. Typically implementation should - /// make the drag shadow follow the mouse position. - /// - /// true if drag should continue, false to end. - public delegate bool OnContinueDrag(float frameTime); - - /// - /// invoked when - /// the drag drop is ending for any reason. This - /// should typically just clear the drag shadow. - /// - public delegate void OnEndDrag(); + break; + } + case DragState.Dragging: + { + if (!_onContinueDrag.Invoke(frameTime)) + { + EndDrag(); + } + break; + } + } + } } + +/// +/// Invoked when a drag is confirmed and going to be initiated. Implementation should +/// typically set the drag shadow texture based on the target. +/// +/// true if drag should begin, false to end. +public delegate bool OnBeginDrag(); + +/// +/// Invoked every frame when drag is ongoing. Typically implementation should +/// make the drag shadow follow the mouse position. +/// +/// true if drag should continue, false to end. +public delegate bool OnContinueDrag(float frameTime); + +/// +/// invoked when +/// the drag drop is ending for any reason. This +/// should typically just clear the drag shadow. +/// +public delegate void OnEndDrag(); diff --git a/Content.Client/DragDrop/DragDropSystem.cs b/Content.Client/DragDrop/DragDropSystem.cs index 1e12d53ba5..5be800260d 100644 --- a/Content.Client/DragDrop/DragDropSystem.cs +++ b/Content.Client/DragDrop/DragDropSystem.cs @@ -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; + +/// +/// Handles clientside drag and drop logic +/// +[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"; + /// - /// Handles clientside drag and drop logic + /// Current entity being dragged around. /// - [UsedImplicitly] - public sealed class DragDropSystem : SharedDragDropSystem + private EntityUid? _draggedEntity; + + /// + /// If an entity is being dragged is there a drag shadow. + /// + 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 float _deadzone; + + private DragState _state = DragState.NotDragging; + + /// + /// screen pos where the mouse down began for the drag + /// + private ScreenCoordinates? _mouseDownScreenPos; + + private ShaderInstance? _dropTargetInRangeShader; + private ShaderInstance? _dropTargetOutOfRangeShader; + + private readonly List _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!; + base.Initialize(); + _sawmill = Logger.GetSawmill("drag_drop"); + UpdatesOutsidePrediction = true; + UpdatesAfter.Add(typeof(EyeUpdateSystem)); - // how often to recheck possible targets (prevents calling expensive - // check logic each update) - private const float TargetRecheckInterval = 0.25f; + _cfgMan.OnValueChanged(CCVars.DragDropDeadZone, SetDeadZone, true); - // 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; + _dropTargetInRangeShader = _prototypeManager.Index(ShaderDropTargetInRange).Instance(); + _dropTargetOutOfRangeShader = _prototypeManager.Index(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(); + } - private const string ShaderDropTargetInRange = "SelectionOutlineInrange"; - private const string ShaderDropTargetOutOfRange = "SelectionOutline"; + private void SetDeadZone(float deadZone) + { + _deadzone = deadZone; + } - // entity performing the drag action + public override void Shutdown() + { + _cfgMan.UnsubValueChanged(CCVars.DragDropDeadZone, SetDeadZone); + CommandBinds.Unregister(); + base.Shutdown(); + } - private EntityUid _dragger; - private readonly List _draggables = new(); - private EntityUid _dragShadow; + private bool OnUse(in PointerInputCmdHandler.PointerInputCmdArgs args) + { + // not currently predicted + if (_inputSystem.Predicted) + return false; - // 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; + // 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; - private DragDropHelper _dragDropHelper = default!; - - private ShaderInstance? _dropTargetInRangeShader; - private ShaderInstance? _dropTargetOutOfRangeShader; - - private readonly List _highlightedSprites = new(); - - public override void Initialize() + if (args.State == BoundKeyState.Down) { - UpdatesOutsidePrediction = true; - UpdatesAfter.Add(typeof(EyeUpdateSystem)); - - _dragDropHelper = new DragDropHelper(OnBeginDrag, OnContinueDrag, OnEndDrag); - _cfgMan.OnValueChanged(CCVars.DragDropDeadZone, SetDeadZone, true); - - _dropTargetInRangeShader = _prototypeManager.Index(ShaderDropTargetInRange).Instance(); - _dropTargetOutOfRangeShader = _prototypeManager.Index(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(); + return OnUseMouseDown(args); } - private void SetDeadZone(float deadZone) + if (args.State == BoundKeyState.Up) { - _dragDropHelper.Deadzone = deadZone; + return OnUseMouseUp(args); } - public override void Shutdown() + return false; + } + + private void EndDrag() + { + if (_state == DragState.NotDragging) + return; + + if (_dragShadow != null) { - _cfgMan.UnsubValueChanged(CCVars.DragDropDeadZone, SetDeadZone); - _dragDropHelper.EndDrag(); - CommandBinds.Unregister(); - base.Shutdown(); + Del(_dragShadow.Value); + _dragShadow = null; } - private bool OnUse(in PointerInputCmdHandler.PointerInputCmdArgs args) + _draggedEntity = null; + _state = DragState.NotDragging; + _mouseDownScreenPos = null; + + 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()) { - // 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) - { - return OnUseMouseDown(args); - } - else if (args.State == BoundKeyState.Up) - { - return OnUseMouseUp(args); - } - return false; } - private bool OnUseMouseDown(in PointerInputCmdHandler.PointerInputCmdArgs args) + // 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 (!Exists(args.EntityUid)) { - 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(); - - // possibly initiating a drag - // check if the clicked entity is draggable - if (!EntityManager.EntityExists(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(args.EntityUid)) - { - var dragEventArgs = new StartDragDropEvent(dragger, args.EntityUid); - - if (!draggable.CanStartDrag(dragEventArgs)) - { - continue; - } - - _draggables.Add(draggable); - canDrag = true; - } - - if (!canDrag) - { - return false; - } - - // 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; - - return true; - - } - - private bool OnBeginDrag() - { - if (_dragDropHelper.Dragged == default || Deleted(_dragDropHelper.Dragged)) - { - // something happened to the clicked entity or we moved the mouse off the target so - // we shouldn't replay the original click - return false; - } - - if (EntityManager.TryGetComponent(_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(_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(_dragShadow).WorldRotation = EntityManager.GetComponent(_dragDropHelper.Dragged).WorldRotation; - } - - HighlightTargets(); - _outline.SetEnabled(false); - - // drag initiated - return true; - } - - Logger.Warning("Unable to display drag shadow for {0} because it" + - " has no sprite component.", EntityManager.GetComponent(_dragDropHelper.Dragged).EntityName); return false; } - private bool OnContinueDrag(float frameTime) + // check if the entity is reachable + if (!_interactionSystem.InRangeUnobstructed(dragger, args.EntityUid)) { - 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; - } - - // 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; - - _targetRecheckTime += frameTime; - if (_targetRecheckTime > TargetRecheckInterval) - { - HighlightTargets(); - _targetRecheckTime -= TargetRecheckInterval; - } - - return true; + return false; } - private void OnEndDrag() - { - RemoveHighlights(); - if (_dragShadow != default) - { - EntityManager.DeleteEntity(_dragShadow); - } + var ev = new CanDragEvent(); - _outline.SetEnabled(true); - _dragShadow = default; - _draggables.Clear(); - _dragger = default; - _mouseDownTime = 0; - _savedMouseDown = null; + RaiseLocalEvent(args.EntityUid, ref ev); + + if (ev.Handled != true) + return false; + + _draggedEntity = args.EntityUid; + _state = DragState.MouseDown; + _mouseDownScreenPos = _inputManager.MouseScreenPosition; + _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; + + 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 OnUseMouseUp(in PointerInputCmdHandler.PointerInputCmdArgs args) + _state = DragState.Dragging; + DebugTools.Assert(_dragShadow == null); + _outline.SetEnabled(false); + HighlightTargets(); + + if (TryComp(_draggedEntity, out var draggedSprite)) { - if (_dragDropHelper.IsDragging == false || _dragDropHelper.Dragged == default) + // pop up drag shadow under mouse + var mousePos = _eyeManager.ScreenToMap(_inputManager.MouseScreenPosition); + _dragShadow = EntityManager.SpawnEntity("dragshadow", mousePos); + var dragSprite = Comp(_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) + { + Transform(_dragShadow.Value).WorldRotation = Transform(_draggedEntity.Value).WorldRotation; + } + + // drag initiated + return; + } + + _sawmill.Warning($"Unable to display drag shadow for {ToPrettyString(_draggedEntity.Value)} because it has no sprite component."); + } + + private bool UpdateDrag(float frameTime) + { + if (!Exists(_draggedEntity) || _combatMode.IsInCombatMode()) + { + EndDrag(); + return false; + } + + var player = _playerManager.LocalPlayer?.ControlledEntity; + + // still in range of the thing we are dragging? + if (player == null || !_interactionSystem.InRangeUnobstructed(player.Value, _draggedEntity.Value)) + { + return false; + } + + if (_dragShadow == null) + return false; + + _targetRecheckTime += frameTime; + + if (_targetRecheckTime > TargetRecheckInterval) + { + HighlightTargets(); + _targetRecheckTime -= TargetRecheckInterval; + } + + return true; + } + + private bool OnUseMouseUp(in PointerInputCmdHandler.PointerInputCmdArgs args) + { + if (_state == DragState.MouseDown) + { + // 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 entities; - - if (_stateManager.CurrentState is GameplayState screen) - { - entities = screen.GetClickableEntities(args.Coordinates).ToList(); - } - else - { - entities = Array.Empty(); - } - - var outOfRange = false; - - 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); - - // TODO: Cache valid CanDragDrops - if (ValidDragDrop(dropArgs) != true) continue; - - if (!_interactionSystem.InRangeUnobstructed(dropArgs.User, dropArgs.Target) - || !_interactionSystem.InRangeUnobstructed(dropArgs.User, dropArgs.Dragged)) - { - outOfRange = true; - continue; - } - - foreach (var draggable in _draggables) - { - if (!draggable.CanDrop(dropArgs)) continue; - - // tell the server about the drop attempt - RaiseNetworkEvent(new DragDropRequestEvent(args.Coordinates, _dragDropHelper.Dragged, - entity)); - - draggable.Drop(dropArgs); - - _dragDropHelper.EndDrag(); - return true; - } - } - - if (outOfRange && - _playerManager.LocalPlayer?.ControlledEntity is { } player && - player.IsValid()) - { - player.PopupMessage(Loc.GetString("drag-drop-system-out-of-range-text")); - } - - _dragDropHelper.EndDrag(); return false; } - // TODO make this just use TargetOutlineSystem - private void HighlightTargets() + var localPlayer = _playerManager.LocalPlayer?.ControlledEntity; + + if (localPlayer == null || !Exists(_draggedEntity)) { - if (_dragDropHelper.Dragged == default || Deleted(_dragDropHelper.Dragged) || - _dragShadow == default || Deleted(_dragShadow)) + EndDrag(); + return false; + } + + IEnumerable entities; + + if (_stateManager.CurrentState is GameplayState screen) + { + entities = screen.GetClickableEntities(args.Coordinates); + } + else + { + entities = Array.Empty(); + } + + var outOfRange = false; + var user = localPlayer.Value; + + foreach (var entity in entities) + { + if (entity == _draggedEntity) + continue; + + // check if it's able to be dropped on by current dragged entity + var valid = ValidDragDrop(user, _draggedEntity.Value, entity); + + if (valid != true) continue; + + if (!_interactionSystem.InRangeUnobstructed(user, entity) + || !_interactionSystem.InRangeUnobstructed(user, _draggedEntity.Value)) { - Logger.Warning("Programming error. Can't highlight drag and drop targets, not currently " + - "dragging anything or dragged entity / shadow was deleted."); - return; + outOfRange = true; + continue; } - // highlights the possible targets which are visible - // and able to be dropped on by the current dragged entity + // tell the server about the drop attempt + RaiseNetworkEvent(new DragDropRequestEvent(_draggedEntity.Value, entity)); + EndDrag(); + return true; + } - // remove current highlights - RemoveHighlights(); + if (outOfRange) + { + _popup.PopupEntity(Loc.GetString("drag-drop-system-out-of-range-text"), _draggedEntity.Value, Filter.Local(), true); + } - // 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) + EndDrag(); + return false; + } + + // TODO make this just use TargetOutlineSystem + private void HighlightTargets() + { + if (!Exists(_draggedEntity) || + !Exists(_dragShadow)) + { + return; + } + + var user = _playerManager.LocalPlayer?.ControlledEntity; + + if (user == null) + return; + + // highlights the possible targets which are visible + // and able to be dropped on by the current dragged entity + + // remove current highlights + RemoveHighlights(); + + // 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); + + var spriteQuery = GetEntityQuery(); + + foreach (var entity in pvsEntities) + { + if (!spriteQuery.TryGetComponent(entity, out var inRangeSprite) || + !inRangeSprite.Visible || + entity == _draggedEntity) { - if (!EntityManager.TryGetComponent(pvsEntity, out SpriteComponent? inRangeSprite) || - !inRangeSprite.Visible || - pvsEntity == _dragDropHelper.Dragged) continue; + continue; + } - // check if it's able to be dropped on by current dragged entity - var dropArgs = new DragDropEvent(_dragger, EntityManager.GetComponent(pvsEntity).Coordinates, _dragDropHelper.Dragged, pvsEntity); + var valid = ValidDragDrop(user.Value, _draggedEntity.Value, entity); - var valid = ValidDragDrop(dropArgs); - if (valid == null) continue; + // 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) + // We'll do a final check given server-side does this before any dragdrop can take place. + if (valid.Value) + { + valid = _interactionSystem.InRangeUnobstructed(user.Value, _draggedEntity.Value) + && _interactionSystem.InRangeUnobstructed(user.Value, entity); + } + + if (inRangeSprite.PostShader != null && + inRangeSprite.PostShader != _dropTargetInRangeShader && + inRangeSprite.PostShader != _dropTargetOutOfRangeShader) + { + continue; + } + + // highlight depending on whether its in or out of range + inRangeSprite.PostShader = valid.Value ? _dropTargetInRangeShader : _dropTargetOutOfRangeShader; + inRangeSprite.RenderOrder = EntityManager.CurrentTick.Value; + _highlightedSprites.Add(inRangeSprite); + } + } + + private void RemoveHighlights() + { + foreach (var highlightedSprite in _highlightedSprites) + { + if (highlightedSprite.PostShader != _dropTargetInRangeShader && highlightedSprite.PostShader != _dropTargetOutOfRangeShader) + continue; + + highlightedSprite.PostShader = null; + highlightedSprite.RenderOrder = 0; + } + + _highlightedSprites.Clear(); + } + + /// + /// Are these args valid for drag-drop? + /// + /// + /// 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. + /// + private bool? ValidDragDrop(EntityUid user, EntityUid dragged, EntityUid target) + { + if (!_actionBlockerSystem.CanInteract(user, target)) + return null; + + // CanInteract() doesn't support checking a second "target" entity. + // Doing so manually: + var ev = new GettingInteractedWithAttemptEvent(user, dragged); + RaiseLocalEvent(dragged, ev, true); + + if (ev.Cancelled) + return false; + + var dropEv = new CanDropDraggedEvent(user, target); + + RaiseLocalEvent(dragged, ref dropEv); + + if (dropEv.Handled) + { + if (!dropEv.CanDrop) + return false; + } + + var dropEv2 = new CanDropTargetEvent(user, dragged); + + RaiseLocalEvent(target, ref dropEv2); + + if (dropEv2.Handled) + return dropEv2.CanDrop; + + return null; + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + switch (_state) + { + // check if dragging should begin + case DragState.MouseDown: + { + var screenPos = _inputManager.MouseScreenPosition; + if ((_mouseDownScreenPos!.Value.Position - screenPos.Position).Length > _deadzone) { - valid = _interactionSystem.InRangeUnobstructed(dropArgs.Target, dropArgs.Dragged) - && _interactionSystem.InRangeUnobstructed(dropArgs.Target, dropArgs.Target); + StartDrag(); } - if (inRangeSprite.PostShader != null && - inRangeSprite.PostShader != _dropTargetInRangeShader && - inRangeSprite.PostShader != _dropTargetOutOfRangeShader) - return; - - // highlight depending on whether its in or out of range - inRangeSprite.PostShader = valid.Value ? _dropTargetInRangeShader : _dropTargetOutOfRangeShader; - inRangeSprite.RenderOrder = EntityManager.CurrentTick.Value; - _highlightedSprites.Add(inRangeSprite); - } - } - - private void RemoveHighlights() - { - foreach (var highlightedSprite in _highlightedSprites) - { - if (highlightedSprite.PostShader != _dropTargetInRangeShader && highlightedSprite.PostShader != _dropTargetOutOfRangeShader) - continue; - - highlightedSprite.PostShader = null; - highlightedSprite.RenderOrder = 0; - } - - _highlightedSprites.Clear(); - } - - /// - /// Are these args valid for drag-drop? - /// - /// - /// null if the target doesn't support IDragDropOn - private bool? ValidDragDrop(DragDropEvent eventArgs) - { - if (!_actionBlockerSystem.CanInteract(eventArgs.User, eventArgs.Target)) - { - return false; - } - - // 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; - - var valid = CheckDragDropOn(eventArgs); - - foreach (var comp in EntityManager.GetComponents(eventArgs.Target)) - { - if (!comp.CanDragDropOn(eventArgs)) - { - valid = false; - // dragDropOn.Add(comp); - continue; - } - - valid = true; break; } - - if (valid != true) return valid; - - // Need at least one IDraggable to return true or else we can't do shit - valid = false; - - foreach (var comp in EntityManager.GetComponents(eventArgs.User)) - { - if (!comp.CanDrop(eventArgs)) continue; - valid = true; + case DragState.Dragging: + UpdateDrag(frameTime); break; - } - - return valid; } + } - public override void Update(float frameTime) + public override void FrameUpdate(float frameTime) + { + base.FrameUpdate(frameTime); + + // Update position every frame to make it smooth. + if (Exists(_dragShadow)) { - base.Update(frameTime); - - _dragDropHelper.Update(frameTime); - } - - public override void FrameUpdate(float frameTime) - { - base.FrameUpdate(frameTime); - - // Update position every frame to make it smooth. - if (_dragDropHelper.IsDragging) - { - var mousePos = _eyeManager.ScreenToMap(_inputManager.MouseScreenPosition); - Transform(_dragShadow).WorldPosition = mousePos.Position; - } + var mousePos = _eyeManager.ScreenToMap(_inputManager.MouseScreenPosition); + Transform(_dragShadow.Value).WorldPosition = mousePos.Position; } } } + +public enum DragState : byte +{ + NotDragging, + + /// + /// Not dragging yet, waiting to see + /// if they hold for long enough + /// + MouseDown, + + /// + /// Currently dragging something + /// + Dragging, +} diff --git a/Content.Client/Kitchen/Components/KitchenSpikeComponent.cs b/Content.Client/Kitchen/Components/KitchenSpikeComponent.cs index 58f271d63f..375677ef60 100644 --- a/Content.Client/Kitchen/Components/KitchenSpikeComponent.cs +++ b/Content.Client/Kitchen/Components/KitchenSpikeComponent.cs @@ -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 index 0000000000..3627a29fa9 --- /dev/null +++ b/Content.Client/Kitchen/KitchenSpikeSystem.cs @@ -0,0 +1,8 @@ +using Content.Shared.Kitchen; + +namespace Content.Client.Kitchen; + +public sealed class KitchenSpikeSystem : SharedKitchenSpikeSystem +{ + +} diff --git a/Content.Client/MedicalScanner/MedicalScannerComponent.cs b/Content.Client/MedicalScanner/MedicalScannerComponent.cs index 49bcbc63a9..1369544cd6 100644 --- a/Content.Client/MedicalScanner/MedicalScannerComponent.cs +++ b/Content.Client/MedicalScanner/MedicalScannerComponent.cs @@ -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; - } } } diff --git a/Content.Client/Movement/Systems/ClimbSystem.cs b/Content.Client/Movement/Systems/ClimbSystem.cs index 1f7b757ba4..826c3b0a52 100644 --- a/Content.Client/Movement/Systems/ClimbSystem.cs +++ b/Content.Client/Movement/Systems/ClimbSystem.cs @@ -13,6 +13,7 @@ public sealed class ClimbSystem : SharedClimbSystem { base.Initialize(); SubscribeLocalEvent(OnClimbingState); + SubscribeLocalEvent(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 index daad176952..0000000000 --- a/Content.Client/Strip/StrippableComponent.cs +++ /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; - } - } -} diff --git a/Content.Client/Strip/StrippableSystem.cs b/Content.Client/Strip/StrippableSystem.cs index 59083c4515..bc7e325550 100644 --- a/Content.Client/Strip/StrippableSystem.cs +++ b/Content.Client/Strip/StrippableSystem.cs @@ -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; /// -/// 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. /// -public sealed class StrippableSystem : EntitySystem +public sealed class StrippableSystem : SharedStrippableSystem { public override void Initialize() { diff --git a/Content.Server/Buckle/Systems/BuckleSystem.Buckle.cs b/Content.Server/Buckle/Systems/BuckleSystem.Buckle.cs index 4ab9439f94..48ec12f9f9 100644 --- a/Content.Server/Buckle/Systems/BuckleSystem.Buckle.cs +++ b/Content.Server/Buckle/Systems/BuckleSystem.Buckle.cs @@ -31,8 +31,8 @@ public sealed partial class BuckleSystem SubscribeLocalEvent(HandleInteractHand); SubscribeLocalEvent>(AddUnbuckleVerb); SubscribeLocalEvent(OnEntityStorageInsertAttempt); - SubscribeLocalEvent(OnBuckleCanDrop); - SubscribeLocalEvent(OnBuckleDragDrop); + SubscribeLocalEvent(OnBuckleCanDrop); + SubscribeLocalEvent(OnBuckleDragDrop); } private void AddUnbuckleVerb(EntityUid uid, BuckleComponent component, GetVerbsEvent 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(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); } diff --git a/Content.Server/Buckle/Systems/BuckleSystem.Strap.cs b/Content.Server/Buckle/Systems/BuckleSystem.Strap.cs index 65e15f0fc2..1ca2acdef3 100644 --- a/Content.Server/Buckle/Systems/BuckleSystem.Strap.cs +++ b/Content.Server/Buckle/Systems/BuckleSystem.Strap.cs @@ -27,7 +27,7 @@ public sealed partial class BuckleSystem SubscribeLocalEvent((_,c,_) => StrapRemoveAll(c)); SubscribeLocalEvent((_, c, _) => StrapRemoveAll(c)); SubscribeLocalEvent((_, c, _) => StrapRemoveAll(c)); - SubscribeLocalEvent(OnStrapDragDrop); + SubscribeLocalEvent(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); diff --git a/Content.Server/Climbing/ClimbSystem.cs b/Content.Server/Climbing/ClimbSystem.cs index f0c953c99a..6f11cb0351 100644 --- a/Content.Server/Climbing/ClimbSystem.cs +++ b/Content.Server/Climbing/ClimbSystem.cs @@ -58,7 +58,7 @@ public sealed class ClimbSystem : SharedClimbSystem SubscribeLocalEvent(Reset); SubscribeLocalEvent>(AddClimbableVerb); - SubscribeLocalEvent(OnClimbableDragDrop); + SubscribeLocalEvent(OnClimbableDragDrop); SubscribeLocalEvent(OnClimbFinished); SubscribeLocalEvent(OnClimbEndCollide); @@ -68,17 +68,17 @@ public sealed class ClimbSystem : SharedClimbSystem SubscribeLocalEvent(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, diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs index 8439295484..0e7ed86331 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs @@ -69,7 +69,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems // Interactions SubscribeLocalEvent(HandleActivate); SubscribeLocalEvent(HandleAfterInteractUsing); - SubscribeLocalEvent(HandleDragDropOn); + SubscribeLocalEvent(HandleDragDropOn); SubscribeLocalEvent(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); } diff --git a/Content.Server/Interaction/InteractionSystem.cs b/Content.Server/Interaction/InteractionSystem.cs index f4fbafcafc..2433332afc 100644 --- a/Content.Server/Interaction/InteractionSystem.cs +++ b/Content.Server/Interaction/InteractionSystem.cs @@ -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"); - return; - } - - if (Deleted(msg.Dropped) || Deleted(msg.Target)) + if (Deleted(msg.Dragged) || Deleted(msg.Target)) return; - if (!_actionBlockerSystem.CanInteract(userEntity.Value, msg.Target)) - return; + var user = args.SenderSession.AttachedEntity; - var interactionArgs = new DragDropEvent(userEntity.Value, msg.DropLocation, msg.Dropped, msg.Target); + if (user == null || !_actionBlockerSystem.CanInteract(user.Value, msg.Target)) + return; // 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(msg.Dropped)) - { - if (dragDrop.CanDrop(interactionArgs) && - dragDrop.Drop(interactionArgs)) - { - return; - } - } + var dropArgs = new DragDropTargetEvent(user.Value, msg.Dragged); - // trigger dragdropons on the targeted entity - RaiseLocalEvent(msg.Target, interactionArgs, false); - - if (interactionArgs.Handled) - return; - - foreach (var dragDropOn in AllComps(msg.Target)) - { - if (dragDropOn.CanDragDropOn(interactionArgs) && - dragDropOn.DragDropOn(interactionArgs)) - { - return; - } - } + RaiseLocalEvent(msg.Target, ref dropArgs); } + #endregion } } diff --git a/Content.Server/Kitchen/Components/KitchenSpikeComponent.cs b/Content.Server/Kitchen/Components/KitchenSpikeComponent.cs index 6abea76ec0..6333ca74e2 100644 --- a/Content.Server/Kitchen/Components/KitchenSpikeComponent.cs +++ b/Content.Server/Kitchen/Components/KitchenSpikeComponent.cs @@ -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? 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; - } } } diff --git a/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs b/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs index 08ae86cef8..78d4a01deb 100644 --- a/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs @@ -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(OnInteractUsing); SubscribeLocalEvent(OnInteractHand); - SubscribeLocalEvent(OnDragDrop); + SubscribeLocalEvent(OnDragDrop); //DoAfter SubscribeLocalEvent(OnSpikingFinished); SubscribeLocalEvent(OnSpikingFail); SubscribeLocalEvent(OnSuicide); + + SubscribeLocalEvent(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(args.VictimUid, out var butcherable)) + if (EntityManager.TryGetComponent(args.VictimUid, out var butcherable)) butcherable.BeingButchered = false; } @@ -70,7 +78,7 @@ namespace Content.Server.Kitchen.EntitySystems { component.InUse = false; - if (EntityManager.TryGetComponent(args.VictimUid, out var butcherable)) + if (EntityManager.TryGetComponent(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) diff --git a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs index 21022bb629..4aceecd419 100644 --- a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs @@ -34,7 +34,7 @@ public sealed class SharpSystem : EntitySystem SubscribeLocalEvent(OnDoafterComplete); SubscribeLocalEvent(OnDoafterCancelled); - SubscribeLocalEvent>(OnGetInteractionVerbs); + SubscribeLocalEvent>(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(target, out var butcher)) + if (!TryComp(target, out var butcher)) return; if (!TryComp(knife, out var sharp)) @@ -79,7 +79,7 @@ public sealed class SharpSystem : EntitySystem private void OnDoafterComplete(SharpButcherDoafterComplete ev) { - if (!TryComp(ev.Entity, out var butcher)) + if (!TryComp(ev.Entity, out var butcher)) return; if (!TryComp(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 args) + private void OnGetInteractionVerbs(EntityUid uid, ButcherableComponent component, GetVerbsEvent args) { if (component.Type != ButcheringType.Knife || args.Hands == null) return; diff --git a/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs b/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs index 9ec1b3a290..7d35105930 100644 --- a/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs +++ b/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs @@ -228,7 +228,7 @@ namespace Content.Server.Medical.BiomassReclaimer { component.BloodReagent = stream.BloodReagent; } - if (TryComp(toProcess, out var butcherableComponent)) + if (TryComp(toProcess, out var butcherableComponent)) { component.SpawnedEntities = butcherableComponent.SpawnedEntities; } diff --git a/Content.Server/Medical/Components/MedicalScannerComponent.cs b/Content.Server/Medical/Components/MedicalScannerComponent.cs index 1c6606ea84..165eb17d20 100644 --- a/Content.Server/Medical/Components/MedicalScannerComponent.cs +++ b/Content.Server/Medical/Components/MedicalScannerComponent.cs @@ -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; - } } } diff --git a/Content.Server/Medical/CryoPodSystem.cs b/Content.Server/Medical/CryoPodSystem.cs index 694ff77b59..b789031044 100644 --- a/Content.Server/Medical/CryoPodSystem.cs +++ b/Content.Server/Medical/CryoPodSystem.cs @@ -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(OnCryoPodCanDropOn); SubscribeLocalEvent(OnComponentInit); SubscribeLocalEvent>(AddAlternativeVerbs); SubscribeLocalEvent(OnEmagged); @@ -63,7 +63,7 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem SubscribeLocalEvent(OnCryoPodPryInterrupted); SubscribeLocalEvent(OnCryoPodUpdateAtmosphere); - SubscribeLocalEvent(HandleDragDropOn); + SubscribeLocalEvent(HandleDragDropOn); SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(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) { diff --git a/Content.Server/Medical/MedicalScannerSystem.cs b/Content.Server/Medical/MedicalScannerSystem.cs index 979ce061f1..d468cb0417 100644 --- a/Content.Server/Medical/MedicalScannerSystem.cs +++ b/Content.Server/Medical/MedicalScannerSystem.cs @@ -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>(AddInsertOtherVerb); SubscribeLocalEvent>(AddAlternativeVerbs); SubscribeLocalEvent(OnDestroyed); - SubscribeLocalEvent(HandleDragDropOn); + SubscribeLocalEvent(OnDragDropOn); SubscribeLocalEvent(OnPortDisconnected); SubscribeLocalEvent(OnAnchorChanged); SubscribeLocalEvent(OnRefreshParts); SubscribeLocalEvent(OnUpgradeExamine); + SubscribeLocalEvent(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(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(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(scannerComponent.Owner, out var appearance)) + if (TryComp(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(user, out var comp)) + if (!HasComp(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 index 141dddec00..0000000000 --- a/Content.Server/Strip/StrippableComponent.cs +++ /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 - { - /// - /// The strip delay for hands. - /// - [DataField("handDelay")] - public float HandStripDelay = 4f; - - public override bool Drop(DragDropEvent args) - { - IoCManager.Resolve().GetEntitySystem().StartOpeningStripper(args.User, this); - return true; - } - - public Dictionary CancelTokens = new(); - } -} diff --git a/Content.Server/Strip/StrippableSystem.cs b/Content.Server/Strip/StrippableSystem.cs index 4acc65fba2..d70d374f69 100644 --- a/Content.Server/Strip/StrippableSystem.cs +++ b/Content.Server/Strip/StrippableSystem.cs @@ -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(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; - } - } } } diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs index 12310ce0f5..e0d772c718 100644 --- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs @@ -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; diff --git a/Content.Shared/Body/Components/BodyComponent.cs b/Content.Shared/Body/Components/BodyComponent.cs index 022d3a7799..312f37f251 100644 --- a/Content.Shared/Body/Components/BodyComponent.cs +++ b/Content.Shared/Body/Components/BodyComponent.cs @@ -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))] public readonly string? Prototype; @@ -27,14 +26,4 @@ public sealed class BodyComponent : Component, IDraggable /// [DataField("requiredLegs")] public int RequiredLegs; - - bool IDraggable.CanStartDrag(StartDragDropEvent args) - { - return true; - } - - bool IDraggable.CanDrop(CanDropEvent args) - { - return true; - } } diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs index 92a0c88a2a..3b464ca115 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs @@ -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(OnBodyGetState); SubscribeLocalEvent(OnBodyHandleState); + SubscribeLocalEvent(OnBodyCanDrag); + } + + private void OnBodyCanDrag(EntityUid uid, BodyComponent component, ref CanDragEvent args) + { + args.Handled = true; } private void OnBodyInit(EntityUid bodyId, BodyComponent body, ComponentInit args) diff --git a/Content.Shared/Buckle/SharedBuckleSystem.Strap.cs b/Content.Shared/Buckle/SharedBuckleSystem.Strap.cs index 8e54bd384e..9e9ffa1583 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.Strap.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.Strap.cs @@ -13,7 +13,7 @@ public abstract partial class SharedBuckleSystem { SubscribeLocalEvent(OnStrapRotate); SubscribeLocalEvent(OnStrapHandleState); - SubscribeLocalEvent(OnStrapCanDragDropOn); + SubscribeLocalEvent(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; } } diff --git a/Content.Shared/Climbing/BonkSystem.cs b/Content.Shared/Climbing/BonkSystem.cs index 9774f59751..dcf534058d 100644 --- a/Content.Shared/Climbing/BonkSystem.cs +++ b/Content.Shared/Climbing/BonkSystem.cs @@ -22,7 +22,7 @@ public sealed class BonkSystem : EntitySystem public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnDragDrop); + SubscribeLocalEvent(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); } } diff --git a/Content.Shared/Climbing/SharedClimbSystem.cs b/Content.Shared/Climbing/SharedClimbSystem.cs index 1d9ff5f606..6521599f54 100644 --- a/Content.Shared/Climbing/SharedClimbSystem.cs +++ b/Content.Shared/Climbing/SharedClimbSystem.cs @@ -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(HandleMoveAttempt); - SubscribeLocalEvent(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(args.Dragged); } diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index db17972ac8..b5cf8a5868 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -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; diff --git a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs index e0312a7af3..e0f0faf1a2 100644 --- a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs +++ b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs @@ -27,7 +27,7 @@ namespace Content.Shared.Disposal { base.Initialize(); SubscribeLocalEvent(OnPreventCollide); - SubscribeLocalEvent(OnCanDragDropOn); + SubscribeLocalEvent(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 index 9667bb7659..0000000000 --- a/Content.Shared/DragDrop/CanDragDropOnEvent.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace Content.Shared.DragDrop; - -/// -/// 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. -/// -public sealed class CanDragDropOnEvent : HandledEntityEventArgs -{ - /// - /// Entity doing the drag and drop. - /// - public EntityUid User { get; } - - /// - /// Entity that is being dragged. - /// - public EntityUid Dragged { get; } - - /// - /// Entity that is being dropped on. - /// - public EntityUid Target { get; } - - /// - /// If the dragged entity can be dropped on the target. - /// - public bool CanDrop { get; set; } = false; - - public CanDragDropOnEvent(EntityUid user, EntityUid dragged, EntityUid target) - { - User = user; - Dragged = dragged; - Target = target; - } -} diff --git a/Content.Shared/DragDrop/DragDropRequestEvent.cs b/Content.Shared/DragDrop/DragDropRequestEvent.cs index b48e3889e6..af32997942 100644 --- a/Content.Shared/DragDrop/DragDropRequestEvent.cs +++ b/Content.Shared/DragDrop/DragDropRequestEvent.cs @@ -1,33 +1,26 @@ -using Robust.Shared.Map; using Robust.Shared.Serialization; namespace Content.Shared.DragDrop { /// - /// Requests a drag / drop interaction to be performed + /// Raised on the client to the server requesting a drag-drop. /// [Serializable, NetSerializable] public sealed class DragDropRequestEvent : EntityEventArgs { - /// - /// Location that the entity was dropped. - /// - public EntityCoordinates DropLocation { get; } - /// /// Entity that was dragged and dropped. /// - public EntityUid Dropped { get; } + public EntityUid Dragged { get; } /// /// Entity that was drag dropped on. /// 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 index 0000000000..288a269e19 --- /dev/null +++ b/Content.Shared/DragDrop/DraggableEvents.cs @@ -0,0 +1,67 @@ +namespace Content.Shared.DragDrop; + +/// +/// Raised directed on an entity when attempting to start a drag. +/// +[ByRefEvent] +public record struct CanDragEvent +{ + /// + /// False if we are unable to drag this entity. + /// + public bool Handled; +} + +/// +/// Raised directed on a dragged entity to indicate whether it has interactions with the target entity. +/// +[ByRefEvent] +public record struct CanDropDraggedEvent(EntityUid User, EntityUid Target) +{ + public readonly EntityUid User = User; + public readonly EntityUid Target = Target; + public bool Handled = false; + + /// + /// Can we drop the entity onto the target? If the event is not handled then there is no supported interactions. + /// + public bool CanDrop = false; +} + +/// +/// Raised directed on the target entity to indicate whether it has interactions with the dragged entity. +/// +[ByRefEvent] +public record struct CanDropTargetEvent(EntityUid User, EntityUid Dragged) +{ + public readonly EntityUid User = User; + public readonly EntityUid Dragged = Dragged; + public bool Handled = false; + + /// + /// + /// + public bool CanDrop = false; +} + +/// +/// Raised directed on a dragged entity when it is dropped on a target entity. +/// +[ByRefEvent] +public record struct DragDropDraggedEvent(EntityUid User, EntityUid Target) +{ + public readonly EntityUid User = User; + public readonly EntityUid Target = Target; + public bool Handled = false; +} + +/// +/// Raised directed on the target entity when a dragged entity is dragged onto it. +/// +[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 index 033a14215f..0000000000 --- a/Content.Shared/DragDrop/DropAttemptEvent.cs +++ /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 index 0fca993bc9..0000000000 --- a/Content.Shared/DragDrop/IDragDropOn.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Content.Shared.DragDrop -{ - /// - /// This interface allows the component's entity to be dragged and dropped - /// onto by another entity and gives it behavior when that occurs. - /// - [RequiresExplicitImplementation] - public interface IDragDropOn - { - /// - /// Invoked when another entity is being dragged and dropped - /// onto this one before invoking . - /// Note that other drag and drop interactions may be attempted if - /// this one fails. - /// - /// - /// true if is valid, false otherwise. - bool CanDragDropOn(DragDropEvent eventArgs); - - /// - /// Invoked server-side when another entity is being dragged and dropped - /// onto this one before invoking - /// Note that other drag and drop interactions may be attempted if - /// this one fails. - /// - /// - /// true if an interaction occurred and no further interaction should - /// be processed for this drop. - /// - bool DragDropOn(DragDropEvent eventArgs); - } -} diff --git a/Content.Shared/DragDrop/IDraggable.cs b/Content.Shared/DragDrop/IDraggable.cs deleted file mode 100644 index f4940426a8..0000000000 --- a/Content.Shared/DragDrop/IDraggable.cs +++ /dev/null @@ -1,124 +0,0 @@ -using Robust.Shared.Map; - -namespace Content.Shared.DragDrop -{ - /// - /// This interface allows a local client to initiate dragging of the component's - /// entity by mouse, for drag and drop interactions. - /// - [RequiresExplicitImplementation] - public interface IDraggable - { - /// - /// 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. - /// - /// - /// The information about the drag, such as who is doing it. - /// - /// True if the drag should be initiated, false otherwise. - bool CanStartDrag(StartDragDropEvent args) - { - return true; - } - - /// - /// 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. - /// - /// - /// True if target is a valid target to be dropped on by this component's - /// entity, false otherwise. - /// - bool CanDrop(CanDropEvent args); - - /// - /// Invoked when this component's entity is being dropped on another. - /// Other drag and drop interactions may be attempted if this one fails. - /// - /// - /// The information about the drag, such as who is doing it. - /// - /// - /// True if an interaction occurred and no further interaction should - /// be processed for this drop, false otherwise. - /// - bool Drop(DragDropEvent args) - { - return false; - } - } - - [Virtual] - public class StartDragDropEvent : HandledEntityEventArgs - { - /// - /// Entity doing the drag and drop. - /// - public EntityUid User { get; } - - /// - /// Entity that is being dragged. - /// - public EntityUid Dragged { get; } - - /// - /// Creates a new instance of . - /// - /// The entity doing the drag and drop. - /// The entity that is being dragged and dropped. - public StartDragDropEvent(EntityUid user, EntityUid dragged) - { - User = user; - Dragged = dragged; - } - } - - [Virtual] - public class CanDropEvent : StartDragDropEvent - { - /// - /// The entity uid that - /// is being dropped onto. - /// - public EntityUid Target { get; } - - /// - /// Creates a new instance of . - /// - /// The entity doing the drag and drop. - /// The entity that is being dragged and dropped. - /// The entity that is being dropped onto. - public CanDropEvent(EntityUid user, EntityUid dragged, EntityUid target) : base(user, dragged) - { - Target = target; - } - } - - [Virtual] - public class DragDropEvent : CanDropEvent - { - /// - /// The location where - /// is being dropped. - /// - public EntityCoordinates DropLocation { get; } - - /// - /// Creates a new instance of . - /// - /// The entity doing the drag and drop. - /// The location where is being dropped. - /// The entity that is being dragged and dropped. - /// The entity that is being dropped onto. - public DragDropEvent(EntityUid user, EntityCoordinates dropLocation, EntityUid dragged, EntityUid target) : base(user, dragged, target) - { - DropLocation = dropLocation; - } - } -} diff --git a/Content.Shared/DragDrop/SharedDragDropSystem.cs b/Content.Shared/DragDrop/SharedDragDropSystem.cs index d6fab4b6ff..7f1f6c23f7 100644 --- a/Content.Shared/DragDrop/SharedDragDropSystem.cs +++ b/Content.Shared/DragDrop/SharedDragDropSystem.cs @@ -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; - } } diff --git a/Content.Shared/Ghost/SharedGhostSystem.cs b/Content.Shared/Ghost/SharedGhostSystem.cs index b480906f97..fa91d5e2ff 100644 --- a/Content.Shared/Ghost/SharedGhostSystem.cs +++ b/Content.Shared/Ghost/SharedGhostSystem.cs @@ -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; } - + /// /// The entity representing the warp point. /// This is passed back to the server in diff --git a/Content.Shared/Hands/HandEvents.cs b/Content.Shared/Hands/HandEvents.cs index 3d5f64986a..29f4b2cad8 100644 --- a/Content.Shared/Hands/HandEvents.cs +++ b/Content.Shared/Hands/HandEvents.cs @@ -7,6 +7,14 @@ using static Robust.Shared.GameObjects.SharedSpriteComponent; namespace Content.Shared.Hands { + /// + /// Raised directed on an entity when attempting to drop its hand items. + /// + public sealed class DropAttemptEvent : CancellableEntityEventArgs + { + public readonly EntityUid Uid; + } + /// /// Raised directed at an item that needs to update its in-hand sprites/layers. /// diff --git a/Content.Shared/Inventory/InventorySystem.Equip.cs b/Content.Shared/Inventory/InventorySystem.Equip.cs index e545ef84a8..cd305bbf96 100644 --- a/Content.Shared/Inventory/InventorySystem.Equip.cs +++ b/Content.Shared/Inventory/InventorySystem.Equip.cs @@ -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(target) && - HasComp(actor) && + HasComp(target) && + HasComp(actor) && HasComp(actor); } diff --git a/Content.Shared/Kitchen/Components/SharedKitchenSpikeComponent.cs b/Content.Shared/Kitchen/Components/SharedKitchenSpikeComponent.cs index cee5232073..1428ef8bb6 100644 --- a/Content.Shared/Kitchen/Components/SharedKitchenSpikeComponent.cs +++ b/Content.Shared/Kitchen/Components/SharedKitchenSpikeComponent.cs @@ -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().HasComponent(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 index 0000000000..fa8d7a9e85 --- /dev/null +++ b/Content.Shared/Kitchen/SharedKitchenSpikeSystem.cs @@ -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(OnCanDrop); + } + + private void OnCanDrop(EntityUid uid, SharedKitchenSpikeComponent component, ref CanDropTargetEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + + if (!HasComp(args.Dragged)) + { + args.CanDrop = false; + return; + } + + // TODO: Once we get silicons need to check organic + args.CanDrop = true; + } +} diff --git a/Content.Shared/Medical/Cryogenics/SharedCryoPodComponent.cs b/Content.Shared/Medical/Cryogenics/SharedCryoPodComponent.cs index b10a929879..3a9c76111c 100644 --- a/Content.Shared/Medical/Cryogenics/SharedCryoPodComponent.cs +++ b/Content.Shared/Medical/Cryogenics/SharedCryoPodComponent.cs @@ -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 { /// /// 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().HasComponent(entity); - } - - bool IDragDropOn.CanDragDropOn(DragDropEvent eventArgs) - { - return CanInsert(eventArgs.Dragged); - } - - bool IDragDropOn.DragDropOn(DragDropEvent eventArgs) - { - return false; - } } diff --git a/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs b/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs index 3c41cec3cd..502669dc5c 100644 --- a/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs +++ b/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs @@ -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(args.Dragged); + args.Handled = true; + } + protected void OnComponentInit(EntityUid uid, SharedCryoPodComponent cryoPodComponent, ComponentInit args) { cryoPodComponent.BodyContainer = _containerSystem.EnsureContainer(uid, "scanner-body"); diff --git a/Content.Shared/MedicalScanner/SharedMedicalScannerComponent.cs b/Content.Shared/MedicalScanner/SharedMedicalScannerComponent.cs index 1b1805a1f1..848370c1d0 100644 --- a/Content.Shared/MedicalScanner/SharedMedicalScannerComponent.cs +++ b/Content.Shared/MedicalScanner/SharedMedicalScannerComponent.cs @@ -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().HasComponent(entity); - } - - bool IDragDropOn.CanDragDropOn(DragDropEvent eventArgs) - { - return CanInsert(eventArgs.Dragged); - } - - public abstract bool DragDropOn(DragDropEvent eventArgs); } } diff --git a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs index 82b183e519..6515ba41a8 100644 --- a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs +++ b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs @@ -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; diff --git a/Content.Shared/Nutrition/Components/SharedButcherableComponent.cs b/Content.Shared/Nutrition/Components/ButcherableComponent.cs similarity index 51% rename from Content.Shared/Nutrition/Components/SharedButcherableComponent.cs rename to Content.Shared/Nutrition/Components/ButcherableComponent.cs index 9f7794b30a..ab8e40b4d4 100644 --- a/Content.Shared/Nutrition/Components/SharedButcherableComponent.cs +++ b/Content.Shared/Nutrition/Components/ButcherableComponent.cs @@ -1,38 +1,31 @@ -using Content.Shared.DragDrop; using Content.Shared.Storage; +using Robust.Shared.GameStates; namespace Content.Shared.Nutrition.Components { /// /// Indicates that the entity can be thrown on a kitchen spike for butchering. /// - [RegisterComponent] - public sealed class SharedButcherableComponent : Component, IDraggable + [RegisterComponent, NetworkedComponent] + public sealed class ButcherableComponent : Component { [DataField("spawned", required: true)] public List 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; /// /// Prevents butchering same entity on two and more spikes simultaneously and multiple doAfters on the same Spike /// + [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 diff --git a/Content.Shared/PAI/SharedPAISystem.cs b/Content.Shared/PAI/SharedPAISystem.cs index 0e25d83824..a29c7bba01 100644 --- a/Content.Shared/PAI/SharedPAISystem.cs +++ b/Content.Shared/PAI/SharedPAISystem.cs @@ -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 index ef4c8b241f..0000000000 --- a/Content.Shared/Strip/Components/SharedStrippingComponent.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Content.Shared.ActionBlocker; -using Content.Shared.DragDrop; -using Content.Shared.Hands.Components; - -namespace Content.Shared.Strip.Components -{ - /// - /// Give to an entity to say they can strip another entity. - /// - [RegisterComponent] - public sealed class SharedStrippingComponent : Component, IDragDropOn - { - bool IDragDropOn.CanDragDropOn(DragDropEvent eventArgs) - { - var ent = IoCManager.Resolve(); - return eventArgs.Target != eventArgs.Dragged && - eventArgs.Target == eventArgs.User && - ent.HasComponent(eventArgs.Dragged) && - ent.HasComponent(eventArgs.User) && - ent.EntitySysManager.GetEntitySystem().CanInteract(eventArgs.User, eventArgs.Dragged); - } - - bool IDragDropOn.DragDropOn(DragDropEvent eventArgs) - { - // Handled by StrippableComponent - return true; - } - } -} diff --git a/Content.Shared/Strip/Components/SharedStrippableComponent.cs b/Content.Shared/Strip/Components/StrippableComponent.cs similarity index 72% rename from Content.Shared/Strip/Components/SharedStrippableComponent.cs rename to Content.Shared/Strip/Components/StrippableComponent.cs index ad8f83b5d6..597795473e 100644 --- a/Content.Shared/Strip/Components/SharedStrippableComponent.cs +++ b/Content.Shared/Strip/Components/StrippableComponent.cs @@ -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(); - return args.Target != args.Dragged && - args.Target == args.User && - ent.HasComponent(args.User) && - ent.HasComponent(args.User) && - ent.EntitySysManager.GetEntitySystem().CanInteract(args.User, args.Dragged); - } - - public abstract bool Drop(DragDropEvent args); + /// + /// The strip delay for hands. + /// + [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 index 0000000000..6893e28d94 --- /dev/null +++ b/Content.Shared/Strip/Components/StrippingComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Strip.Components +{ + /// + /// Give to an entity to say they can strip another entity. + /// + [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 index 0000000000..ec707b51fc --- /dev/null +++ b/Content.Shared/Strip/SharedStrippableSystem.cs @@ -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(OnCanDropOn); + SubscribeLocalEvent(OnCanDrop); + SubscribeLocalEvent(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(args.Dragged) && + HasComp(args.User); + } + + private void OnCanDrop(EntityUid uid, StrippableComponent component, ref CanDropDraggedEvent args) + { + args.CanDrop |= args.Target == args.User && + HasComp(args.User) && + HasComp(args.User); + + if (args.CanDrop) + args.Handled = true; + } +} diff --git a/Content.Shared/Stunnable/SharedStunSystem.cs b/Content.Shared/Stunnable/SharedStunSystem.cs index 530b0252be..458d28dbf9 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.cs @@ -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;