using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Client.HUD; using Content.Client.Items.Managers; using Content.Client.Items.UI; using Content.Client.Resources; using Content.Shared; using Content.Shared.CCVar; using Content.Shared.Hands.Components; using Content.Shared.Input; using Robust.Client.Graphics; using Robust.Client.Player; using Robust.Client.ResourceManagement; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Shared.Configuration; using Robust.Shared.GameObjects; using Robust.Shared.Input; using Robust.Shared.IoC; using Robust.Shared.Timing; using Robust.Shared.ViewVariables; namespace Content.Client.Hands { public class HandsGui : Control { [Dependency] private readonly IResourceCache _resourceCache = default!; [Dependency] private readonly IItemSlotManager _itemSlotManager = default!; [Dependency] private readonly IGameHud _gameHud = default!; [Dependency] private readonly INetConfigurationManager _configManager = default!; private Texture StorageTexture => _gameHud.GetHudTexture("back.png"); private Texture BlockedTexture => _resourceCache.GetTexture("/Textures/Interface/Inventory/blocked.png"); private ItemStatusPanel StatusPanel { get; } private HBoxContainer HandsContainer { get; } [ViewVariables] public IReadOnlyList Hands => _hands; private List _hands = new(); private string? ActiveHand { get; set; } public Action? HandClick; //TODO: Move to Eventbus public Action? HandActivate; //TODO: Move to Eventbus public HandsGui() { IoCManager.InjectDependencies(this); _configManager.OnValueChanged(CCVars.HudTheme, UpdateHudTheme); AddChild(new HBoxContainer { SeparationOverride = 0, HorizontalAlignment = HAlignment.Center, Children = { new VBoxContainer { Children = { (StatusPanel = ItemStatusPanel.FromSide(HandLocation.Middle)), (HandsContainer = new HBoxContainer() { HorizontalAlignment = HAlignment.Center } ), } }, } }); } public void SetState(HandsGuiState state) { ActiveHand = state.ActiveHand; _hands = state.GuiHands; UpdateGui(); } private void UpdateGui() { HandsContainer.DisposeAllChildren(); foreach (var hand in _hands) { var newButton = MakeHandButton(hand.HandLocation); HandsContainer.AddChild(newButton); hand.HandButton = newButton; var handName = hand.Name; newButton.OnPressed += args => OnHandPressed(args, handName); newButton.OnStoragePressed += args => OnStoragePressed(handName); newButton.Blocked.Visible = !hand.Enabled; _itemSlotManager.SetItemSlot(newButton, hand.HeldItem); } if (TryGetActiveHand(out var activeHand)) { activeHand.HandButton.SetActiveHand(true); StatusPanel.Update(activeHand.HeldItem); } } private void OnHandPressed(GUIBoundKeyEventArgs args, string handName) { if (args.Function == EngineKeyFunctions.UIClick) { HandClick?.Invoke(new HandClickEventArgs(handName)); } else if (TryGetHand(handName, out var hand)) { _itemSlotManager.OnButtonPressed(args, hand.HeldItem); } } private void OnStoragePressed(string handName) { HandActivate?.Invoke(new HandActivateEventArgs(handName)); } private bool TryGetActiveHand([NotNullWhen(true)] out GuiHand? activeHand) { TryGetHand(ActiveHand, out activeHand); return activeHand != null; } private bool TryGetHand(string? handName, [NotNullWhen(true)] out GuiHand? foundHand) { foundHand = null; if (handName == null) return false; foreach (var hand in _hands) { if (hand.Name == handName) foundHand = hand; } return foundHand != null; } protected override void FrameUpdate(FrameEventArgs args) { base.FrameUpdate(args); foreach (var hand in _hands) _itemSlotManager.UpdateCooldown(hand.HandButton, hand.HeldItem); } private HandButton MakeHandButton(HandLocation buttonLocation) { var buttonTextureName = buttonLocation switch { HandLocation.Right => "hand_r.png", _ => "hand_l.png" }; var buttonTexture = _gameHud.GetHudTexture(buttonTextureName); return new HandButton(buttonTexture, StorageTexture, buttonTextureName, BlockedTexture, buttonLocation); } private void UpdateHudTheme(int idx) { UpdateGui(); } public class HandClickEventArgs { public string HandClicked { get; } public HandClickEventArgs(string handClicked) { HandClicked = handClicked; } } public class HandActivateEventArgs { public string HandUsed { get; } public HandActivateEventArgs(string handUsed) { HandUsed = handUsed; } } } /// /// Info on a set of hands to be displayed. /// public class HandsGuiState { /// /// The set of hands to be displayed. /// [ViewVariables] public List GuiHands { get; } = new(); /// /// The name of the currently active hand. /// [ViewVariables] public string? ActiveHand { get; } public HandsGuiState(List guiHands, string? activeHand = null) { GuiHands = guiHands; ActiveHand = activeHand; } } /// /// Info on an individual hand to be displayed. /// public class GuiHand { /// /// The name of this hand. /// [ViewVariables] public string Name { get; } /// /// Where this hand is located. /// [ViewVariables] public HandLocation HandLocation { get; } /// /// The item being held in this hand. /// [ViewVariables] public IEntity? HeldItem { get; } /// /// The button in the gui associated with this hand. Assumed to be set by gui shortly after being received from the client HandsComponent. /// [ViewVariables] public HandButton HandButton { get; set; } = default!; /// /// If this hand can be used by the player. /// [ViewVariables] public bool Enabled { get; } public GuiHand(string name, HandLocation handLocation, IEntity? heldItem, bool enabled) { Name = name; HandLocation = handLocation; HeldItem = heldItem; Enabled = enabled; } } }