using SS14.Server.GameObjects.Components.Container; using System; using System.Collections.Generic; using Content.Shared.GameObjects; using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines; using SS14.Shared.GameObjects; using SS14.Shared.Interfaces.GameObjects; using SS14.Shared.Interfaces.Network; using static Content.Shared.GameObjects.SharedInventoryComponent.ClientInventoryMessage; using SS14.Shared.IoC; using SS14.Server.Interfaces.Player; using SS14.Shared.ContentPack; using System.Linq; using SS14.Shared.Serialization; using SS14.Shared.Interfaces.GameObjects.Components; using SS14.Shared.Utility; using SS14.Shared.ViewVariables; namespace Content.Server.GameObjects { public class InventoryComponent : SharedInventoryComponent { [ViewVariables] private Dictionary SlotContainers = new Dictionary(); string TemplateName = "HumanInventory"; //stored for serialization purposes public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); serializer.DataField(ref TemplateName, "Template", "HumanInventory"); if (serializer.Reading) { CreateInventory(TemplateName); } } private void CreateInventory(string TemplateName) { Type type = AppDomain.CurrentDomain.GetAssemblyByName("Content.Shared") .GetType("Content.Shared.GameObjects." + TemplateName); Inventory inventory = (Inventory) Activator.CreateInstance(type); foreach (Slots slotnames in inventory.SlotMasks) { if (slotnames != Slots.NONE) { AddSlot(slotnames); } } } public override void OnRemove() { var slots = SlotContainers.Keys.ToList(); foreach (var slot in slots) { RemoveSlot(slot); } base.OnRemove(); } /// /// Helper to get container name for specified slot on this component /// /// /// private string GetSlotString(Slots slot) { return Name + "_" + Enum.GetName(typeof(Slots), slot); } /// /// Gets the clothing equipped to the specified slot. /// /// The slot to get the item for. /// Null if the slot is empty, otherwise the item. public ItemComponent GetSlotItem(Slots slot) { return SlotContainers[slot].ContainedEntity?.GetComponent(); } /// /// Equips slothing to the specified slot. /// /// /// This will fail if there is already an item in the specified slot. /// /// The slot to put the item in. /// The item to insert into the slot. /// True if the item was successfully inserted, false otherwise. public bool Equip(Slots slot, ClothingComponent clothing) { if (clothing == null) { throw new ArgumentNullException(nameof(clothing), "Clothing must be passed here. To remove some clothing from a slot, use Unequip()"); } if (clothing.SlotFlags == SlotFlags.PREVENTEQUIP //Flag to prevent equipping at all || (clothing.SlotFlags & SlotMasks[slot]) == 0 ) //Does the clothing flag have any of our requested slot flags { return false; } var inventorySlot = SlotContainers[slot]; if (!inventorySlot.Insert(clothing.Owner)) { return false; } clothing.EquippedToSlot(inventorySlot); Dirty(); return true; } /// /// Checks whether an item can be put in the specified slot. /// /// The slot to check for. /// The item to check for. /// True if the item can be inserted into the specified slot. public bool CanEquip(Slots slot, ClothingComponent item) { return SlotContainers[slot].CanInsert(item.Owner); } /// /// Drops the item in a slot. /// /// The slot to drop the item from. /// True if an item was dropped, false otherwise. public bool Unequip(Slots slot) { if (!CanUnequip(slot)) { return false; } var inventorySlot = SlotContainers[slot]; var item = inventorySlot.ContainedEntity.GetComponent(); if (!inventorySlot.Remove(inventorySlot.ContainedEntity)) { return false; } item.RemovedFromSlot(); // TODO: The item should be dropped to the container our owner is in, if any. var itemTransform = item.Owner.GetComponent(); itemTransform.LocalPosition = Owner.GetComponent().LocalPosition; Dirty(); return true; } /// /// Checks whether an item can be dropped from the specified slot. /// /// The slot to check for. /// /// True if there is an item in the slot and it can be dropped, false otherwise. /// public bool CanUnequip(Slots slot) { var InventorySlot = SlotContainers[slot]; return InventorySlot.ContainedEntity != null && InventorySlot.CanRemove(InventorySlot.ContainedEntity); } /// /// Adds a new slot to this inventory component. /// /// The name of the slot to add. /// /// Thrown if the slot with specified name already exists. /// public ContainerSlot AddSlot(Slots slot) { if (HasSlot(slot)) { throw new InvalidOperationException($"Slot '{slot}' already exists."); } Dirty(); return SlotContainers[slot] = ContainerManagerComponent.Create(GetSlotString(slot), Owner); } /// /// Removes a slot from this inventory component. /// /// /// If the slot contains an item, the item is dropped. /// /// The name of the slot to remove. public void RemoveSlot(Slots slot) { if (!HasSlot(slot)) { throw new InvalidOperationException($"Slow '{slot}' does not exist."); } if (GetSlotItem(slot) != null && !Unequip(slot)) { // TODO: Handle this potential failiure better. throw new InvalidOperationException( "Unable to remove slot as the contained clothing could not be dropped"); } SlotContainers.Remove(slot); Dirty(); } /// /// Checks whether a slot with the specified name exists. /// /// The slot name to check. /// True if the slot exists, false otherwise. public bool HasSlot(Slots slot) { return SlotContainers.ContainsKey(slot); } /// /// Message that tells us to equip or unequip items from the inventory slots /// /// private void HandleInventoryMessage(ClientInventoryMessage msg) { if (msg.Updatetype == ClientInventoryUpdate.Equip) { var hands = Owner.GetComponent(); var activehand = hands.GetActiveHand; if (activehand != null && activehand.Owner.TryGetComponent(out ClothingComponent clothing)) { hands.Drop(hands.ActiveIndex, null); if (!Equip(msg.Inventoryslot, clothing)) { hands.PutInHand(clothing); } } } else if (msg.Updatetype == ClientInventoryUpdate.Unequip) { var hands = Owner.GetComponent(); var activehand = hands.GetActiveHand; var itemcontainedinslot = GetSlotItem(msg.Inventoryslot); if (activehand == null && itemcontainedinslot != null && Unequip(msg.Inventoryslot)) { hands.PutInHand(itemcontainedinslot); } } } public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null) { base.HandleMessage(message, netChannel, component); switch (message) { case ClientInventoryMessage msg: var playerMan = IoCManager.Resolve(); var session = playerMan.GetSessionByChannel(netChannel); var playerentity = session.AttachedEntity; if (playerentity == Owner) HandleInventoryMessage(msg); break; } } public override ComponentState GetComponentState() { var list = new List>(); foreach (var (slot, container) in SlotContainers) { if (container.ContainedEntity != null) { list.Add(new KeyValuePair(slot, container.ContainedEntity.Uid)); } } return new InventoryComponentState(list); } } }