using System; using System.Collections.Generic; using System.Linq; using Content.Client.Animations; using Content.Client.Hands; using Content.Client.Items.Managers; using Content.Client.Items.Components; using Content.Client.UserInterface.Controls; using Content.Shared.DragDrop; using Content.Shared.Storage; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.Player; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Shared.GameObjects; using Robust.Shared.Input; using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Maths; using Robust.Shared.Network; using Robust.Shared.Players; using static Robust.Client.UserInterface.Control; using static Robust.Client.UserInterface.Controls.BoxContainer; namespace Content.Client.Storage { /// /// Client version of item storage containers, contains a UI which displays stored entities and their size /// [RegisterComponent] public class ClientStorageComponent : SharedStorageComponent, IDraggable { [Dependency] private readonly IItemSlotManager _itemSlotManager = default!; private List _storedEntities = new(); private int StorageSizeUsed; private int StorageCapacityMax; private StorageWindow? _window; public bool UIOpen => _window?.IsOpen ?? false; public override IReadOnlyList StoredEntities => _storedEntities; protected override void OnAdd() { base.OnAdd(); _window = new StorageWindow(this) {Title = IoCManager.Resolve().GetComponent(Owner).EntityName}; _window.EntityList.GenerateItem += GenerateButton; _window.EntityList.ItemPressed += Interact; } protected override void OnRemove() { _window?.Dispose(); base.OnRemove(); } public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) { base.HandleComponentState(curState, nextState); if (curState is not StorageComponentState state) { return; } _storedEntities = state.StoredEntities .Select(id => IoCManager.Resolve().GetEntity(id)) .ToList(); } [Obsolete("Component Messages are deprecated, use Entity Events instead.")] public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession? session = null) { base.HandleNetworkMessage(message, channel, session); switch (message) { //Updates what we are storing for the UI case StorageHeldItemsMessage msg: HandleStorageMessage(msg); break; //Opens the UI case OpenStorageUIMessage _: ToggleUI(); break; case CloseStorageUIMessage _: CloseUI(); break; case AnimateInsertingEntitiesMessage msg: HandleAnimatingInsertingEntities(msg); break; } } /// /// Copies received values from server about contents of storage container /// /// private void HandleStorageMessage(StorageHeldItemsMessage storageState) { _storedEntities = storageState.StoredEntities.Select(id => IoCManager.Resolve().GetEntity(id)).ToList(); StorageSizeUsed = storageState.StorageSizeUsed; StorageCapacityMax = storageState.StorageSizeMax; _window?.BuildEntityList(storageState.StoredEntities.ToList()); } /// /// Animate the newly stored entities in flying towards this storage's position /// /// private void HandleAnimatingInsertingEntities(AnimateInsertingEntitiesMessage msg) { for (var i = 0; msg.StoredEntities.Count > i; i++) { var entityId = msg.StoredEntities[i]; var initialPosition = msg.EntityPositions[i]; if (IoCManager.Resolve().TryGetEntity(entityId, out var entity)) { ReusableAnimations.AnimateEntityPickup(entity, initialPosition, IoCManager.Resolve().GetComponent(Owner).LocalPosition); } } } /// /// Opens the storage UI if closed. Closes it if opened. /// private void ToggleUI() { if (_window == null) return; if (_window.IsOpen) _window.Close(); else _window.OpenCentered(); } private void CloseUI() { _window?.Close(); } /// /// Function for clicking one of the stored entity buttons in the UI, tells server to remove that entity /// /// private void Interact(BaseButton.ButtonEventArgs buttonEventArgs, EntityUid entityUid) { if (buttonEventArgs.Event.Function == EngineKeyFunctions.UIClick) { #pragma warning disable 618 SendNetworkMessage(new RemoveEntityMessage(entityUid)); #pragma warning restore 618 buttonEventArgs.Event.Handle(); } else if (IoCManager.Resolve().TryGetEntity(entityUid, out var entity)) { _itemSlotManager.OnButtonPressed(buttonEventArgs.Event, entity); } } public override bool Remove(IEntity entity) { if (_storedEntities.Remove(entity)) { Dirty(); return true; } return false; } /// /// Button created for each entity that represents that item in the storage UI, with a texture, and name and size label /// private void GenerateButton(EntityUid entityUid, EntityContainerButton button) { if (!IoCManager.Resolve().TryGetEntity(entityUid, out var entity)) return; IoCManager.Resolve().TryGetComponent(entity, out ISpriteComponent? sprite); IoCManager.Resolve().TryGetComponent(entity, out ItemComponent? item); button.AddChild(new BoxContainer { Orientation = LayoutOrientation.Horizontal, SeparationOverride = 2, Children = { new SpriteView { HorizontalAlignment = HAlignment.Left, VerticalAlignment = VAlignment.Center, MinSize = new Vector2(32.0f, 32.0f), OverrideDirection = Direction.South, Sprite = sprite }, new Label { HorizontalExpand = true, ClipText = true, Text = IoCManager.Resolve().GetComponent(entity).EntityName }, new Label { Align = Label.AlignMode.Right, Text = item?.Size.ToString() ?? Loc.GetString("no-item-size") } } }); button.EnableAllKeybinds = true; } /// /// GUI class for client storage component /// private class StorageWindow : SS14Window { private Control _vBox; private readonly Label _information; public readonly EntityListDisplay EntityList; public ClientStorageComponent StorageEntity; private readonly StyleBoxFlat _hoveredBox = new() { BackgroundColor = Color.Black.WithAlpha(0.35f) }; private readonly StyleBoxFlat _unHoveredBox = new() { BackgroundColor = Color.Black.WithAlpha(0.0f) }; public StorageWindow(ClientStorageComponent storageEntity) { StorageEntity = storageEntity; SetSize = (200, 320); Title = Loc.GetString("comp-storage-window-title"); RectClipContent = true; var containerButton = new ContainerButton { Name = "StorageContainerButton", MouseFilter = MouseFilterMode.Pass, }; Contents.AddChild(containerButton); var innerContainerButton = new PanelContainer { PanelOverride = _unHoveredBox, }; containerButton.AddChild(innerContainerButton); containerButton.OnPressed += args => { var controlledEntity = IoCManager.Resolve().LocalPlayer?.ControlledEntity; if (controlledEntity != null && IoCManager.Resolve().TryGetComponent(controlledEntity, out HandsComponent? hands)) { #pragma warning disable 618 StorageEntity.SendNetworkMessage(new InsertEntityMessage()); #pragma warning restore 618 } }; _vBox = new BoxContainer() { Orientation = LayoutOrientation.Vertical, MouseFilter = MouseFilterMode.Ignore, }; containerButton.AddChild(_vBox); _information = new Label { Text = Loc.GetString("comp-storage-window-volume", ("itemCount", 0), ("usedVolume", 0), ("maxVolume", 0)), VerticalAlignment = VAlignment.Center }; _vBox.AddChild(_information); EntityList = new EntityListDisplay { Name = "EntityListContainer", }; _vBox.AddChild(EntityList); EntityList.OnMouseEntered += args => { innerContainerButton.PanelOverride = _hoveredBox; }; EntityList.OnMouseExited += args => { innerContainerButton.PanelOverride = _unHoveredBox; }; } public override void Close() { #pragma warning disable 618 StorageEntity.SendNetworkMessage(new CloseStorageUIMessage()); #pragma warning restore 618 base.Close(); } /// /// Loops through stored entities creating buttons for each, updates information labels /// public void BuildEntityList(List entityUids) { EntityList.PopulateList(entityUids); //Sets information about entire storage container current capacity if (StorageEntity.StorageCapacityMax != 0) { _information.Text = Loc.GetString("comp-storage-window-volume", ("itemCount", entityUids.Count), ("usedVolume", StorageEntity.StorageSizeUsed), ("maxVolume", StorageEntity.StorageCapacityMax)); } else { _information.Text = Loc.GetString("comp-storage-window-volume-unlimited", ("itemCount", entityUids.Count)); } } } } }