Various minor interaction features. There is no concept of gravity in the game yet, so items drift through space or the hallways of a station until they are stopped. Thrown items do not collide with walls because making them collidable makes them collide with EVERYTHING, including the player. We need collision groups in the physics system. Because of the previous problems the velocity things are throw at is set quite low, it can be tweaked after the other mentioned issues are resolved.
280 lines
10 KiB
C#
280 lines
10 KiB
C#
using SS14.Server.GameObjects;
|
|
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 static Content.Shared.GameObjects.SharedInventoryComponent.ServerInventoryMessage;
|
|
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;
|
|
|
|
namespace Content.Server.GameObjects
|
|
{
|
|
public class InventoryComponent : SharedInventoryComponent
|
|
{
|
|
private Dictionary<Slots, ContainerSlot> SlotContainers = new Dictionary<Slots, ContainerSlot>();
|
|
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)
|
|
{
|
|
var newslot = AddSlot(slotnames);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void OnRemove()
|
|
{
|
|
var slots = SlotContainers.Keys.ToList();
|
|
foreach (var slot in slots)
|
|
{
|
|
RemoveSlot(slot);
|
|
}
|
|
base.OnRemove();
|
|
}
|
|
|
|
/// <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 SlotContainers[slot].ContainedEntity?.GetComponent<ItemComponent>();
|
|
}
|
|
|
|
/// <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="clothing">The item to insert into the slot.</param>
|
|
/// <returns>True if the item was successfully inserted, false otherwise.</returns>
|
|
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);
|
|
|
|
var UIupdatemessage = new ServerInventoryMessage()
|
|
{
|
|
Inventoryslot = slot,
|
|
EntityUid = clothing.Owner.Uid,
|
|
Updatetype = ServerInventoryUpdate.Addition
|
|
};
|
|
|
|
SendNetworkMessage(UIupdatemessage);
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <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>
|
|
/// <returns>True if the item can be inserted into the specified slot.</returns>
|
|
public bool CanEquip(Slots slot, ClothingComponent item)
|
|
{
|
|
return SlotContainers[slot].CanInsert(item.Owner);
|
|
}
|
|
|
|
/// <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>
|
|
public bool Unequip(Slots slot)
|
|
{
|
|
if (!CanUnequip(slot))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var inventorySlot = SlotContainers[slot];
|
|
var item = inventorySlot.ContainedEntity.GetComponent<ItemComponent>();
|
|
if (!inventorySlot.Remove(inventorySlot.ContainedEntity))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var UIupdatemessage = new ServerInventoryMessage()
|
|
{
|
|
Inventoryslot = slot,
|
|
EntityUid = item.Owner.Uid,
|
|
Updatetype = ServerInventoryUpdate.Removal
|
|
};
|
|
SendNetworkMessage(UIupdatemessage);
|
|
|
|
item.RemovedFromSlot();
|
|
|
|
// TODO: The item should be dropped to the container our owner is in, if any.
|
|
var itemTransform = item.Owner.GetComponent<ITransformComponent>();
|
|
itemTransform.LocalPosition = Owner.GetComponent<ITransformComponent>().LocalPosition;
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks whether an item can be dropped from the specified slot.
|
|
/// </summary>
|
|
/// <param name="slot">The slot to check for.</param>
|
|
/// <returns>
|
|
/// True if there is an item in the slot and it can be dropped, false otherwise.
|
|
/// </returns>
|
|
public bool CanUnequip(Slots slot)
|
|
{
|
|
var InventorySlot = SlotContainers[slot];
|
|
return InventorySlot.ContainedEntity != null && InventorySlot.CanRemove(InventorySlot.ContainedEntity);
|
|
}
|
|
|
|
/// <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.");
|
|
}
|
|
|
|
return SlotContainers[slot] = ContainerManagerComponent.Create<ContainerSlot>(GetSlotString(slot), Owner);
|
|
}
|
|
|
|
/// <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($"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);
|
|
}
|
|
|
|
/// <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>
|
|
/// Message that tells us to equip or unequip items from the inventory slots
|
|
/// </summary>
|
|
/// <param name="msg"></param>
|
|
private void HandleInventoryMessage(ClientInventoryMessage msg)
|
|
{
|
|
if (msg.Updatetype == ClientInventoryUpdate.Equip)
|
|
{
|
|
var hands = Owner.GetComponent<HandsComponent>();
|
|
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<HandsComponent>();
|
|
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<IPlayerManager>();
|
|
var session = playerMan.GetSessionByChannel(netChannel);
|
|
var playerentity = session.AttachedEntity;
|
|
|
|
if (playerentity == Owner)
|
|
HandleInventoryMessage(msg);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|