Refactor drag and drop to use a shared interface (#2012)

* WIP in progress hours

* Cleanup

* Fix bugle

* Fix nullable error

* Merge fixes

* Merge fixes

* Merge fixes
This commit is contained in:
DrSmugleaf
2020-10-14 15:24:07 +02:00
committed by GitHub
parent f715eed63c
commit cdedaeb12e
37 changed files with 527 additions and 377 deletions

View File

@@ -1,17 +1,17 @@
#nullable enable #nullable enable
using Content.Client.GameObjects.Components.Disposal; using Content.Client.GameObjects.Components.Disposal;
using Content.Client.GameObjects.Components.MedicalScanner; using Content.Client.GameObjects.Components.MedicalScanner;
using Content.Client.Interfaces.GameObjects.Components.Interaction;
using Content.Shared.GameObjects.Components.Body; using Content.Shared.GameObjects.Components.Body;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects.Components.Body namespace Content.Client.GameObjects.Components.Body
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(IBody))] [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<DisposalUnitComponent>() || if (eventArgs.Target.HasComponent<DisposalUnitComponent>() ||
eventArgs.Target.HasComponent<MedicalScannerComponent>()) eventArgs.Target.HasComponent<MedicalScannerComponent>())
@@ -21,10 +21,5 @@ namespace Content.Client.GameObjects.Components.Body
return false; return false;
} }
public bool ClientCanDrag(CanDragEventArgs eventArgs)
{
return true;
}
} }
} }

View File

@@ -1,19 +1,24 @@
using Content.Client.GameObjects.Components.Strap;
using Content.Client.Interfaces.GameObjects.Components.Interaction;
using Content.Shared.GameObjects.Components.Buckle; using Content.Shared.GameObjects.Components.Buckle;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Client.GameObjects.Components.Buckle namespace Content.Client.GameObjects.Components.Buckle
{ {
[RegisterComponent] [RegisterComponent]
public class BuckleComponent : SharedBuckleComponent, IClientDraggable public class BuckleComponent : SharedBuckleComponent
{ {
private bool _buckled; private bool _buckled;
private int? _originalDrawDepth; private int? _originalDrawDepth;
public override bool Buckled => _buckled; 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) public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{ {
if (!(curState is BuckleComponentState buckle)) if (!(curState is BuckleComponentState buckle))
@@ -41,15 +46,5 @@ namespace Content.Client.GameObjects.Components.Buckle
_originalDrawDepth = null; _originalDrawDepth = null;
} }
} }
bool IClientDraggable.ClientCanDropOn(CanDropEventArgs eventArgs)
{
return eventArgs.Target.HasComponent<StrapComponent>();
}
bool IClientDraggable.ClientCanDrag(CanDragEventArgs eventArgs)
{
return true;
}
} }
} }

View File

@@ -1,22 +1,17 @@
using Content.Client.GameObjects.Components.Items; using Content.Shared.GameObjects.Components.GUI;
using Content.Client.Interfaces.GameObjects.Components.Interaction; using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.GameObjects.Components.GUI;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects.Components.GUI namespace Content.Client.GameObjects.Components.GUI
{ {
[RegisterComponent] [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<HandsComponent>() // TODO: Prediction
&& eventArgs.Target != eventArgs.Dragged && eventArgs.Target == eventArgs.User; return false;
}
public bool ClientCanDrag(CanDragEventArgs eventArgs)
{
return true;
} }
} }
} }

View File

@@ -1,7 +1,7 @@
using Content.Client.GameObjects.Components.Disposal; using Content.Client.GameObjects.Components.Disposal;
using Content.Client.Interfaces.GameObjects.Components.Interaction;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Items; using Content.Shared.GameObjects.Components.Items;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.Interfaces.ResourceManagement; using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.ResourceManagement; using Robust.Client.ResourceManagement;
@@ -19,7 +19,7 @@ namespace Content.Client.GameObjects.Components.Items
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(IItemComponent))] [ComponentReference(typeof(IItemComponent))]
public class ItemComponent : Component, IItemComponent, IClientDraggable public class ItemComponent : Component, IItemComponent, IDraggable
{ {
public override string Name => "Item"; public override string Name => "Item";
public override uint? NetID => ContentNetIDs.ITEM; public override uint? NetID => ContentNetIDs.ITEM;
@@ -85,14 +85,15 @@ namespace Content.Client.GameObjects.Components.Items
EquippedPrefix = itemComponentState.EquippedPrefix; EquippedPrefix = itemComponentState.EquippedPrefix;
} }
bool IClientDraggable.ClientCanDropOn(CanDropEventArgs eventArgs) bool IDraggable.CanDrop(CanDropEventArgs args)
{ {
return eventArgs.Target.HasComponent<DisposalUnitComponent>(); return args.Target.HasComponent<DisposalUnitComponent>();
} }
bool IClientDraggable.ClientCanDrag(CanDragEventArgs eventArgs) public bool Drop(DragDropEventArgs args)
{ {
return true; // TODO: Shared item class
return false;
} }
} }
} }

View File

