using System; using System.Collections.Generic; using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces.GameObjects; using Content.Shared.GameObjects; using SS14.Server.GameObjects; using SS14.Server.GameObjects.Components.Container; using SS14.Server.Interfaces.Player; using SS14.Shared.GameObjects; using SS14.Shared.GameObjects.Serialization; using SS14.Shared.Input; using SS14.Shared.Interfaces.GameObjects; using SS14.Shared.Interfaces.Network; using SS14.Shared.IoC; namespace Content.Server.GameObjects { public class HandsComponent : SharedHandsComponent, IHandsComponent { private string activeIndex; public string ActiveIndex { get => activeIndex; set { if (!hands.ContainsKey(value)) { throw new ArgumentException($"No hand '{value}'"); } activeIndex = value; } } private Dictionary hands = new Dictionary(); private List orderedHands = new List(); // Mostly arbitrary. public const float PICKUP_RANGE = 2; public override void ExposeData(EntitySerializer serializer) { base.ExposeData(serializer); serializer.DataField(ref orderedHands, "hands", new List(0)); foreach (var handsname in orderedHands) { AddHand(handsname); } } public IEnumerable GetAllHeldItems() { foreach (var slot in hands.Values) { if (slot.ContainedEntity != null) { yield return slot.ContainedEntity.GetComponent(); } } } public ItemComponent GetHand(string index) { var slot = hands[index]; return slot.ContainedEntity?.GetComponent(); } public ItemComponent GetActiveHand => GetHand(ActiveIndex); /// /// Enumerates over the hand keys, returning the active hand first. /// private IEnumerable ActivePriorityEnumerable() { yield return ActiveIndex; foreach (var hand in hands.Keys) { if (hand == ActiveIndex) { continue; } yield return hand; } } public bool PutInHand(ItemComponent item) { foreach (var hand in ActivePriorityEnumerable()) { if (PutInHand(item, hand, fallback: false)) { return true; } } return false; } public bool PutInHand(ItemComponent item, string index, bool fallback = true) { if (!CanPutInHand(item, index)) { return fallback && PutInHand(item); } var slot = hands[index]; return slot.Insert(item.Owner); } public bool CanPutInHand(ItemComponent item) { foreach (var hand in ActivePriorityEnumerable()) { if (CanPutInHand(item, hand)) { return true; } } return false; } public bool CanPutInHand(ItemComponent item, string index) { var slot = hands[index]; return 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 Drop(string slot) { if (!CanDrop(slot)) { return false; } var inventorySlot = hands[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; 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 CanDrop(string slot) { var inventorySlot = hands[slot]; return inventorySlot.CanRemove(inventorySlot.ContainedEntity); } public void AddHand(string index) { if (HasHand(index)) { throw new InvalidOperationException($"Hand '{index}' already exists."); } var slot = ContainerManagerComponent.Create(Name + "_" + index, Owner); hands[index] = slot; if(!orderedHands.Contains(index)) { orderedHands.Add(index); } if (ActiveIndex == null) { ActiveIndex = index; } } public void RemoveHand(string index) { if (!HasHand(index)) { throw new InvalidOperationException($"Hand '{index}' does not exist."); } hands[index].Shutdown(); //TODO verify this hands.Remove(index); orderedHands.Remove(index); if (index == ActiveIndex) { if (orderedHands.Count == 0) { activeIndex = null; } else { activeIndex = orderedHands[0]; } } } public bool HasHand(string index) { return hands.ContainsKey(index); } /// /// Get the name of the slot passed to the inventory component. /// private string HandSlotName(string index) => $"_hand_{index}"; public override ComponentState GetComponentState() { var dict = new Dictionary(hands.Count); foreach (var hand in hands) { if (hand.Value.ContainedEntity != null) { dict[hand.Key] = hand.Value.ContainedEntity.Uid; } } return new HandsComponentState(dict, ActiveIndex); } private void SwapHands() { var index = orderedHands.FindIndex(x => x == ActiveIndex); index++; if (index >= orderedHands.Count) { index = 0; } ActiveIndex = orderedHands[index]; } public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null) { base.HandleMessage(message, netChannel, component); switch (message) { case ClientChangedHandMsg msg: { var playerMan = IoCManager.Resolve(); var session = playerMan.GetSessionByChannel(netChannel); var playerentity = session.AttachedEntity; if (playerentity == Owner && HasHand(msg.Index)) ActiveIndex = msg.Index; break; } case ActivateInhandMsg msg: { var playerMan = IoCManager.Resolve(); var session = playerMan.GetSessionByChannel(netChannel); var playerentity = session.AttachedEntity; var used = GetActiveHand?.Owner; if (playerentity == Owner && used != null) { InteractionSystem.TryUseInteraction(Owner, used); } break; } //Boundkeychangedmsg only works for the player entity and doesn't need any extra verification case BoundKeyChangedMsg msg: if (msg.State != BoundKeyState.Down) return; switch (msg.Function) { case BoundKeyFunctions.SwitchHands: SwapHands(); break; case BoundKeyFunctions.Drop: Drop(ActiveIndex); break; case BoundKeyFunctions.ActivateItemInHand: var used = GetActiveHand?.Owner; if(used != null) { InteractionSystem.TryUseInteraction(Owner, used); } break; } break; } } } }