get that crap outta here (completely rewrites inventorysystem) (#5807)

* some work

* equip: done
unequip: todo

* unequipping done & refactored events

* workin

* movin

* reee namespaces

* stun

* mobstate

* fixes

* some work on events

* removes serverside itemcomp & misc fixes

* work

* smol merge fix

* ports template to prototype & finishes ui

* moves relay & adds containerenumerator

* actions & cuffs

* my god what is actioncode

* more fixes

* im loosing my grasp on reality

* more fixes

* more work

* explosions

* yes

* more work

* more fixes

* merge master & misc fixed because i forgot to commit before merging master

* more fixes

* fixes

* moar

* more work

* moar fixes

* suffixmap

* more work on client

* motivation low

* no. no containers

* mirroring client to server

* fixes

* move serverinvcomp

* serverinventorycomponent is dead

* gaming

* only strippable & ai left...

* only ai and richtext left

* fixes ai

* fixes

* fixes sprite layers

* more fixes

* resolves optional

* yes

* stable™️

* fixes

* moar fixes

* moar

* fix some tests

* lmao

* no comment

* good to merge™️

* fixes build but for real

* adresses some reviews

* adresses some more reviews

* nullables, yo

* fixes lobbyscreen

* timid refactor to differentiate actor & target

* adresses more reviews

* more

* my god what a mess

* removed the rest of duplicates

* removed duplicate slotflags and renamed shoes to feet

* removes another unused one

* yes

* fixes lobby & makes tryunequip return unequipped item

* fixes

* some funny renames

* fixes

* misc improvements to attemptevents

* fixes

* merge fixes

Co-authored-by: Paul Ritter <ritter.paul1@gmail.com>
This commit is contained in:
Paul Ritter
2021-12-30 22:56:10 +01:00
committed by GitHub
parent 7a5adb47a1
commit 512d6a38c3
199 changed files with 2493 additions and 3300 deletions

View File

@@ -1,40 +0,0 @@
using Content.Shared.Hands;
using Content.Shared.Inventory;
using Content.Shared.Popups;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.Inventory.Components
{
/// <summary>
/// Pops up a message when equipped / unequipped (including hands).
/// For debugging purposes.
/// </summary>
[RegisterComponent]
public class DebugEquipComponent : Component, IEquipped, IEquippedHand, IUnequipped, IUnequippedHand
{
[Dependency] private readonly IEntityManager _entMan = default!;
public override string Name => "DebugEquip";
void IEquipped.Equipped(EquippedEventArgs eventArgs)
{
eventArgs.User.PopupMessage("equipped " + _entMan.GetComponent<MetaDataComponent>(Owner).EntityName);
}
void IEquippedHand.EquippedHand(EquippedHandEventArgs eventArgs)
{
eventArgs.User.PopupMessage("equipped hand " + _entMan.GetComponent<MetaDataComponent>(Owner).EntityName);
}
void IUnequipped.Unequipped(UnequippedEventArgs eventArgs)
{
eventArgs.User.PopupMessage("unequipped " + _entMan.GetComponent<MetaDataComponent>(Owner).EntityName);
}
void IUnequippedHand.UnequippedHand(UnequippedHandEventArgs eventArgs)
{
eventArgs.User.PopupMessage("unequipped hand" + _entMan.GetComponent<MetaDataComponent>(Owner).EntityName);
}
}
}

View File

@@ -1,91 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using Content.Server.Items;
using Content.Shared.Item;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using static Content.Shared.Inventory.EquipmentSlotDefines;
namespace Content.Server.Inventory.Components
{
// Handles the special behavior of pockets/ID card slot and their relation to uniforms.
[RegisterComponent]
[ComponentReference(typeof(IInventoryController))]
public class HumanInventoryControllerComponent : Component, IInventoryController
{
public override string Name => "HumanInventoryController";
private InventoryComponent _inventory = default!;
protected override void Initialize()
{
base.Initialize();
_inventory = Owner.EnsureComponent<InventoryComponent>();
}
bool IInventoryController.CanEquip(Slots slot, EntityUid entity, bool flagsCheck, [NotNullWhen(false)] out string? reason)
{
var slotMask = SlotMasks[slot];
reason = null;
if ((slotMask & (SlotFlags.POCKET | SlotFlags.IDCARD)) != SlotFlags.NONE)
{
// Can't wear stuff in ID card or pockets unless you have a uniform.
if (_inventory.GetSlotItem(Slots.INNERCLOTHING) == null)
{
reason = Loc.GetString(slotMask == SlotFlags.IDCARD
? "human-inventory-controller-component-need-uniform-to-store-in-id-slot-text"
: "human-inventory-controller-component-need-uniform-to-store-in-pockets-text");
return false;
}
if (slotMask == SlotFlags.POCKET)
{
var itemComponent = IoCManager.Resolve<IEntityManager>().GetComponent<ItemComponent>(entity);
// If this item is small enough then it always fits in pockets.
if (itemComponent.Size <= (int) ReferenceSizes.Pocket)
{
return true;
}
else if (!flagsCheck)
{
reason = Loc.GetString("human-inventory-controller-component-too-large-text");
}
}
}
// Standard flag check.
return flagsCheck;
}
public void CheckUniformExists() { Owner.SpawnTimer(0, DropIdAndPocketsIfWeNoLongerHaveAUniform); }
// Hey, it's descriptive.
private void DropIdAndPocketsIfWeNoLongerHaveAUniform()
{
if (Deleted)
{
return;
}
if (_inventory.GetSlotItem(Slots.INNERCLOTHING) != null)
{
return;
}
void DropMaybe(Slots slot)
{
if (_inventory.GetSlotItem(slot) != null)
{
_inventory.Unequip(slot);
}
}
DropMaybe(Slots.POCKET1);
DropMaybe(Slots.POCKET2);
DropMaybe(Slots.IDCARD);
}
}
}

View File

@@ -1,28 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using Robust.Shared.GameObjects;
using static Content.Shared.Inventory.EquipmentSlotDefines;
namespace Content.Server.Inventory.Components
{
/// <summary>
/// Allows for overriding inventory-related behavior on an entity.
/// </summary>
public interface IInventoryController
{
/// <summary>
/// Can be implemented to override "can this item be equipped" behavior.
/// </summary>
/// <param name="slot">The slot to be equipped into.</param>
/// <param name="entity">The entity to equip.</param>
/// <param name="flagsCheck">Whether the entity passes default slot masks & flags checks.</param>
/// <param name="reason">The translated reason why the item cannot be equiped, if this function returns false. Can be null.</param>
/// <returns>True if the entity can be equipped, false otherwise</returns>
bool CanEquip(Slots slot, EntityUid entity, bool flagsCheck, [NotNullWhen(false)] out string? reason)
{
reason = null;
return flagsCheck;
}
bool CanEquip(Slots slot, EntityUid entity, bool flagsCheck) => CanEquip(slot, entity, flagsCheck, out _);
}
}

View File

@@ -1,576 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
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.EntitySystems;
using Content.Shared.Popups;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Players;
using Robust.Shared.ViewVariables;
using static Content.Shared.Inventory.EquipmentSlotDefines;
using static Content.Shared.Inventory.SharedInventoryComponent.ClientInventoryMessage;
namespace Content.Server.Inventory.Components
{
[RegisterComponent]
[ComponentReference(typeof(SharedInventoryComponent))]
public class InventoryComponent : SharedInventoryComponent, IExAct
{
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
[Dependency] private readonly IEntityManager _entities = default!;
[ViewVariables] private readonly Dictionary<Slots, ContainerSlot> _slotContainers = new();
private KeyValuePair<Slots, (EntityUid entity, bool fits)>? _hoverEntity;
public IEnumerable<Slots> Slots => _slotContainers.Keys;
public event Action? OnItemChanged;
protected override void Initialize()
{
base.Initialize();
foreach (var slotName in InventoryInstance.SlotMasks)
{
if (slotName != EquipmentSlotDefines.Slots.NONE)
{
AddSlot(slotName);
}
}
}
protected override void OnRemove()
{
var slots = _slotContainers.Keys.ToList();
foreach (var slot in slots)
{
if (TryGetSlotItem(slot, out ItemComponent? item))
{
_entities.DeleteEntity(item.Owner);
}
RemoveSlot(slot);
}
base.OnRemove();
}
public IEnumerable<EntityUid> GetAllHeldItems()
{
foreach (var (_, container) in _slotContainers)
{
foreach (var entity in container.ContainedEntities)
{
yield return entity;
}
}
}
/// <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>
public ItemComponent? GetSlotItem(Slots slot)
{
return GetSlotItem<ItemComponent>(slot);
}
public IEnumerable<T?> LookupItems<T>() where T : Component
{
return _slotContainers.Values
.SelectMany(x => x.ContainedEntities.Select(e => _entities.GetComponentOrNull<T>(e)))
.Where(x => x != null);
}
public T? GetSlotItem<T>(Slots slot) where T : ItemComponent
{
if (!_slotContainers.ContainsKey(slot))
{
return null;
}
var containedEntity = _slotContainers[slot].ContainedEntity;
if (containedEntity != null && _entities.GetComponent<MetaDataComponent>(containedEntity.Value).EntityDeleted)
{
_slotContainers.Remove(slot);
containedEntity = null;
Dirty();
}
return containedEntity.HasValue ? _entities.GetComponent<T>(containedEntity.Value) : null;
}
public bool TryGetSlotItem<T>(Slots slot, [NotNullWhen(true)] out T? itemComponent) where T : ItemComponent
{
itemComponent = GetSlotItem<T>(slot);
return itemComponent != null;
}
/// <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>
/// <param name="item">The item to insert into the slot.</param>
/// <param name="mobCheck">Whether to perform an ActionBlocker check to the entity.</param>
/// <param name="reason">The translated reason why the item cannot be equipped, if this function returns false. Can be null.</param>
/// <returns>True if the item was successfully inserted, false otherwise.</returns>
public bool Equip(Slots slot, ItemComponent item, bool mobCheck, [NotNullWhen(false)] out string? reason)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item),
"Clothing must be passed here. To remove some clothing from a slot, use Unequip()");
}
if (!CanEquip(slot, item, mobCheck, out reason))
{
return false;
}
var inventorySlot = _slotContainers[slot];
if (!inventorySlot.Insert(item.Owner))
{
reason = Loc.GetString("inventory-component-on-equip-cannot");
return false;
}
// 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.
if (mobCheck && item is ClothingComponent { EquipSound: {} equipSound })
{
SoundSystem.Play(Filter.Pvs(Owner), equipSound.GetSound(), Owner, AudioParams.Default.WithVolume(-2f));
}
_entitySystemManager.GetEntitySystem<InteractionSystem>().EquippedInteraction(Owner, item.Owner, slot);
OnItemChanged?.Invoke();
Dirty();
UpdateMovementSpeed();
return true;
}
public bool Equip(Slots slot, ItemComponent item, bool mobCheck = true) =>
Equip(slot, item, mobCheck, out var _);
public bool Equip(Slots slot, EntityUid entity, bool mobCheck = true) =>
Equip(slot, _entities.GetComponent<ItemComponent>(entity), mobCheck);
/// <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>
/// <param name="reason">The translated reason why the item cannot be equiped, if this function returns false. Can be null.</param>
/// <returns>True if the item can be inserted into the specified slot.</returns>
public bool CanEquip(Slots slot, ItemComponent item, bool mobCheck, [NotNullWhen(false)] out string? reason)
{
var pass = false;
reason = null;
if (mobCheck && !EntitySystem.Get<ActionBlockerSystem>().CanEquip(Owner))
{
reason = Loc.GetString("inventory-component-can-equip-cannot");
return false;
}
if (item is ClothingComponent clothing)
{
if (clothing.SlotFlags != SlotFlags.PREVENTEQUIP && (clothing.SlotFlags & SlotMasks[slot]) != 0)
{
pass = true;
}
else
{
reason = Loc.GetString("inventory-component-can-equip-does-not-fit");
}
}
if (_entities.TryGetComponent(Owner, out IInventoryController? controller))
{
pass = controller.CanEquip(slot, item.Owner, pass, out var controllerReason);
reason = controllerReason ?? reason;
}
if (!pass)
{
reason = reason ?? Loc.GetString("inventory-component-can-equip-cannot");
return false;
}
var canEquip = pass && _slotContainers[slot].CanInsert(item.Owner);
if (!canEquip)
{
reason = Loc.GetString("inventory-component-can-equip-cannot");
}
return canEquip;
}
public bool CanEquip(Slots slot, ItemComponent item, bool mobCheck = true) =>
CanEquip(slot, item, mobCheck, out var _);
public bool CanEquip(Slots slot, EntityUid entity, bool mobCheck = true) =>
CanEquip(slot, _entities.GetComponent<ItemComponent>(entity), mobCheck);
/// <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>
/// <param name="mobCheck">Whether to perform an ActionBlocker check to the entity.</param>
public bool Unequip(Slots slot, bool mobCheck = true)
{
if (!CanUnequip(slot, mobCheck))
{
return false;
}
var inventorySlot = _slotContainers[slot];
if (inventorySlot.ContainedEntity is not {Valid: true} entity)
{
return false;
}
if (!inventorySlot.Remove(entity))
{
return false;
}
// TODO: The item should be dropped to the container our owner is in, if any.
_entities.GetComponent<TransformComponent>(entity).AttachParentToContainerOrGrid();
_entitySystemManager.GetEntitySystem<InteractionSystem>().UnequippedInteraction(Owner, entity, slot);
OnItemChanged?.Invoke();
Dirty();
UpdateMovementSpeed();
return true;
}
private void UpdateMovementSpeed()
{
EntitySystem.Get<MovementSpeedModifierSystem>().RefreshMovementSpeedModifiers(Owner);
}
public void ForceUnequip(Slots slot)
{
var inventorySlot = _slotContainers[slot];
if (inventorySlot.ContainedEntity is not {Valid: true} entity)
{
return;
}
var item = _entities.GetComponent<ItemComponent>(entity);
inventorySlot.ForceRemove(entity);
var itemTransform = _entities.GetComponent<TransformComponent>(entity);
itemTransform.AttachParentToContainerOrGrid();
_entitySystemManager.GetEntitySystem<InteractionSystem>().UnequippedInteraction(Owner, item.Owner, slot);
OnItemChanged?.Invoke();
Dirty();
}
/// <summary>
/// Checks whether an item can be dropped from the specified slot.
/// </summary>
/// <param name="slot">The slot to check for.</param>
/// <param name="mobCheck">Whether to perform an ActionBlocker check to the entity.</param>
/// <returns>
/// True if there is an item in the slot and it can be dropped, false otherwise.
/// </returns>
public bool CanUnequip(Slots slot, bool mobCheck = true)
{
if (mobCheck && !EntitySystem.Get<ActionBlockerSystem>().CanUnequip(Owner))
return false;
var inventorySlot = _slotContainers[slot];
return inventorySlot.ContainedEntity != null && inventorySlot.CanRemove(inventorySlot.ContainedEntity.Value);
}
/// <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.");
}
Dirty();
var container = ContainerHelpers.CreateContainer<ContainerSlot>(Owner, GetSlotString(slot));
container.OccludesLight = false;
_slotContainers[slot] = container;
OnItemChanged?.Invoke();
return _slotContainers[slot];
}
/// <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))
{
throw new InvalidOperationException($"Slot '{slot}' does not exist.");
}
ForceUnequip(slot);
var container = _slotContainers[slot];
container.Shutdown();
_slotContainers.Remove(slot);
OnItemChanged?.Invoke();
Dirty();
}
/// <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)
{
return _slotContainers.ContainsKey(slot);
}
/// <summary>
/// The underlying Container System just notified us that an entity was removed from it.
/// We need to make sure we process that removed entity as being unequipped from the slot.
/// </summary>
public void ForceUnequip(IContainer container, EntityUid entity)
{
// 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.
if (container is not ContainerSlot slot || !_slotContainers.ContainsValue(slot))
return;
if (_entities.TryGetComponent(entity, out ItemComponent? itemComp))
{
itemComp.RemovedFromSlot();
}
OnItemChanged?.Invoke();
Dirty();
}
/// <summary>
/// Message that tells us to equip or unequip items from the inventory slots
/// </summary>
/// <param name="msg"></param>
private async void HandleInventoryMessage(ClientInventoryMessage msg)
{
switch (msg.Updatetype)
{
case ClientInventoryUpdate.Equip:
{
var hands = _entities.GetComponent<HandsComponent>(Owner);
var activeHand = hands.ActiveHand;
var activeItem = hands.GetActiveHand;
if (activeHand != null && activeItem != null && _entities.TryGetComponent(activeItem.Owner, out ItemComponent? item))
{
hands.TryDropNoInteraction();
if (!Equip(msg.Inventoryslot, item, true, out var reason))
{
hands.PutInHand(item);
Owner.PopupMessageCursor(reason);
}
}
break;
}
case ClientInventoryUpdate.Use:
{
var interactionSystem = _entitySystemManager.GetEntitySystem<InteractionSystem>();
var hands = _entities.GetComponent<HandsComponent>(Owner);
var activeHand = hands.GetActiveHand;
var itemContainedInSlot = GetSlotItem(msg.Inventoryslot);
if (itemContainedInSlot != null)
{
if (activeHand != null)
{
await interactionSystem.InteractUsing(Owner, activeHand.Owner, itemContainedInSlot.Owner,
new EntityCoordinates());
}
else if (Unequip(msg.Inventoryslot))
{
hands.PutInHand(itemContainedInSlot);
}
}
break;
}
case ClientInventoryUpdate.Hover:
{
var hands = _entities.GetComponent<HandsComponent>(Owner);
var activeHand = hands.GetActiveHand;
if (activeHand != null && GetSlotItem(msg.Inventoryslot) == null)
{
var canEquip = CanEquip(msg.Inventoryslot, activeHand, true, out var reason);
_hoverEntity =
new KeyValuePair<Slots, (EntityUid entity, bool fits)>(msg.Inventoryslot,
(Uid: activeHand.Owner, canEquip));
Dirty();
}
break;
}
}
}
/// <inheritdoc />
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel,
ICommonSession? session = null)
{
base.HandleNetworkMessage(message, netChannel, session);
if (session == null)
{
throw new ArgumentNullException(nameof(session));
}
switch (message)
{
case ClientInventoryMessage msg:
var playerentity = session.AttachedEntity;
if (playerentity == Owner)
HandleInventoryMessage(msg);
break;
case OpenSlotStorageUIMessage msg:
if (!HasSlot(msg.Slot)) // client input sanitization
return;
var item = GetSlotItem(msg.Slot);
if (item != null && _entities.TryGetComponent(item.Owner, out ServerStorageComponent? storage))
storage.OpenStorageUI(Owner);
break;
}
}
public override ComponentState GetComponentState()
{
var list = new List<KeyValuePair<Slots, EntityUid>>();
foreach (var (slot, container) in _slotContainers)
{
if (container is {ContainedEntity: { }})
{
list.Add(new KeyValuePair<Slots, EntityUid>(slot, container.ContainedEntity.Value));
}
}
var hover = _hoverEntity;
_hoverEntity = null;
return new InventoryComponentState(list, hover);
}
void IExAct.OnExplosion(ExplosionEventArgs eventArgs)
{
if (eventArgs.Severity < ExplosionSeverity.Heavy)
{
return;
}
foreach (var slot in _slotContainers.Values.ToList())
{
foreach (var entity in slot.ContainedEntities)
{
var exActs = _entities.GetComponents<IExAct>(entity).ToList();
foreach (var exAct in exActs)
{
exAct.OnExplosion(eventArgs);
}
}
}
}
public override bool IsEquipped(EntityUid item)
{
if (item == default) return false;
foreach (var containerSlot in _slotContainers.Values)
{
// we don't want a recursive check here
if (containerSlot.Contains(item))
{
return true;
}
}
return false;
}
public override bool TryGetSlot(Slots slot, [NotNullWhen(true)] out EntityUid? item)
{
if (_slotContainers.TryGetValue(slot, out var container))
{
item = container.ContainedEntity;
return item != null;
}
item = null;
return false;
}
}
}