@@ -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; using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects.Components.Movement namespace Content.Client.GameObjects.Components.Movement
{ {
[RegisterComponent] [RegisterComponent]
public class ClimbingComponent : SharedClimbingComponent, IClientDraggable public class ClimbingComponent : SharedClimbingComponent
{ {
public override void HandleComponentState(ComponentState curState, ComponentState nextState) public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{ {
@@ -18,15 +17,5 @@ namespace Content.Client.GameObjects.Components.Movement
} }
public override bool IsClimbing { get; set; } public override bool IsClimbing { get; set; }
bool IClientDraggable.ClientCanDropOn(CanDropEventArgs eventArgs)
{
return eventArgs.Target.HasComponent<IClimbable>();
}
bool IClientDraggable.ClientCanDrag(CanDragEventArgs eventArgs)
{
return true;
}
} }
} }

View File

@@ -1,11 +1,41 @@
#nullable enable
using Content.Shared.GameObjects.Components; using Content.Shared.GameObjects.Components;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects.Components namespace Content.Client.GameObjects.Components
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(SharedPlaceableSurfaceComponent))]
public class PlaceableSurfaceComponent : 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;
}
} }
} }

View File

@@ -1,8 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Content.Client.GameObjects.Components.Items; using Content.Client.GameObjects.Components.Items;
using Content.Client.Interfaces.GameObjects.Components.Interaction;
using Content.Shared.GameObjects.Components.Storage; using Content.Shared.GameObjects.Components.Storage;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Client.Graphics.Drawing; using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.GameObjects.Components; using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Client.Player; 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 /// Client version of item storage containers, contains a UI which displays stored entities and their size
/// </summary> /// </summary>
[RegisterComponent] [RegisterComponent]
public class ClientStorageComponent : SharedStorageComponent, IClientDraggable public class ClientStorageComponent : SharedStorageComponent, IDraggable
{ {
private Dictionary<EntityUid, int> StoredEntities { get; set; } = new Dictionary<EntityUid, int>(); [Dependency] private readonly IEntityManager _entityManager = default!;
private List<IEntity> _storedEntities = new List<IEntity>();
private int StorageSizeUsed; private int StorageSizeUsed;
private int StorageCapacityMax; private int StorageCapacityMax;
private StorageWindow Window; private StorageWindow Window;
public override IReadOnlyList<IEntity> StoredEntities => _storedEntities;
public override void OnAdd() public override void OnAdd()
{ {
base.OnAdd(); base.OnAdd();
@@ -43,6 +48,20 @@ namespace Content.Client.GameObjects.Components.Storage
base.OnRemove(); 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) public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession session = null)
{ {
base.HandleNetworkMessage(message, channel, session); base.HandleNetworkMessage(message, channel, session);
@@ -69,7 +88,7 @@ namespace Content.Client.GameObjects.Components.Storage
/// <param name="storageState"></param> /// <param name="storageState"></param>
private void HandleStorageMessage(StorageHeldItemsMessage storageState) private void HandleStorageMessage(StorageHeldItemsMessage storageState)
{ {
StoredEntities = new Dictionary<EntityUid, int>(storageState.StoredEntities); _storedEntities = storageState.StoredEntities.Select(id => _entityManager.GetEntity(id)).ToList();
StorageSizeUsed = storageState.StorageSizeUsed; StorageSizeUsed = storageState.StorageSizeUsed;
StorageCapacityMax = storageState.StorageSizeMax; StorageCapacityMax = storageState.StorageSizeMax;
Window.BuildEntityList(); Window.BuildEntityList();
@@ -100,6 +119,17 @@ namespace Content.Client.GameObjects.Components.Storage
SendNetworkMessage(new RemoveEntityMessage(entityUid)); SendNetworkMessage(new RemoveEntityMessage(entityUid));
} }
public override bool Remove(IEntity entity)
{
if (_storedEntities.Remove(entity))
{
Dirty();
return true;
}
return false;
}
/// <summary> /// <summary>
/// GUI class for client storage component /// GUI class for client storage component
/// </summary> /// </summary>
@@ -200,19 +230,25 @@ namespace Content.Client.GameObjects.Components.Storage
var storageList = StorageEntity.StoredEntities; var storageList = StorageEntity.StoredEntities;
foreach (var entityUid in storageList) var storedGrouped = storageList.GroupBy(e => e).Select(e => new
{ {
var entity = IoCManager.Resolve<IEntityManager>().GetEntity(entityUid.Key); Entity = e.Key,
Amount = e.Count()
});
foreach (var group in storedGrouped)
{
var entity = group.Entity;
var button = new EntityButton() var button = new EntityButton()
{ {
EntityUid = entityUid.Key, EntityUid = entity.Uid,
MouseFilter = MouseFilterMode.Stop, MouseFilter = MouseFilterMode.Stop,
}; };
button.ActualButton.OnToggled += OnItemButtonToggled; button.ActualButton.OnToggled += OnItemButtonToggled;
//Name and Size labels set //Name and Size labels set
button.EntityName.Text = entity.Name; 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 //Gets entity sprite and assigns it to button texture
if (entity.TryGetComponent(out ISpriteComponent sprite)) if (entity.TryGetComponent(out ISpriteComponent sprite))
@@ -320,17 +356,5 @@ namespace Content.Client.GameObjects.Components.Storage
AddChild(hBoxContainer); AddChild(hBoxContainer);
} }
} }
public bool ClientCanDropOn(CanDropEventArgs eventArgs)
{
//can only drop on placeable surfaces to empty out contents
return eventArgs.Target.HasComponent<PlaceableSurfaceComponent>();
}
public bool ClientCanDrag(CanDragEventArgs eventArgs)
{
//always draggable, at least for now
return true;
}
} }
} }

View File

