Drag changes (#2487)

* Drag changes

* Higlights only show near cursor
* Don't highlight un-droppable entities
* Fixes invalid highlights issue

* Also the scanner

* 2 months fix

* Address reviews

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
This commit is contained in:
metalgearsloth
2021-01-11 22:14:01 +11:00
committed by GitHub
parent 067397c713
commit acb3c72d99
28 changed files with 335 additions and 131 deletions

View File

@@ -1,6 +1,4 @@
#nullable enable #nullable enable
using Content.Client.GameObjects.Components.Disposal;
using Content.Client.GameObjects.Components.MedicalScanner;
using Content.Shared.GameObjects.Components.Body; using Content.Shared.GameObjects.Components.Body;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -11,16 +9,14 @@ namespace Content.Client.GameObjects.Components.Body
[ComponentReference(typeof(IBody))] [ComponentReference(typeof(IBody))]
public class BodyComponent : SharedBodyComponent, IDraggable public class BodyComponent : SharedBodyComponent, IDraggable
{ {
public bool CanDrop(CanDropEventArgs eventArgs) bool IDraggable.CanStartDrag(StartDragDropEventArgs args)
{ {
if (eventArgs.Target.HasComponent<DisposalUnitComponent>() || return true;
eventArgs.Target.HasComponent<MedicalScannerComponent>() || }
eventArgs.Target.HasComponent<DisposalMailingUnitComponent>())
{
return true;
}
return false; public bool CanDrop(CanDropEventArgs args)
{
return true;
} }
} }
} }

View File

@@ -6,6 +6,7 @@ using Robust.Shared.Interfaces.GameObjects;
namespace Content.Client.GameObjects.Components.Buckle namespace Content.Client.GameObjects.Components.Buckle
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(SharedBuckleComponent))]
public class BuckleComponent : SharedBuckleComponent public class BuckleComponent : SharedBuckleComponent
{ {
private bool _buckled; private bool _buckled;

View File

@@ -1,4 +1,5 @@
using Content.Shared.GameObjects.Components.Disposal; using Content.Shared.GameObjects.Components.Disposal;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects.Components.Disposal namespace Content.Client.GameObjects.Components.Disposal
@@ -7,5 +8,9 @@ namespace Content.Client.GameObjects.Components.Disposal
[ComponentReference(typeof(SharedDisposalMailingUnitComponent))] [ComponentReference(typeof(SharedDisposalMailingUnitComponent))]
public class DisposalMailingUnitComponent : SharedDisposalMailingUnitComponent public class DisposalMailingUnitComponent : SharedDisposalMailingUnitComponent
{ {
public override bool DragDropOn(DragDropEventArgs eventArgs)
{
return false;
}
} }
} }

View File

@@ -1,4 +1,5 @@
using Content.Shared.GameObjects.Components.Disposal; using Content.Shared.GameObjects.Components.Disposal;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects.Components.Disposal namespace Content.Client.GameObjects.Components.Disposal
@@ -7,5 +8,9 @@ namespace Content.Client.GameObjects.Components.Disposal
[ComponentReference(typeof(SharedDisposalUnitComponent))] [ComponentReference(typeof(SharedDisposalUnitComponent))]
public class DisposalUnitComponent : SharedDisposalUnitComponent public class DisposalUnitComponent : SharedDisposalUnitComponent
{ {
public override bool DragDropOn(DragDropEventArgs eventArgs)
{
return false;
}
} }
} }

View File

@@ -1,4 +1,5 @@
using Content.Shared.GameObjects.Components.Medical; using Content.Shared.GameObjects.Components.Medical;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects.Components.MedicalScanner namespace Content.Client.GameObjects.Components.MedicalScanner
@@ -7,5 +8,9 @@ namespace Content.Client.GameObjects.Components.MedicalScanner
[ComponentReference(typeof(SharedMedicalScannerComponent))] [ComponentReference(typeof(SharedMedicalScannerComponent))]
public class MedicalScannerComponent : SharedMedicalScannerComponent public class MedicalScannerComponent : SharedMedicalScannerComponent
{ {
public override bool DragDropOn(DragDropEventArgs eventArgs)
{
return false;
}
} }
} }

View File

@@ -1,5 +1,8 @@
using Content.Shared.GameObjects.Components.Movement; using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Utility;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Client.GameObjects.Components.Movement namespace Content.Client.GameObjects.Components.Movement
{ {
@@ -7,6 +10,22 @@ namespace Content.Client.GameObjects.Components.Movement
[ComponentReference(typeof(IClimbable))] [ComponentReference(typeof(IClimbable))]
public class ClimbableComponent : SharedClimbableComponent public class ClimbableComponent : SharedClimbableComponent
{ {
public override bool CanDragDropOn(DragDropEventArgs eventArgs)
{
if (!base.CanDragDropOn(eventArgs))
return false;
var user = eventArgs.User;
var target = eventArgs.Target;
var dragged = eventArgs.Dragged;
bool Ignored(IEntity entity) => entity == target || entity == user || entity == dragged;
return user.InRangeUnobstructed(target, Range, predicate: Ignored) && user.InRangeUnobstructed(dragged, Range, predicate: Ignored);
}
public override bool DragDropOn(DragDropEventArgs eventArgs)
{
return false;
}
} }
} }

View File

@@ -4,6 +4,7 @@ using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects.Components.Movement namespace Content.Client.GameObjects.Components.Movement
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(SharedClimbingComponent))]
public class ClimbingComponent : SharedClimbingComponent public class ClimbingComponent : SharedClimbingComponent
{ {
public override void HandleComponentState(ComponentState curState, ComponentState nextState) public override void HandleComponentState(ComponentState curState, ComponentState nextState)

View File

@@ -1,5 +1,6 @@
#nullable enable #nullable enable
using Content.Shared.GameObjects.Components.Strap; using Content.Shared.GameObjects.Components.Strap;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects.Components.Strap namespace Content.Client.GameObjects.Components.Strap
@@ -8,5 +9,9 @@ namespace Content.Client.GameObjects.Components.Strap
[ComponentReference(typeof(SharedStrapComponent))] [ComponentReference(typeof(SharedStrapComponent))]
public class StrapComponent : SharedStrapComponent public class StrapComponent : SharedStrapComponent
{ {
public override bool DragDropOn(DragDropEventArgs eventArgs)
{
return false;
}
} }
} }

