Dumpable component to use a doafter to empty storage into a disposal unit, placeable surface, or the ground (#7792)
This commit is contained in:
@@ -8,7 +8,7 @@ namespace Content.Client.Storage
|
||||
/// Client version of item storage containers, contains a UI which displays stored entities and their size
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class ClientStorageComponent : SharedStorageComponent, IDraggable
|
||||
public sealed class ClientStorageComponent : SharedStorageComponent
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
private List<EntityUid> _storedEntities = new();
|
||||
|
||||
@@ -162,6 +162,17 @@ namespace Content.Server.Disposal.Unit.EntitySystems
|
||||
AfterInsert(unit, toInsert);
|
||||
}
|
||||
|
||||
public void DoInsertDisposalUnit(EntityUid unit, EntityUid toInsert, DisposalUnitComponent? disposal = null)
|
||||
{
|
||||
if (!Resolve(unit, ref disposal))
|
||||
return;
|
||||
|
||||
if (!disposal.Container.Insert(toInsert))
|
||||
return;
|
||||
|
||||
AfterInsert(disposal, toInsert);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
193
Content.Server/Storage/EntitySystems/DumpableSystem.cs
Normal file
193
Content.Server/Storage/EntitySystems/DumpableSystem.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
using System.Threading;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Server.Storage.Components;
|
||||
using Content.Shared.Storage.Components;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Server.Disposal.Unit.Components;
|
||||
using Content.Server.Disposal.Unit.EntitySystems;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Shared.Placeable;
|
||||
using Content.Server.Hands.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Server.Storage.EntitySystems
|
||||
{
|
||||
public sealed class DumpableSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly DisposalUnitSystem _disposalUnitSystem = default!;
|
||||
|
||||
[Dependency] private readonly HandsSystem _handsSystem = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<DumpableComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
SubscribeLocalEvent<DumpableComponent, GetVerbsEvent<AlternativeVerb>>(AddDumpVerb);
|
||||
SubscribeLocalEvent<DumpableComponent, GetVerbsEvent<UtilityVerb>>(AddUtilityVerbs);
|
||||
SubscribeLocalEvent<DumpCompletedEvent>(OnDumpCompleted);
|
||||
SubscribeLocalEvent<DumpCancelledEvent>(OnDumpCancelled);
|
||||
}
|
||||
|
||||
private void OnAfterInteract(EntityUid uid, DumpableComponent component, AfterInteractEvent args)
|
||||
{
|
||||
if (!args.CanReach)
|
||||
return;
|
||||
|
||||
if (!TryComp<ServerStorageComponent>(args.Used, out var storage))
|
||||
return;
|
||||
|
||||
if (storage.StoredEntities == null || storage.StoredEntities.Count == 0)
|
||||
return;
|
||||
|
||||
if (HasComp<DisposalUnitComponent>(args.Target) || HasComp<PlaceableSurfaceComponent>(args.Target))
|
||||
{
|
||||
StartDoAfter(uid, args.Target.Value, args.User, component, storage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
private void AddDumpVerb(EntityUid uid, DumpableComponent dumpable, GetVerbsEvent<AlternativeVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
if (!TryComp<ServerStorageComponent>(uid, out var storage) || storage.StoredEntities == null || storage.StoredEntities.Count == 0)
|
||||
return;
|
||||
|
||||
AlternativeVerb verb = new()
|
||||
{
|
||||
Act = () =>
|
||||
{
|
||||
StartDoAfter(uid, null, args.User, dumpable, storage, 0.6f);
|
||||
},
|
||||
Text = Loc.GetString("dump-verb-name"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/drop.svg.192dpi.png",
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
private void AddUtilityVerbs(EntityUid uid, DumpableComponent dumpable, GetVerbsEvent<UtilityVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
if (!TryComp<ServerStorageComponent>(uid, out var storage) || storage.StoredEntities == null || storage.StoredEntities.Count == 0)
|
||||
return;
|
||||
|
||||
if (HasComp<DisposalUnitComponent>(args.Target))
|
||||
{
|
||||
UtilityVerb verb = new()
|
||||
{
|
||||
Act = () =>
|
||||
{
|
||||
StartDoAfter(uid, args.Target, args.User, dumpable, storage);
|
||||
},
|
||||
Text = Loc.GetString("dump-disposal-verb-name", ("unit", args.Target)),
|
||||
IconEntity = uid
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
if (HasComp<PlaceableSurfaceComponent>(args.Target))
|
||||
{
|
||||
UtilityVerb verb = new()
|
||||
{
|
||||
Act = () =>
|
||||
{
|
||||
StartDoAfter(uid, args.Target, args.User, dumpable, storage);
|
||||
},
|
||||
Text = Loc.GetString("dump-placeable-verb-name", ("surface", args.Target)),
|
||||
IconEntity = uid
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
}
|
||||
|
||||
private void StartDoAfter(EntityUid storageUid, EntityUid? targetUid, EntityUid userUid, DumpableComponent dumpable, ServerStorageComponent storage, float multiplier = 1)
|
||||
{
|
||||
if (dumpable.CancelToken != null)
|
||||
{
|
||||
dumpable.CancelToken.Cancel();
|
||||
dumpable.CancelToken = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (storage.StoredEntities == null)
|
||||
return;
|
||||
|
||||
float delay = storage.StoredEntities.Count * (float) dumpable.DelayPerItem.TotalSeconds * multiplier;
|
||||
|
||||
dumpable.CancelToken = new CancellationTokenSource();
|
||||
_doAfterSystem.DoAfter(new DoAfterEventArgs(userUid, delay, dumpable.CancelToken.Token, target: targetUid)
|
||||
{
|
||||
BroadcastFinishedEvent = new DumpCompletedEvent(userUid, targetUid, storage.StoredEntities),
|
||||
BroadcastCancelledEvent = new DumpCancelledEvent(dumpable.Owner),
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnStun = true,
|
||||
NeedHand = true
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void OnDumpCompleted(DumpCompletedEvent args)
|
||||
{
|
||||
Queue<EntityUid> dumpQueue = new();
|
||||
foreach (var entity in args.StoredEntities)
|
||||
{
|
||||
dumpQueue.Enqueue(entity);
|
||||
}
|
||||
|
||||
if (TryComp<DisposalUnitComponent>(args.Target, out var disposal))
|
||||
{
|
||||
foreach (var entity in dumpQueue)
|
||||
{
|
||||
_disposalUnitSystem.DoInsertDisposalUnit(args.Target.Value, entity);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var entity in dumpQueue)
|
||||
{
|
||||
Transform(entity).AttachParentToContainerOrGrid(EntityManager);
|
||||
}
|
||||
|
||||
if (HasComp<PlaceableSurfaceComponent>(args.Target))
|
||||
{
|
||||
foreach (var entity in dumpQueue)
|
||||
{
|
||||
Transform(entity).LocalPosition = Transform(args.Target.Value).LocalPosition;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDumpCancelled(DumpCancelledEvent args)
|
||||
{
|
||||
if (TryComp<DumpableComponent>(args.Uid, out var dumpable))
|
||||
dumpable.CancelToken = null;
|
||||
}
|
||||
|
||||
private sealed class DumpCancelledEvent : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Uid;
|
||||
public DumpCancelledEvent(EntityUid uid)
|
||||
{
|
||||
Uid = uid;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class DumpCompletedEvent : EntityEventArgs
|
||||
{
|
||||
public EntityUid User { get; }
|
||||
public EntityUid? Target { get; }
|
||||
public IReadOnlyList<EntityUid> StoredEntities { get; }
|
||||
|
||||
public DumpCompletedEvent(EntityUid user, EntityUid? target, IReadOnlyList<EntityUid> storedEntities)
|
||||
{
|
||||
User = user;
|
||||
Target = target;
|
||||
StoredEntities = storedEntities;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,19 +175,6 @@ namespace Content.Server.Storage.EntitySystems
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
// if the target is a disposal unit, add a verb to transfer storage into the unit (e.g., empty a trash bag).
|
||||
if (!TryComp(args.Target, out DisposalUnitComponent? disposal))
|
||||
return;
|
||||
|
||||
UtilityVerb dispose = new()
|
||||
{
|
||||
Text = Loc.GetString("storage-component-dispose-verb"),
|
||||
IconEntity = args.Using,
|
||||
Act = () => DisposeEntities(args.User, uid, args.Target, component, lockComponent, disposal)
|
||||
};
|
||||
|
||||
args.Verbs.Add(dispose);
|
||||
}
|
||||
|
||||
|
||||
@@ -446,35 +433,6 @@ namespace Content.Server.Storage.EntitySystems
|
||||
UpdateStorageUI(source, sourceComp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move entities from storage into a disposal unit.
|
||||
/// </summary>
|
||||
public void DisposeEntities(EntityUid user, EntityUid source, EntityUid target,
|
||||
ServerStorageComponent? sourceComp = null, LockComponent? sourceLock = null,
|
||||
DisposalUnitComponent? disposalComp = null)
|
||||
{
|
||||
if (!Resolve(source, ref sourceComp) || !Resolve(target, ref disposalComp))
|
||||
return;
|
||||
|
||||
var entities = sourceComp.Storage?.ContainedEntities;
|
||||
if (entities == null || entities.Count == 0)
|
||||
return;
|
||||
|
||||
if (Resolve(source, ref sourceLock, false) && sourceLock.Locked)
|
||||
return;
|
||||
|
||||
foreach (var entity in entities.ToList())
|
||||
{
|
||||
if (_disposalSystem.CanInsert(disposalComp, entity)
|
||||
&& disposalComp.Container.Insert(entity))
|
||||
{
|
||||
_disposalSystem.AfterInsert(disposalComp, entity);
|
||||
}
|
||||
}
|
||||
RecalculateStorageUsed(sourceComp);
|
||||
UpdateStorageUI(source, sourceComp);
|
||||
}
|
||||
|
||||
public void HandleRemoveEntity(EntityUid uid, EntityUid player, EntityUid itemToRemove, ServerStorageComponent? storageComp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref storageComp))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Storage.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Robust.Shared.GameStates;
|
||||
@@ -52,6 +52,11 @@ namespace Content.Shared.Placeable
|
||||
if (!surface.IsPlaceable)
|
||||
return;
|
||||
|
||||
// 99% of the time they want to dump the stuff inside on the table, they can manually place with q if they really need to.
|
||||
// Just causes prediction CBT otherwise.
|
||||
if (HasComp<DumpableComponent>(args.Used))
|
||||
return;
|
||||
|
||||
if (!_handsSystem.TryDrop(args.User, args.Used))
|
||||
return;
|
||||
|
||||
|
||||
23
Content.Shared/Storage/Components/DumpableComponent.cs
Normal file
23
Content.Shared/Storage/Components/DumpableComponent.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Threading;
|
||||
|
||||
namespace Content.Shared.Storage.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Lets you dump this container on the ground using a verb,
|
||||
/// or when interacting with it on a disposal unit or placeable surface.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class DumpableComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// How long each item adds to the doafter.
|
||||
/// </summary>
|
||||
[DataField("delayPerItem")]
|
||||
public TimeSpan DelayPerItem = TimeSpan.FromSeconds(0.2);
|
||||
|
||||
/// <summary>
|
||||
/// Cancellation token for the doafter.
|
||||
/// <summary>
|
||||
public CancellationTokenSource? CancelToken;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ using Robust.Shared.Serialization;
|
||||
namespace Content.Shared.Storage
|
||||
{
|
||||
[NetworkedComponent()]
|
||||
public abstract class SharedStorageComponent : Component, IDraggable
|
||||
public abstract class SharedStorageComponent : Component
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class StorageBoundUserInterfaceState : BoundUserInterfaceState
|
||||
@@ -56,32 +56,6 @@ namespace Content.Shared.Storage
|
||||
/// <param name="entity">The entity to remove</param>
|
||||
/// <returns>True if no longer in storage, false otherwise</returns>
|
||||
public abstract bool Remove(EntityUid entity);
|
||||
|
||||
bool IDraggable.CanDrop(CanDropEvent args)
|
||||
{
|
||||
return _entMan.TryGetComponent(args.Target, out PlaceableSurfaceComponent? placeable) &&
|
||||
placeable.IsPlaceable;
|
||||
}
|
||||
|
||||
bool IDraggable.Drop(DragDropEvent eventArgs)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(eventArgs.User, eventArgs.Target))
|
||||
return false;
|
||||
|
||||
var storedEntities = StoredEntities?.ToArray();
|
||||
|
||||
if (storedEntities == null)
|
||||
return false;
|
||||
|
||||
// empty everything out
|
||||
foreach (var storedEntity in storedEntities)
|
||||
{
|
||||
if (Remove(storedEntity))
|
||||
_entMan.GetComponent<TransformComponent>(storedEntity).WorldPosition = eventArgs.DropLocation.Position;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
dump-verb-name = Dump out on ground
|
||||
dump-disposal-verb-name = Dump out into {$unit}
|
||||
dump-placeable-verb-name = Dump out onto {$surface}
|
||||
@@ -1,2 +1 @@
|
||||
storage-component-transfer-verb = Transfer contents
|
||||
storage-component-dispose-verb = Dispose of contents
|
||||
@@ -130,3 +130,4 @@
|
||||
components:
|
||||
- Produce
|
||||
- Seed
|
||||
- type: Dumpable
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
- type: StorageFillVisualizer
|
||||
maxFillLevels: 4
|
||||
fillBaseName: icon
|
||||
- type: Dumpable
|
||||
- type: Clothing
|
||||
Slots: [belt]
|
||||
sprite: Objects/Specific/Janitorial/trashbag.rsi
|
||||
|
||||
Reference in New Issue
Block a user