View File

@@ -1,48 +0,0 @@
using Content.Server.Inventory.Components;
using Content.Server.Items;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using static Content.Shared.Inventory.EquipmentSlotDefines;
namespace Content.Server.Inventory
{
public static class InventoryHelpers
{
public static bool SpawnItemInSlot(this InventoryComponent inventory, Slots slot, string prototype, bool mobCheck = false)
{
var entityManager = IoCManager.Resolve<IEntityManager>();
var protoManager = IoCManager.Resolve<IPrototypeManager>();
var user = inventory.Owner;
// Let's do nothing if the owner of the inventory has been deleted.
if (entityManager.Deleted(user))
return false;
// If we don't have that slot or there's already an item there, we do nothing.
if (!inventory.HasSlot(slot) || inventory.TryGetSlotItem(slot, out ItemComponent? _))
return false;
// If the prototype in question doesn't exist, we do nothing.
if (!protoManager.HasIndex<EntityPrototype>(prototype))
return false;
// Let's spawn this first...
var item = entityManager.SpawnEntity(prototype, entityManager.GetComponent<TransformComponent>(user).MapPosition);
// Helper method that deletes the item and returns false.
bool DeleteItem()
{
entityManager.DeleteEntity(item);
return false;
}
// If this doesn't have an item component, then we can't do anything with it.
if (!entityManager.TryGetComponent(item, out ItemComponent? itemComp))
return DeleteItem();
// We finally try to equip the item, otherwise we delete it.
return inventory.Equip(slot, itemComp, mobCheck) || DeleteItem();
}
}
}