@@ -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;
}
}
}

View File

@@ -5,6 +5,7 @@ using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects.Components.Strap namespace Content.Client.GameObjects.Components.Strap
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(SharedStrapComponent))]
public class StrapComponent : SharedStrapComponent public class StrapComponent : SharedStrapComponent
{ {
} }

View File

@@ -1,10 +1,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Client.Interfaces.GameObjects.Components.Interaction;
using Content.Client.State; using Content.Client.State;
using Content.Shared.GameObjects; 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.GameObjects.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.GameObjects.EntitySystems; using Robust.Client.GameObjects.EntitySystems;
@@ -52,7 +52,7 @@ namespace Content.Client.GameObjects.EntitySystems
// entity performing the drag action // entity performing the drag action
private IEntity _dragger; private IEntity _dragger;
private IEntity _draggedEntity; private IEntity _draggedEntity;
private readonly List<IClientDraggable> _draggables = new List<IClientDraggable>(); private readonly List<IDraggable> _draggables = new List<IDraggable>();
private IEntity _dragShadow; private IEntity _dragShadow;
private DragState _state; private DragState _state;
// time since mouse down over the dragged entity // time since mouse down over the dragged entity
@@ -146,10 +146,10 @@ namespace Content.Client.GameObjects.EntitySystems
} }
var canDrag = false; var canDrag = false;
foreach (var draggable in entity.GetAllComponents<IClientDraggable>()) foreach (var draggable in entity.GetAllComponents<IDraggable>())
{ {
var dragEventArgs = new CanDragEventArgs(args.Session.AttachedEntity, entity); var dragEventArgs = new StartDragDropEventArgs(args.Session.AttachedEntity, entity);
if (draggable.ClientCanDrag(dragEventArgs)) if (draggable.CanStartDrag(dragEventArgs))
{ {
// wait to initiate a drag // wait to initiate a drag
_dragger = dragger; _dragger = dragger;
@@ -202,19 +202,26 @@ namespace Content.Client.GameObjects.EntitySystems
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 canDropArgs = new CanDropEventArgs(_dragger, _draggedEntity, entity); var dropArgs = new DragDropEventArgs(_dragger, args.Coordinates, _draggedEntity, entity);
var anyValidDraggable = _draggables.Any(draggable => draggable.ClientCanDropOn(canDropArgs));
if (anyValidDraggable) foreach (var draggable in _draggables)
{ {
if (!draggable.CanDrop(dropArgs))
{
continue;
}
// tell the server about the drop attempt // tell the server about the drop attempt
RaiseNetworkEvent(new DragDropMessage(args.Coordinates, _draggedEntity.Uid, RaiseNetworkEvent(new DragDropMessage(args.Coordinates, _draggedEntity.Uid,
entity.Uid)); entity.Uid));
draggable.Drop(dropArgs);
CancelDrag(false, null); CancelDrag(false, null);
return true; return true;
} }
} }
CancelDrag(false, null); CancelDrag(false, null);
return false; return false;
} }
@@ -284,7 +291,7 @@ namespace Content.Client.GameObjects.EntitySystems
// 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 canDropArgs = new CanDropEventArgs(_dragger, _draggedEntity, pvsEntity); var canDropArgs = new CanDropEventArgs(_dragger, _draggedEntity, pvsEntity);
var anyValidDraggable = _draggables.Any(draggable => draggable.ClientCanDropOn(canDropArgs)); var anyValidDraggable = _draggables.Any(draggable => draggable.CanDrop(canDropArgs));
if (anyValidDraggable) if (anyValidDraggable)
{ {

View File

@@ -28,7 +28,6 @@
"Wrench", "Wrench",
"Crowbar", "Crowbar",
"MeleeWeapon", "MeleeWeapon",
"Storable",
"Dice", "Dice",
"Construction", "Construction",
"Door", "Door",

View File

@@ -1,88 +0,0 @@
using System;
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Client.Interfaces.GameObjects.Components.Interaction
{
/// <summary>
/// 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
/// </summary>
public interface IClientDraggable
{
/// <summary>
/// Invoked on entities visible to the user to check if this component's entity
/// can be dropped on the indicated target entity. No need to check range / reachability in here.
/// </summary>
/// <returns>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.</returns>
bool ClientCanDropOn(CanDropEventArgs eventArgs);
/// <summary>
/// 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.
/// </summary>
/// <param name="eventArgs"></param>
/// <returns>true iff drag should be initiated</returns>
bool ClientCanDrag(CanDragEventArgs eventArgs);
}
public class CanDropEventArgs : EventArgs
{
/// <summary>
/// Creates a new instance of <see cref="CanDropEventArgs"/>.
/// </summary>
/// <param name="user">The entity doing the drag and drop.</param>
/// <param name="dragged">The entity that is being dragged and dropped.</param>
/// <param name="target">The entity that <see cref="dragged"/> is being dropped onto.</param>
public CanDropEventArgs(IEntity user, IEntity dragged, IEntity target)
{
User = user;
Dragged = dragged;
Target = target;
}
/// <summary>
/// The entity doing the drag and drop.
/// </summary>
public IEntity User { get; }
/// <summary>
/// The entity that is being dragged and dropped.
/// </summary>
public IEntity Dragged { get; }
/// <summary>
/// The entity that <see cref="Dragged"/> is being dropped onto.
/// </summary>
public IEntity Target { get; }
}
public class CanDragEventArgs : EventArgs
{
/// <summary>
/// Creates a new instance of <see cref="CanDragEventArgs"/>.
/// </summary>
/// <param name="user">The entity doing the drag and drop.</param>
/// <param name="dragged">The entity that is being dragged and dropped.</param>
public CanDragEventArgs(IEntity user, IEntity dragged)
{
User = user;
Dragged = dragged;
}
/// <summary>
/// The entity doing the drag and drop.
/// </summary>
public IEntity User { get; }
/// <summary>
/// The entity that is being dragged and dropped.
/// </summary>
public IEntity Dragged { get; }
}
}

View File

@@ -34,7 +34,7 @@ using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Buckle namespace Content.Server.GameObjects.Components.Buckle
{ {
[RegisterComponent] [RegisterComponent]
public class BuckleComponent : SharedBuckleComponent, IInteractHand, IDragDrop public class BuckleComponent : SharedBuckleComponent, IInteractHand
{ {
[Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IEntitySystemManager _entitySystem = default!; [Dependency] private readonly IEntitySystemManager _entitySystem = default!;
@@ -256,18 +256,7 @@ namespace Content.Server.GameObjects.Components.Buckle
return true; return true;
} }
/// <summary> public override bool TryBuckle(IEntity user, IEntity to)
/// Tries to make an entity buckle the owner of this component to another.
/// </summary>
/// <param name="user">
/// The entity buckling the owner of this component, can be the owner itself.
/// </param>
/// <param name="to">The entity to buckle the owner of this component to.</param>
/// <returns>
/// true if the owner was buckled, otherwise false even if the owner was
/// previously already buckled.
/// </returns>
public bool TryBuckle(IEntity user, IEntity to)
{ {
if (!CanBuckle(user, to, out var strap)) if (!CanBuckle(user, to, out var strap))
{ {
@@ -544,16 +533,6 @@ namespace Content.Server.GameObjects.Components.Buckle
return TryUnbuckle(eventArgs.User); return TryUnbuckle(eventArgs.User);
} }
bool IDragDrop.CanDragDrop(DragDropEventArgs eventArgs)
{
return eventArgs.Target.HasComponent<StrapComponent>();
}
bool IDragDrop.DragDrop(DragDropEventArgs eventArgs)
{
return TryBuckle(eventArgs.User, eventArgs.Target);
}
/// <summary> /// <summary>
/// Allows the unbuckling of the owning entity through a verb if /// Allows the unbuckling of the owning entity through a verb if
/// anyone right clicks them. /// anyone right clicks them.

View File

@@ -674,12 +674,12 @@ namespace Content.Server.GameObjects.Components.Disposal
bool IDragDropOn.CanDragDropOn(DragDropEventArgs eventArgs) bool IDragDropOn.CanDragDropOn(DragDropEventArgs eventArgs)
{ {
return CanInsert(eventArgs.Dropped); return CanInsert(eventArgs.Dragged);
} }
bool IDragDropOn.DragDropOn(DragDropEventArgs eventArgs) bool IDragDropOn.DragDropOn(DragDropEventArgs eventArgs)
{ {
_ = TryInsert(eventArgs.Dropped, eventArgs.User); _ = TryInsert(eventArgs.Dragged, eventArgs.User);
return true; return true;
} }

View File

@@ -45,7 +45,7 @@ namespace Content.Server.GameObjects.Components.GUI
var itemComponent = entity.GetComponent<ItemComponent>(); var itemComponent = entity.GetComponent<ItemComponent>();
// If this item is small enough then it always fits in pockets. // 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; return true;
} }

View File

@@ -23,7 +23,7 @@ using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefine
namespace Content.Server.GameObjects.Components.GUI namespace Content.Server.GameObjects.Components.GUI
{ {
[RegisterComponent] [RegisterComponent]
public sealed class StrippableComponent : SharedStrippableComponent, IDragDrop public sealed class StrippableComponent : SharedStrippableComponent
{ {
public const float StripDelay = 2f; public const float StripDelay = 2f;
@@ -75,23 +75,9 @@ namespace Content.Server.GameObjects.Components.GUI
UserInterface.SetState(new StrippingBoundUserInterfaceState(inventory, hands, cuffs)); UserInterface.SetState(new StrippingBoundUserInterfaceState(inventory, hands, cuffs));
} }
public bool CanBeStripped(IEntity by) public override bool Drop(DragDropEventArgs args)
{ {
return by != Owner if (!args.User.TryGetComponent(out IActorComponent? actor)) return false;
&& by.HasComponent<HandsComponent>()
&& 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;
OpenUserInterface(actor.playerSession); OpenUserInterface(actor.playerSession);
return true; return true;

View File

@@ -4,6 +4,7 @@ using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.Items.Storage;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Items; using Content.Shared.GameObjects.Components.Items;
using Content.Shared.GameObjects.Components.Storage;
using Content.Shared.Interfaces; using Content.Shared.Interfaces;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -17,6 +18,7 @@ namespace Content.Server.GameObjects.Components.Items.Clothing
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(ItemComponent))] [ComponentReference(typeof(ItemComponent))]
[ComponentReference(typeof(StorableComponent))] [ComponentReference(typeof(StorableComponent))]
[ComponentReference(typeof(SharedStorableComponent))]
[ComponentReference(typeof(IItemComponent))] [ComponentReference(typeof(IItemComponent))]
public class ClothingComponent : ItemComponent, IUse public class ClothingComponent : ItemComponent, IUse
{ {

View File

@@ -3,6 +3,7 @@ using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Server.Throw; using Content.Server.Throw;
using Content.Shared.GameObjects; using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Items; using Content.Shared.GameObjects.Components.Items;
using Content.Shared.GameObjects.Components.Storage;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.GameObjects.Verbs; using Content.Shared.GameObjects.Verbs;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
@@ -19,6 +20,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(StorableComponent))] [ComponentReference(typeof(StorableComponent))]
[ComponentReference(typeof(SharedStorableComponent))]
[ComponentReference(typeof(IItemComponent))] [ComponentReference(typeof(IItemComponent))]
public class ItemComponent : StorableComponent, IInteractHand, IExAct, IEquipped, IUnequipped, IItemComponent public class ItemComponent : StorableComponent, IInteractHand, IExAct, IEquipped, IUnequipped, IItemComponent
{ {

View File

@@ -34,8 +34,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(IActivate))] [ComponentReference(typeof(IActivate))]
[ComponentReference(typeof(IStorageComponent))] [ComponentReference(typeof(IStorageComponent))]
public class ServerStorageComponent : SharedStorageComponent, IInteractUsing, IUse, IActivate, IStorageComponent, IDestroyAct, IExAct, public class ServerStorageComponent : SharedStorageComponent, IInteractUsing, IUse, IActivate, IStorageComponent, IDestroyAct, IExAct
IDragDrop
{ {
[Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!;
@@ -50,7 +49,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
public readonly HashSet<IPlayerSession> SubscribedSessions = new HashSet<IPlayerSession>(); public readonly HashSet<IPlayerSession> SubscribedSessions = new HashSet<IPlayerSession>();
[ViewVariables] [ViewVariables]
public IReadOnlyCollection<IEntity>? StoredEntities => _storage?.ContainedEntities; public override IReadOnlyList<IEntity>? StoredEntities => _storage?.ContainedEntities;
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public bool OccludesLight public bool OccludesLight
@@ -87,7 +86,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
foreach (var entity in _storage.ContainedEntities) foreach (var entity in _storage.ContainedEntities)
{ {
var item = entity.GetComponent<StorableComponent>(); var item = entity.GetComponent<StorableComponent>();
_storageUsed += item.ObjectSize; _storageUsed += item.Size;
} }
} }
@@ -107,7 +106,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
} }
if (entity.TryGetComponent(out StorableComponent? store) && if (entity.TryGetComponent(out StorableComponent? store) &&
store.ObjectSize > _storageCapacityMax - _storageUsed) store.Size > _storageCapacityMax - _storageUsed)
{ {
return false; return false;
} }
@@ -125,12 +124,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
return CanInsert(entity) && _storage?.Insert(entity) == true; return CanInsert(entity) && _storage?.Insert(entity) == true;
} }
/// <summary> public override bool Remove(IEntity entity)
/// Removes from the storage container and updates the stored value
/// </summary>
/// <param name="entity">The entity to remove</param>
/// <returns>true if no longer in storage, false otherwise</returns>
public bool Remove(IEntity entity)
{ {
EnsureInitialCalculated(); EnsureInitialCalculated();
return _storage?.Remove(entity) == true; 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."); Logger.DebugS(LoggerName, $"Storage (UID {Owner.Uid}) had entity (UID {message.Entity.Uid}) inserted into it.");
_storageUsed += message.Entity.GetComponent<StorableComponent>().ObjectSize; _storageUsed += message.Entity.GetComponent<StorableComponent>().Size;
UpdateClientInventories(); UpdateClientInventories();
} }
@@ -171,7 +165,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
return; return;
} }
_storageUsed -= storable.ObjectSize; _storageUsed -= storable.Size;
UpdateClientInventories(); UpdateClientInventories();
} }
@@ -258,14 +252,16 @@ namespace Content.Server.GameObjects.Components.Items.Storage
return; return;
} }
var storedEntities = new Dictionary<EntityUid, int>(); if (StoredEntities == null)
foreach (var entities in _storage.ContainedEntities)
{ {
storedEntities.Add(entities.Uid, entities.GetComponent<StorableComponent>().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);
} }
/// <summary> /// <summary>
@@ -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<PlaceableSurfaceComponent>(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;
}
} }
} }

