diff --git a/Content.Client/GameObjects/Components/Body/BodyComponent.cs b/Content.Client/GameObjects/Components/Body/BodyComponent.cs index 96d30e18bf..fb36486055 100644 --- a/Content.Client/GameObjects/Components/Body/BodyComponent.cs +++ b/Content.Client/GameObjects/Components/Body/BodyComponent.cs @@ -1,17 +1,17 @@ #nullable enable using Content.Client.GameObjects.Components.Disposal; using Content.Client.GameObjects.Components.MedicalScanner; -using Content.Client.Interfaces.GameObjects.Components.Interaction; using Content.Shared.GameObjects.Components.Body; +using Content.Shared.Interfaces.GameObjects.Components; using Robust.Shared.GameObjects; namespace Content.Client.GameObjects.Components.Body { [RegisterComponent] [ComponentReference(typeof(IBody))] - public class BodyComponent : SharedBodyComponent, IClientDraggable + public class BodyComponent : SharedBodyComponent, IDraggable { - public bool ClientCanDropOn(CanDropEventArgs eventArgs) + public bool CanDrop(CanDropEventArgs eventArgs) { if (eventArgs.Target.HasComponent() || eventArgs.Target.HasComponent()) @@ -21,10 +21,5 @@ namespace Content.Client.GameObjects.Components.Body return false; } - - public bool ClientCanDrag(CanDragEventArgs eventArgs) - { - return true; - } } } diff --git a/Content.Client/GameObjects/Components/Buckle/BuckleComponent.cs b/Content.Client/GameObjects/Components/Buckle/BuckleComponent.cs index 77f0b099b4..3ee1b85144 100644 --- a/Content.Client/GameObjects/Components/Buckle/BuckleComponent.cs +++ b/Content.Client/GameObjects/Components/Buckle/BuckleComponent.cs @@ -1,19 +1,24 @@ -using Content.Client.GameObjects.Components.Strap; -using Content.Client.Interfaces.GameObjects.Components.Interaction; using Content.Shared.GameObjects.Components.Buckle; using Robust.Client.GameObjects; using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; namespace Content.Client.GameObjects.Components.Buckle { [RegisterComponent] - public class BuckleComponent : SharedBuckleComponent, IClientDraggable + public class BuckleComponent : SharedBuckleComponent { private bool _buckled; private int? _originalDrawDepth; public override bool Buckled => _buckled; + public override bool TryBuckle(IEntity user, IEntity to) + { + // TODO: Prediction + return false; + } + public override void HandleComponentState(ComponentState curState, ComponentState nextState) { if (!(curState is BuckleComponentState buckle)) @@ -41,15 +46,5 @@ namespace Content.Client.GameObjects.Components.Buckle _originalDrawDepth = null; } } - - bool IClientDraggable.ClientCanDropOn(CanDropEventArgs eventArgs) - { - return eventArgs.Target.HasComponent(); - } - - bool IClientDraggable.ClientCanDrag(CanDragEventArgs eventArgs) - { - return true; - } } } diff --git a/Content.Client/GameObjects/Components/GUI/StrippableComponent.cs b/Content.Client/GameObjects/Components/GUI/StrippableComponent.cs index 6fa50ded40..05fc41976f 100644 --- a/Content.Client/GameObjects/Components/GUI/StrippableComponent.cs +++ b/Content.Client/GameObjects/Components/GUI/StrippableComponent.cs @@ -1,22 +1,17 @@ -using Content.Client.GameObjects.Components.Items; -using Content.Client.Interfaces.GameObjects.Components.Interaction; -using Content.Shared.GameObjects.Components.GUI; +using Content.Shared.GameObjects.Components.GUI; +using Content.Shared.Interfaces.GameObjects.Components; using Robust.Shared.GameObjects; namespace Content.Client.GameObjects.Components.GUI { [RegisterComponent] - public class StrippableComponent : SharedStrippableComponent, IClientDraggable + [ComponentReference(typeof(SharedStrippableComponent))] + public class StrippableComponent : SharedStrippableComponent { - public bool ClientCanDropOn(CanDropEventArgs eventArgs) + public override bool Drop(DragDropEventArgs args) { - return eventArgs.Target.HasComponent() - && eventArgs.Target != eventArgs.Dragged && eventArgs.Target == eventArgs.User; - } - - public bool ClientCanDrag(CanDragEventArgs eventArgs) - { - return true; + // TODO: Prediction + return false; } } } diff --git a/Content.Client/GameObjects/Components/Items/ItemComponent.cs b/Content.Client/GameObjects/Components/Items/ItemComponent.cs index 4560b6171e..5da0dfaec5 100644 --- a/Content.Client/GameObjects/Components/Items/ItemComponent.cs +++ b/Content.Client/GameObjects/Components/Items/ItemComponent.cs @@ -1,7 +1,7 @@ using Content.Client.GameObjects.Components.Disposal; -using Content.Client.Interfaces.GameObjects.Components.Interaction; using Content.Shared.GameObjects; using Content.Shared.GameObjects.Components.Items; +using Content.Shared.Interfaces.GameObjects.Components; using Robust.Client.Graphics; using Robust.Client.Interfaces.ResourceManagement; using Robust.Client.ResourceManagement; @@ -19,7 +19,7 @@ namespace Content.Client.GameObjects.Components.Items { [RegisterComponent] [ComponentReference(typeof(IItemComponent))] - public class ItemComponent : Component, IItemComponent, IClientDraggable + public class ItemComponent : Component, IItemComponent, IDraggable { public override string Name => "Item"; public override uint? NetID => ContentNetIDs.ITEM; @@ -85,14 +85,15 @@ namespace Content.Client.GameObjects.Components.Items EquippedPrefix = itemComponentState.EquippedPrefix; } - bool IClientDraggable.ClientCanDropOn(CanDropEventArgs eventArgs) + bool IDraggable.CanDrop(CanDropEventArgs args) { - return eventArgs.Target.HasComponent(); + return args.Target.HasComponent(); } - bool IClientDraggable.ClientCanDrag(CanDragEventArgs eventArgs) + public bool Drop(DragDropEventArgs args) { - return true; + // TODO: Shared item class + return false; } } } diff --git a/Content.Client/GameObjects/Components/Movement/ClimbingComponent.cs b/Content.Client/GameObjects/Components/Movement/ClimbingComponent.cs index 05694ab06a..b624ad9a75 100644 --- a/Content.Client/GameObjects/Components/Movement/ClimbingComponent.cs +++ b/Content.Client/GameObjects/Components/Movement/ClimbingComponent.cs @@ -1,11 +1,10 @@ -using Content.Client.Interfaces.GameObjects.Components.Interaction; -using Content.Shared.GameObjects.Components.Movement; +using Content.Shared.GameObjects.Components.Movement; using Robust.Shared.GameObjects; namespace Content.Client.GameObjects.Components.Movement { [RegisterComponent] - public class ClimbingComponent : SharedClimbingComponent, IClientDraggable + public class ClimbingComponent : SharedClimbingComponent { public override void HandleComponentState(ComponentState curState, ComponentState nextState) { @@ -14,19 +13,9 @@ namespace Content.Client.GameObjects.Components.Movement return; } - IsClimbing = climbModeState.Climbing; + IsClimbing = climbModeState.Climbing; } public override bool IsClimbing { get; set; } - - bool IClientDraggable.ClientCanDropOn(CanDropEventArgs eventArgs) - { - return eventArgs.Target.HasComponent(); - } - - bool IClientDraggable.ClientCanDrag(CanDragEventArgs eventArgs) - { - return true; - } } } diff --git a/Content.Client/GameObjects/Components/PlaceableSurfaceComponent.cs b/Content.Client/GameObjects/Components/PlaceableSurfaceComponent.cs index 88c12cad83..e5a27e117c 100644 --- a/Content.Client/GameObjects/Components/PlaceableSurfaceComponent.cs +++ b/Content.Client/GameObjects/Components/PlaceableSurfaceComponent.cs @@ -1,11 +1,41 @@ +#nullable enable using Content.Shared.GameObjects.Components; using Robust.Shared.GameObjects; namespace Content.Client.GameObjects.Components { [RegisterComponent] + [ComponentReference(typeof(SharedPlaceableSurfaceComponent))] public class PlaceableSurfaceComponent : SharedPlaceableSurfaceComponent { + private bool _isPlaceable; + public override bool IsPlaceable + { + get => _isPlaceable; + set + { + if (_isPlaceable == value) + { + return; + } + + _isPlaceable = value; + + Dirty(); + } + } + + public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) + { + base.HandleComponentState(curState, nextState); + + if (!(curState is PlaceableSurfaceComponentState state)) + { + return; + } + + _isPlaceable = state.IsPlaceable; + } } } diff --git a/Content.Client/GameObjects/Components/Storage/ClientStorageComponent.cs b/Content.Client/GameObjects/Components/Storage/ClientStorageComponent.cs index 5f8ada7730..0c5daace70 100644 --- a/Content.Client/GameObjects/Components/Storage/ClientStorageComponent.cs +++ b/Content.Client/GameObjects/Components/Storage/ClientStorageComponent.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using Content.Client.GameObjects.Components.Items; -using Content.Client.Interfaces.GameObjects.Components.Interaction; using Content.Shared.GameObjects.Components.Storage; +using Content.Shared.Interfaces.GameObjects.Components; using Robust.Client.Graphics.Drawing; using Robust.Client.Interfaces.GameObjects.Components; using Robust.Client.Player; @@ -22,13 +23,17 @@ namespace Content.Client.GameObjects.Components.Storage /// Client version of item storage containers, contains a UI which displays stored entities and their size /// [RegisterComponent] - public class ClientStorageComponent : SharedStorageComponent, IClientDraggable + public class ClientStorageComponent : SharedStorageComponent, IDraggable { - private Dictionary StoredEntities { get; set; } = new Dictionary(); + [Dependency] private readonly IEntityManager _entityManager = default!; + + private List _storedEntities = new List(); private int StorageSizeUsed; private int StorageCapacityMax; private StorageWindow Window; + public override IReadOnlyList StoredEntities => _storedEntities; + public override void OnAdd() { base.OnAdd(); @@ -43,6 +48,20 @@ namespace Content.Client.GameObjects.Components.Storage base.OnRemove(); } + public override void HandleComponentState(ComponentState curState, ComponentState nextState) + { + base.HandleComponentState(curState, nextState); + + if (!(curState is StorageComponentState state)) + { + return; + } + + _storedEntities = state.StoredEntities + .Select(id => _entityManager.GetEntity(id)) + .ToList(); + } + public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession session = null) { base.HandleNetworkMessage(message, channel, session); @@ -69,7 +88,7 @@ namespace Content.Client.GameObjects.Components.Storage /// private void HandleStorageMessage(StorageHeldItemsMessage storageState) { - StoredEntities = new Dictionary(storageState.StoredEntities); + _storedEntities = storageState.StoredEntities.Select(id => _entityManager.GetEntity(id)).ToList(); StorageSizeUsed = storageState.StorageSizeUsed; StorageCapacityMax = storageState.StorageSizeMax; Window.BuildEntityList(); @@ -100,6 +119,17 @@ namespace Content.Client.GameObjects.Components.Storage SendNetworkMessage(new RemoveEntityMessage(entityUid)); } + public override bool Remove(IEntity entity) + { + if (_storedEntities.Remove(entity)) + { + Dirty(); + return true; + } + + return false; + } + /// /// GUI class for client storage component /// @@ -200,19 +230,25 @@ namespace Content.Client.GameObjects.Components.Storage var storageList = StorageEntity.StoredEntities; - foreach (var entityUid in storageList) + var storedGrouped = storageList.GroupBy(e => e).Select(e => new { - var entity = IoCManager.Resolve().GetEntity(entityUid.Key); + Entity = e.Key, + Amount = e.Count() + }); + foreach (var group in storedGrouped) + { + var entity = group.Entity; var button = new EntityButton() { - EntityUid = entityUid.Key, + EntityUid = entity.Uid, MouseFilter = MouseFilterMode.Stop, }; button.ActualButton.OnToggled += OnItemButtonToggled; //Name and Size labels set button.EntityName.Text = entity.Name; - button.EntitySize.Text = string.Format("{0}", entityUid.Value); + + button.EntitySize.Text = group.Amount.ToString(); //Gets entity sprite and assigns it to button texture if (entity.TryGetComponent(out ISpriteComponent sprite)) @@ -320,17 +356,5 @@ namespace Content.Client.GameObjects.Components.Storage AddChild(hBoxContainer); } } - - public bool ClientCanDropOn(CanDropEventArgs eventArgs) - { - //can only drop on placeable surfaces to empty out contents - return eventArgs.Target.HasComponent(); - } - - public bool ClientCanDrag(CanDragEventArgs eventArgs) - { - //always draggable, at least for now - return true; - } } } diff --git a/Content.Client/GameObjects/Components/Storage/StorableComponent.cs b/Content.Client/GameObjects/Components/Storage/StorableComponent.cs new file mode 100644 index 0000000000..ab6e4d1b1f --- /dev/null +++ b/Content.Client/GameObjects/Components/Storage/StorableComponent.cs @@ -0,0 +1,41 @@ +#nullable enable +using Content.Shared.GameObjects.Components.Storage; +using Robust.Shared.GameObjects; + +namespace Content.Client.GameObjects.Components.Storage +{ + [RegisterComponent] + [ComponentReference(typeof(SharedStorableComponent))] + public class StorableComponent : SharedStorableComponent + { + private int _size; + + public override int Size + { + get => _size; + set + { + if (_size == value) + { + return; + } + + _size = value; + + Dirty(); + } + } + + public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) + { + base.HandleComponentState(curState, nextState); + + if (!(curState is StorableComponentState state)) + { + return; + } + + _size = state.Size; + } + } +} diff --git a/Content.Client/GameObjects/Components/Strap/StrapComponent.cs b/Content.Client/GameObjects/Components/Strap/StrapComponent.cs index 3eff3636e8..7da4776258 100644 --- a/Content.Client/GameObjects/Components/Strap/StrapComponent.cs +++ b/Content.Client/GameObjects/Components/Strap/StrapComponent.cs @@ -5,6 +5,7 @@ using Robust.Shared.GameObjects; namespace Content.Client.GameObjects.Components.Strap { [RegisterComponent] + [ComponentReference(typeof(SharedStrapComponent))] public class StrapComponent : SharedStrapComponent { } diff --git a/Content.Client/GameObjects/EntitySystems/DragDropSystem.cs b/Content.Client/GameObjects/EntitySystems/DragDropSystem.cs index d42dd57fce..4aadc3a0a7 100644 --- a/Content.Client/GameObjects/EntitySystems/DragDropSystem.cs +++ b/Content.Client/GameObjects/EntitySystems/DragDropSystem.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using System.Linq; -using Content.Client.Interfaces.GameObjects.Components.Interaction; using Content.Client.State; using Content.Shared.GameObjects; using Content.Shared.GameObjects.EntitySystemMessages; using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Interfaces.GameObjects.Components; using JetBrains.Annotations; using Robust.Client.GameObjects; using Robust.Client.GameObjects.EntitySystems; @@ -52,7 +52,7 @@ namespace Content.Client.GameObjects.EntitySystems // entity performing the drag action private IEntity _dragger; private IEntity _draggedEntity; - private readonly List _draggables = new List(); + private readonly List _draggables = new List(); private IEntity _dragShadow; private DragState _state; // time since mouse down over the dragged entity @@ -146,10 +146,10 @@ namespace Content.Client.GameObjects.EntitySystems } var canDrag = false; - foreach (var draggable in entity.GetAllComponents()) + foreach (var draggable in entity.GetAllComponents()) { - var dragEventArgs = new CanDragEventArgs(args.Session.AttachedEntity, entity); - if (draggable.ClientCanDrag(dragEventArgs)) + var dragEventArgs = new StartDragDropEventArgs(args.Session.AttachedEntity, entity); + if (draggable.CanStartDrag(dragEventArgs)) { // wait to initiate a drag _dragger = dragger; @@ -202,19 +202,26 @@ namespace Content.Client.GameObjects.EntitySystems foreach (var entity in entities) { // check if it's able to be dropped on by current dragged entity - var canDropArgs = new CanDropEventArgs(_dragger, _draggedEntity, entity); - var anyValidDraggable = _draggables.Any(draggable => draggable.ClientCanDropOn(canDropArgs)); + var dropArgs = new DragDropEventArgs(_dragger, args.Coordinates, _draggedEntity, entity); - if (anyValidDraggable) + foreach (var draggable in _draggables) { + if (!draggable.CanDrop(dropArgs)) + { + continue; + } + // tell the server about the drop attempt RaiseNetworkEvent(new DragDropMessage(args.Coordinates, _draggedEntity.Uid, entity.Uid)); + draggable.Drop(dropArgs); + CancelDrag(false, null); return true; } } + CancelDrag(false, null); return false; } @@ -283,8 +290,8 @@ namespace Content.Client.GameObjects.EntitySystems if (inRangeSprite.Visible == false) continue; // check if it's able to be dropped on by current dragged entity - var canDropArgs = new CanDropEventArgs(_dragger, _draggedEntity, pvsEntity); - var anyValidDraggable = _draggables.Any(draggable => draggable.ClientCanDropOn(canDropArgs)); + var canDropArgs = new CanDropEventArgs(_dragger, _draggedEntity, pvsEntity); + var anyValidDraggable = _draggables.Any(draggable => draggable.CanDrop(canDropArgs)); if (anyValidDraggable) { diff --git a/Content.Client/IgnoredComponents.cs b/Content.Client/IgnoredComponents.cs index ec567c9dca..e43a9ecc2d 100644 --- a/Content.Client/IgnoredComponents.cs +++ b/Content.Client/IgnoredComponents.cs @@ -28,7 +28,6 @@ "Wrench", "Crowbar", "MeleeWeapon", - "Storable", "Dice", "Construction", "Door", diff --git a/Content.Client/Interfaces/GameObjects/Components/Interaction/IClientDraggable.cs b/Content.Client/Interfaces/GameObjects/Components/Interaction/IClientDraggable.cs deleted file mode 100644 index 8e28b297c9..0000000000 --- a/Content.Client/Interfaces/GameObjects/Components/Interaction/IClientDraggable.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using Robust.Shared.Interfaces.GameObjects; - -namespace Content.Client.Interfaces.GameObjects.Components.Interaction -{ - /// - /// This interface allows a local client to initiate dragging of the component's entity by mouse, for drag and - /// drop interactions. The actual logic of what happens on drop - /// is handled by IDragDrop - /// - public interface IClientDraggable - { - - /// - /// 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. - /// - /// true iff target is a valid target to be dropped on by this - /// component's entity. Returning true will cause the target entity to be highlighted as a potential - /// target and allow dropping when in range. - bool ClientCanDropOn(CanDropEventArgs eventArgs); - - /// - /// Invoked clientside when user is attempting to initiate a drag with this component's entity - /// in range. Return true if the drag should be initiated. 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. - /// - /// - /// true iff drag should be initiated - bool ClientCanDrag(CanDragEventArgs eventArgs); - } - - public class CanDropEventArgs : EventArgs - { - /// - /// 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 CanDropEventArgs(IEntity user, IEntity dragged, IEntity target) - { - User = user; - Dragged = dragged; - Target = target; - } - - /// - /// The entity doing the drag and drop. - /// - public IEntity User { get; } - - /// - /// The entity that is being dragged and dropped. - /// - public IEntity Dragged { get; } - - /// - /// The entity that is being dropped onto. - /// - public IEntity Target { get; } - } - - public class CanDragEventArgs : EventArgs - { - /// - /// Creates a new instance of . - /// - /// The entity doing the drag and drop. - /// The entity that is being dragged and dropped. - public CanDragEventArgs(IEntity user, IEntity dragged) - { - User = user; - Dragged = dragged; - } - - /// - /// The entity doing the drag and drop. - /// - public IEntity User { get; } - - /// - /// The entity that is being dragged and dropped. - /// - public IEntity Dragged { get; } - } -} diff --git a/Content.Server/GameObjects/Components/Buckle/BuckleComponent.cs b/Content.Server/GameObjects/Components/Buckle/BuckleComponent.cs index b64920a386..245f4051a2 100644 --- a/Content.Server/GameObjects/Components/Buckle/BuckleComponent.cs +++ b/Content.Server/GameObjects/Components/Buckle/BuckleComponent.cs @@ -34,7 +34,7 @@ using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Buckle { [RegisterComponent] - public class BuckleComponent : SharedBuckleComponent, IInteractHand, IDragDrop + public class BuckleComponent : SharedBuckleComponent, IInteractHand { [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IEntitySystemManager _entitySystem = default!; @@ -256,18 +256,7 @@ namespace Content.Server.GameObjects.Components.Buckle return true; } - /// - /// Tries to make an entity buckle the owner of this component to another. - /// - /// - /// The entity buckling the owner of this component, can be the owner itself. - /// - /// The entity to buckle the owner of this component to. - /// - /// true if the owner was buckled, otherwise false even if the owner was - /// previously already buckled. - /// - public bool TryBuckle(IEntity user, IEntity to) + public override bool TryBuckle(IEntity user, IEntity to) { if (!CanBuckle(user, to, out var strap)) { @@ -544,16 +533,6 @@ namespace Content.Server.GameObjects.Components.Buckle return TryUnbuckle(eventArgs.User); } - bool IDragDrop.CanDragDrop(DragDropEventArgs eventArgs) - { - return eventArgs.Target.HasComponent(); - } - - bool IDragDrop.DragDrop(DragDropEventArgs eventArgs) - { - return TryBuckle(eventArgs.User, eventArgs.Target); - } - /// /// Allows the unbuckling of the owning entity through a verb if /// anyone right clicks them. diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs b/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs index 8c967e39cb..e6c9bd7389 100644 --- a/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs +++ b/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs @@ -674,12 +674,12 @@ namespace Content.Server.GameObjects.Components.Disposal bool IDragDropOn.CanDragDropOn(DragDropEventArgs eventArgs) { - return CanInsert(eventArgs.Dropped); + return CanInsert(eventArgs.Dragged); } bool IDragDropOn.DragDropOn(DragDropEventArgs eventArgs) { - _ = TryInsert(eventArgs.Dropped, eventArgs.User); + _ = TryInsert(eventArgs.Dragged, eventArgs.User); return true; } diff --git a/Content.Server/GameObjects/Components/GUI/HumanInventoryControllerComponent.cs b/Content.Server/GameObjects/Components/GUI/HumanInventoryControllerComponent.cs index 9a72da1520..d5569f8092 100644 --- a/Content.Server/GameObjects/Components/GUI/HumanInventoryControllerComponent.cs +++ b/Content.Server/GameObjects/Components/GUI/HumanInventoryControllerComponent.cs @@ -45,7 +45,7 @@ namespace Content.Server.GameObjects.Components.GUI var itemComponent = entity.GetComponent(); // If this item is small enough then it always fits in pockets. - if (itemComponent.ObjectSize <= (int) ReferenceSizes.Pocket) + if (itemComponent.Size <= (int) ReferenceSizes.Pocket) { return true; } diff --git a/Content.Server/GameObjects/Components/GUI/StrippableComponent.cs b/Content.Server/GameObjects/Components/GUI/StrippableComponent.cs index d43600f803..fa4f3969b6 100644 --- a/Content.Server/GameObjects/Components/GUI/StrippableComponent.cs +++ b/Content.Server/GameObjects/Components/GUI/StrippableComponent.cs @@ -23,7 +23,7 @@ using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefine namespace Content.Server.GameObjects.Components.GUI { [RegisterComponent] - public sealed class StrippableComponent : SharedStrippableComponent, IDragDrop + public sealed class StrippableComponent : SharedStrippableComponent { public const float StripDelay = 2f; @@ -75,23 +75,9 @@ namespace Content.Server.GameObjects.Components.GUI UserInterface.SetState(new StrippingBoundUserInterfaceState(inventory, hands, cuffs)); } - public bool CanBeStripped(IEntity by) + public override bool Drop(DragDropEventArgs args) { - return by != Owner - && by.HasComponent() - && ActionBlockerSystem.CanInteract(by); - } - - public bool CanDragDrop(DragDropEventArgs eventArgs) - { - return eventArgs.Target != eventArgs.Dropped - && eventArgs.Target == eventArgs.User - && CanBeStripped(eventArgs.User); - } - - public bool DragDrop(DragDropEventArgs eventArgs) - { - if (!eventArgs.User.TryGetComponent(out IActorComponent? actor)) return false; + if (!args.User.TryGetComponent(out IActorComponent? actor)) return false; OpenUserInterface(actor.playerSession); return true; diff --git a/Content.Server/GameObjects/Components/Items/Clothing/ClothingComponent.cs b/Content.Server/GameObjects/Components/Items/Clothing/ClothingComponent.cs index 78d1a5659f..e25dfd1ca9 100644 --- a/Content.Server/GameObjects/Components/Items/Clothing/ClothingComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Clothing/ClothingComponent.cs @@ -4,6 +4,7 @@ using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.Items.Storage; using Content.Shared.GameObjects; using Content.Shared.GameObjects.Components.Items; +using Content.Shared.GameObjects.Components.Storage; using Content.Shared.Interfaces; using Content.Shared.Interfaces.GameObjects.Components; using Robust.Shared.GameObjects; @@ -17,6 +18,7 @@ namespace Content.Server.GameObjects.Components.Items.Clothing [RegisterComponent] [ComponentReference(typeof(ItemComponent))] [ComponentReference(typeof(StorableComponent))] + [ComponentReference(typeof(SharedStorableComponent))] [ComponentReference(typeof(IItemComponent))] public class ClothingComponent : ItemComponent, IUse { diff --git a/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs index 75fa3cb208..bed816133f 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs @@ -3,6 +3,7 @@ using Content.Server.Interfaces.GameObjects.Components.Items; using Content.Server.Throw; using Content.Shared.GameObjects; using Content.Shared.GameObjects.Components.Items; +using Content.Shared.GameObjects.Components.Storage; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.Verbs; using Content.Shared.Interfaces.GameObjects.Components; @@ -19,6 +20,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage { [RegisterComponent] [ComponentReference(typeof(StorableComponent))] + [ComponentReference(typeof(SharedStorableComponent))] [ComponentReference(typeof(IItemComponent))] public class ItemComponent : StorableComponent, IInteractHand, IExAct, IEquipped, IUnequipped, IItemComponent { diff --git a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs index 8c8cde3022..95db5b6c57 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs @@ -34,8 +34,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage [RegisterComponent] [ComponentReference(typeof(IActivate))] [ComponentReference(typeof(IStorageComponent))] - public class ServerStorageComponent : SharedStorageComponent, IInteractUsing, IUse, IActivate, IStorageComponent, IDestroyAct, IExAct, - IDragDrop + public class ServerStorageComponent : SharedStorageComponent, IInteractUsing, IUse, IActivate, IStorageComponent, IDestroyAct, IExAct { [Dependency] private readonly IEntityManager _entityManager = default!; @@ -50,7 +49,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage public readonly HashSet SubscribedSessions = new HashSet(); [ViewVariables] - public IReadOnlyCollection? StoredEntities => _storage?.ContainedEntities; + public override IReadOnlyList? StoredEntities => _storage?.ContainedEntities; [ViewVariables(VVAccess.ReadWrite)] public bool OccludesLight @@ -87,7 +86,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage foreach (var entity in _storage.ContainedEntities) { var item = entity.GetComponent(); - _storageUsed += item.ObjectSize; + _storageUsed += item.Size; } } @@ -107,7 +106,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage } if (entity.TryGetComponent(out StorableComponent? store) && - store.ObjectSize > _storageCapacityMax - _storageUsed) + store.Size > _storageCapacityMax - _storageUsed) { return false; } @@ -125,12 +124,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage return CanInsert(entity) && _storage?.Insert(entity) == true; } - /// - /// Removes from the storage container and updates the stored value - /// - /// The entity to remove - /// true if no longer in storage, false otherwise - public bool Remove(IEntity entity) + public override bool Remove(IEntity entity) { EnsureInitialCalculated(); return _storage?.Remove(entity) == true; @@ -147,7 +141,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage Logger.DebugS(LoggerName, $"Storage (UID {Owner.Uid}) had entity (UID {message.Entity.Uid}) inserted into it."); - _storageUsed += message.Entity.GetComponent().ObjectSize; + _storageUsed += message.Entity.GetComponent().Size; UpdateClientInventories(); } @@ -171,7 +165,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage return; } - _storageUsed -= storable.ObjectSize; + _storageUsed -= storable.Size; UpdateClientInventories(); } @@ -258,14 +252,16 @@ namespace Content.Server.GameObjects.Components.Items.Storage return; } - var storedEntities = new Dictionary(); - - foreach (var entities in _storage.ContainedEntities) + if (StoredEntities == null) { - storedEntities.Add(entities.Uid, entities.GetComponent().ObjectSize); + Logger.WarningS(LoggerName, $"{nameof(UpdateClientInventory)} called with null {nameof(StoredEntities)}"); + + return; } - SendNetworkMessage(new StorageHeldItemsMessage(storedEntities, _storageUsed, _storageCapacityMax), session.ConnectedClient); + var stored = StoredEntities.Select(e => e.Uid).ToArray(); + + SendNetworkMessage(new StorageHeldItemsMessage(stored, _storageUsed, _storageCapacityMax), session.ConnectedClient); } /// @@ -500,43 +496,5 @@ namespace Content.Server.GameObjects.Components.Items.Storage } } } - - bool IDragDrop.CanDragDrop(DragDropEventArgs eventArgs) - { - return eventArgs.Target.TryGetComponent(out PlaceableSurfaceComponent? placeable) && - placeable.IsPlaceable; - } - - bool IDragDrop.DragDrop(DragDropEventArgs eventArgs) - { - if (!ActionBlockerSystem.CanInteract(eventArgs.User)) - { - return false; - } - - if (!eventArgs.Target.TryGetComponent(out var placeableSurface) || - !placeableSurface.IsPlaceable) - { - return false; - } - - var storedEntities = StoredEntities?.ToList(); - - if (storedEntities == null) - { - return false; - } - - // empty everything out - foreach (var storedEntity in StoredEntities.ToList()) - { - if (Remove(storedEntity)) - { - storedEntity.Transform.WorldPosition = eventArgs.DropLocation.Position; - } - } - - return true; - } } } diff --git a/Content.Server/GameObjects/Components/Items/Storage/StorableComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/StorableComponent.cs index 17163dc08a..fabf505c9e 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/StorableComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/StorableComponent.cs @@ -1,20 +1,33 @@ -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization; +using Content.Shared.GameObjects.Components.Storage; +using Robust.Shared.GameObjects; namespace Content.Server.GameObjects.Components.Items.Storage { [RegisterComponent] - public class StorableComponent : Component + [ComponentReference(typeof(SharedStorableComponent))] + public class StorableComponent : SharedStorableComponent { - public override string Name => "Storable"; + private int _size; - public int ObjectSize; - - public override void ExposeData(ObjectSerializer serializer) + public override int Size { - base.ExposeData(serializer); + get => _size; + set + { + if (_size == value) + { + return; + } - serializer.DataField(ref ObjectSize, "size", 1); + _size = value; + + Dirty(); + } + } + + public override ComponentState GetComponentState() + { + return new StorableComponentState(_size); } } diff --git a/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs b/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs index 1dd3fc6481..505712ad94 100644 --- a/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs +++ b/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs @@ -31,6 +31,7 @@ namespace Content.Server.GameObjects.Components.Medical { [RegisterComponent] [ComponentReference(typeof(IActivate))] + [ComponentReference(typeof(SharedMedicalScannerComponent))] public class MedicalScannerComponent : SharedMedicalScannerComponent, IActivate, IDragDropOn { private ContainerSlot _bodyContainer = default!; @@ -250,12 +251,12 @@ namespace Content.Server.GameObjects.Components.Medical public bool CanDragDropOn(DragDropEventArgs eventArgs) { - return eventArgs.Dropped.HasComponent(); + return eventArgs.Dragged.HasComponent(); } public bool DragDropOn(DragDropEventArgs eventArgs) { - _bodyContainer.Insert(eventArgs.Dropped); + _bodyContainer.Insert(eventArgs.Dragged); return true; } } diff --git a/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs b/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs index c7c5e8f777..cfda030efd 100644 --- a/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs +++ b/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs @@ -67,10 +67,10 @@ namespace Content.Server.GameObjects.Components.Movement string reason; bool canVault; - if (eventArgs.User == eventArgs.Dropped) + if (eventArgs.User == eventArgs.Dragged) canVault = CanVault(eventArgs.User, eventArgs.Target, out reason); else - canVault = CanVault(eventArgs.User, eventArgs.Dropped, eventArgs.Target, out reason); + canVault = CanVault(eventArgs.User, eventArgs.Dragged, eventArgs.Target, out reason); if (!canVault) eventArgs.User.PopupMessage(reason); @@ -154,13 +154,13 @@ namespace Content.Server.GameObjects.Components.Movement bool IDragDropOn.DragDropOn(DragDropEventArgs eventArgs) { - if (eventArgs.User == eventArgs.Dropped) + if (eventArgs.User == eventArgs.Dragged) { TryClimb(eventArgs.User); } else { - TryMoveEntity(eventArgs.User, eventArgs.Dropped); + TryMoveEntity(eventArgs.User, eventArgs.Dragged); } return true; diff --git a/Content.Server/GameObjects/Components/Movement/ClimbingComponent.cs b/Content.Server/GameObjects/Components/Movement/ClimbingComponent.cs index 95ab445659..93f02320ac 100644 --- a/Content.Server/GameObjects/Components/Movement/ClimbingComponent.cs +++ b/Content.Server/GameObjects/Components/Movement/ClimbingComponent.cs @@ -1,5 +1,4 @@ using Content.Shared.GameObjects.Components.Movement; -using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Physics; using Robust.Shared.GameObjects; using Robust.Shared.Maths; @@ -7,7 +6,7 @@ using Robust.Shared.Maths; namespace Content.Server.GameObjects.Components.Movement { [RegisterComponent] - public class ClimbingComponent : SharedClimbingComponent, IActionBlocker + public class ClimbingComponent : SharedClimbingComponent { private bool _isClimbing = false; private ClimbController _climbController = default; diff --git a/Content.Server/GameObjects/Components/PlaceableSurfaceComponent.cs b/Content.Server/GameObjects/Components/PlaceableSurfaceComponent.cs index a14d624ad1..056aba614f 100644 --- a/Content.Server/GameObjects/Components/PlaceableSurfaceComponent.cs +++ b/Content.Server/GameObjects/Components/PlaceableSurfaceComponent.cs @@ -9,11 +9,27 @@ using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components { [RegisterComponent] + [ComponentReference(typeof(SharedPlaceableSurfaceComponent))] public class PlaceableSurfaceComponent : SharedPlaceableSurfaceComponent, IInteractUsing { private bool _isPlaceable; + [ViewVariables(VVAccess.ReadWrite)] - public bool IsPlaceable { get => _isPlaceable; set => _isPlaceable = value; } + public override bool IsPlaceable + { + get => _isPlaceable; + set + { + if (_isPlaceable == value) + { + return; + } + + _isPlaceable = value; + + Dirty(); + } + } [ViewVariables] int IInteractUsing.Priority => 1; @@ -25,6 +41,10 @@ namespace Content.Server.GameObjects.Components serializer.DataField(ref _isPlaceable, "IsPlaceable", true); } + public override ComponentState GetComponentState() + { + return new PlaceableSurfaceComponentState(_isPlaceable); + } public async Task InteractUsing(InteractUsingEventArgs eventArgs) { diff --git a/Content.Server/GameObjects/Components/PottedPlantHideComponent.cs b/Content.Server/GameObjects/Components/PottedPlantHideComponent.cs index f3747aa382..cc924cffd4 100644 --- a/Content.Server/GameObjects/Components/PottedPlantHideComponent.cs +++ b/Content.Server/GameObjects/Components/PottedPlantHideComponent.cs @@ -41,7 +41,7 @@ namespace Content.Server.GameObjects.Components return false; } - var size = eventArgs.Using.GetComponent().ObjectSize; + var size = eventArgs.Using.GetComponent().Size; // TODO: use proper text macro system for this. diff --git a/Content.Server/GameObjects/Components/Strap/StrapComponent.cs b/Content.Server/GameObjects/Components/Strap/StrapComponent.cs index e3d36aa88a..3bd7b5c4cb 100644 --- a/Content.Server/GameObjects/Components/Strap/StrapComponent.cs +++ b/Content.Server/GameObjects/Components/Strap/StrapComponent.cs @@ -18,6 +18,7 @@ using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Strap { [RegisterComponent] + [ComponentReference(typeof(SharedStrapComponent))] public class StrapComponent : SharedStrapComponent, IInteractHand { [ComponentDependency] public readonly SpriteComponent? SpriteComponent = null; diff --git a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs index d290ec2d2f..2f3f71efec 100644 --- a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs @@ -75,10 +75,10 @@ namespace Content.Server.GameObjects.EntitySystems.Click if (!interactionArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return; // trigger dragdrops on the dropped entity - foreach (var dragDrop in dropped.GetAllComponents()) + foreach (var dragDrop in dropped.GetAllComponents()) { - if (dragDrop.CanDragDrop(interactionArgs) && - dragDrop.DragDrop(interactionArgs)) + if (dragDrop.CanDrop(interactionArgs) && + dragDrop.Drop(interactionArgs)) { return; } diff --git a/Content.Shared/GameObjects/Components/Buckle/SharedBuckleComponent.cs b/Content.Shared/GameObjects/Components/Buckle/SharedBuckleComponent.cs index d10e77468a..c1556ab2f8 100644 --- a/Content.Shared/GameObjects/Components/Buckle/SharedBuckleComponent.cs +++ b/Content.Shared/GameObjects/Components/Buckle/SharedBuckleComponent.cs @@ -1,12 +1,14 @@ using System; +using Content.Shared.GameObjects.Components.Strap; using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Interfaces.GameObjects.Components; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Serialization; namespace Content.Shared.GameObjects.Components.Buckle { - public abstract class SharedBuckleComponent : Component, IActionBlocker, IEffectBlocker + public abstract class SharedBuckleComponent : Component, IActionBlocker, IEffectBlocker, IDraggable { public sealed override string Name => "Buckle"; @@ -17,6 +19,8 @@ namespace Content.Shared.GameObjects.Components.Buckle /// public abstract bool Buckled { get; } + public abstract bool TryBuckle(IEntity user, IEntity to); + bool IActionBlocker.CanMove() { return !Buckled; @@ -31,6 +35,16 @@ namespace Content.Shared.GameObjects.Components.Buckle { return !Buckled; } + + bool IDraggable.CanDrop(CanDropEventArgs args) + { + return args.Target.HasComponent(); + } + + public bool Drop(DragDropEventArgs args) + { + return TryBuckle(args.User, args.Dragged); + } } [Serializable, NetSerializable] diff --git a/Content.Shared/GameObjects/Components/GUI/SharedStrippableComponent.cs b/Content.Shared/GameObjects/Components/GUI/SharedStrippableComponent.cs index 96a3ac63d2..9b172820a4 100644 --- a/Content.Shared/GameObjects/Components/GUI/SharedStrippableComponent.cs +++ b/Content.Shared/GameObjects/Components/GUI/SharedStrippableComponent.cs @@ -1,16 +1,36 @@ using System; using System.Collections.Generic; +using Content.Shared.GameObjects.Components.Items; +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Interfaces.GameObjects.Components; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Components.UserInterface; +using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Serialization; using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines; namespace Content.Shared.GameObjects.Components.GUI { - public class SharedStrippableComponent : Component + public abstract class SharedStrippableComponent : Component, IDraggable { public override string Name => "Strippable"; + public bool CanBeStripped(IEntity by) + { + return by != Owner + && by.HasComponent() + && ActionBlockerSystem.CanInteract(by); + } + + bool IDraggable.CanDrop(CanDropEventArgs args) + { + return args.Target != args.Dragged + && args.Target == args.User + && CanBeStripped(args.User); + } + + public abstract bool Drop(DragDropEventArgs args); + [NetSerializable, Serializable] public enum StrippingUiKey { diff --git a/Content.Shared/GameObjects/Components/Movement/SharedClimbingComponent.cs b/Content.Shared/GameObjects/Components/Movement/SharedClimbingComponent.cs index 287065bdec..cf81cdd7ac 100644 --- a/Content.Shared/GameObjects/Components/Movement/SharedClimbingComponent.cs +++ b/Content.Shared/GameObjects/Components/Movement/SharedClimbingComponent.cs @@ -5,10 +5,11 @@ using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Components; using Robust.Shared.Physics; using Robust.Shared.Serialization; +using Content.Shared.Interfaces.GameObjects.Components; namespace Content.Shared.GameObjects.Components.Movement { - public abstract class SharedClimbingComponent : Component, IActionBlocker, ICollideSpecial + public abstract class SharedClimbingComponent : Component, IActionBlocker, ICollideSpecial, IDraggable { public sealed override string Name => "Climbing"; public sealed override uint? NetID => ContentNetIDs.CLIMBING; @@ -45,6 +46,16 @@ namespace Content.Shared.GameObjects.Components.Movement return false; } + bool IDraggable.CanDrop(CanDropEventArgs args) + { + return args.Target.HasComponent(); + } + + bool IDraggable.Drop(DragDropEventArgs args) + { + return false; + } + public override void Initialize() { base.Initialize(); diff --git a/Content.Shared/GameObjects/Components/SharedPlaceableSurfaceComponent.cs b/Content.Shared/GameObjects/Components/SharedPlaceableSurfaceComponent.cs index 2461021357..9ed7a48cd1 100644 --- a/Content.Shared/GameObjects/Components/SharedPlaceableSurfaceComponent.cs +++ b/Content.Shared/GameObjects/Components/SharedPlaceableSurfaceComponent.cs @@ -1,9 +1,25 @@ +using System; using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; namespace Content.Shared.GameObjects.Components { public abstract class SharedPlaceableSurfaceComponent : Component { public override string Name => "PlaceableSurface"; + public override uint? NetID => ContentNetIDs.PLACEABLE_SURFACE; + + public virtual bool IsPlaceable { get; set; } + } + + [Serializable, NetSerializable] + public class PlaceableSurfaceComponentState : ComponentState + { + public readonly bool IsPlaceable; + + public PlaceableSurfaceComponentState(bool placeable) : base(ContentNetIDs.PLACEABLE_SURFACE) + { + IsPlaceable = placeable; + } } } diff --git a/Content.Shared/GameObjects/Components/Storage/SharedStorableComponent.cs b/Content.Shared/GameObjects/Components/Storage/SharedStorableComponent.cs new file mode 100644 index 0000000000..26544b32b9 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Storage/SharedStorableComponent.cs @@ -0,0 +1,32 @@ +using System; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Storage +{ + public abstract class SharedStorableComponent : Component + { + public override string Name => "Storable"; + public override uint? NetID => ContentNetIDs.STORABLE; + + public virtual int Size { get; set; } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(this, s => s.Size, "size", 1); + } + } + + [Serializable, NetSerializable] + public class StorableComponentState : ComponentState + { + public readonly int Size; + + public StorableComponentState(int size) : base(ContentNetIDs.STORABLE) + { + Size = size; + } + } +} diff --git a/Content.Shared/GameObjects/Components/Storage/SharedStorageComponent.cs b/Content.Shared/GameObjects/Components/Storage/SharedStorageComponent.cs index 6e6b051a86..45f69c6636 100644 --- a/Content.Shared/GameObjects/Components/Storage/SharedStorageComponent.cs +++ b/Content.Shared/GameObjects/Components/Storage/SharedStorageComponent.cs @@ -1,14 +1,71 @@ -using System; +#nullable enable +using System; using System.Collections.Generic; +using System.Linq; +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Interfaces.GameObjects.Components; using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Serialization; namespace Content.Shared.GameObjects.Components.Storage { - public abstract class SharedStorageComponent : Component + public abstract class SharedStorageComponent : Component, IDraggable { public override string Name => "Storage"; public override uint? NetID => ContentNetIDs.INVENTORY; + + public abstract IReadOnlyList? StoredEntities { get; } + + /// + /// Removes from the storage container and updates the stored value + /// + /// The entity to remove + /// True if no longer in storage, false otherwise + public abstract bool Remove(IEntity entity); + + public bool CanDrop(CanDropEventArgs args) + { + return args.Target.TryGetComponent(out SharedPlaceableSurfaceComponent? placeable) && + placeable.IsPlaceable; + } + + public bool Drop(DragDropEventArgs eventArgs) + { + if (!ActionBlockerSystem.CanInteract(eventArgs.User)) + { + return false; + } + + var storedEntities = StoredEntities?.ToArray(); + + if (storedEntities == null) + { + return false; + } + + // empty everything out + foreach (var storedEntity in storedEntities) + { + if (Remove(storedEntity)) + { + storedEntity.Transform.WorldPosition = eventArgs.DropLocation.Position; + } + } + + return true; + } + } + + [Serializable, NetSerializable] + public class StorageComponentState : ComponentState + { + public readonly EntityUid[] StoredEntities; + + public StorageComponentState(EntityUid[] storedEntities) : base(ContentNetIDs.INVENTORY) + { + StoredEntities = storedEntities; + } } /// @@ -19,14 +76,14 @@ namespace Content.Shared.GameObjects.Components.Storage { public readonly int StorageSizeMax; public readonly int StorageSizeUsed; - public Dictionary StoredEntities; + public readonly EntityUid[] StoredEntities; - public StorageHeldItemsMessage(Dictionary storedentities, int storageused, int storagemaxsize) + public StorageHeldItemsMessage(EntityUid[] storedEntities, int storageUsed, int storageMaxSize) { Directed = true; - StorageSizeMax = storagemaxsize; - StorageSizeUsed = storageused; - StoredEntities = storedentities; + StorageSizeMax = storageMaxSize; + StorageSizeUsed = storageUsed; + StoredEntities = storedEntities; } } diff --git a/Content.Shared/GameObjects/ContentNetIDs.cs b/Content.Shared/GameObjects/ContentNetIDs.cs index 2eaa5a4db7..32a5222915 100644 --- a/Content.Shared/GameObjects/ContentNetIDs.cs +++ b/Content.Shared/GameObjects/ContentNetIDs.cs @@ -79,6 +79,8 @@ public const uint BLOCKGAME_ARCADE = 1073; public const uint BODY_PART = 1074; public const uint CRAYONS = 1075; + public const uint PLACEABLE_SURFACE = 1076; + public const uint STORABLE = 1077; // Net IDs for integration tests. public const uint PREDICTION_TEST = 10001; diff --git a/Content.Shared/GameObjects/EntitySystems/SharedInteractionSystem.cs b/Content.Shared/GameObjects/EntitySystems/SharedInteractionSystem.cs index dbd202a7cd..d6c2122457 100644 --- a/Content.Shared/GameObjects/EntitySystems/SharedInteractionSystem.cs +++ b/Content.Shared/GameObjects/EntitySystems/SharedInteractionSystem.cs @@ -445,7 +445,7 @@ namespace Content.Shared.GameObjects.EntitySystems bool popup = false) { var user = args.User; - var dropped = args.Dropped; + var dropped = args.Dragged; var target = args.Target; if (!InRangeUnobstructed(user, target, range, collisionMask, predicate, ignoreInsideBlocker)) diff --git a/Content.Shared/Interfaces/GameObjects/Components/Interaction/IDragDrop.cs b/Content.Shared/Interfaces/GameObjects/Components/Interaction/IDragDrop.cs deleted file mode 100644 index e71bd265b8..0000000000 --- a/Content.Shared/Interfaces/GameObjects/Components/Interaction/IDragDrop.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using Robust.Shared.Interfaces.GameObjects; -using Robust.Shared.Map; - -namespace Content.Shared.Interfaces.GameObjects.Components -{ - /// - /// This interface allows the component's entity to be dragged and dropped - /// by mouse onto another entity and gives it behavior when that occurs. - /// - public interface IDragDrop - { - /// - /// Invoked server-side when this component's entity is being dragged - /// and dropped on another before invoking . - /// Note that other drag and drop interactions may be attempted if - /// this one fails. - /// - /// - /// true if is valid, false otherwise. - bool CanDragDrop(DragDropEventArgs eventArgs); - - /// - /// Invoked server-side when this component's entity is being dragged - /// and dropped on another. - /// 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 DragDrop(DragDropEventArgs eventArgs); - } - - public class DragDropEventArgs : EventArgs - { - /// - /// 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 DragDropEventArgs(IEntity user, EntityCoordinates dropLocation, IEntity dropped, IEntity target) - { - User = user; - DropLocation = dropLocation; - Dropped = dropped; - Target = target; - } - - /// - /// The entity doing the drag and drop. - /// - public IEntity User { get; } - - /// - /// The location where is being dropped. - /// - public EntityCoordinates DropLocation { get; } - - /// - /// The entity that is being dragged and dropped. - /// - public IEntity Dropped { get; } - - /// - /// The entity that is being dropped onto. - /// - public IEntity Target { get; } - } -} diff --git a/Content.Shared/Interfaces/GameObjects/Components/Interaction/IDraggable.cs b/Content.Shared/Interfaces/GameObjects/Components/Interaction/IDraggable.cs new file mode 100644 index 0000000000..2daff16ab7 --- /dev/null +++ b/Content.Shared/Interfaces/GameObjects/Components/Interaction/IDraggable.cs @@ -0,0 +1,121 @@ +using System; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Map; + +namespace Content.Shared.Interfaces.GameObjects.Components +{ + /// + /// This interface allows a local client to initiate dragging of the component's + /// entity by mouse, for drag and drop interactions. + /// + 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(StartDragDropEventArgs 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(CanDropEventArgs 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(DragDropEventArgs args) + { + return false; + } + } + + public class StartDragDropEventArgs : EventArgs + { + /// + /// Creates a new instance of . + /// + /// The entity doing the drag and drop. + /// The entity that is being dragged and dropped. + public StartDragDropEventArgs(IEntity user, IEntity dragged) + { + User = user; + Dragged = dragged; + } + + /// + /// The entity doing the drag and drop. + /// + public IEntity User { get; } + + /// + /// The entity that is being dragged. + /// + public IEntity Dragged { get; } + } + + public class CanDropEventArgs : StartDragDropEventArgs + { + /// + /// 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 CanDropEventArgs(IEntity user, IEntity dragged, IEntity target) : base(user, dragged) + { + Target = target; + } + + /// + /// The entity that + /// is being dropped onto. + /// + public IEntity Target { get; } + } + + public class DragDropEventArgs : CanDropEventArgs + { + /// + /// 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 DragDropEventArgs(IEntity user, EntityCoordinates dropLocation, IEntity dragged, IEntity target) : base(user, dragged, target) + { + DropLocation = dropLocation; + } + /// + /// The location where + /// is being dropped. + /// + public EntityCoordinates DropLocation { get; } + } +}