View File

@@ -1,88 +0,0 @@
using Content.Server.Atmos;
using Content.Server.Inventory.Components;
using Content.Server.Items;
using Content.Server.Temperature.Systems;
using Content.Shared.Inventory;
using Content.Shared.Slippery;
using Content.Shared.Damage;
using Content.Shared.Electrocution;
using Content.Shared.Movement.EntitySystems;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
namespace Content.Server.Inventory
{
class InventorySystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<HumanInventoryControllerComponent, EntRemovedFromContainerMessage>(HandleRemovedFromContainer);
SubscribeLocalEvent<InventoryComponent, EntRemovedFromContainerMessage>(HandleInvRemovedFromContainer);
SubscribeLocalEvent<InventoryComponent, HighPressureEvent>(OnHighPressureEvent);
SubscribeLocalEvent<InventoryComponent, LowPressureEvent>(OnLowPressureEvent);
SubscribeLocalEvent<InventoryComponent, DamageModifyEvent>(OnDamageModify);
SubscribeLocalEvent<InventoryComponent, ElectrocutionAttemptEvent>(OnElectrocutionAttempt);
SubscribeLocalEvent<InventoryComponent, SlipAttemptEvent>(OnSlipAttemptEvent);
SubscribeLocalEvent<InventoryComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovespeed);
SubscribeLocalEvent<InventoryComponent, ModifyChangedTemperatureEvent>(OnModifyTemperature);
}
private void OnModifyTemperature(EntityUid uid, InventoryComponent component, ModifyChangedTemperatureEvent args)
{
RelayInventoryEvent(component, args);
}
private void OnSlipAttemptEvent(EntityUid uid, InventoryComponent component, SlipAttemptEvent args)
{
if (component.TryGetSlotItem(EquipmentSlotDefines.Slots.SHOES, out ItemComponent? shoes))
{
RaiseLocalEvent(shoes.Owner, args, false);
}
}
private void OnRefreshMovespeed(EntityUid uid, InventoryComponent component, RefreshMovementSpeedModifiersEvent args)
{
RelayInventoryEvent(component, args);
}
private static void HandleInvRemovedFromContainer(EntityUid uid, InventoryComponent component, EntRemovedFromContainerMessage args)
{
component.ForceUnequip(args.Container, args.Entity);
}
private static void HandleRemovedFromContainer(EntityUid uid, HumanInventoryControllerComponent component, EntRemovedFromContainerMessage args)
{
component.CheckUniformExists();
}
private void OnHighPressureEvent(EntityUid uid, InventoryComponent component, HighPressureEvent args)
{
RelayInventoryEvent(component, args);
}
private void OnLowPressureEvent(EntityUid uid, InventoryComponent component, LowPressureEvent args)
{
RelayInventoryEvent(component, args);
}
private void OnElectrocutionAttempt(EntityUid uid, InventoryComponent component, ElectrocutionAttemptEvent args)
{
RelayInventoryEvent(component, args);
}
private void OnDamageModify(EntityUid uid, InventoryComponent component, DamageModifyEvent args)
{
RelayInventoryEvent(component, args);
}
private void RelayInventoryEvent<T>(InventoryComponent component, T args) where T : EntityEventArgs
{
foreach (var equipped in component.GetAllHeldItems())
{
RaiseLocalEvent(equipped, args, false);
}
}
}
}

