166 lines
6.5 KiB
C#
166 lines
6.5 KiB
C#
using Content.Shared._NF.Interaction.Components;
|
|
using Content.Shared.Hands.Components;
|
|
using Content.Shared.Hands.EntitySystems;
|
|
using Content.Shared.Interaction;
|
|
using Content.Shared.Interaction.Events;
|
|
using Content.Shared.Item;
|
|
using Content.Shared.Prototypes;
|
|
using Content.Shared.Whitelist;
|
|
using Robust.Shared.Containers;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Content.Shared._NF.Interaction.Systems;
|
|
|
|
/// <summary>
|
|
/// Handles interactions with items that swap with HandPlaceholder items.
|
|
/// </summary>
|
|
public sealed partial class HandPlaceholderSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly SharedContainerSystem _container = default!;
|
|
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
|
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
|
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
|
[Dependency] private readonly MetaDataSystem _metadata = default!;
|
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
|
|
|
public readonly EntProtoId<HandPlaceholderComponent> Placeholder = "HandPlaceholder";
|
|
|
|
public override void Initialize()
|
|
{
|
|
SubscribeLocalEvent<HandPlaceholderRemoveableComponent, EntGotRemovedFromContainerMessage>(OnEntityRemovedFromContainer);
|
|
|
|
SubscribeLocalEvent<HandPlaceholderComponent, BeforeRangedInteractEvent>(BeforeRangedInteract);
|
|
SubscribeLocalEvent<HandPlaceholderComponent, ContainerGettingRemovedAttemptEvent>(OnRemoveAttempt);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Spawns a new placeholder and ties it to an item.
|
|
/// When dropped the item will replace itself with the placeholder in its container.
|
|
/// </summary>
|
|
public EntityUid SpawnPlaceholder(BaseContainer container, EntityUid item, EntProtoId id, EntityWhitelist whitelist, EntityWhitelist? blacklist)
|
|
{
|
|
var placeholder = Spawn(Placeholder);
|
|
var proto = _proto.Index(id);
|
|
var comp = Comp<HandPlaceholderComponent>(placeholder);
|
|
comp.Prototype = id;
|
|
comp.Whitelist = whitelist;
|
|
comp.Blacklist = blacklist;
|
|
comp.Source = container.Owner;
|
|
comp.ContainerId = container.ID;
|
|
comp.AllowNonItems = !proto.HasComponent<ItemComponent>();
|
|
Dirty(placeholder, comp);
|
|
|
|
var name = proto.Name;
|
|
_metadata.SetEntityName(placeholder, name);
|
|
SetPlaceholder(item, placeholder);
|
|
|
|
var succeeded = _container.Insert(placeholder, container, force: true);
|
|
DebugTools.Assert(succeeded, $"Failed to insert placeholder {ToPrettyString(placeholder)} into {ToPrettyString(comp.Source)}");
|
|
return placeholder;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the placeholder entity for an item.
|
|
/// </summary>
|
|
public void SetPlaceholder(EntityUid item, EntityUid placeholder)
|
|
{
|
|
if (!item.Valid)
|
|
return;
|
|
|
|
var comp = EnsureComp<HandPlaceholderRemoveableComponent>(item);
|
|
comp.Placeholder = placeholder;
|
|
Dirty(item, comp);
|
|
}
|
|
|
|
public void SetEnabled(EntityUid item, bool enabled)
|
|
{
|
|
if (TryComp<HandPlaceholderRemoveableComponent>(item, out var comp))
|
|
{
|
|
comp.Enabled = enabled;
|
|
Dirty(item, comp);
|
|
}
|
|
else if (TryComp<HandPlaceholderComponent>(item, out var placeholder))
|
|
{
|
|
placeholder.Enabled = enabled;
|
|
Dirty(item, placeholder);
|
|
}
|
|
}
|
|
|
|
private void SwapPlaceholder(Entity<HandPlaceholderRemoveableComponent> ent, BaseContainer container)
|
|
{
|
|
// trying to insert when deleted is an error, and only handle when it is being actually dropped
|
|
var owner = container.Owner;
|
|
if (!ent.Comp.Enabled || TerminatingOrDeleted(owner) || Transform(owner).MapID == MapId.Nullspace)
|
|
return;
|
|
|
|
var placeholder = ent.Comp.Placeholder;
|
|
|
|
ent.Comp.Enabled = false;
|
|
RemCompDeferred<HandPlaceholderRemoveableComponent>(ent);
|
|
|
|
// stop tests failing
|
|
if (TerminatingOrDeleted(placeholder))
|
|
return;
|
|
|
|
SetEnabled(placeholder, false);
|
|
var succeeded = _container.Insert(placeholder, container, force: true);
|
|
DebugTools.Assert(succeeded, $"Failed to insert placeholder {ToPrettyString(placeholder)} of {ToPrettyString(ent)} into container of {ToPrettyString(owner)}");
|
|
SetEnabled(placeholder, true); // prevent dropping it now that it's in hand
|
|
}
|
|
|
|
private void OnEntityRemovedFromContainer(Entity<HandPlaceholderRemoveableComponent> ent, ref EntGotRemovedFromContainerMessage args)
|
|
{
|
|
SwapPlaceholder(ent, args.Container);
|
|
}
|
|
|
|
private void BeforeRangedInteract(Entity<HandPlaceholderComponent> ent, ref BeforeRangedInteractEvent args)
|
|
{
|
|
if (args.Handled || !args.CanReach || args.Target is not { } target)
|
|
return;
|
|
|
|
args.Handled = true;
|
|
TryToPickUpTarget(ent, target, args.User);
|
|
}
|
|
|
|
private void OnRemoveAttempt(Entity<HandPlaceholderComponent> ent, ref ContainerGettingRemovedAttemptEvent args)
|
|
{
|
|
if (ent.Comp.Enabled)
|
|
args.Cancel();
|
|
}
|
|
|
|
private void TryToPickUpTarget(Entity<HandPlaceholderComponent> ent, EntityUid target, EntityUid user)
|
|
{
|
|
// require items regardless of the whitelist
|
|
if (!ent.Comp.AllowNonItems && !HasComp<ItemComponent>(target) || _whitelist.IsWhitelistFail(ent.Comp.Whitelist, target) || _whitelist.IsBlacklistPass(ent.Comp.Blacklist, target))
|
|
return;
|
|
|
|
if (!TryComp<HandsComponent>(user, out var hands))
|
|
return;
|
|
|
|
// Can't get the hand we're holding this with? Something's wrong, abort. No empty hands.
|
|
if (!_hands.IsHolding(user, ent, out var hand, hands))
|
|
return;
|
|
|
|
SetEnabled(ent, false); // allow inserting into the source container
|
|
|
|
if (ent.Comp.Source is { } source)
|
|
{
|
|
var container = _container.GetContainer(source, ent.Comp.ContainerId);
|
|
var succeeded = _container.Insert(ent.Owner, container, force: true);
|
|
DebugTools.Assert(succeeded, $"Failed to insert {ToPrettyString(ent)} into {container.ID} of {ToPrettyString(source)}");
|
|
}
|
|
else
|
|
{
|
|
Log.Error($"Placeholder {ToPrettyString(ent)} had no source set");
|
|
}
|
|
|
|
_hands.DoPickup(user, hand, target, hands); // Force pickup - empty hands are not okay
|
|
_interaction.DoContactInteraction(user, target); // allow for forensics and other systems to work (why does hands system not do this???)
|
|
|
|
SetPlaceholder(target, ent);
|
|
SetEnabled(target, true);
|
|
}
|
|
}
|