2021-02-01 16:49:43 -08:00
|
|
|
using System;
|
2018-04-25 06:42:35 -05:00
|
|
|
using System.Collections.Generic;
|
2021-03-16 15:50:20 +01:00
|
|
|
using System.Diagnostics.CodeAnalysis;
|
2019-07-23 23:24:47 +02:00
|
|
|
using System.Linq;
|
2021-01-08 10:29:08 -03:00
|
|
|
using Content.Server.Administration.Commands;
|
2021-06-09 22:19:39 +02:00
|
|
|
using Content.Server.Clothing.Components;
|
|
|
|
|
using Content.Server.Hands.Components;
|
|
|
|
|
using Content.Server.Interaction;
|
|
|
|
|
using Content.Server.Items;
|
|
|
|
|
using Content.Server.Storage.Components;
|
|
|
|
|
using Content.Shared.ActionBlocker;
|
|
|
|
|
using Content.Shared.Acts;
|
|
|
|
|
using Content.Shared.Inventory;
|
|
|
|
|
using Content.Shared.Movement.Components;
|
2021-11-07 22:17:35 -07:00
|
|
|
using Content.Shared.Movement.EntitySystems;
|
2021-09-26 15:18:45 +02:00
|
|
|
using Content.Shared.Popups;
|
2021-06-09 22:19:39 +02:00
|
|
|
using Content.Shared.Verbs;
|
2021-01-08 10:29:08 -03:00
|
|
|
using Robust.Server.Console;
|
2021-02-11 01:13:03 -08:00
|
|
|
using Robust.Server.GameObjects;
|
2021-01-08 10:29:08 -03:00
|
|
|
using Robust.Server.Player;
|
2021-09-16 12:16:14 +02:00
|
|
|
using Robust.Shared.Audio;
|
2021-02-01 16:49:43 -08:00
|
|
|
using Robust.Shared.Console;
|
2020-08-21 14:54:38 +02:00
|
|
|
using Robust.Shared.Containers;
|
2019-04-15 21:11:38 -06:00
|
|
|
using Robust.Shared.GameObjects;
|
|
|
|
|
using Robust.Shared.IoC;
|
2020-05-23 17:27:57 +02:00
|
|
|
using Robust.Shared.Localization;
|
2019-11-13 17:37:46 -05:00
|
|
|
using Robust.Shared.Map;
|
2021-02-11 01:13:03 -08:00
|
|
|
using Robust.Shared.Network;
|
2021-09-16 12:16:14 +02:00
|
|
|
using Robust.Shared.Player;
|
2020-04-20 10:36:02 +01:00
|
|
|
using Robust.Shared.Players;
|
2019-04-15 21:11:38 -06:00
|
|
|
using Robust.Shared.ViewVariables;
|
2021-06-09 22:19:39 +02:00
|
|
|
using static Content.Shared.Inventory.EquipmentSlotDefines;
|
|
|
|
|
using static Content.Shared.Inventory.SharedInventoryComponent.ClientInventoryMessage;
|
2018-04-25 06:42:35 -05:00
|
|
|
|
2021-06-09 22:19:39 +02:00
|
|
|
namespace Content.Server.Inventory.Components
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2019-07-31 15:02:36 +02:00
|
|
|
[RegisterComponent]
|
2020-12-13 14:28:20 -08:00
|
|
|
[ComponentReference(typeof(SharedInventoryComponent))]
|
2021-10-24 23:43:49 -07:00
|
|
|
public class InventoryComponent : SharedInventoryComponent, IExAct
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2020-08-24 14:10:28 +02:00
|
|
|
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
2019-08-07 15:56:22 -07:00
|
|
|
|
2021-01-08 10:29:08 -03:00
|
|
|
[ViewVariables] private readonly Dictionary<Slots, ContainerSlot> _slotContainers = new();
|
2018-09-19 18:54:04 +02:00
|
|
|
|
2020-08-15 20:33:42 +02:00
|
|
|
private KeyValuePair<Slots, (EntityUid entity, bool fits)>? _hoverEntity;
|
|
|
|
|
|
|
|
|
|
public IEnumerable<Slots> Slots => _slotContainers.Keys;
|
|
|
|
|
|
2021-03-16 15:50:20 +01:00
|
|
|
public event Action? OnItemChanged;
|
2020-07-26 07:25:38 -05:00
|
|
|
|
2021-06-19 19:41:26 -07:00
|
|
|
protected override void Initialize()
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2019-07-23 23:24:47 +02:00
|
|
|
base.Initialize();
|
2018-04-25 06:42:35 -05:00
|
|
|
|
2019-07-23 23:24:47 +02:00
|
|
|
foreach (var slotName in InventoryInstance.SlotMasks)
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2020-08-15 20:33:42 +02:00
|
|
|
if (slotName != EquipmentSlotDefines.Slots.NONE)
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2019-07-23 23:24:47 +02:00
|
|
|
AddSlot(slotName);
|
2018-04-25 06:42:35 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-19 19:41:26 -07:00
|
|
|
protected override void OnRemove()
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2020-08-15 20:33:42 +02:00
|
|
|
var slots = _slotContainers.Keys.ToList();
|
2020-08-21 14:54:38 +02:00
|
|
|
|
2018-05-10 18:52:44 +02:00
|
|
|
foreach (var slot in slots)
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2021-03-16 15:50:20 +01:00
|
|
|
if (TryGetSlotItem(slot, out ItemComponent? item))
|
2020-08-21 14:54:38 +02:00
|
|
|
{
|
|
|
|
|
item.Owner.Delete();
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-25 06:42:35 -05:00
|
|
|
RemoveSlot(slot);
|
|
|
|
|
}
|
2018-09-19 18:54:04 +02:00
|
|
|
|
2018-04-25 06:42:35 -05:00
|
|
|
base.OnRemove();
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-08 17:41:23 +02:00
|
|
|
public IEnumerable<IEntity> GetAllHeldItems()
|
|
|
|
|
{
|
|
|
|
|
foreach (var (_, container) in _slotContainers)
|
|
|
|
|
{
|
|
|
|
|
foreach (var entity in container.ContainedEntities)
|
|
|
|
|
{
|
|
|
|
|
yield return entity;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-25 06:42:35 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Helper to get container name for specified slot on this component
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="slot"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
private string GetSlotString(Slots slot)
|
|
|
|
|
{
|
|
|
|
|
return Name + "_" + Enum.GetName(typeof(Slots), slot);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the clothing equipped to the specified slot.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="slot">The slot to get the item for.</param>
|
|
|
|
|
/// <returns>Null if the slot is empty, otherwise the item.</returns>
|
2021-03-16 15:50:20 +01:00
|
|
|
public ItemComponent? GetSlotItem(Slots slot)
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2019-04-06 17:11:51 +02:00
|
|
|
return GetSlotItem<ItemComponent>(slot);
|
|
|
|
|
}
|
2020-10-27 20:53:44 +01:00
|
|
|
|
2021-03-16 15:50:20 +01:00
|
|
|
public IEnumerable<T?> LookupItems<T>() where T : Component
|
2020-10-27 20:53:44 +01:00
|
|
|
{
|
2021-03-16 15:50:20 +01:00
|
|
|
return _slotContainers.Values
|
|
|
|
|
.SelectMany(x => x.ContainedEntities.Select(e => e.GetComponentOrNull<T>()))
|
2020-10-27 20:53:44 +01:00
|
|
|
.Where(x => x != null);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-16 15:50:20 +01:00
|
|
|
public T? GetSlotItem<T>(Slots slot) where T : ItemComponent
|
2019-04-06 17:11:51 +02:00
|
|
|
{
|
2020-08-15 20:33:42 +02:00
|
|
|
if (!_slotContainers.ContainsKey(slot))
|
2020-07-26 07:25:38 -05:00
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-15 20:33:42 +02:00
|
|
|
var containedEntity = _slotContainers[slot].ContainedEntity;
|
2020-06-29 01:44:33 +10:00
|
|
|
if (containedEntity?.Deleted == true)
|
|
|
|
|
{
|
2021-03-16 15:50:20 +01:00
|
|
|
_slotContainers.Remove(slot);
|
2020-06-29 01:44:33 +10:00
|
|
|
containedEntity = null;
|
|
|
|
|
Dirty();
|
|
|
|
|
}
|
2021-01-11 19:24:09 +01:00
|
|
|
|
2020-06-29 01:44:33 +10:00
|
|
|
return containedEntity?.GetComponent<T>();
|
2019-04-06 17:11:51 +02:00
|
|
|
}
|
|
|
|
|
|
2021-03-16 15:50:20 +01:00
|
|
|
public bool TryGetSlotItem<T>(Slots slot, [NotNullWhen(true)] out T? itemComponent) where T : ItemComponent
|
2019-04-06 17:11:51 +02:00
|
|
|
{
|
|
|
|
|
itemComponent = GetSlotItem<T>(slot);
|
|
|
|
|
return itemComponent != null;
|
2018-04-25 06:42:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Equips slothing to the specified slot.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// This will fail if there is already an item in the specified slot.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <param name="slot">The slot to put the item in.</param>
|
2020-01-20 00:26:11 +01:00
|
|
|
/// <param name="item">The item to insert into the slot.</param>
|
2020-08-16 16:30:52 +02:00
|
|
|
/// <param name="mobCheck">Whether to perform an ActionBlocker check to the entity.</param>
|
2020-08-15 20:33:42 +02:00
|
|
|
/// <param name="reason">The translated reason why the item cannot be equipped, if this function returns false. Can be null.</param>
|
2018-04-25 06:42:35 -05:00
|
|
|
/// <returns>True if the item was successfully inserted, false otherwise.</returns>
|
2021-03-16 15:50:20 +01:00
|
|
|
public bool Equip(Slots slot, ItemComponent item, bool mobCheck, [NotNullWhen(false)] out string? reason)
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2020-01-20 00:26:11 +01:00
|
|
|
if (item == null)
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2020-01-20 00:26:11 +01:00
|
|
|
throw new ArgumentNullException(nameof(item),
|
2018-09-19 18:54:04 +02:00
|
|
|
"Clothing must be passed here. To remove some clothing from a slot, use Unequip()");
|
2018-04-25 06:42:35 -05:00
|
|
|
}
|
|
|
|
|
|
2020-08-16 16:30:52 +02:00
|
|
|
if (!CanEquip(slot, item, mobCheck, out reason))
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-15 20:33:42 +02:00
|
|
|
var inventorySlot = _slotContainers[slot];
|
2020-01-20 00:26:11 +01:00
|
|
|
if (!inventorySlot.Insert(item.Owner))
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2021-06-21 02:13:54 +02:00
|
|
|
reason = Loc.GetString("inventory-component-on-equip-cannot");
|
2018-04-25 06:42:35 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-16 12:16:14 +02:00
|
|
|
// TODO: Make clothing component not inherit ItemComponent, for fuck's sake.
|
|
|
|
|
// TODO: Make clothing component not required for playing a sound on equip... Move it to its own component.
|
2021-09-21 17:28:01 +02:00
|
|
|
if (mobCheck && item is ClothingComponent { EquipSound: {} equipSound })
|
2021-09-16 12:16:14 +02:00
|
|
|
{
|
|
|
|
|
SoundSystem.Play(Filter.Pvs(Owner), equipSound.GetSound(), Owner, AudioParams.Default.WithVolume(-2f));
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-21 14:38:35 +02:00
|
|
|
_entitySystemManager.GetEntitySystem<InteractionSystem>().EquippedInteraction(Owner, item.Owner, slot);
|
2018-04-25 06:42:35 -05:00
|
|
|
|
2020-08-15 20:33:42 +02:00
|
|
|
OnItemChanged?.Invoke();
|
|
|
|
|
|
2018-09-19 18:54:04 +02:00
|
|
|
Dirty();
|
2020-06-18 22:52:44 +10:00
|
|
|
|
2021-01-11 19:24:09 +01:00
|
|
|
UpdateMovementSpeed();
|
|
|
|
|
|
2018-04-25 06:42:35 -05:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-11 19:24:09 +01:00
|
|
|
public bool Equip(Slots slot, ItemComponent item, bool mobCheck = true) =>
|
|
|
|
|
Equip(slot, item, mobCheck, out var _);
|
2020-01-20 22:13:47 +01:00
|
|
|
|
2021-01-11 19:24:09 +01:00
|
|
|
public bool Equip(Slots slot, IEntity entity, bool mobCheck = true) =>
|
|
|
|
|
Equip(slot, entity.GetComponent<ItemComponent>(), mobCheck);
|
2020-01-20 22:13:47 +01:00
|
|
|
|
2018-04-25 06:42:35 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Checks whether an item can be put in the specified slot.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="slot">The slot to check for.</param>
|
|
|
|
|
/// <param name="item">The item to check for.</param>
|
2020-05-23 17:27:57 +02:00
|
|
|
/// <param name="reason">The translated reason why the item cannot be equiped, if this function returns false. Can be null.</param>
|
2018-04-25 06:42:35 -05:00
|
|
|
/// <returns>True if the item can be inserted into the specified slot.</returns>
|
2021-03-16 15:50:20 +01:00
|
|
|
public bool CanEquip(Slots slot, ItemComponent item, bool mobCheck, [NotNullWhen(false)] out string? reason)
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2020-01-20 22:13:47 +01:00
|
|
|
var pass = false;
|
2020-05-23 17:27:57 +02:00
|
|
|
reason = null;
|
2020-01-20 22:13:47 +01:00
|
|
|
|
2021-11-09 13:45:21 +01:00
|
|
|
if (mobCheck && !EntitySystem.Get<ActionBlockerSystem>().CanEquip(OwnerUid))
|
2021-03-16 15:50:20 +01:00
|
|
|
{
|
2021-06-21 02:13:54 +02:00
|
|
|
reason = Loc.GetString("inventory-component-can-equip-cannot");
|
2020-05-13 16:53:01 +02:00
|
|
|
return false;
|
2021-03-16 15:50:20 +01:00
|
|
|
}
|
2020-05-13 16:53:01 +02:00
|
|
|
|
2020-01-20 22:13:47 +01:00
|
|
|
if (item is ClothingComponent clothing)
|
|
|
|
|
{
|
|
|
|
|
if (clothing.SlotFlags != SlotFlags.PREVENTEQUIP && (clothing.SlotFlags & SlotMasks[slot]) != 0)
|
|
|
|
|
{
|
|
|
|
|
pass = true;
|
|
|
|
|
}
|
2020-05-23 17:27:57 +02:00
|
|
|
else
|
|
|
|
|
{
|
2021-06-21 02:13:54 +02:00
|
|
|
reason = Loc.GetString("inventory-component-can-equip-does-not-fit");
|
2020-05-23 17:27:57 +02:00
|
|
|
}
|
2020-01-20 22:13:47 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-16 15:50:20 +01:00
|
|
|
if (Owner.TryGetComponent(out IInventoryController? controller))
|
2020-01-20 22:13:47 +01:00
|
|
|
{
|
2020-05-23 17:27:57 +02:00
|
|
|
pass = controller.CanEquip(slot, item.Owner, pass, out var controllerReason);
|
|
|
|
|
reason = controllerReason ?? reason;
|
2020-01-20 22:13:47 +01:00
|
|
|
}
|
|
|
|
|
|
2021-11-16 19:05:41 -05:00
|
|
|
if (!pass)
|
2020-05-23 22:57:25 +02:00
|
|
|
{
|
2021-11-16 19:05:41 -05:00
|
|
|
reason = reason ?? Loc.GetString("inventory-component-can-equip-cannot");
|
|
|
|
|
return false;
|
2020-05-23 22:57:25 +02:00
|
|
|
}
|
|
|
|
|
|
2021-03-16 15:50:20 +01:00
|
|
|
var canEquip = pass && _slotContainers[slot].CanInsert(item.Owner);
|
|
|
|
|
|
|
|
|
|
if (!canEquip)
|
|
|
|
|
{
|
2021-06-21 02:13:54 +02:00
|
|
|
reason = Loc.GetString("inventory-component-can-equip-cannot");
|
2021-03-16 15:50:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return canEquip;
|
2018-04-25 06:42:35 -05:00
|
|
|
}
|
|
|
|
|
|
2021-01-08 10:29:08 -03:00
|
|
|
public bool CanEquip(Slots slot, ItemComponent item, bool mobCheck = true) =>
|
|
|
|
|
CanEquip(slot, item, mobCheck, out var _);
|
2020-05-23 17:27:57 +02:00
|
|
|
|
2021-01-08 10:29:08 -03:00
|
|
|
public bool CanEquip(Slots slot, IEntity entity, bool mobCheck = true) =>
|
|
|
|
|
CanEquip(slot, entity.GetComponent<ItemComponent>(), mobCheck);
|
2020-01-20 22:13:47 +01:00
|
|
|
|
2018-04-25 06:42:35 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Drops the item in a slot.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="slot">The slot to drop the item from.</param>
|
|
|
|
|
/// <returns>True if an item was dropped, false otherwise.</returns>
|
2020-08-16 16:30:52 +02:00
|
|
|
/// <param name="mobCheck">Whether to perform an ActionBlocker check to the entity.</param>
|
|
|
|
|
public bool Unequip(Slots slot, bool mobCheck = true)
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2020-08-16 16:30:52 +02:00
|
|
|
if (!CanUnequip(slot, mobCheck))
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-15 20:33:42 +02:00
|
|
|
var inventorySlot = _slotContainers[slot];
|
2020-08-21 14:54:38 +02:00
|
|
|
var entity = inventorySlot.ContainedEntity;
|
2021-03-16 15:50:20 +01:00
|
|
|
|
|
|
|
|
if (entity == null)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-21 14:54:38 +02:00
|
|
|
if (!inventorySlot.Remove(entity))
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: The item should be dropped to the container our owner is in, if any.
|
2020-11-13 20:25:04 +13:00
|
|
|
entity.Transform.AttachParentToContainerOrGrid();
|
2020-04-21 14:38:35 +02:00
|
|
|
|
2020-08-21 14:54:38 +02:00
|
|
|
_entitySystemManager.GetEntitySystem<InteractionSystem>().UnequippedInteraction(Owner, entity, slot);
|
2020-04-21 14:38:35 +02:00
|
|
|
|
2020-08-15 20:33:42 +02:00
|
|
|
OnItemChanged?.Invoke();
|
|
|
|
|
|
2018-09-19 18:54:04 +02:00
|
|
|
Dirty();
|
2020-06-18 22:52:44 +10:00
|
|
|
|
2021-01-11 19:24:09 +01:00
|
|
|
UpdateMovementSpeed();
|
|
|
|
|
|
2018-04-25 06:42:35 -05:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-11 19:24:09 +01:00
|
|
|
private void UpdateMovementSpeed()
|
|
|
|
|
{
|
2021-11-07 22:17:35 -07:00
|
|
|
EntitySystem.Get<MovementSpeedModifierSystem>().RefreshMovementSpeedModifiers(OwnerUid);
|
2021-01-11 19:24:09 +01:00
|
|
|
}
|
|
|
|
|
|
2020-08-21 14:54:38 +02:00
|
|
|
public void ForceUnequip(Slots slot)
|
|
|
|
|
{
|
|
|
|
|
var inventorySlot = _slotContainers[slot];
|
|
|
|
|
var entity = inventorySlot.ContainedEntity;
|
|
|
|
|
if (entity == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var item = entity.GetComponent<ItemComponent>();
|
|
|
|
|
inventorySlot.ForceRemove(entity);
|
|
|
|
|
|
|
|
|
|
var itemTransform = entity.Transform;
|
|
|
|
|
|
2020-11-13 20:25:04 +13:00
|
|
|
itemTransform.AttachParentToContainerOrGrid();
|
2020-08-21 14:54:38 +02:00
|
|
|
|
|
|
|
|
_entitySystemManager.GetEntitySystem<InteractionSystem>().UnequippedInteraction(Owner, item.Owner, slot);
|
|
|
|
|
|
|
|
|
|
OnItemChanged?.Invoke();
|
|
|
|
|
|
|
|
|
|
Dirty();
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-25 06:42:35 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Checks whether an item can be dropped from the specified slot.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="slot">The slot to check for.</param>
|
2020-08-16 16:30:52 +02:00
|
|
|
/// <param name="mobCheck">Whether to perform an ActionBlocker check to the entity.</param>
|
2018-04-25 06:42:35 -05:00
|
|
|
/// <returns>
|
|
|
|
|
/// True if there is an item in the slot and it can be dropped, false otherwise.
|
|
|
|
|
/// </returns>
|
2020-08-16 16:30:52 +02:00
|
|
|
public bool CanUnequip(Slots slot, bool mobCheck = true)
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2021-11-09 13:46:48 +01:00
|
|
|
if (mobCheck && !EntitySystem.Get<ActionBlockerSystem>().CanUnequip(OwnerUid))
|
2020-05-13 16:53:01 +02:00
|
|
|
return false;
|
|
|
|
|
|
2020-08-16 16:30:52 +02:00
|
|
|
var inventorySlot = _slotContainers[slot];
|
|
|
|
|
return inventorySlot.ContainedEntity != null && inventorySlot.CanRemove(inventorySlot.ContainedEntity);
|
2018-04-25 06:42:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Adds a new slot to this inventory component.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="slot">The name of the slot to add.</param>
|
|
|
|
|
/// <exception cref="InvalidOperationException">
|
|
|
|
|
/// Thrown if the slot with specified name already exists.
|
|
|
|
|
/// </exception>
|
|
|
|
|
public ContainerSlot AddSlot(Slots slot)
|
|
|
|
|
{
|
|
|
|
|
if (HasSlot(slot))
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException($"Slot '{slot}' already exists.");
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-19 18:54:04 +02:00
|
|
|
Dirty();
|
2020-08-15 20:33:42 +02:00
|
|
|
|
2021-03-06 02:32:23 -08:00
|
|
|
var container = ContainerHelpers.CreateContainer<ContainerSlot>(Owner, GetSlotString(slot));
|
|
|
|
|
container.OccludesLight = false;
|
|
|
|
|
_slotContainers[slot] = container;
|
2020-08-15 20:33:42 +02:00
|
|
|
|
|
|
|
|
OnItemChanged?.Invoke();
|
|
|
|
|
|
|
|
|
|
return _slotContainers[slot];
|
2018-04-25 06:42:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Removes a slot from this inventory component.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// If the slot contains an item, the item is dropped.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <param name="slot">The name of the slot to remove.</param>
|
|
|
|
|
public void RemoveSlot(Slots slot)
|
|
|
|
|
{
|
|
|
|
|
if (!HasSlot(slot))
|
|
|
|
|
{
|
2021-07-26 00:04:47 -07:00
|
|
|
throw new InvalidOperationException($"Slot '{slot}' does not exist.");
|
2018-04-25 06:42:35 -05:00
|
|
|
}
|
|
|
|
|
|
2020-08-21 14:54:38 +02:00
|
|
|
ForceUnequip(slot);
|
|
|
|
|
|
|
|
|
|
var container = _slotContainers[slot];
|
2018-04-25 06:42:35 -05:00
|
|
|
|
2020-08-21 14:54:38 +02:00
|
|
|
container.Shutdown();
|
2020-08-15 20:33:42 +02:00
|
|
|
_slotContainers.Remove(slot);
|
|
|
|
|
|
|
|
|
|
OnItemChanged?.Invoke();
|
|
|
|
|
|
2018-09-19 18:54:04 +02:00
|
|
|
Dirty();
|
2018-04-25 06:42:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks whether a slot with the specified name exists.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="slot">The slot name to check.</param>
|
|
|
|
|
/// <returns>True if the slot exists, false otherwise.</returns>
|
|
|
|
|
public bool HasSlot(Slots slot)
|
|
|
|
|
{
|
2020-08-15 20:33:42 +02:00
|
|
|
return _slotContainers.ContainsKey(slot);
|
2018-04-25 06:42:35 -05:00
|
|
|
}
|
|
|
|
|
|
2019-09-09 10:46:20 -07:00
|
|
|
/// <summary>
|
|
|
|
|
/// The underlying Container System just notified us that an entity was removed from it.
|
2020-06-18 22:52:44 +10:00
|
|
|
/// We need to make sure we process that removed entity as being unequipped from the slot.
|
2019-09-09 10:46:20 -07:00
|
|
|
/// </summary>
|
2021-06-18 01:49:18 -07:00
|
|
|
public void ForceUnequip(IContainer container, IEntity entity)
|
2019-09-09 10:46:20 -07:00
|
|
|
{
|
|
|
|
|
// make sure this is one of our containers.
|
|
|
|
|
// Technically the correct way would be to enumerate the possible slot names
|
|
|
|
|
// comparing with this container, but I might as well put the dictionary to good use.
|
2020-11-26 14:33:31 +01:00
|
|
|
if (container is not ContainerSlot slot || !_slotContainers.ContainsValue(slot))
|
2019-09-09 10:46:20 -07:00
|
|
|
return;
|
|
|
|
|
|
2021-03-16 15:50:20 +01:00
|
|
|
if (entity.TryGetComponent(out ItemComponent? itemComp))
|
2020-06-18 22:52:44 +10:00
|
|
|
{
|
2019-09-09 10:46:20 -07:00
|
|
|
itemComp.RemovedFromSlot();
|
2020-06-18 22:52:44 +10:00
|
|
|
}
|
2019-09-09 10:46:20 -07:00
|
|
|
|
2020-08-15 20:33:42 +02:00
|
|
|
OnItemChanged?.Invoke();
|
|
|
|
|
|
2019-09-09 10:46:20 -07:00
|
|
|
Dirty();
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-25 06:42:35 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Message that tells us to equip or unequip items from the inventory slots
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="msg"></param>
|
2020-11-10 22:47:43 +01:00
|
|
|
private async void HandleInventoryMessage(ClientInventoryMessage msg)
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2019-08-07 15:56:22 -07:00
|
|
|
switch (msg.Updatetype)
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2019-08-07 15:56:22 -07:00
|
|
|
case ClientInventoryUpdate.Equip:
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2019-08-07 15:56:22 -07:00
|
|
|
var hands = Owner.GetComponent<HandsComponent>();
|
2021-03-16 15:50:20 +01:00
|
|
|
var activeHand = hands.ActiveHand;
|
|
|
|
|
var activeItem = hands.GetActiveHand;
|
2021-09-16 12:16:14 +02:00
|
|
|
if (activeHand != null && activeItem != null && activeItem.Owner.TryGetComponent(out ItemComponent? item))
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2021-06-21 02:21:20 -07:00
|
|
|
hands.TryDropNoInteraction();
|
2021-09-16 12:16:14 +02:00
|
|
|
if (!Equip(msg.Inventoryslot, item, true, out var reason))
|
|
|
|
|
{
|
|
|
|
|
hands.PutInHand(item);
|
|
|
|
|
Owner.PopupMessageCursor(reason);
|
|
|
|
|
}
|
2018-04-25 06:42:35 -05:00
|
|
|
}
|
2021-01-11 19:24:09 +01:00
|
|
|
|
2019-08-07 15:56:22 -07:00
|
|
|
break;
|
2018-04-25 06:42:35 -05:00
|
|
|
}
|
2019-08-07 15:56:22 -07:00
|
|
|
case ClientInventoryUpdate.Use:
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
2019-08-07 15:56:22 -07:00
|
|
|
var interactionSystem = _entitySystemManager.GetEntitySystem<InteractionSystem>();
|
|
|
|
|
var hands = Owner.GetComponent<HandsComponent>();
|
|
|
|
|
var activeHand = hands.GetActiveHand;
|
|
|
|
|
var itemContainedInSlot = GetSlotItem(msg.Inventoryslot);
|
|
|
|
|
if (itemContainedInSlot != null)
|
|
|
|
|
{
|
|
|
|
|
if (activeHand != null)
|
|
|
|
|
{
|
2021-06-07 05:49:43 -07:00
|
|
|
await interactionSystem.InteractUsing(Owner, activeHand.Owner, itemContainedInSlot.Owner,
|
2021-01-08 10:29:08 -03:00
|
|
|
new EntityCoordinates());
|
2019-08-07 15:56:22 -07:00
|
|
|
}
|
|
|
|
|
else if (Unequip(msg.Inventoryslot))
|
|
|
|
|
{
|
|
|
|
|
hands.PutInHand(itemContainedInSlot);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-08 10:29:08 -03:00
|
|
|
|
2019-08-07 15:56:22 -07:00
|
|
|
break;
|
2018-04-25 06:42:35 -05:00
|
|
|
}
|
2020-07-26 07:25:38 -05:00
|
|
|
case ClientInventoryUpdate.Hover:
|
|
|
|
|
{
|
|
|
|
|
var hands = Owner.GetComponent<HandsComponent>();
|
|
|
|
|
var activeHand = hands.GetActiveHand;
|
|
|
|
|
if (activeHand != null && GetSlotItem(msg.Inventoryslot) == null)
|
|
|
|
|
{
|
2020-08-16 16:30:52 +02:00
|
|
|
var canEquip = CanEquip(msg.Inventoryslot, activeHand, true, out var reason);
|
2021-01-11 19:24:09 +01:00
|
|
|
_hoverEntity =
|
|
|
|
|
new KeyValuePair<Slots, (EntityUid entity, bool fits)>(msg.Inventoryslot,
|
|
|
|
|
(activeHand.Owner.Uid, canEquip));
|
2020-07-26 07:25:38 -05:00
|
|
|
|
|
|
|
|
Dirty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-04-25 06:42:35 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-20 10:36:02 +01:00
|
|
|
/// <inheritdoc />
|
2021-10-27 18:10:40 +02:00
|
|
|
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
|
2021-01-11 19:24:09 +01:00
|
|
|
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel,
|
2021-03-16 15:50:20 +01:00
|
|
|
ICommonSession? session = null)
|
2020-04-20 10:36:02 +01:00
|
|
|
{
|
|
|
|
|
base.HandleNetworkMessage(message, netChannel, session);
|
|
|
|
|
|
|
|
|
|
if (session == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(session));
|
|
|
|
|
}
|
2018-04-25 06:42:35 -05:00
|
|
|
|
2018-05-10 18:52:44 +02:00
|
|
|
switch (message)
|
2018-04-25 06:42:35 -05:00
|
|
|
{
|
|
|
|
|
case ClientInventoryMessage msg:
|
|
|
|
|
var playerentity = session.AttachedEntity;
|
|
|
|
|
|
|
|
|
|
if (playerentity == Owner)
|
|
|
|
|
HandleInventoryMessage(msg);
|
|
|
|
|
break;
|
2019-09-06 16:28:21 -07:00
|
|
|
|
2019-07-31 16:38:24 -07:00
|
|
|
case OpenSlotStorageUIMessage msg:
|
2019-09-06 16:28:21 -07:00
|
|
|
if (!HasSlot(msg.Slot)) // client input sanitization
|
|
|
|
|
return;
|
|
|
|
|
var item = GetSlotItem(msg.Slot);
|
2021-03-16 15:50:20 +01:00
|
|
|
if (item != null && item.Owner.TryGetComponent(out ServerStorageComponent? storage))
|
2019-07-31 16:38:24 -07:00
|
|
|
storage.OpenStorageUI(Owner);
|
|
|
|
|
break;
|
2018-04-25 06:42:35 -05:00
|
|
|
}
|
|
|
|
|
}
|
2018-09-19 18:54:04 +02:00
|
|
|
|
2021-11-30 15:20:38 +01:00
|
|
|
public override ComponentState GetComponentState()
|
2018-09-19 18:54:04 +02:00
|
|
|
{
|
|
|
|
|
var list = new List<KeyValuePair<Slots, EntityUid>>();
|
2020-08-15 20:33:42 +02:00
|
|
|
foreach (var (slot, container) in _slotContainers)
|
2018-09-19 18:54:04 +02:00
|
|
|
{
|
2020-10-28 19:19:47 +01:00
|
|
|
if (container != null && container.ContainedEntity != null)
|
2018-09-19 18:54:04 +02:00
|
|
|
{
|
|
|
|
|
list.Add(new KeyValuePair<Slots, EntityUid>(slot, container.ContainedEntity.Uid));
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-26 07:25:38 -05:00
|
|
|
|
2020-08-15 20:33:42 +02:00
|
|
|
var hover = _hoverEntity;
|
|
|
|
|
_hoverEntity = null;
|
2020-07-26 07:25:38 -05:00
|
|
|
|
|
|
|
|
return new InventoryComponentState(list, hover);
|
2018-09-19 18:54:04 +02:00
|
|
|
}
|
2020-06-21 21:57:22 +02:00
|
|
|
|
|
|
|
|
void IExAct.OnExplosion(ExplosionEventArgs eventArgs)
|
|
|
|
|
{
|
|
|
|
|
if (eventArgs.Severity < ExplosionSeverity.Heavy)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-15 20:33:42 +02:00
|
|
|
foreach (var slot in _slotContainers.Values.ToList())
|
2020-06-21 21:57:22 +02:00
|
|
|
{
|
|
|
|
|
foreach (var entity in slot.ContainedEntities)
|
|
|
|
|
{
|
2020-06-25 01:30:39 +10:00
|
|
|
var exActs = entity.GetAllComponents<IExAct>().ToList();
|
2020-06-21 21:57:22 +02:00
|
|
|
foreach (var exAct in exActs)
|
|
|
|
|
{
|
|
|
|
|
exAct.OnExplosion(eventArgs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-13 14:28:20 -08:00
|
|
|
|
|
|
|
|
public override bool IsEquipped(IEntity item)
|
|
|
|
|
{
|
|
|
|
|
if (item == null) return false;
|
|
|
|
|
foreach (var containerSlot in _slotContainers.Values)
|
|
|
|
|
{
|
|
|
|
|
// we don't want a recursive check here
|
|
|
|
|
if (containerSlot.Contains(item))
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-04-25 06:42:35 -05:00
|
|
|
}
|
|
|
|
|
}
|