View File

@@ -1,20 +1,33 @@
using Robust.Shared.GameObjects; using Content.Shared.GameObjects.Components.Storage;
using Robust.Shared.Serialization; using Robust.Shared.GameObjects;
namespace Content.Server.GameObjects.Components.Items.Storage namespace Content.Server.GameObjects.Components.Items.Storage
{ {
[RegisterComponent] [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 int Size
public override void ExposeData(ObjectSerializer serializer)
{ {
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);
} }
} }

View File

@@ -31,6 +31,7 @@ namespace Content.Server.GameObjects.Components.Medical
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(IActivate))] [ComponentReference(typeof(IActivate))]
[ComponentReference(typeof(SharedMedicalScannerComponent))]
public class MedicalScannerComponent : SharedMedicalScannerComponent, IActivate, IDragDropOn public class MedicalScannerComponent : SharedMedicalScannerComponent, IActivate, IDragDropOn
{ {
private ContainerSlot _bodyContainer = default!; private ContainerSlot _bodyContainer = default!;
@@ -250,12 +251,12 @@ namespace Content.Server.GameObjects.Components.Medical
public bool CanDragDropOn(DragDropEventArgs eventArgs) public bool CanDragDropOn(DragDropEventArgs eventArgs)
{ {
return eventArgs.Dropped.HasComponent<IBody>(); return eventArgs.Dragged.HasComponent<IBody>();
} }
public bool DragDropOn(DragDropEventArgs eventArgs) public bool DragDropOn(DragDropEventArgs eventArgs)
{ {
_bodyContainer.Insert(eventArgs.Dropped); _bodyContainer.Insert(eventArgs.Dragged);
return true; return true;
} }
} }

