using System; using System.Collections.Generic; using System.Linq; using Content.Client.GameObjects.Components.Clothing; using Content.Client.UserInterface; using Content.Shared.GameObjects; using Robust.Client.GameObjects; using Robust.Client.Interfaces.GameObjects.Components; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Network; using Robust.Shared.Interfaces.Reflection; using Robust.Shared.IoC; using Robust.Shared.Serialization; using Robust.Shared.Utility; using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines; using static Content.Shared.GameObjects.SharedInventoryComponent.ClientInventoryMessage; namespace Content.Client.GameObjects { /// /// A character UI which shows items the user has equipped within his inventory /// public class ClientInventoryComponent : SharedInventoryComponent { private readonly Dictionary _slots = new Dictionary(); [Dependency] #pragma warning disable 649 private readonly IGameHud _gameHud; #pragma warning restore 649 /// /// Holds the godot control for the inventory window /// private InventoryWindow _window; public SS14Window Window => _window; private string _templateName = "HumanInventory"; //stored for serialization purposes /// /// Inventory template after being loaded from instance creator and string name /// private Inventory _inventory; private ISpriteComponent _sprite; public override void OnRemove() { base.OnRemove(); _window.Dispose(); } public override void Initialize() { base.Initialize(); //Loads inventory template var reflectionManager = IoCManager.Resolve(); var type = reflectionManager.LooseGetType(_templateName); DebugTools.Assert(type != null); _inventory = (Inventory) Activator.CreateInstance(type); //Creates godot control class for inventory _window = new InventoryWindow(this); _window.CreateInventory(_inventory); _window.OnClose += () => _gameHud.InventoryButtonDown = false; if (Owner.TryGetComponent(out _sprite)) { foreach (var mask in _inventory.SlotMasks.OrderBy(s => _inventory.SlotDrawingOrder(s))) { if (mask == Slots.NONE) { continue; } _sprite.LayerMapReserveBlank(mask); } } // Component state already came in but we couldn't set anything visually because, well, we didn't initialize yet. foreach (var (slot, entity) in _slots) { _setSlot(slot, entity); } } public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); serializer.DataField(ref _templateName, "Template", "HumanInventory"); } public override void HandleComponentState(ComponentState curState, ComponentState nextState) { base.HandleComponentState(curState, nextState); if (curState == null) return; var cast = (InventoryComponentState) curState; var doneSlots = new HashSet(); foreach (var (slot, entityUid) in cast.Entities) { if (_slots.ContainsKey(slot)) { _slots.Remove(slot); _clearSlot(slot); } var entity = Owner.EntityManager.GetEntity(entityUid); _slots[slot] = entity; _setSlot(slot, entity); doneSlots.Add(slot); } foreach (var slot in _slots.Keys.ToList()) { if (!doneSlots.Contains(slot)) { _clearSlot(slot); _slots.Remove(slot); } } } private void _setSlot(Slots slot, IEntity entity) { if (_sprite != null && entity.TryGetComponent(out ClothingComponent clothing)) { var flag = SlotMasks[slot]; var data = clothing.GetEquippedStateInfo(flag); if (data == null) { _sprite.LayerSetVisible(slot, false); } else { var (rsi, state) = data.Value; _sprite.LayerSetVisible(slot, true); _sprite.LayerSetRSI(slot, rsi); _sprite.LayerSetState(slot, state); } } _window?.AddToSlot(slot, entity); } private void _clearSlot(Slots slot) { _window?.RemoveFromSlot(slot); _sprite?.LayerSetVisible(slot, false); } public void SendUnequipMessage(Slots slot) { var unequipmessage = new ClientInventoryMessage(slot, ClientInventoryUpdate.Unequip); SendNetworkMessage(unequipmessage); } public void SendEquipMessage(Slots slot) { var equipmessage = new ClientInventoryMessage(slot, ClientInventoryUpdate.Equip); SendNetworkMessage(equipmessage); } public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null) { base.HandleMessage(message, netChannel, component); switch (message) { case PlayerAttachedMsg _: _gameHud.InventoryButtonVisible = true; _gameHud.InventoryButtonToggled = b => { if (b) { Window.Open(); } else { Window.Close(); } }; break; case PlayerDetachedMsg _: _gameHud.InventoryButtonVisible = false; _window.Close(); break; } } /// /// Temporary window to hold the basis for inventory hud /// private sealed class InventoryWindow : SS14Window { private List IndexedSlots; private Dictionary InventorySlots = new Dictionary(); //ordered dictionary? private readonly ClientInventoryComponent InventoryComponent; private readonly GridContainer _gridContainer; public InventoryWindow(ClientInventoryComponent inventory) { Title = "Inventory"; InventoryComponent = inventory; _gridContainer = new GridContainer(); Contents.AddChild(_gridContainer); Size = CombinedMinimumSize; } /// /// Creates a grid container filled with slot buttons loaded from an inventory template /// public void CreateInventory(Inventory inventory) { _gridContainer.Columns = inventory.Columns; IndexedSlots = new List(inventory.SlotMasks); foreach (var slot in IndexedSlots) { var newButton = new InventoryButton(slot); if (slot == Slots.NONE) { //TODO: Re-enable when godot grid container maintains grid with invisible elements //newbutton.Visible = false; } else { // Store slot button and give it the default onpress behavior for empty elements newButton.GetChild