rewrite borg hand code (#3126)
* fully rework borg hands update modules * not real * fix plant bag --------- Co-authored-by: deltanedas <@deltanedas:kde.org>
This commit is contained in:
parent
ff692ed87e
commit
0201167941
|
|
@ -4,7 +4,7 @@ using Content.Shared.Interaction.Components;
|
|||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Containers;
|
||||
using Content.Shared._NF.Interaction.Components; // Frontier
|
||||
using Content.Shared._NF.Silicons.Borgs; // Frontier
|
||||
|
||||
namespace Content.Server.Silicons.Borgs;
|
||||
|
||||
|
|
@ -198,7 +198,6 @@ public sealed partial class BorgSystem
|
|||
if (!component.ItemsCreated)
|
||||
{
|
||||
item = Spawn(itemProto, xform.Coordinates);
|
||||
_interaction.DoContactInteraction(chassis, item); // DeltaV - give items fibers before they might be dropped
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -227,63 +226,6 @@ public sealed partial class BorgSystem
|
|||
component.ProvidedItems.Add(handId, item);
|
||||
}
|
||||
|
||||
// Frontier: droppable cyborg items
|
||||
foreach (var itemProto in component.DroppableItems)
|
||||
{
|
||||
EntityUid item;
|
||||
|
||||
if (!component.ItemsCreated)
|
||||
{
|
||||
item = Spawn(itemProto.ID, xform.Coordinates);
|
||||
var placeComp = EnsureComp<HandPlaceholderRemoveableComponent>(item);
|
||||
placeComp.Whitelist = itemProto.Whitelist;
|
||||
placeComp.Prototype = itemProto.ID;
|
||||
Dirty(item, placeComp);
|
||||
}
|
||||
else
|
||||
{
|
||||
item = component.ProvidedContainer.ContainedEntities
|
||||
.FirstOrDefault(ent => _whitelistSystem.IsWhitelistPassOrNull(itemProto.Whitelist, ent) || TryComp<HandPlaceholderComponent>(ent, out var placeholder));
|
||||
if (!item.IsValid())
|
||||
{
|
||||
Log.Debug($"no items found: {component.ProvidedContainer.ContainedEntities.Count}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Just in case, make sure the borg can't drop the placeholder.
|
||||
if (!HasComp<HandPlaceholderComponent>(item))
|
||||
{
|
||||
var placeComp = EnsureComp<HandPlaceholderRemoveableComponent>(item);
|
||||
placeComp.Whitelist = itemProto.Whitelist;
|
||||
placeComp.Prototype = itemProto.ID;
|
||||
Dirty(item, placeComp);
|
||||
}
|
||||
}
|
||||
|
||||
if (!item.IsValid())
|
||||
{
|
||||
Log.Debug("no valid item");
|
||||
continue;
|
||||
}
|
||||
|
||||
var handId = $"{uid}-item{component.HandCounter}";
|
||||
component.HandCounter++;
|
||||
_hands.AddHand(chassis, handId, HandLocation.Middle, hands);
|
||||
_hands.DoPickup(chassis, hands.Hands[handId], item, hands);
|
||||
if (hands.Hands[handId].HeldEntity != item)
|
||||
{
|
||||
// If we didn't pick up our expected item, delete the hand. No free hands!
|
||||
_hands.RemoveHand(chassis, handId);
|
||||
}
|
||||
else if (HasComp<HandPlaceholderComponent>(item))
|
||||
{
|
||||
// Placeholders can't be put down, must be changed after picked up (otherwise it'll fail to pick up)
|
||||
EnsureComp<UnremoveableComponent>(item);
|
||||
}
|
||||
component.DroppableProvidedItems.Add(handId, (item, itemProto));
|
||||
}
|
||||
// End Frontier: droppable cyborg items
|
||||
|
||||
component.ItemsCreated = true;
|
||||
}
|
||||
|
||||
|
|
@ -303,14 +245,6 @@ public sealed partial class BorgSystem
|
|||
_hands.RemoveHand(chassis, hand, hands);
|
||||
}
|
||||
component.ProvidedItems.Clear();
|
||||
// Frontier: droppable items
|
||||
foreach (var (hand, item) in component.DroppableProvidedItems)
|
||||
{
|
||||
QueueDel(item.Item1);
|
||||
_hands.RemoveHand(chassis, hand, hands);
|
||||
}
|
||||
component.DroppableProvidedItems.Clear();
|
||||
// End Frontier: droppable items
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -324,20 +258,6 @@ public sealed partial class BorgSystem
|
|||
_hands.RemoveHand(chassis, handId, hands);
|
||||
}
|
||||
component.ProvidedItems.Clear();
|
||||
// Frontier: remove all items from borg hands directly, not from the provided items set
|
||||
foreach (var (handId, _) in component.DroppableProvidedItems)
|
||||
{
|
||||
_hands.TryGetHand(chassis, handId, out var hand, hands);
|
||||
if (hand?.HeldEntity != null)
|
||||
{
|
||||
RemComp<UnremoveableComponent>(hand.HeldEntity.Value);
|
||||
_container.Insert(hand.HeldEntity.Value, component.ProvidedContainer);
|
||||
}
|
||||
|
||||
_hands.RemoveHand(chassis, handId, hands);
|
||||
}
|
||||
component.DroppableProvidedItems.Clear();
|
||||
// End Frontier
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -362,18 +282,22 @@ public sealed partial class BorgSystem
|
|||
return false;
|
||||
}
|
||||
|
||||
// Frontier - event for DroppableBorgModule to use
|
||||
var ev = new BorgCanInsertModuleEvent((uid, component), user);
|
||||
RaiseLocalEvent(module, ref ev);
|
||||
if (ev.Cancelled)
|
||||
return false;
|
||||
// End Frontier
|
||||
|
||||
if (TryComp<ItemBorgModuleComponent>(module, out var itemModuleComp))
|
||||
{
|
||||
var droppableComparer = new DroppableBorgItemComparer(); // Frontier: cached comparer
|
||||
foreach (var containedModuleUid in component.ModuleContainer.ContainedEntities)
|
||||
{
|
||||
if (!TryComp<ItemBorgModuleComponent>(containedModuleUid, out var containedItemModuleComp))
|
||||
continue;
|
||||
|
||||
if (containedItemModuleComp.Items.Count == itemModuleComp.Items.Count &&
|
||||
containedItemModuleComp.DroppableItems.Count == itemModuleComp.DroppableItems.Count && // Frontier
|
||||
containedItemModuleComp.Items.All(itemModuleComp.Items.Contains) &&
|
||||
containedItemModuleComp.DroppableItems.All(x => itemModuleComp.DroppableItems.Contains(x, droppableComparer))) // Frontier
|
||||
containedItemModuleComp.Items.All(itemModuleComp.Items.Contains))
|
||||
{
|
||||
if (user != null)
|
||||
Popup.PopupEntity(Loc.GetString("borg-module-duplicate"), uid, user.Value);
|
||||
|
|
@ -385,30 +309,6 @@ public sealed partial class BorgSystem
|
|||
return true;
|
||||
}
|
||||
|
||||
// Frontier: droppable borg item comparator
|
||||
private sealed class DroppableBorgItemComparer : IEqualityComparer<DroppableBorgItem>
|
||||
{
|
||||
public bool Equals(DroppableBorgItem? x, DroppableBorgItem? y)
|
||||
{
|
||||
// Same object (or both null)
|
||||
if (ReferenceEquals(x, y))
|
||||
return true;
|
||||
// One-side null
|
||||
if (x == null || y == null)
|
||||
return false;
|
||||
// Otherwise, use EntProtoId of item
|
||||
return x.ID == y.ID;
|
||||
}
|
||||
|
||||
public int GetHashCode(DroppableBorgItem obj)
|
||||
{
|
||||
if (obj is null)
|
||||
return 0;
|
||||
return obj.ID.GetHashCode();
|
||||
}
|
||||
}
|
||||
// End Frontier
|
||||
|
||||
/// <summary>
|
||||
/// Check if a module can be removed from a borg.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
|
|
@ -15,27 +14,15 @@ public sealed partial class ItemBorgModuleComponent : Component
|
|||
/// <summary>
|
||||
/// The items that are provided.
|
||||
/// </summary>
|
||||
[DataField("items", customTypeSerializer: typeof(PrototypeIdListSerializer<EntityPrototype>))] // Frontier: removed
|
||||
[DataField("items", customTypeSerializer: typeof(PrototypeIdListSerializer<EntityPrototype>), required: true)]
|
||||
public List<string> Items = new();
|
||||
|
||||
/// <summary>
|
||||
/// Frontier: The droppable items that are provided.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<DroppableBorgItem> DroppableItems = new();
|
||||
|
||||
/// <summary>
|
||||
/// The entities from <see cref="Items"/> that were spawned.
|
||||
/// </summary>
|
||||
[DataField("providedItems")]
|
||||
public SortedDictionary<string, EntityUid> ProvidedItems = new();
|
||||
|
||||
/// <summary>
|
||||
/// The entities from <see cref="Items"/> that were spawned.
|
||||
/// </summary>
|
||||
[DataField("droppableProvidedItems")]
|
||||
public SortedDictionary<string, (EntityUid, DroppableBorgItem)> DroppableProvidedItems = new();
|
||||
|
||||
/// <summary>
|
||||
/// A counter that ensures a unique
|
||||
/// </summary>
|
||||
|
|
@ -62,13 +49,3 @@ public sealed partial class ItemBorgModuleComponent : Component
|
|||
public string ProvidedContainerId = "provided_container";
|
||||
}
|
||||
|
||||
// Frontier: droppable borg item data definitions
|
||||
[DataDefinition]
|
||||
public sealed partial class DroppableBorgItem
|
||||
{
|
||||
[IdDataField]
|
||||
public EntProtoId ID;
|
||||
|
||||
[DataField]
|
||||
public EntityWhitelist Whitelist;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,45 @@
|
|||
using Content.Shared._NF.Interaction.Systems;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._NF.Interaction.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
[NetworkedComponent]
|
||||
/// <summary>
|
||||
/// Lets this placeholder entity "pick up" items by clicking on them.
|
||||
/// The picked up item will then have <see cref="HandPlaceholderRemoveableComponent"/> added, referencing this placeholder.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(HandPlaceholderSystem))]
|
||||
[AutoGenerateComponentState(true)]
|
||||
// When an entity with this is removed from a hand, it is replaced with a placeholder entity that blocks the hand's use until re-equipped with the same prototype.
|
||||
public sealed partial class HandPlaceholderComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// A whitelist to match entities that this should accept.
|
||||
/// </summary>
|
||||
[ViewVariables, AutoNetworkedField]
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityWhitelist? Whitelist;
|
||||
|
||||
[ViewVariables, AutoNetworkedField]
|
||||
/// <summary>
|
||||
/// The prototype to use for placeholder icon visuals.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntProtoId? Prototype;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The source entity that this placeholder gets stored in when an item is picked up.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? Source;
|
||||
|
||||
/// <summary>
|
||||
/// The container on <see cref="Source"/> to insert this placeholder into.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public string ContainerId = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Controls preventing removal from containers.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool Enabled;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,22 @@
|
|||
using Content.Shared.Whitelist;
|
||||
using Content.Shared._NF.Interaction.Systems;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._NF.Interaction.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
[NetworkedComponent]
|
||||
// When an entity with this is removed from a hand, it is replaced with a placeholder entity that blocks the hand's use until re-equipped with the same prototype.
|
||||
/// <summary>
|
||||
/// When an entity with this is removed from a hand, it is replaced with an existing placeholder entity.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(HandPlaceholderSystem))]
|
||||
[AutoGenerateComponentState]
|
||||
public sealed partial class HandPlaceholderRemoveableComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public EntityWhitelist? Whitelist;
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid Placeholder;
|
||||
|
||||
[DataField]
|
||||
public EntProtoId? Prototype;
|
||||
/// <summary>
|
||||
/// Used to prevent it incorrectly replacing with the placeholder,
|
||||
/// when selecting and deselecting a module.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool Enabled;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,154 @@
|
|||
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.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 SharedTransformSystem _transform = 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, AfterInteractEvent>(AfterInteract);
|
||||
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 void SpawnPlaceholder(BaseContainer container, EntityUid item, EntProtoId id, EntityWhitelist whitelist)
|
||||
{
|
||||
var placeholder = Spawn(Placeholder);
|
||||
var comp = Comp<HandPlaceholderComponent>(placeholder);
|
||||
comp.Prototype = id;
|
||||
comp.Whitelist = whitelist;
|
||||
comp.Source = container.Owner;
|
||||
comp.ContainerId = container.ID;
|
||||
Dirty(placeholder, comp);
|
||||
|
||||
var name = _proto.Index(id).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)}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the placeholder entity for an item.
|
||||
/// </summary>
|
||||
public void SetPlaceholder(EntityUid item, EntityUid placeholder)
|
||||
{
|
||||
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))
|
||||
return;
|
||||
|
||||
var placeholder = ent.Comp.Placeholder;
|
||||
|
||||
ent.Comp.Enabled = false;
|
||||
RemCompDeferred<HandPlaceholderRemoveableComponent>(ent);
|
||||
|
||||
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 AfterInteract(Entity<HandPlaceholderComponent> ent, ref AfterInteractEvent 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 (!HasComp<ItemComponent>(target) || _whitelist.IsWhitelistFail(ent.Comp.Whitelist, 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;
|
||||
|
||||
SetPlaceholder(target, ent);
|
||||
SetEnabled(target, true);
|
||||
|
||||
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???)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
using Content.Shared._NF.Interaction.Components;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Whitelist;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._NF.Interaction.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles interactions with items that spawn HandPlaceholder items.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed partial class HandPlaceholderSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interaction = default!; // DeltaV
|
||||
[Dependency] private readonly SharedItemSystem _item = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metadata = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<HandPlaceholderRemoveableComponent, EntGotRemovedFromContainerMessage>(OnEntityRemovedFromContainer);
|
||||
|
||||
SubscribeLocalEvent<HandPlaceholderComponent, AfterInteractEvent>(AfterInteract);
|
||||
SubscribeLocalEvent<HandPlaceholderComponent, BeforeRangedInteractEvent>(BeforeRangedInteract);
|
||||
}
|
||||
|
||||
private void OnEntityRemovedFromContainer(Entity<HandPlaceholderRemoveableComponent> ent, ref EntGotRemovedFromContainerMessage args)
|
||||
{
|
||||
if (!TerminatingOrDeleted(args.Container.Owner))
|
||||
SpawnAndPickUpPlaceholder(ent, args.Container);
|
||||
RemCompDeferred<HandPlaceholderRemoveableComponent>(ent);
|
||||
}
|
||||
|
||||
private void SpawnAndPickUpPlaceholder(Entity<HandPlaceholderRemoveableComponent> ent, BaseContainer container)
|
||||
{
|
||||
if (_net.IsServer)
|
||||
{
|
||||
var placeholder = Spawn("HandPlaceholder");
|
||||
if (TryComp<HandPlaceholderComponent>(placeholder, out var placeComp))
|
||||
{
|
||||
placeComp.Whitelist = ent.Comp.Whitelist;
|
||||
placeComp.Prototype = ent.Comp.Prototype;
|
||||
Dirty(placeholder, placeComp);
|
||||
}
|
||||
|
||||
if (_proto.TryIndex(ent.Comp.Prototype, out var itemProto))
|
||||
_metadata.SetEntityName(placeholder, itemProto.Name);
|
||||
|
||||
if (!_container.Insert(placeholder, container, force: true))
|
||||
QueueDel(placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
private void BeforeRangedInteract(Entity<HandPlaceholderComponent> ent, ref BeforeRangedInteractEvent args)
|
||||
{
|
||||
if (args.Target == null || args.Handled)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
TryToPickUpTarget(ent, args.Target.Value, args.User);
|
||||
}
|
||||
|
||||
private void AfterInteract(Entity<HandPlaceholderComponent> ent, ref AfterInteractEvent args)
|
||||
{
|
||||
if (args.Target == null || args.Handled)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
TryToPickUpTarget(ent, args.Target.Value, args.User);
|
||||
}
|
||||
|
||||
private void TryToPickUpTarget(Entity<HandPlaceholderComponent> ent, EntityUid target, EntityUid user)
|
||||
{
|
||||
if (_whitelist.IsWhitelistFail(ent.Comp.Whitelist, target))
|
||||
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))
|
||||
return;
|
||||
|
||||
// Cache the whitelist/prototype, entity might be deleted.
|
||||
var whitelist = ent.Comp.Whitelist;
|
||||
var prototype = ent.Comp.Prototype;
|
||||
|
||||
if (_net.IsServer)
|
||||
Del(ent);
|
||||
|
||||
_hands.DoPickup(user, hand, target); // Force pickup - empty hands are not okay
|
||||
var placeComp = EnsureComp<HandPlaceholderRemoveableComponent>(target);
|
||||
placeComp.Whitelist = whitelist;
|
||||
placeComp.Prototype = prototype;
|
||||
Dirty(target, placeComp);
|
||||
_interaction.DoContactInteraction(user, target); // DeltaV - borgs picking up items leaves fibers
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._NF.Silicons.Borgs;
|
||||
|
||||
/// <summary>
|
||||
/// Uses placeholder entities to give borgs "hands" that are whitelisted for a certain kind of item.
|
||||
/// The items in it can be dropped and picked up if they match its whitelist.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(DroppableBorgModuleSystem))]
|
||||
public sealed partial class DroppableBorgModuleComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The items to spawn in borg hands.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public List<DroppableBorgItem> Items = new();
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the container to add that stores items when not in hands.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string ContainerId = "nf-droppable-items";
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the container to add that stores item placeholders when not in hands.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string PlaceholderContainerId = "nf-item-placeholders";
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
public sealed partial class DroppableBorgItem
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity to spawn and use for the placeholder sprite.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public EntProtoId Id;
|
||||
|
||||
/// <summary>
|
||||
/// A whitelist that items must match to be picked up by the placeholder.
|
||||
/// Regardless of this whitelist entities must have <c>ItemComponent</c> to be picked up.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public EntityWhitelist Whitelist;
|
||||
}
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
using Content.Shared._NF.Interaction.Components;
|
||||
using Content.Shared._NF.Interaction.Systems;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared._NF.Silicons.Borgs;
|
||||
|
||||
public sealed class DroppableBorgModuleSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly HandPlaceholderSystem _placeholder = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DroppableBorgModuleComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<DroppableBorgModuleComponent, BorgCanInsertModuleEvent>(OnCanInsertModule);
|
||||
SubscribeLocalEvent<DroppableBorgModuleComponent, BorgModuleSelectedEvent>(OnModuleSelected);
|
||||
SubscribeLocalEvent<DroppableBorgModuleComponent, BorgModuleUnselectedEvent>(OnModuleUnselected);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<DroppableBorgModuleComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
_container.EnsureContainer<Container>(ent, ent.Comp.ContainerId);
|
||||
var placeholders = _container.EnsureContainer<Container>(ent, ent.Comp.PlaceholderContainerId);
|
||||
|
||||
foreach (var slot in ent.Comp.Items)
|
||||
{
|
||||
// only the server runs mapinit, this wont make clientside entities
|
||||
var successful = TrySpawnInContainer(slot.Id, ent, ent.Comp.ContainerId, out var item);
|
||||
// this would only fail if the current entity is being terminated, which is impossible for mapinit
|
||||
DebugTools.Assert(successful, $"Somehow failed to insert {ToPrettyString(item)} into {ToPrettyString(ent)}");
|
||||
_placeholder.SpawnPlaceholder(placeholders, item!.Value, slot.Id, slot.Whitelist);
|
||||
}
|
||||
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
private void OnCanInsertModule(Entity<DroppableBorgModuleComponent> ent, ref BorgCanInsertModuleEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
foreach (var module in args.Chassis.Comp.ModuleContainer.ContainedEntities)
|
||||
{
|
||||
if (!TryComp<DroppableBorgModuleComponent>(module, out var comp))
|
||||
continue;
|
||||
|
||||
if (!SameItems(comp.Items, ent.Comp.Items))
|
||||
continue;
|
||||
|
||||
if (args.User is {} user)
|
||||
_popup.PopupEntity(Loc.GetString("borg-module-duplicate"), args.Chassis, user); // event is only raised by server so not using PopupClient
|
||||
args.Cancelled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnModuleSelected(Entity<DroppableBorgModuleComponent> ent, ref BorgModuleSelectedEvent args)
|
||||
{
|
||||
var chassis = args.Chassis;
|
||||
if (!TryComp<HandsComponent>(chassis, out var hands))
|
||||
return;
|
||||
|
||||
var container = _container.GetContainer(ent, ent.Comp.ContainerId);
|
||||
var items = container.ContainedEntities;
|
||||
for (int i = 0; i < ent.Comp.Items.Count; i++)
|
||||
{
|
||||
var item = items[0]; // the contained items will gradually go to 0
|
||||
var handId = HandId(ent, i);
|
||||
_hands.AddHand(chassis, handId, HandLocation.Middle, hands);
|
||||
var hand = hands.Hands[handId];
|
||||
_hands.DoPickup(chassis, hand, item, hands);
|
||||
if (hand.HeldEntity != item)
|
||||
{
|
||||
Log.Error($"Failed to pick up {ToPrettyString(item)} into hand {handId} of {ToPrettyString(chassis)}, it holds {ToPrettyString(hand.HeldEntity)}");
|
||||
// If we didn't pick up our expected item, delete the hand. No free hands!
|
||||
_hands.RemoveHand(chassis, handId, hands);
|
||||
}
|
||||
else
|
||||
{
|
||||
_interaction.DoContactInteraction(chassis, item); // for potential forensics or other systems (why does hands system not do this)
|
||||
_placeholder.SetEnabled(item, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnModuleUnselected(Entity<DroppableBorgModuleComponent> ent, ref BorgModuleUnselectedEvent args)
|
||||
{
|
||||
var chassis = args.Chassis;
|
||||
if (!TryComp<HandsComponent>(chassis, out var hands))
|
||||
return;
|
||||
|
||||
if (TerminatingOrDeleted(ent))
|
||||
{
|
||||
for (int i = 0; i < ent.Comp.Items.Count; i++)
|
||||
{
|
||||
var handId = HandId(ent, i);
|
||||
_hands.TryGetHand(chassis, handId, out var hand, hands);
|
||||
if (hand?.HeldEntity is {} item)
|
||||
QueueDel(item);
|
||||
else
|
||||
Log.Error($"Borg {ToPrettyString(chassis)} terminated with empty hand {i} in {ToPrettyString(ent)}");
|
||||
_hands.RemoveHand(chassis, handId, hands);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var container = _container.GetContainer(ent, ent.Comp.ContainerId);
|
||||
for (int i = 0; i < ent.Comp.Items.Count; i++)
|
||||
{
|
||||
var handId = HandId(ent, i);
|
||||
_hands.TryGetHand(chassis, handId, out var hand, hands);
|
||||
if (hand?.HeldEntity is {} item)
|
||||
{
|
||||
_placeholder.SetEnabled(item, false);
|
||||
_container.Insert(item, container, force: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"Borg {ToPrettyString(chassis)} had an empty hand in the slot for {ent.Comp.Items[i].Id}");
|
||||
}
|
||||
|
||||
_hands.RemoveHand(chassis, handId, hands);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Format the hand ID for a given module and item number.
|
||||
/// </summary>
|
||||
private static string HandId(EntityUid uid, int i)
|
||||
{
|
||||
return $"nf-{uid}-item-{i}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks that 2 droppable modules have the same starting items.
|
||||
/// Used for duplicate module check.
|
||||
/// </summary>
|
||||
private static bool SameItems(List<DroppableBorgItem> a, List<DroppableBorgItem> b)
|
||||
{
|
||||
if (a.Count != b.Count)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < a.Count; i++)
|
||||
{
|
||||
if (a[i].Id != b[i].Id)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event raised on a module to check if it can be installed.
|
||||
/// This should exist upstream but doesn't.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct BorgCanInsertModuleEvent(Entity<BorgChassisComponent> Chassis, EntityUid? User, bool Cancelled = false);
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace Content.Server._NF.Whitelist.Components;
|
||||
namespace Content.Shared._NF.Whitelist.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Whitelist component for book bags to avoid tag redefinition and collisions
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace Content.Server._NF.Whitelist.Components;
|
||||
namespace Content.Shared._NF.Whitelist.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Whitelist component for lighters to avoid tag redefinition and collisions
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace Content.Server._NF.Whitelist.Components;
|
||||
namespace Content.Shared._NF.Whitelist.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Whitelist component for ore bags to avoid tag redefinition and collisions
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace Content.Server._NF.Whitelist.Components;
|
||||
namespace Content.Shared._NF.Whitelist.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Whitelist component for plant bags to avoid tag redefinition and collisions
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace Content.Server._NF.Whitelist.Components;
|
||||
namespace Content.Shared._NF.Whitelist.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Whitelist component for shakers to avoid tag redefinition and collisions
|
||||
|
|
@ -133,14 +133,17 @@
|
|||
layers:
|
||||
- state: generic
|
||||
- state: icon-fire-extinguisher
|
||||
- type: ItemBorgModule
|
||||
# Frontier: droppable borg items
|
||||
droppableItems:
|
||||
# Frontier: droppable borg items
|
||||
#- type: ItemBorgModule
|
||||
# items:
|
||||
# - FireExtinguisher
|
||||
- type: DroppableBorgModule
|
||||
items:
|
||||
- id: FireExtinguisher
|
||||
whitelist:
|
||||
tags:
|
||||
- FireExtinguisher
|
||||
# End Frontier
|
||||
# End Frontier
|
||||
- type: BorgModuleIcon
|
||||
icon: { sprite: Interface/Actions/actions_borg.rsi, state: extinguisher-module }
|
||||
|
||||
|
|
@ -229,13 +232,14 @@
|
|||
# - OreBag # Frontier
|
||||
- Crowbar
|
||||
- RadioHandheld
|
||||
# Frontier: droppable borg items
|
||||
droppableItems:
|
||||
# Frontier: droppable borg items
|
||||
- type: DroppableBorgModule
|
||||
items:
|
||||
- id: OreBag
|
||||
whitelist:
|
||||
components:
|
||||
- NFOreBag
|
||||
# End Frontier: droppable borg items
|
||||
# End Frontier: droppable borg items
|
||||
- type: BorgModuleIcon
|
||||
icon: { sprite: Interface/Actions/actions_borg.rsi, state: mining-module }
|
||||
|
||||
|
|
@ -359,13 +363,14 @@
|
|||
- MopItem
|
||||
# - Bucket # Frontier
|
||||
- TrashBag
|
||||
# Frontier: droppable items
|
||||
droppableItems:
|
||||
# Frontier: droppable items
|
||||
- type: DroppableBorgModule
|
||||
items:
|
||||
- id: Bucket
|
||||
whitelist:
|
||||
tags:
|
||||
- Bucket
|
||||
# End Frontier
|
||||
# End Frontier
|
||||
- type: BorgModuleIcon
|
||||
icon: { sprite: Interface/Actions/actions_borg.rsi, state: cleaning-module }
|
||||
|
||||
|
|
@ -456,8 +461,9 @@
|
|||
# - Beaker # Frontier
|
||||
- BorgDropper
|
||||
- BorgHypo
|
||||
# Frontier: droppable borg items
|
||||
droppableItems:
|
||||
# Frontier: droppable borg items
|
||||
- type: DroppableBorgModule
|
||||
items:
|
||||
- id: Beaker
|
||||
whitelist:
|
||||
tags:
|
||||
|
|
@ -466,7 +472,7 @@
|
|||
whitelist:
|
||||
tags:
|
||||
- GlassBeaker
|
||||
# End Frontier: droppable borg items
|
||||
# End Frontier: droppable borg items
|
||||
- type: BorgModuleIcon
|
||||
icon: { sprite: Interface/Actions/actions_borg.rsi, state: adv-diagnosis-module }
|
||||
|
||||
|
|
@ -524,8 +530,9 @@
|
|||
# - Lighter # Frontier
|
||||
# - DrinkShaker # Frontier
|
||||
- BorgDropper
|
||||
# Frontier: droppable
|
||||
droppableItems:
|
||||
# Frontier: droppable items
|
||||
- type: DroppableBorgModule
|
||||
items:
|
||||
- id: BooksBag
|
||||
whitelist:
|
||||
components:
|
||||
|
|
@ -538,7 +545,7 @@
|
|||
whitelist:
|
||||
components:
|
||||
- NFShaker
|
||||
# End Frontier
|
||||
# End Frontier
|
||||
- type: BorgModuleIcon
|
||||
icon: { sprite: Interface/Actions/actions_borg.rsi, state: service-module }
|
||||
|
||||
|
|
@ -574,13 +581,14 @@
|
|||
- HydroponicsToolSpade
|
||||
- HydroponicsToolClippers
|
||||
# - Bucket # Frontier
|
||||
# Frontier: droppable borg items
|
||||
droppableItems:
|
||||
# Frontier: droppable borg items
|
||||
- type: DroppableBorgModule
|
||||
items:
|
||||
- id: Bucket
|
||||
whitelist:
|
||||
tags:
|
||||
- Bucket
|
||||
# End Frontier
|
||||
# End Frontier
|
||||
- type: BorgModuleIcon
|
||||
icon: { sprite: Interface/Actions/actions_borg.rsi, state: gardening-module }
|
||||
|
||||
|
|
@ -598,13 +606,14 @@
|
|||
- HydroponicsToolScythe
|
||||
- HydroponicsToolHatchet
|
||||
# - PlantBag # Frontier
|
||||
# Frontier: droppable borg items
|
||||
droppableItems:
|
||||
# Frontier: droppable borg items
|
||||
- type: DroppableBorgModule
|
||||
items:
|
||||
- id: PlantBag
|
||||
whitelist:
|
||||
components:
|
||||
- NFPlantBag
|
||||
# End Frontier
|
||||
# End Frontier
|
||||
- type: BorgModuleIcon
|
||||
icon: { sprite: Interface/Actions/actions_borg.rsi, state: harvesting-module }
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,5 @@
|
|||
components:
|
||||
- type: Item
|
||||
size: Ginormous # no storage insertion visuals
|
||||
- type: Unremoveable
|
||||
- type: HandPlaceholder
|
||||
- type: HandPlaceholderVisuals
|
||||
|
|
|
|||
Loading…
Reference in New Issue