View File

@@ -67,10 +67,10 @@ namespace Content.Server.GameObjects.Components.Movement
string reason; string reason;
bool canVault; bool canVault;
if (eventArgs.User == eventArgs.Dropped) if (eventArgs.User == eventArgs.Dragged)
canVault = CanVault(eventArgs.User, eventArgs.Target, out reason); canVault = CanVault(eventArgs.User, eventArgs.Target, out reason);
else else
canVault = CanVault(eventArgs.User, eventArgs.Dropped, eventArgs.Target, out reason); canVault = CanVault(eventArgs.User, eventArgs.Dragged, eventArgs.Target, out reason);
if (!canVault) if (!canVault)
eventArgs.User.PopupMessage(reason); eventArgs.User.PopupMessage(reason);
@@ -154,13 +154,13 @@ namespace Content.Server.GameObjects.Components.Movement
bool IDragDropOn.DragDropOn(DragDropEventArgs eventArgs) bool IDragDropOn.DragDropOn(DragDropEventArgs eventArgs)
{ {
if (eventArgs.User == eventArgs.Dropped) if (eventArgs.User == eventArgs.Dragged)
{ {
TryClimb(eventArgs.User); TryClimb(eventArgs.User);
} }
else else
{ {
TryMoveEntity(eventArgs.User, eventArgs.Dropped); TryMoveEntity(eventArgs.User, eventArgs.Dragged);
} }
return true; return true;

View File

@@ -1,5 +1,4 @@
using Content.Shared.GameObjects.Components.Movement; using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Physics; using Content.Shared.Physics;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Maths; using Robust.Shared.Maths;
@@ -7,7 +6,7 @@ using Robust.Shared.Maths;
namespace Content.Server.GameObjects.Components.Movement namespace Content.Server.GameObjects.Components.Movement
{ {
[RegisterComponent] [RegisterComponent]
public class ClimbingComponent : SharedClimbingComponent, IActionBlocker public class ClimbingComponent : SharedClimbingComponent
{ {
private bool _isClimbing = false; private bool _isClimbing = false;
private ClimbController _climbController = default; private ClimbController _climbController = default;

View File

@@ -9,11 +9,27 @@ using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components namespace Content.Server.GameObjects.Components
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(SharedPlaceableSurfaceComponent))]
public class PlaceableSurfaceComponent : SharedPlaceableSurfaceComponent, IInteractUsing public class PlaceableSurfaceComponent : SharedPlaceableSurfaceComponent, IInteractUsing
{ {
private bool _isPlaceable; private bool _isPlaceable;
[ViewVariables(VVAccess.ReadWrite)] [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] [ViewVariables]
int IInteractUsing.Priority => 1; int IInteractUsing.Priority => 1;
@@ -25,6 +41,10 @@ namespace Content.Server.GameObjects.Components
serializer.DataField(ref _isPlaceable, "IsPlaceable", true); serializer.DataField(ref _isPlaceable, "IsPlaceable", true);
} }
public override ComponentState GetComponentState()
{
return new PlaceableSurfaceComponentState(_isPlaceable);
}
public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs) public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
{ {

View File

@@ -41,7 +41,7 @@ namespace Content.Server.GameObjects.Components
return false; return false;
} }
var size = eventArgs.Using.GetComponent<ItemComponent>().ObjectSize; var size = eventArgs.Using.GetComponent<ItemComponent>().Size;
// TODO: use proper text macro system for this. // TODO: use proper text macro system for this.

View File

@@ -18,6 +18,7 @@ using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Strap namespace Content.Server.GameObjects.Components.Strap
{ {
[RegisterComponent] [RegisterComponent]
[ComponentReference(typeof(SharedStrapComponent))]
public class StrapComponent : SharedStrapComponent, IInteractHand public class StrapComponent : SharedStrapComponent, IInteractHand
{ {
[ComponentDependency] public readonly SpriteComponent? SpriteComponent = null; [ComponentDependency] public readonly SpriteComponent? SpriteComponent = null;

View File

@@ -75,10 +75,10 @@ namespace Content.Server.GameObjects.EntitySystems.Click
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
foreach (var dragDrop in dropped.GetAllComponents<IDragDrop>()) foreach (var dragDrop in dropped.GetAllComponents<IDraggable>())
{ {
if (dragDrop.CanDragDrop(interactionArgs) && if (dragDrop.CanDrop(interactionArgs) &&
dragDrop.DragDrop(interactionArgs)) dragDrop.Drop(interactionArgs))
{ {
return; return;
} }

View File

@@ -1,12 +1,14 @@
using System; using System;
using Content.Shared.GameObjects.Components.Strap;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Interfaces.GameObjects.Components;
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;
namespace Content.Shared.GameObjects.Components.Buckle 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"; public sealed override string Name => "Buckle";
@@ -17,6 +19,8 @@ namespace Content.Shared.GameObjects.Components.Buckle
/// </summary> /// </summary>
public abstract bool Buckled { get; } public abstract bool Buckled { get; }
public abstract bool TryBuckle(IEntity user, IEntity to);
bool IActionBlocker.CanMove() bool IActionBlocker.CanMove()
{ {
return !Buckled; return !Buckled;
@@ -31,6 +35,16 @@ namespace Content.Shared.GameObjects.Components.Buckle
{ {
return !Buckled; return !Buckled;
} }
bool IDraggable.CanDrop(CanDropEventArgs args)
{
return args.Target.HasComponent<SharedStrapComponent>();
}
public bool Drop(DragDropEventArgs args)
{
return TryBuckle(args.User, args.Dragged);
}
} }
[Serializable, NetSerializable] [Serializable, NetSerializable]

View File

@@ -1,16 +1,36 @@
using System; using System;
using System.Collections.Generic; 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;
using Robust.Shared.GameObjects.Components.UserInterface; using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines; using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
namespace Content.Shared.GameObjects.Components.GUI namespace Content.Shared.GameObjects.Components.GUI
{ {
public class SharedStrippableComponent : Component public abstract class SharedStrippableComponent : Component, IDraggable
{ {
public override string Name => "Strippable"; public override string Name => "Strippable";
public bool CanBeStripped(IEntity by)
{
return by != Owner
&& by.HasComponent<ISharedHandsComponent>()
&& 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] [NetSerializable, Serializable]
public enum StrippingUiKey public enum StrippingUiKey
{ {

View File

@@ -5,10 +5,11 @@ 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 public abstract class SharedClimbingComponent : Component, IActionBlocker, ICollideSpecial, IDraggable
{ {
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;
@@ -45,6 +46,16 @@ 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,9 +1,25 @@
using System;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components namespace Content.Shared.GameObjects.Components
{ {
public abstract class SharedPlaceableSurfaceComponent : Component public abstract class SharedPlaceableSurfaceComponent : Component
{ {
public override string Name => "PlaceableSurface"; 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;
}
} }
} }

View File

@@ -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;
}
}
}

View File

@@ -1,14 +1,71 @@
using System; #nullable enable
using System;
using System.Collections.Generic; 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.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Storage namespace Content.Shared.GameObjects.Components.Storage
{ {
public abstract class SharedStorageComponent : Component public abstract class SharedStorageComponent : Component, IDraggable
{ {
public override string Name => "Storage"; public override string Name => "Storage";
public override uint? NetID => ContentNetIDs.INVENTORY; public override uint? NetID => ContentNetIDs.INVENTORY;
public abstract IReadOnlyList<IEntity>? StoredEntities { get; }
/// <summary>
/// Removes from the storage container and updates the stored value
/// </summary>
/// <param name="entity">The entity to remove</param>
/// <returns>True if no longer in storage, false otherwise</returns>
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;
}
} }
/// <summary> /// <summary>
@@ -19,14 +76,14 @@ namespace Content.Shared.GameObjects.Components.Storage
{ {
public readonly int StorageSizeMax; public readonly int StorageSizeMax;
public readonly int StorageSizeUsed; public readonly int StorageSizeUsed;
public Dictionary<EntityUid, int> StoredEntities; public readonly EntityUid[] StoredEntities;
public StorageHeldItemsMessage(Dictionary<EntityUid, int> storedentities, int storageused, int storagemaxsize) public StorageHeldItemsMessage(EntityUid[] storedEntities, int storageUsed, int storageMaxSize)
{ {
Directed = true; Directed = true;
StorageSizeMax = storagemaxsize; StorageSizeMax = storageMaxSize;
StorageSizeUsed = storageused; StorageSizeUsed = storageUsed;
StoredEntities = storedentities; StoredEntities = storedEntities;
} }
} }

View File

@@ -79,6 +79,8 @@
public const uint BLOCKGAME_ARCADE = 1073; public const uint BLOCKGAME_ARCADE = 1073;
public const uint BODY_PART = 1074; public const uint BODY_PART = 1074;
public const uint CRAYONS = 1075; public const uint CRAYONS = 1075;
public const uint PLACEABLE_SURFACE = 1076;
public const uint STORABLE = 1077;
// Net IDs for integration tests. // Net IDs for integration tests.
public const uint PREDICTION_TEST = 10001; public const uint PREDICTION_TEST = 10001;

View File

@@ -445,7 +445,7 @@ namespace Content.Shared.GameObjects.EntitySystems
bool popup = false) bool popup = false)
{ {
var user = args.User; var user = args.User;
var dropped = args.Dropped; var dropped = args.Dragged;
var target = args.Target; var target = args.Target;
if (!InRangeUnobstructed(user, target, range, collisionMask, predicate, ignoreInsideBlocker)) if (!InRangeUnobstructed(user, target, range, collisionMask, predicate, ignoreInsideBlocker))

View File

@@ -1,73 +0,0 @@
using System;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Map;
namespace Content.Shared.Interfaces.GameObjects.Components
{
/// <summary>
/// This interface allows the component's entity to be dragged and dropped
/// by mouse onto another entity and gives it behavior when that occurs.
/// </summary>
public interface IDragDrop
{
/// <summary>
/// Invoked server-side when this component's entity is being dragged
/// and dropped on another before invoking <see cref="DragDrop"/>.
/// Note that other drag and drop interactions may be attempted if
/// this one fails.
/// </summary>
/// <param name="eventArgs"></param>
/// <returns>true if <see cref="eventArgs"/> is valid, false otherwise.</returns>
bool CanDragDrop(DragDropEventArgs eventArgs);
/// <summary>
/// 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.
/// </summary>
/// <returns>
/// true if an interaction occurred and no further interaction should
/// be processed for this drop.
/// </returns>
bool DragDrop(DragDropEventArgs eventArgs);
}
public class DragDropEventArgs : EventArgs
{
/// <summary>
/// Creates a new instance of <see cref="DragDropEventArgs"/>.
/// </summary>
/// <param name="user">The entity doing the drag and drop.</param>
/// <param name="dropLocation">The location where <see cref="dropped"/> is being dropped.</param>
/// <param name="dropped">The entity that is being dragged and dropped.</param>
/// <param name="target">The entity that <see cref="dropped"/> is being dropped onto.</param>
public DragDropEventArgs(IEntity user, EntityCoordinates dropLocation, IEntity dropped, IEntity target)
{
User = user;
DropLocation = dropLocation;
Dropped = dropped;
Target = target;
}
/// <summary>
/// The entity doing the drag and drop.
/// </summary>
public IEntity User { get; }
/// <summary>
/// The location where <see cref="Dropped"/> is being dropped.
/// </summary>
public EntityCoordinates DropLocation { get; }
/// <summary>
/// The entity that is being dragged and dropped.
/// </summary>
public IEntity Dropped { get; }
/// <summary>
/// The entity that <see cref="Dropped"/> is being dropped onto.
/// </summary>
public IEntity Target { get; }
}
}

View File

@@ -0,0 +1,121 @@
using System;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Map;
namespace Content.Shared.Interfaces.GameObjects.Components
{
/// <summary>
/// This interface allows a local client to initiate dragging of the component's
/// entity by mouse, for drag and drop interactions.
/// </summary>
public interface IDraggable
{
/// <summary>
/// Invoked when an user is attempting to initiate a drag with
/// this component's entity in range. It's fine to return true even if there
/// wouldn't be any valid targets - just return true if this entity is in a
/// "draggable" state.
/// </summary>
/// <param name="args">
/// The information about the drag, such as who is doing it.
/// </param>
/// <returns>True if the drag should be initiated, false otherwise.</returns>
bool CanStartDrag(StartDragDropEventArgs args)
{
return true;
}
/// <summary>
/// Invoked on entities visible to the user to check if this component's
/// entity can be dropped on the indicated target entity.
/// No need to check range / reachability in here.
/// Returning true will cause the target entity to be highlighted as
/// a potential target and allow dropping when in range.
/// </summary>
/// <returns>
/// True if target is a valid target to be dropped on by this component's
/// entity, false otherwise.
/// </returns>
bool CanDrop(CanDropEventArgs args);
/// <summary>
/// Invoked when this component's entity is being dropped on another.
/// Other drag and drop interactions may be attempted if this one fails.
/// </summary>
/// <param name="args">
/// The information about the drag, such as who is doing it.
/// </param>
/// <returns>
/// True if an interaction occurred and no further interaction should
/// be processed for this drop, false otherwise.
/// </returns>
bool Drop(DragDropEventArgs args)
{
return false;
}
}
public class StartDragDropEventArgs : EventArgs
{
/// <summary>
/// Creates a new instance of <see cref="StartDragDropEventArgs"/>.
/// </summary>
/// <param name="user">The entity doing the drag and drop.</param>
/// <param name="dragged">The entity that is being dragged and dropped.</param>
public StartDragDropEventArgs(IEntity user, IEntity dragged)
{
User = user;
Dragged = dragged;
}
/// <summary>
/// The entity doing the drag and drop.
/// </summary>
public IEntity User { get; }
/// <summary>
/// The entity that is being dragged.
/// </summary>
public IEntity Dragged { get; }
}
public class CanDropEventArgs : StartDragDropEventArgs
{
/// <summary>
/// Creates a new instance of <see cref="CanDropEventArgs"/>.
/// </summary>
/// <param name="user">The entity doing the drag and drop.</param>
/// <param name="dragged">The entity that is being dragged and dropped.</param>
/// <param name="target">The entity that <see cref="dropped"/> is being dropped onto.</param>
public CanDropEventArgs(IEntity user, IEntity dragged, IEntity target) : base(user, dragged)
{
Target = target;
}
/// <summary>
/// The entity that <see cref="StartDragDropEventArgs.Dragged"/>
/// is being dropped onto.
/// </summary>
public IEntity Target { get; }
}
public class DragDropEventArgs : CanDropEventArgs
{
/// <summary>
/// Creates a new instance of <see cref="DragDropEventArgs"/>.
/// </summary>
/// <param name="user">The entity doing the drag and drop.</param>
/// <param name="dropLocation">The location where <see cref="dropped"/> is being dropped.</param>
/// <param name="dragged">The entity that is being dragged and dropped.</param>
/// <param name="target">The entity that <see cref="dropped"/> is being dropped onto.</param>
public DragDropEventArgs(IEntity user, EntityCoordinates dropLocation, IEntity dragged, IEntity target) : base(user, dragged, target)
{
DropLocation = dropLocation;
}
/// <summary>
/// The location where <see cref="StartDragDropEventArgs.Dragged"/>
/// is being dropped.
/// </summary>
public EntityCoordinates DropLocation { get; }
}
}