View File

@@ -0,0 +1,8 @@
using Content.Shared.Inventory;
using Robust.Shared.GameObjects;
namespace Content.Server.Inventory;
[RegisterComponent]
[ComponentReference(typeof(InventoryComponent))]
public class ServerInventoryComponent : InventoryComponent { }

View File

@@ -0,0 +1,70 @@
using Content.Server.Atmos;
using Content.Server.Hands.Components;
using Content.Server.Interaction;
using Content.Server.Storage.Components;
using Content.Server.Temperature.Systems;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using InventoryComponent = Content.Shared.Inventory.InventoryComponent;
namespace Content.Server.Inventory
{
class ServerInventorySystem : InventorySystem
{
[Dependency] private readonly InteractionSystem _interactionSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<InventoryComponent, HighPressureEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, LowPressureEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, ModifyChangedTemperatureEvent>(RelayInventoryEvent);
SubscribeNetworkEvent<TryEquipNetworkMessage>(OnNetworkEquip);
SubscribeNetworkEvent<TryUnequipNetworkMessage>(OnNetworkUnequip);
SubscribeNetworkEvent<OpenSlotStorageNetworkMessage>(OnOpenSlotStorage);
SubscribeNetworkEvent<UseSlotNetworkMessage>(OnUseSlot);
}
private void OnUseSlot(UseSlotNetworkMessage ev)
{
if (!TryComp<HandsComponent>(ev.Uid, out var hands) || !TryGetSlotEntity(ev.Uid, ev.Slot, out var itemUid))
return;
var activeHand = hands.GetActiveHand;
if (activeHand != null)
{
_interactionSystem.InteractUsing(ev.Uid, activeHand.Owner, itemUid.Value,
new EntityCoordinates());
}
else if (TryUnequip(ev.Uid, ev.Slot))
{
hands.PutInHand(itemUid.Value);
}
}
private void OnOpenSlotStorage(OpenSlotStorageNetworkMessage ev)
{
if (TryGetSlotEntity(ev.Uid, ev.Slot, out var entityUid) && TryComp<ServerStorageComponent>(entityUid, out var storageComponent))
{
storageComponent.OpenStorageUI(ev.Uid);
}
}
private void OnNetworkUnequip(TryUnequipNetworkMessage ev)
{
TryUnequip(ev.Actor, ev.Target, ev.Slot, ev.Silent, ev.Force);
}
private void OnNetworkEquip(TryEquipNetworkMessage ev)
{
TryEquip(ev.Actor, ev.Target, ev.ItemUid, ev.Slot, ev.Silent, ev.Force);
}
}
}