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));
}
}
}
}
}