View File

@@ -1,24 +1,32 @@
#nullable enable
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Content.Client.State; using Content.Client.State;
using Content.Client.Utility; using Content.Client.Utility;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.EntitySystemMessages; using Content.Shared.GameObjects.EntitySystemMessages;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Interfaces;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Utility;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.GameObjects.EntitySystems; using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Graphics.Shaders; using Robust.Client.Graphics.Shaders;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Client.Interfaces.Graphics.ClientEye; using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.State; using Robust.Client.Interfaces.State;
using Robust.Client.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Input; using Robust.Shared.Input;
using Robust.Shared.Input.Binding; using Robust.Shared.Input.Binding;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Log; using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using DrawDepth = Content.Shared.GameObjects.DrawDepth;
namespace Content.Client.GameObjects.EntitySystems namespace Content.Client.GameObjects.EntitySystems
{ {
@@ -29,7 +37,9 @@ namespace Content.Client.GameObjects.EntitySystems
public class DragDropSystem : EntitySystem public class DragDropSystem : EntitySystem
{ {
[Dependency] private readonly IStateManager _stateManager = default!; [Dependency] private readonly IStateManager _stateManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!; [Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
// how often to recheck possible targets (prevents calling expensive // how often to recheck possible targets (prevents calling expensive
@@ -45,9 +55,11 @@ namespace Content.Client.GameObjects.EntitySystems
private const string ShaderDropTargetOutOfRange = "SelectionOutline"; private const string ShaderDropTargetOutOfRange = "SelectionOutline";
// entity performing the drag action // entity performing the drag action
private IEntity _dragger;
private IEntity? _dragger;
private readonly List<IDraggable> _draggables = new(); private readonly List<IDraggable> _draggables = new();
private IEntity _dragShadow; private IEntity? _dragShadow;
// time since mouse down over the dragged entity // time since mouse down over the dragged entity
private float _mouseDownTime; private float _mouseDownTime;
// how much time since last recheck of all possible targets // how much time since last recheck of all possible targets
@@ -58,14 +70,14 @@ namespace Content.Client.GameObjects.EntitySystems
// can ignore any events sent to this system // can ignore any events sent to this system
private bool _isReplaying; private bool _isReplaying;
private DragDropHelper<IEntity> _dragDropHelper; private DragDropHelper<IEntity> _dragDropHelper = default!;
private ShaderInstance _dropTargetInRangeShader; private ShaderInstance? _dropTargetInRangeShader;
private ShaderInstance _dropTargetOutOfRangeShader; private ShaderInstance? _dropTargetOutOfRangeShader;
private SharedInteractionSystem _interactionSystem; private SharedInteractionSystem _interactionSystem = default!;
private InputSystem _inputSystem; private InputSystem _inputSystem = default!;
private readonly List<SpriteComponent> _highlightedSprites = new(); private readonly List<ISpriteComponent> _highlightedSprites = new();
public override void Initialize() public override void Initialize()
{ {
@@ -112,7 +124,7 @@ namespace Content.Client.GameObjects.EntitySystems
private bool OnUseMouseDown(in PointerInputCmdHandler.PointerInputCmdArgs args) private bool OnUseMouseDown(in PointerInputCmdHandler.PointerInputCmdArgs args)
{ {
var dragger = args.Session.AttachedEntity; var dragger = args.Session?.AttachedEntity;
// cancel any current dragging if there is one (shouldn't be because they would've had to have lifted // 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) // the mouse, canceling the drag, but just being cautious)
_dragDropHelper.EndDrag(); _dragDropHelper.EndDrag();
@@ -130,7 +142,7 @@ namespace Content.Client.GameObjects.EntitySystems
var canDrag = false; var canDrag = false;
foreach (var draggable in entity.GetAllComponents<IDraggable>()) foreach (var draggable in entity.GetAllComponents<IDraggable>())
{ {
var dragEventArgs = new StartDragDropEventArgs(args.Session.AttachedEntity, entity); var dragEventArgs = new StartDragDropEventArgs(dragger, entity);
if (draggable.CanStartDrag(dragEventArgs)) if (draggable.CanStartDrag(dragEventArgs))
{ {
_draggables.Add(draggable); _draggables.Add(draggable);
@@ -156,7 +168,6 @@ namespace Content.Client.GameObjects.EntitySystems
return false; return false;
} }
private bool OnBeginDrag() private bool OnBeginDrag()
{ {
if (_dragDropHelper.Dragged == null || _dragDropHelper.Dragged.Deleted) if (_dragDropHelper.Dragged == null || _dragDropHelper.Dragged.Deleted)
@@ -183,6 +194,7 @@ namespace Content.Client.GameObjects.EntitySystems
} }
HighlightTargets(); HighlightTargets();
EntityManager.EventBus.RaiseEvent(EventSource.Local, new OutlineToggleMessage(false));
// drag initiated // drag initiated
return true; return true;
@@ -209,6 +221,9 @@ namespace Content.Client.GameObjects.EntitySystems
var mousePos = _eyeManager.ScreenToMap(_dragDropHelper.MouseScreenPosition); var mousePos = _eyeManager.ScreenToMap(_dragDropHelper.MouseScreenPosition);
// TODO: would use MapPosition instead if it had a setter, but it has no setter. // 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? // is that intentional, or should we add a setter for Transform.MapPosition?
if (_dragShadow == null)
return false;
_dragShadow.Transform.WorldPosition = mousePos.Position; _dragShadow.Transform.WorldPosition = mousePos.Position;
_targetRecheckTime += frameTime; _targetRecheckTime += frameTime;
@@ -229,6 +244,7 @@ namespace Content.Client.GameObjects.EntitySystems
EntityManager.DeleteEntity(_dragShadow); EntityManager.DeleteEntity(_dragShadow);
} }
EntityManager.EventBus.RaiseEvent(EventSource.Local, new OutlineToggleMessage(true));
_dragShadow = null; _dragShadow = null;
_draggables.Clear(); _draggables.Clear();
_dragger = null; _dragger = null;
@@ -238,7 +254,7 @@ namespace Content.Client.GameObjects.EntitySystems
private bool OnUseMouseUp(in PointerInputCmdHandler.PointerInputCmdArgs args) private bool OnUseMouseUp(in PointerInputCmdHandler.PointerInputCmdArgs args)
{ {
if (!_dragDropHelper.IsDragging) if (_dragDropHelper.IsDragging == false)
{ {
// haven't started the drag yet, quick mouseup, definitely treat it as a normal click by // haven't started the drag yet, quick mouseup, definitely treat it as a normal click by
// replaying the original cmd // replaying the original cmd
@@ -251,40 +267,68 @@ namespace Content.Client.GameObjects.EntitySystems
var adjustedInputMsg = new FullInputCmdMessage(args.OriginalMessage.Tick, args.OriginalMessage.SubTick, var adjustedInputMsg = new FullInputCmdMessage(args.OriginalMessage.Tick, args.OriginalMessage.SubTick,
replayMsg.InputFunctionId, replayMsg.State, replayMsg.Coordinates, replayMsg.ScreenCoordinates, replayMsg.Uid); replayMsg.InputFunctionId, replayMsg.State, replayMsg.Coordinates, replayMsg.ScreenCoordinates, replayMsg.Uid);
_inputSystem.HandleInputCommand(savedValue.Session, EngineKeyFunctions.Use, if (savedValue.Session != null)
adjustedInputMsg, true); {
_inputSystem.HandleInputCommand(savedValue.Session, EngineKeyFunctions.Use, adjustedInputMsg, true);
}
_isReplaying = false; _isReplaying = false;
} }
_dragDropHelper.EndDrag(); _dragDropHelper.EndDrag();
return false; return false;
} }
// now when ending the drag, we will not replay the click because if (_dragger == null)
// by this time we've determined the input was actually a drag attempt {
_dragDropHelper.EndDrag();
return false;
}
// now when ending the drag, we will not replay the click because
// by this time we've determined the input was actually a drag attempt
var range = (args.Coordinates.ToMapPos(EntityManager) - _dragger.Transform.MapPosition.Position).Length + 0.01f;
// tell the server we are dropping if we are over a valid drop target in range. // tell the server we are dropping if we are over a valid drop target in range.
// We don't use args.EntityUid here because drag interactions generally should // We don't use args.EntityUid here because drag interactions generally should
// work even if there's something "on top" of the drop target // work even if there's something "on top" of the drop target
if (!_interactionSystem.InRangeUnobstructed(_dragger, if (!_interactionSystem.InRangeUnobstructed(_dragger,
args.Coordinates, ignoreInsideBlocker: true)) args.Coordinates, range, ignoreInsideBlocker: true))
{ {
_dragDropHelper.EndDrag(); _dragDropHelper.EndDrag();
return false; return false;
} }
var entities = GameScreenBase.GetEntitiesUnderPosition(_stateManager, args.Coordinates); var entities = GameScreenBase.GetEntitiesUnderPosition(_stateManager, args.Coordinates);
var outOfRange = false;
foreach (var entity in entities) foreach (var entity in entities)
{ {
// check if it's able to be dropped on by current dragged entity // check if it's able to be dropped on by current dragged entity
var dropArgs = new DragDropEventArgs(_dragger, args.Coordinates, _dragDropHelper.Dragged, entity); var dropArgs = new DragDropEventArgs(_dragger, args.Coordinates, _dragDropHelper.Dragged, entity);
var valid = true;
var anyDragDrop = false;
var dragDropOn = new List<IDragDropOn>();
foreach (var comp in entity.GetAllComponents<IDragDropOn>())
{
anyDragDrop = true;
if (!comp.CanDragDropOn(dropArgs))
{
valid = false;
dragDropOn.Add(comp);
}
}
if (!valid || !anyDragDrop) continue;
if (!dropArgs.InRangeUnobstructed(ignoreInsideBlocker: true))
{
outOfRange = true;
continue;
}
foreach (var draggable in _draggables) foreach (var draggable in _draggables)
{ {
if (!draggable.CanDrop(dropArgs)) if (!draggable.CanDrop(dropArgs)) continue;
{
continue;
}
// tell the server about the drop attempt // tell the server about the drop attempt
RaiseNetworkEvent(new DragDropMessage(args.Coordinates, _dragDropHelper.Dragged.Uid, RaiseNetworkEvent(new DragDropMessage(args.Coordinates, _dragDropHelper.Dragged.Uid,
@@ -292,11 +336,22 @@ namespace Content.Client.GameObjects.EntitySystems
draggable.Drop(dropArgs); draggable.Drop(dropArgs);
// Don't fail if it isn't handled as server may do something with it
foreach (var comp in dragDropOn)
{
if (!comp.DragDropOn(dropArgs)) continue;
}
_dragDropHelper.EndDrag(); _dragDropHelper.EndDrag();
return true; return true;
} }
} }
if (outOfRange)
{
_playerManager.LocalPlayer?.ControlledEntity?.PopupMessage(Loc.GetString("You can't reach there!"));
}
_dragDropHelper.EndDrag(); _dragDropHelper.EndDrag();
return false; return false;
} }
@@ -304,7 +359,9 @@ namespace Content.Client.GameObjects.EntitySystems
private void HighlightTargets() private void HighlightTargets()
{ {
if (_dragDropHelper.Dragged == null || if (_dragDropHelper.Dragged == null ||
_dragDropHelper.Dragged.Deleted || _dragShadow == null || _dragShadow.Deleted) _dragDropHelper.Dragged.Deleted ||
_dragShadow == null ||
_dragShadow.Deleted)
{ {
Logger.Warning("Programming error. Can't highlight drag and drop targets, not currently " + Logger.Warning("Programming error. Can't highlight drag and drop targets, not currently " +
"dragging anything or dragged entity / shadow was deleted."); "dragging anything or dragged entity / shadow was deleted.");
@@ -319,28 +376,42 @@ namespace Content.Client.GameObjects.EntitySystems
// find possible targets on screen even if not reachable // find possible targets on screen even if not reachable
// TODO: Duplicated in SpriteSystem // TODO: Duplicated in SpriteSystem
var pvsBounds = _eyeManager.GetWorldViewport().Enlarged(5); var mousePos = _eyeManager.ScreenToMap(_inputManager.MouseScreenPosition).Position;
var pvsEntities = EntityManager.GetEntitiesIntersecting(_eyeManager.CurrentMap, pvsBounds, true); var bounds = new Box2(mousePos - 1.5f, mousePos + 1.5f);
var pvsEntities = EntityManager.GetEntitiesIntersecting(_eyeManager.CurrentMap, bounds, true);
foreach (var pvsEntity in pvsEntities) foreach (var pvsEntity in pvsEntities)
{ {
if (pvsEntity.TryGetComponent<SpriteComponent>(out var inRangeSprite)) if (!pvsEntity.TryGetComponent(out ISpriteComponent? inRangeSprite)) continue;
// can't highlight if there's no sprite or it's not visible
if (inRangeSprite.Visible == false) continue;
var valid = (bool?) null;
// check if it's able to be dropped on by current dragged entity
var dropArgs = new DragDropEventArgs(_dragger, pvsEntity.Transform.Coordinates, _dragDropHelper.Dragged, pvsEntity);
foreach (var comp in pvsEntity.GetAllComponents<IDragDropOn>())
{ {
// can't highlight if there's no sprite or it's not visible valid = comp.CanDragDropOn(dropArgs);
if (inRangeSprite.Visible == false) continue;
// check if it's able to be dropped on by current dragged entity if (valid.Value)
var canDropArgs = new CanDropEventArgs(_dragger, _dragDropHelper.Dragged, pvsEntity); break;
var anyValidDraggable = _draggables.Any(draggable => draggable.CanDrop(canDropArgs));
if (anyValidDraggable)
{
// highlight depending on whether its in or out of range
var inRange = _interactionSystem.InRangeUnobstructed(_dragger, pvsEntity);
inRangeSprite.PostShader = inRange ? _dropTargetInRangeShader : _dropTargetOutOfRangeShader;
inRangeSprite.RenderOrder = EntityManager.CurrentTick.Value;
_highlightedSprites.Add(inRangeSprite);
}
} }
// Can't do anything so no highlight
if (!valid.HasValue)
continue;
// We'll do a final check given server-side does this before any dragdrop can take place.
if (valid.Value)
{
valid = dropArgs.InRangeUnobstructed(ignoreInsideBlocker: true);
}
// highlight depending on whether its in or out of range
inRangeSprite.PostShader = valid.Value ? _dropTargetInRangeShader : _dropTargetOutOfRangeShader;
inRangeSprite.RenderOrder = EntityManager.CurrentTick.Value;
_highlightedSprites.Add(inRangeSprite);
} }
} }
@@ -351,6 +422,7 @@ namespace Content.Client.GameObjects.EntitySystems
highlightedSprite.PostShader = null; highlightedSprite.PostShader = null;
highlightedSprite.RenderOrder = 0; highlightedSprite.RenderOrder = 0;
} }
_highlightedSprites.Clear(); _highlightedSprites.Clear();
} }

View File

@@ -27,7 +27,7 @@ namespace Content.Client.State
// OH GOD. // OH GOD.
// Ok actually it's fine. // Ok actually it's fine.
// Instantiated dynamically through the StateManager, Dependencies will be resolved. // Instantiated dynamically through the StateManager, Dependencies will be resolved.
public partial class GameScreenBase : Robust.Client.State.State public partial class GameScreenBase : Robust.Client.State.State, IEntityEventSubscriber
{ {
[Dependency] protected readonly IClientEntityManager EntityManager = default!; [Dependency] protected readonly IClientEntityManager EntityManager = default!;
[Dependency] protected readonly IInputManager InputManager = default!; [Dependency] protected readonly IInputManager InputManager = default!;
@@ -38,17 +38,29 @@ namespace Content.Client.State
[Dependency] protected readonly IMapManager MapManager = default!; [Dependency] protected readonly IMapManager MapManager = default!;
[Dependency] protected readonly IUserInterfaceManager UserInterfaceManager = default!; [Dependency] protected readonly IUserInterfaceManager UserInterfaceManager = default!;
[Dependency] protected readonly IConfigurationManager ConfigurationManager = default!; [Dependency] protected readonly IConfigurationManager ConfigurationManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
private IEventBus _eventBus => _entityManager.EventBus;
private IEntity _lastHoveredEntity; private IEntity _lastHoveredEntity;
private bool _outlineEnabled = true;
public override void Startup() public override void Startup()
{ {
InputManager.KeyBindStateChanged += OnKeyBindStateChanged; InputManager.KeyBindStateChanged += OnKeyBindStateChanged;
_eventBus.SubscribeEvent<OutlineToggleMessage>(EventSource.Local, this, HandleOutlineToggle);
} }
public override void Shutdown() public override void Shutdown()
{ {
InputManager.KeyBindStateChanged -= OnKeyBindStateChanged; InputManager.KeyBindStateChanged -= OnKeyBindStateChanged;
_eventBus.UnsubscribeEvent<OutlineToggleMessage>(EventSource.Local, this);
}
private void HandleOutlineToggle(OutlineToggleMessage message)
{
_outlineEnabled = message.Enabled;
} }
public override void FrameUpdate(FrameEventArgs e) public override void FrameUpdate(FrameEventArgs e)
@@ -72,7 +84,7 @@ namespace Content.Client.State
} }
InteractionOutlineComponent outline; InteractionOutlineComponent outline;
if(!ConfigurationManager.GetCVar(CCVars.OutlineEnabled)) if(!_outlineEnabled || !ConfigurationManager.GetCVar(CCVars.OutlineEnabled))
{ {
if(entityToClick != null && entityToClick.TryGetComponent(out outline)) if(entityToClick != null && entityToClick.TryGetComponent(out outline))
{ {

View File

@@ -0,0 +1,14 @@
using Robust.Shared.GameObjects;
namespace Content.Client.State
{
public sealed class OutlineToggleMessage : EntitySystemMessage
{
public bool Enabled { get; }
public OutlineToggleMessage(bool enabled)
{
Enabled = enabled;
}
}
}

View File

@@ -33,6 +33,7 @@ using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Buckle namespace Content.Server.GameObjects.Components.Buckle
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(SharedBuckleComponent))]
public class BuckleComponent : SharedBuckleComponent, IInteractHand public class BuckleComponent : SharedBuckleComponent, IInteractHand
{ {
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
@@ -44,11 +45,6 @@ namespace Content.Server.GameObjects.Components.Buckle
private int _size; private int _size;
/// <summary>
/// The range from which this entity can buckle to a <see cref="StrapComponent"/>.
/// </summary>
[ViewVariables]
private float _range;
/// <summary> /// <summary>
/// The amount of time that must pass for this entity to /// The amount of time that must pass for this entity to
/// be able to unbuckle after recently buckling. /// be able to unbuckle after recently buckling.
@@ -184,7 +180,7 @@ namespace Content.Server.GameObjects.Components.Buckle
var component = strap; var component = strap;
bool Ignored(IEntity entity) => entity == Owner || entity == user || entity == component.Owner; bool Ignored(IEntity entity) => entity == Owner || entity == user || entity == component.Owner;
if (!Owner.InRangeUnobstructed(strap, _range, predicate: Ignored, popup: true)) if (!Owner.InRangeUnobstructed(strap, Range, predicate: Ignored, popup: true))
{ {
return false; return false;
} }
@@ -319,7 +315,7 @@ namespace Content.Server.GameObjects.Components.Buckle
return false; return false;
} }
if (!user.InRangeUnobstructed(oldBuckledTo, _range, popup: true)) if (!user.InRangeUnobstructed(oldBuckledTo, Range, popup: true))
{ {
return false; return false;
} }
@@ -385,7 +381,6 @@ namespace Content.Server.GameObjects.Components.Buckle
base.ExposeData(serializer); base.ExposeData(serializer);
serializer.DataField(ref _size, "size", 100); serializer.DataField(ref _size, "size", 100);
serializer.DataField(ref _range, "range", SharedInteractionSystem.InteractionRange / 1.4f);
var seconds = 0.25f; var seconds = 0.25f;
serializer.DataField(ref seconds, "cooldown", 0.25f); serializer.DataField(ref seconds, "cooldown", 0.25f);

View File

@@ -10,13 +10,11 @@ using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Power.ApcNetComponents; using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Server.GameObjects.EntitySystems.DeviceNetwork; using Content.Server.GameObjects.EntitySystems.DeviceNetwork;
using Content.Server.GameObjects.EntitySystems.DoAfter; using Content.Server.GameObjects.EntitySystems.DoAfter;
using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects.Components.Items; using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Server.Utility; using Content.Server.Utility;
using Content.Shared.GameObjects.Components; using Content.Shared.GameObjects.Components;
using Content.Shared.GameObjects.Components.Body; using Content.Shared.GameObjects.Components.Body;
using Content.Shared.GameObjects.Components.Disposal; using Content.Shared.GameObjects.Components.Disposal;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker; using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Content.Shared.GameObjects.Verbs; using Content.Shared.GameObjects.Verbs;
using Content.Shared.Interfaces; using Content.Shared.Interfaces;
@@ -47,7 +45,7 @@ namespace Content.Server.GameObjects.Components.Disposal
[ComponentReference(typeof(SharedDisposalMailingUnitComponent))] [ComponentReference(typeof(SharedDisposalMailingUnitComponent))]
[ComponentReference(typeof(IActivate))] [ComponentReference(typeof(IActivate))]
[ComponentReference(typeof(IInteractUsing))] [ComponentReference(typeof(IInteractUsing))]
public class DisposalMailingUnitComponent : SharedDisposalMailingUnitComponent, IInteractHand, IActivate, IInteractUsing, IDragDropOn public class DisposalMailingUnitComponent : SharedDisposalMailingUnitComponent, IInteractHand, IActivate, IInteractUsing
{ {
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
@@ -145,7 +143,7 @@ namespace Content.Server.GameObjects.Components.Disposal
/// </summary> /// </summary>
private (PressureState State, string Localized) _locState; private (PressureState State, string Localized) _locState;
public bool CanInsert(IEntity entity) public override bool CanInsert(IEntity entity)
{ {
if (!Anchored) if (!Anchored)
{ {
@@ -763,12 +761,12 @@ namespace Content.Server.GameObjects.Components.Disposal
return TryDrop(eventArgs.User, eventArgs.Using); return TryDrop(eventArgs.User, eventArgs.Using);
} }
bool IDragDropOn.CanDragDropOn(DragDropEventArgs eventArgs) public override bool CanDragDropOn(DragDropEventArgs eventArgs)
{ {
return CanInsert(eventArgs.Dragged); return CanInsert(eventArgs.Dragged);
} }
bool IDragDropOn.DragDropOn(DragDropEventArgs eventArgs) public override bool DragDropOn(DragDropEventArgs eventArgs)
{ {
_ = TryInsert(eventArgs.Dragged, eventArgs.User); _ = TryInsert(eventArgs.Dragged, eventArgs.User);
return true; return true;

View File

@@ -6,20 +6,17 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.Atmos; using Content.Server.Atmos;
using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Power.ApcNetComponents; using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.EntitySystems; using Content.Server.GameObjects.EntitySystems;
using Content.Server.GameObjects.EntitySystems.DoAfter; using Content.Server.GameObjects.EntitySystems.DoAfter;
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects.Components.Items; using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Server.Utility; using Content.Server.Utility;
using Content.Shared.GameObjects.Components.Disposal;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.GameObjects.Components.Body; using Content.Shared.GameObjects.Components.Body;
using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Disposal;
using Content.Shared.GameObjects.Components.Items;
using Content.Shared.GameObjects.Components.Mobs.State; using Content.Shared.GameObjects.Components.Mobs.State;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker; using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Content.Shared.GameObjects.Verbs; using Content.Shared.GameObjects.Verbs;
using Content.Shared.Interfaces; using Content.Shared.Interfaces;
@@ -51,7 +48,7 @@ namespace Content.Server.GameObjects.Components.Disposal
[ComponentReference(typeof(SharedDisposalUnitComponent))] [ComponentReference(typeof(SharedDisposalUnitComponent))]
[ComponentReference(typeof(IActivate))] [ComponentReference(typeof(IActivate))]
[ComponentReference(typeof(IInteractUsing))] [ComponentReference(typeof(IInteractUsing))]
public class DisposalUnitComponent : SharedDisposalUnitComponent, IInteractHand, IActivate, IInteractUsing, IDragDropOn, IThrowCollide, IGasMixtureHolder public class DisposalUnitComponent : SharedDisposalUnitComponent, IInteractHand, IActivate, IInteractUsing, IThrowCollide, IGasMixtureHolder
{ {
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
@@ -137,17 +134,16 @@ namespace Content.Server.GameObjects.Components.Disposal
public GasMixture Air { get; set; } = default!; public GasMixture Air { get; set; } = default!;
public bool CanInsert(IEntity entity) public override bool CanInsert(IEntity entity)
{ {
if (!Anchored) if (!base.CanInsert(entity))
{
return false; return false;
}
if (!entity.TryGetComponent(out IPhysicsComponent? physics) || if (!entity.TryGetComponent(out IPhysicsComponent? physics) ||
!physics.CanCollide) !physics.CanCollide)
{ {
if (!(entity.TryGetComponent(out IMobStateComponent? state) && state.IsDead())) { if (entity.TryGetComponent(out IMobStateComponent? state) && state.IsDead())
{
return false; return false;
} }
} }
@@ -676,12 +672,14 @@ namespace Content.Server.GameObjects.Components.Disposal
return TryDrop(eventArgs.User, eventArgs.Using); return TryDrop(eventArgs.User, eventArgs.Using);
} }
bool IDragDropOn.CanDragDropOn(DragDropEventArgs eventArgs) public override bool CanDragDropOn(DragDropEventArgs eventArgs)
{ {
// Base is redundant given this already calls the base CanInsert
// If that changes then update this
return CanInsert(eventArgs.Dragged); return CanInsert(eventArgs.Dragged);
} }
bool IDragDropOn.DragDropOn(DragDropEventArgs eventArgs) public override bool DragDropOn(DragDropEventArgs eventArgs)
{ {
_ = TryInsert(eventArgs.Dragged, eventArgs.User); _ = TryInsert(eventArgs.Dragged, eventArgs.User);
return true; return true;

View File

@@ -35,7 +35,7 @@ namespace Content.Server.GameObjects.Components.Medical
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(IActivate))] [ComponentReference(typeof(IActivate))]
[ComponentReference(typeof(SharedMedicalScannerComponent))] [ComponentReference(typeof(SharedMedicalScannerComponent))]
public class MedicalScannerComponent : SharedMedicalScannerComponent, IActivate, IDragDropOn, IDestroyAct public class MedicalScannerComponent : SharedMedicalScannerComponent, IActivate, IDestroyAct
{ {
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
@@ -292,12 +292,7 @@ namespace Content.Server.GameObjects.Components.Medical
} }
} }
public bool CanDragDropOn(DragDropEventArgs eventArgs) public override bool DragDropOn(DragDropEventArgs eventArgs)
{
return eventArgs.Dragged.HasComponent<IBody>();
}
public bool DragDropOn(DragDropEventArgs eventArgs)
{ {
_bodyContainer.Insert(eventArgs.Dragged); _bodyContainer.Insert(eventArgs.Dragged);
return true; return true;

View File

@@ -27,41 +27,36 @@ namespace Content.Server.GameObjects.Components.Movement
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(IClimbable))] [ComponentReference(typeof(IClimbable))]
public class ClimbableComponent : SharedClimbableComponent, IDragDropOn public class ClimbableComponent : SharedClimbableComponent
{ {
/// <summary>
/// The range from which this entity can be climbed.
/// </summary>
[ViewVariables]
private float _range;
/// <summary> /// <summary>
/// The time it takes to climb onto the entity. /// The time it takes to climb onto the entity.
/// </summary> /// </summary>
[ViewVariables] [ViewVariables]
private float _climbDelay; private float _climbDelay;
private DoAfterSystem _doAfterSystem;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
Owner.EnsureComponentWarn(out PhysicsComponent _); if (!Owner.EnsureComponent(out PhysicsComponent _))
{
_doAfterSystem = EntitySystem.Get<DoAfterSystem>(); Logger.Warning($"Entity {Owner.Name} at {Owner.Transform.MapPosition} didn't have a {nameof(PhysicsComponent)}");
}
} }
public override void ExposeData(ObjectSerializer serializer) public override void ExposeData(ObjectSerializer serializer)
{ {
base.ExposeData(serializer); base.ExposeData(serializer);
serializer.DataField(ref _range, "range", SharedInteractionSystem.InteractionRange / 1.4f);
serializer.DataField(ref _climbDelay, "delay", 0.8f); serializer.DataField(ref _climbDelay, "delay", 0.8f);
} }
bool IDragDropOn.CanDragDropOn(DragDropEventArgs eventArgs) public override bool CanDragDropOn(DragDropEventArgs eventArgs)
{ {
if (!base.CanDragDropOn(eventArgs))
return false;
string reason; string reason;
bool canVault; bool canVault;
@@ -105,7 +100,7 @@ namespace Content.Server.GameObjects.Components.Movement
return false; return false;
} }
if (!user.InRangeUnobstructed(target, _range)) if (!user.InRangeUnobstructed(target, Range))
{ {
reason = Loc.GetString("You can't reach there!"); reason = Loc.GetString("You can't reach there!");
return false; return false;
@@ -139,8 +134,8 @@ namespace Content.Server.GameObjects.Components.Movement
bool Ignored(IEntity entity) => entity == target || entity == user || entity == dragged; bool Ignored(IEntity entity) => entity == target || entity == user || entity == dragged;
if (!user.InRangeUnobstructed(target, _range, predicate: Ignored) || if (!user.InRangeUnobstructed(target, Range, predicate: Ignored) ||
!user.InRangeUnobstructed(dragged, _range, predicate: Ignored)) !user.InRangeUnobstructed(dragged, Range, predicate: Ignored))
{ {
reason = Loc.GetString("You can't reach there!"); reason = Loc.GetString("You can't reach there!");
return false; return false;
@@ -150,7 +145,7 @@ namespace Content.Server.GameObjects.Components.Movement
return true; return true;
} }
bool IDragDropOn.DragDropOn(DragDropEventArgs eventArgs) public override bool DragDropOn(DragDropEventArgs eventArgs)
{ {
if (eventArgs.User == eventArgs.Dragged) if (eventArgs.User == eventArgs.Dragged)
{ {
@@ -174,7 +169,7 @@ namespace Content.Server.GameObjects.Components.Movement
BreakOnStun = true BreakOnStun = true
}; };
var result = await _doAfterSystem.DoAfter(doAfterEventArgs); var result = await EntitySystem.Get<DoAfterSystem>().DoAfter(doAfterEventArgs);
if (result != DoAfterStatus.Cancelled && entityToMove.TryGetComponent(out IPhysicsComponent body) && body.PhysicsShapes.Count >= 1) if (result != DoAfterStatus.Cancelled && entityToMove.TryGetComponent(out IPhysicsComponent body) && body.PhysicsShapes.Count >= 1)
{ {
@@ -219,7 +214,7 @@ namespace Content.Server.GameObjects.Components.Movement
BreakOnStun = true BreakOnStun = true
}; };
var result = await _doAfterSystem.DoAfter(doAfterEventArgs); var result = await EntitySystem.Get<DoAfterSystem>().DoAfter(doAfterEventArgs);
if (result != DoAfterStatus.Cancelled && user.TryGetComponent(out IPhysicsComponent body) && body.PhysicsShapes.Count >= 1) if (result != DoAfterStatus.Cancelled && user.TryGetComponent(out IPhysicsComponent body) && body.PhysicsShapes.Count >= 1)
{ {

View File

@@ -9,6 +9,7 @@ using Robust.Shared.Maths;
namespace Content.Server.GameObjects.Components.Movement namespace Content.Server.GameObjects.Components.Movement
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(SharedClimbingComponent))]
public class ClimbingComponent : SharedClimbingComponent public class ClimbingComponent : SharedClimbingComponent
{ {
private bool _isClimbing; private bool _isClimbing;

View File

@@ -207,9 +207,7 @@ namespace Content.Server.GameObjects.Components.Strap
parent = parent.Parent; parent = parent.Parent;
} }
var range = SharedInteractionSystem.InteractionRange / 2; if (!user.InRangeUnobstructed(component, buckle.Range))
if (!user.InRangeUnobstructed(component, range))
{ {
return; return;
} }
@@ -228,5 +226,11 @@ namespace Content.Server.GameObjects.Components.Strap
buckle.ToggleBuckle(user, component.Owner); buckle.ToggleBuckle(user, component.Owner);
} }
} }
public override bool DragDropOn(DragDropEventArgs eventArgs)
{
if (!eventArgs.Dragged.TryGetComponent(out BuckleComponent? buckleComponent)) return false;
return buckleComponent.TryBuckle(eventArgs.User, Owner);
}
} }
} }

View File

@@ -306,7 +306,8 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
// If we've moved to space or the likes then remove us. // If we've moved to space or the likes then remove us.
if (moveEvent.Sender.Deleted || if (moveEvent.Sender.Deleted ||
!moveEvent.Sender.TryGetComponent(out IPhysicsComponent physics) || !moveEvent.Sender.TryGetComponent(out IPhysicsComponent physics) ||
!PathfindingNode.IsRelevant(moveEvent.Sender, physics)) !PathfindingNode.IsRelevant(moveEvent.Sender, physics) ||
moveEvent.NewPosition.GetGridId(EntityManager) == GridId.Invalid)
{ {
HandleEntityRemove(moveEvent.Sender); HandleEntityRemove(moveEvent.Sender);
return; return;

View File

@@ -71,6 +71,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click
var interactionArgs = new DragDropEventArgs(performer, msg.DropLocation, dropped, target); var interactionArgs = new DragDropEventArgs(performer, msg.DropLocation, dropped, target);
// must be in range of both the target and the object they are drag / dropping // 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 (!interactionArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return; if (!interactionArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return;
// trigger dragdrops on the dropped entity // trigger dragdrops on the dropped entity

View File

@@ -11,6 +11,7 @@ using Robust.Shared.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Buckle namespace Content.Shared.GameObjects.Components.Buckle
{ {
@@ -19,6 +20,18 @@ namespace Content.Shared.GameObjects.Components.Buckle
public sealed override string Name => "Buckle"; public sealed override string Name => "Buckle";
public sealed override uint? NetID => ContentNetIDs.BUCKLE; public sealed override uint? NetID => ContentNetIDs.BUCKLE;
/// <summary>
/// The range from which this entity can buckle to a <see cref="StrapComponent"/>.
/// </summary>
[ViewVariables]
public float Range { get; protected set; }
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataReadWriteFunction("range", SharedInteractionSystem.InteractionRange / 1.4f, value => Range = value, () => Range);
}
/// <summary> /// <summary>
/// True if the entity is buckled, false otherwise. /// True if the entity is buckled, false otherwise.
/// </summary> /// </summary>

View File

@@ -7,7 +7,7 @@ using Robust.Shared.Utility;
namespace Content.Shared.GameObjects.Components.Disposal namespace Content.Shared.GameObjects.Components.Disposal
{ {
public abstract class SharedDisposalMailingUnitComponent : SharedDisposalUnitComponent, ICollideSpecial public abstract class SharedDisposalMailingUnitComponent : SharedDisposalUnitComponent
{ {
public override string Name => "DisposalMailingUnit"; public override string Name => "DisposalMailingUnit";

View File

@@ -1,6 +1,11 @@
#nullable enable #nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Shared.GameObjects.Components.Body;
using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Mobs.State;
using Content.Shared.GameObjects.Components.Storage;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components; using Robust.Shared.GameObjects.Components;
using Robust.Shared.GameObjects.Components.UserInterface; using Robust.Shared.GameObjects.Components.UserInterface;
@@ -12,7 +17,7 @@ using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Disposal namespace Content.Shared.GameObjects.Components.Disposal
{ {
public abstract class SharedDisposalUnitComponent : Component, ICollideSpecial public abstract class SharedDisposalUnitComponent : Component, ICollideSpecial, IDragDropOn
{ {
public override string Name => "DisposalUnit"; public override string Name => "DisposalUnit";
@@ -160,5 +165,34 @@ namespace Content.Shared.GameObjects.Components.Disposal
{ {
Key Key
} }
public virtual bool CanInsert(IEntity entity)
{
if (!Anchored)
return false;
if (!entity.TryGetComponent(out IPhysicsComponent? physics) ||
!physics.CanCollide)
{
if (!(entity.TryGetComponent(out IMobStateComponent? damageState) && damageState.IsDead())) {
return false;
}
}
if (!entity.HasComponent<SharedStorableComponent>() &&
!entity.HasComponent<IBody>())
{
return false;
}
return true;
}
public virtual bool CanDragDropOn(DragDropEventArgs eventArgs)
{
return CanInsert(eventArgs.Dragged);
}
public abstract bool DragDropOn(DragDropEventArgs eventArgs);
} }
} }

View File

@@ -1,13 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.GameObjects.Components.Body;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.UserInterface; using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Medical namespace Content.Shared.GameObjects.Components.Medical
{ {
public class SharedMedicalScannerComponent : Component public abstract class SharedMedicalScannerComponent : Component, IDragDropOn
{ {
public override string Name => "MedicalScanner"; public override string Name => "MedicalScanner";
@@ -78,5 +80,11 @@ namespace Content.Shared.GameObjects.Components.Medical
} }
public bool CanDragDropOn(DragDropEventArgs eventArgs)
{
return eventArgs.Dragged.HasComponent<IBody>();
}
public abstract bool DragDropOn(DragDropEventArgs eventArgs);
} }
} }

View File

@@ -1,11 +1,34 @@
using Robust.Shared.GameObjects; using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Movement namespace Content.Shared.GameObjects.Components.Movement
{ {
public interface IClimbable { }; public interface IClimbable { };
public class SharedClimbableComponent : Component, IClimbable public abstract class SharedClimbableComponent : Component, IClimbable, IDragDropOn
{ {
public sealed override string Name => "Climbable"; public sealed override string Name => "Climbable";
/// <summary>
/// The range from which this entity can be climbed.
/// </summary>
[ViewVariables]
protected float Range;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref Range, "range", SharedInteractionSystem.InteractionRange / 1.4f);
}
public virtual bool CanDragDropOn(DragDropEventArgs eventArgs)
{
return eventArgs.Dragged.HasComponent<SharedClimbingComponent>();
}
public abstract bool DragDropOn(DragDropEventArgs eventArgs);
} }
} }

View File

@@ -6,11 +6,10 @@ using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components; using Robust.Shared.GameObjects.Components;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Content.Shared.Interfaces.GameObjects.Components;
namespace Content.Shared.GameObjects.Components.Movement namespace Content.Shared.GameObjects.Components.Movement
{ {
public abstract class SharedClimbingComponent : Component, IActionBlocker, ICollideSpecial, IDraggable public abstract class SharedClimbingComponent : Component, IActionBlocker, ICollideSpecial
{ {
public sealed override string Name => "Climbing"; public sealed override string Name => "Climbing";
public sealed override uint? NetID => ContentNetIDs.CLIMBING; public sealed override uint? NetID => ContentNetIDs.CLIMBING;
@@ -47,16 +46,6 @@ namespace Content.Shared.GameObjects.Components.Movement
return false; return false;
} }
bool IDraggable.CanDrop(CanDropEventArgs args)
{
return args.Target.HasComponent<IClimbable>();
}
bool IDraggable.Drop(DragDropEventArgs args)
{
return false;
}
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();

View File

@@ -1,4 +1,8 @@
using System; using System;
using Content.Shared.GameObjects.Components.Buckle;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Utility;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
@@ -23,11 +27,21 @@ namespace Content.Shared.GameObjects.Components.Strap
Down Down
} }
public abstract class SharedStrapComponent : Component public abstract class SharedStrapComponent : Component, IDragDropOn
{ {
public sealed override string Name => "Strap"; public sealed override string Name => "Strap";
public sealed override uint? NetID => ContentNetIDs.STRAP; public sealed override uint? NetID => ContentNetIDs.STRAP;
public virtual bool CanDragDropOn(DragDropEventArgs eventArgs)
{
if (!eventArgs.Dragged.TryGetComponent(out SharedBuckleComponent buckleComponent)) return false;
bool Ignored(IEntity entity) => entity == eventArgs.User || entity == eventArgs.Dragged || entity == eventArgs.Target;
return eventArgs.Target.InRangeUnobstructed(eventArgs.Dragged, buckleComponent.Range, predicate: Ignored);
}
public abstract bool DragDropOn(DragDropEventArgs eventArgs);
} }
[Serializable, NetSerializable] [Serializable, NetSerializable]

View File

@@ -7,7 +7,7 @@ namespace Content.Shared.Interfaces.GameObjects.Components
public interface IDragDropOn public interface IDragDropOn
{ {
/// <summary> /// <summary>
/// Invoked server-side when another entity is being dragged and dropped /// Invoked when another entity is being dragged and dropped
/// onto this one before invoking <see cref="DragDropOn"/>. /// onto this one before invoking <see cref="DragDropOn"/>.
/// Note that other drag and drop interactions may be attempted if /// Note that other drag and drop interactions may be attempted if
/// this one fails. /// this one fails.