Make chemistry machines and IdCardConsole use item slots (#5428)
* chemistry item slots * item slots id card console
This commit is contained in:
@@ -133,10 +133,47 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
[ViewVariables]
|
||||
public ContainerSlot ContainerSlot = default!;
|
||||
|
||||
/// <summary>
|
||||
/// If this slot belongs to some de-constructible component, should the item inside the slot be ejected upon
|
||||
/// deconstruction?
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The actual deconstruction logic is handled by the server-side EmptyOnMachineDeconstructSystem.
|
||||
/// </remarks>
|
||||
[DataField("ejectOnDeconstruct")]
|
||||
public bool EjectOnDeconstruct = true;
|
||||
|
||||
/// <summary>
|
||||
/// If this slot belongs to some breakable or destructible entity, should the item inside the slot be
|
||||
/// ejected when it is broken or destroyed?
|
||||
/// </summary>
|
||||
[DataField("ejectOnBreak")]
|
||||
public bool EjectOnBreak = false;
|
||||
|
||||
/// <summary>
|
||||
/// If this is not an empty string, this will generate a popup when someone attempts to insert a bad item
|
||||
/// into this slot. This string will be passed through localization.
|
||||
/// </summary>
|
||||
[DataField("whitelistFailPopup")]
|
||||
public string WhitelistFailPopup = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// If the user interacts with an entity with an already-filled item slot, should they attempt to swap out the item?
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Useful for things like chem dispensers, but undesirable for things like the ID card console, where you
|
||||
/// want to insert more than one item that matches the same whitelist.
|
||||
/// </remarks>
|
||||
[DataField("swap")]
|
||||
public bool Swap = true;
|
||||
|
||||
public string ID => ContainerSlot.ID;
|
||||
|
||||
// Convenience properties
|
||||
public bool HasItem => ContainerSlot.ContainedEntity != null;
|
||||
public IEntity? Item => ContainerSlot.ContainedEntity;
|
||||
|
||||
// and to make it easier for whenever IEntity is removed
|
||||
public EntityUid? ItemUid => ContainerSlot.ContainedEntity?.Uid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Acts;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
@@ -22,6 +24,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
public class ItemSlotsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -36,6 +39,9 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
SubscribeLocalEvent<ItemSlotsComponent, GetAlternativeVerbsEvent>(AddEjectVerbs);
|
||||
SubscribeLocalEvent<ItemSlotsComponent, GetInteractionVerbsEvent>(AddInteractionVerbsVerbs);
|
||||
|
||||
SubscribeLocalEvent<ItemSlotsComponent, BreakageEventArgs>(OnBreak);
|
||||
SubscribeLocalEvent<ItemSlotsComponent, DestructionEventArgs>(OnBreak);
|
||||
|
||||
SubscribeLocalEvent<ItemSlotsComponent, ComponentGetState>(GetItemSlotsState);
|
||||
SubscribeLocalEvent<ItemSlotsComponent, ComponentHandleState>(HandleItemSlotsState);
|
||||
}
|
||||
@@ -139,7 +145,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
|
||||
foreach (var slot in itemSlots.Slots.Values)
|
||||
{
|
||||
if (!CanInsert(args.UsedUid, slot, swap: true))
|
||||
if (!CanInsert(uid, args.UsedUid, slot, swap: slot.Swap, popup: args.UserUid))
|
||||
continue;
|
||||
|
||||
// Drop the held item onto the floor. Return if the user cannot drop.
|
||||
@@ -174,7 +180,11 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
/// Check whether a given item can be inserted into a slot. Unless otherwise specified, this will return
|
||||
/// false if the slot is already filled.
|
||||
/// </summary>
|
||||
public bool CanInsert(EntityUid uid, ItemSlot slot, bool swap = false)
|
||||
/// <remarks>
|
||||
/// If a popup entity is given, and if the item slot is set to generate a popup message when it fails to
|
||||
/// pass the whitelist, then this will generate a popup.
|
||||
/// </remarks>
|
||||
public bool CanInsert(EntityUid uid, EntityUid usedUid, ItemSlot slot, bool swap = false, EntityUid? popup = null)
|
||||
{
|
||||
if (slot.Locked)
|
||||
return false;
|
||||
@@ -182,8 +192,12 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
if (!swap && slot.HasItem)
|
||||
return false;
|
||||
|
||||
if (slot.Whitelist != null && !slot.Whitelist.IsValid(uid))
|
||||
if (slot.Whitelist != null && !slot.Whitelist.IsValid(usedUid))
|
||||
{
|
||||
if (popup.HasValue && !string.IsNullOrWhiteSpace(slot.WhitelistFailPopup))
|
||||
_popupSystem.PopupEntity(Loc.GetString(slot.WhitelistFailPopup), uid, Filter.Entities(popup.Value));
|
||||
return false;
|
||||
}
|
||||
|
||||
// We should also check ContainerSlot.CanInsert, but that prevents swapping interactions. Given that
|
||||
// ContainerSlot.CanInsert gets called when the item is actually inserted anyways, we can just get away with
|
||||
@@ -212,7 +226,30 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
/// <returns>False if failed to insert item</returns>
|
||||
public bool TryInsert(EntityUid uid, ItemSlot slot, IEntity item)
|
||||
{
|
||||
if (!CanInsert(item.Uid, slot))
|
||||
if (!CanInsert(uid, item.Uid, slot))
|
||||
return false;
|
||||
|
||||
Insert(uid, slot, item);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to insert item into a specific slot from an entity's hand.
|
||||
/// </summary>
|
||||
/// <returns>False if failed to insert item</returns>
|
||||
public bool TryInsertFromHand(EntityUid uid, ItemSlot slot, EntityUid user, SharedHandsComponent? hands = null)
|
||||
{
|
||||
if (!Resolve(user, ref hands, false))
|
||||
return false;
|
||||
|
||||
if (!hands.TryGetActiveHeldEntity(out var item))
|
||||
return false;
|
||||
|
||||
if (!CanInsert(uid, item.Uid, slot))
|
||||
return false;
|
||||
|
||||
// hands.Drop(item) checks CanDrop action blocker
|
||||
if (!_actionBlockerSystem.CanInteract(user) && hands.Drop(item))
|
||||
return false;
|
||||
|
||||
Insert(uid, slot, item);
|
||||
@@ -364,7 +401,7 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
|
||||
foreach (var slot in itemSlots.Slots.Values)
|
||||
{
|
||||
if (!CanInsert(args.Using.Uid, slot))
|
||||
if (!CanInsert(uid, args.Using.Uid, slot))
|
||||
continue;
|
||||
|
||||
var verbSubject = slot.Name != string.Empty
|
||||
@@ -397,6 +434,18 @@ namespace Content.Shared.Containers.ItemSlots
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Eject items from (some) slots when the entity is destroyed.
|
||||
/// </summary>
|
||||
private void OnBreak(EntityUid uid, ItemSlotsComponent component, EntityEventArgs args)
|
||||
{
|
||||
foreach (var slot in component.Slots.Values)
|
||||
{
|
||||
if (slot.EjectOnBreak && slot.HasItem)
|
||||
TryEject(uid, slot, out var _);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the contents of some item slot.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user