Add changing the amount of hands on the GUI depending on your body parts (#1406)
* Multiple hands in gui first pass * Remove IHandsComponent interface * Create hand class and more hand textures * Refactor ServerHandsComponent to use a single list of hands * Seal SharedHand * Fix picked up items not showing on top of the hand buttons * Remove HandsGui buttons and panels dictionaries * Fix items in hands rendering * Fix wrong hand container comparison * Fix not updating the location of duplicate hands * Change ClientHandsComponent to use a SortedList instead of a dictionary * More merge conflict fixes * Change SortedList to List * Fix hand button order * Add item tooltip for more than 2 hands and updating when removing hands * Add add hand and remove hand command * Merge conflict fixes * Remove nullable reference type from ContainerSlot * Fix texture errors * Fix error when reaching 0 hands * Fix error when swapping hands with no hands * Merged remove hand methods * Fix item panel texture errors * Merge conflict fixes * Fix addhand and removehand command descriptions * Add properly displaying tooltips for 2 hands * Make hand indexes and locations consistent across the client and server * Add dropping held entity if a hand is removed * Change hand location to be calculated by index * Made different hand gui updates more consistent * Remove human body yml testing changes * Sanitize addhand and removehand commands * Merge conflict fixes * Remove testing changes * Revert body system changes * Add missing imports * Remove obsolete hands parameter in yml files * Fix broken import * Fix startup error and adding and removing hands on the same tick * Make hand container id use an uint In case someone gets more than 2 billion hands * Rename hand component files * Make hands state use an array
This commit is contained in:
@@ -1,14 +1,15 @@
|
||||
using Content.Client.GameObjects;
|
||||
using Content.Client.Interfaces.GameObjects;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Client.GameObjects.Components.Items;
|
||||
using Content.Client.Utility;
|
||||
using Content.Shared.GameObjects.Components.Items;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
@@ -16,67 +17,140 @@ namespace Content.Client.UserInterface
|
||||
{
|
||||
public class HandsGui : Control
|
||||
{
|
||||
private const string HandNameLeft = "left";
|
||||
private const string HandNameRight = "right";
|
||||
|
||||
#pragma warning disable 0649
|
||||
[Dependency] private readonly IPlayerManager _playerManager;
|
||||
[Dependency] private readonly IResourceCache _resourceCache;
|
||||
[Dependency] private readonly IItemSlotManager _itemSlotManager;
|
||||
#pragma warning restore 0649
|
||||
|
||||
private IEntity _leftHand;
|
||||
private IEntity _rightHand;
|
||||
private readonly TextureRect _activeHandRect;
|
||||
|
||||
private readonly TextureRect ActiveHandRect;
|
||||
private readonly Texture _leftHandTexture;
|
||||
private readonly Texture _middleHandTexture;
|
||||
private readonly Texture _rightHandTexture;
|
||||
|
||||
private readonly ItemSlotButton _leftButton;
|
||||
private readonly ItemSlotButton _rightButton;
|
||||
private readonly ItemStatusPanel _leftPanel;
|
||||
private readonly ItemStatusPanel _topPanel;
|
||||
private readonly ItemStatusPanel _rightPanel;
|
||||
|
||||
private readonly ItemStatusPanel _rightStatusPanel;
|
||||
private readonly ItemStatusPanel _leftStatusPanel;
|
||||
private readonly HBoxContainer _guiContainer;
|
||||
private readonly VBoxContainer _handsColumn;
|
||||
private readonly HBoxContainer _handsContainer;
|
||||
|
||||
private int _lastHands;
|
||||
|
||||
public HandsGui()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
var textureHandLeft = _resourceCache.GetTexture("/Textures/Interface/Inventory/hand_l.png");
|
||||
var textureHandRight = _resourceCache.GetTexture("/Textures/Interface/Inventory/hand_r.png");
|
||||
var textureHandActive = _resourceCache.GetTexture("/Textures/Interface/Inventory/hand_active.png");
|
||||
var storageTexture = _resourceCache.GetTexture("/Textures/Interface/Inventory/back.png");
|
||||
|
||||
_rightStatusPanel = new ItemStatusPanel(true);
|
||||
_leftStatusPanel = new ItemStatusPanel(false);
|
||||
|
||||
_leftButton = new ItemSlotButton(textureHandLeft, storageTexture);
|
||||
_rightButton = new ItemSlotButton(textureHandRight, storageTexture);
|
||||
var hBox = new HBoxContainer
|
||||
AddChild(_guiContainer = new HBoxContainer
|
||||
{
|
||||
SeparationOverride = 0,
|
||||
Children = {_rightStatusPanel, _rightButton, _leftButton, _leftStatusPanel}
|
||||
};
|
||||
Children =
|
||||
{
|
||||
(_rightPanel = ItemStatusPanel.FromSide(HandLocation.Right)),
|
||||
(_handsColumn = new VBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
(_topPanel = ItemStatusPanel.FromSide(HandLocation.Middle)),
|
||||
(_handsContainer = new HBoxContainer {SeparationOverride = 0})
|
||||
}
|
||||
}),
|
||||
(_leftPanel = ItemStatusPanel.FromSide(HandLocation.Left))
|
||||
}
|
||||
});
|
||||
|
||||
AddChild(hBox);
|
||||
|
||||
_leftButton.OnPressed += args => HandKeyBindDown(args, HandNameLeft);
|
||||
_leftButton.OnStoragePressed += args => _OnStoragePressed(args, HandNameLeft);
|
||||
_rightButton.OnPressed += args => HandKeyBindDown(args, HandNameRight);
|
||||
_rightButton.OnStoragePressed += args => _OnStoragePressed(args, HandNameRight);
|
||||
var textureHandActive = _resourceCache.GetTexture("/Textures/Interface/Inventory/hand_active.png");
|
||||
|
||||
// Active hand
|
||||
_leftButton.AddChild(ActiveHandRect = new TextureRect
|
||||
_activeHandRect = new TextureRect
|
||||
{
|
||||
Texture = textureHandActive,
|
||||
TextureScale = (2, 2)
|
||||
});
|
||||
};
|
||||
|
||||
_leftHandTexture = _resourceCache.GetTexture("/Textures/Interface/Inventory/hand_l.png");
|
||||
_middleHandTexture = _resourceCache.GetTexture("/Textures/Interface/Inventory/hand_middle.png");
|
||||
_rightHandTexture = _resourceCache.GetTexture("/Textures/Interface/Inventory/hand_r.png");
|
||||
}
|
||||
|
||||
private ItemStatusPanel GetItemPanel(Hand hand)
|
||||
{
|
||||
return hand.Location switch
|
||||
{
|
||||
HandLocation.Left => _rightPanel,
|
||||
HandLocation.Middle => _topPanel,
|
||||
HandLocation.Right => _leftPanel,
|
||||
_ => throw new IndexOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
private Texture HandTexture(HandLocation location)
|
||||
{
|
||||
switch (location)
|
||||
{
|
||||
case HandLocation.Left:
|
||||
return _leftHandTexture;
|
||||
case HandLocation.Middle:
|
||||
return _middleHandTexture;
|
||||
case HandLocation.Right:
|
||||
return _rightHandTexture;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(location), location, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hands component controling this gui, returns true if successful and false if failure
|
||||
/// Adds a new hand to this control
|
||||
/// </summary>
|
||||
/// <param name="hand">The hand to add to this control</param>
|
||||
/// <param name="buttonLocation">
|
||||
/// The actual location of the button. The right hand is drawn
|
||||
/// on the LEFT of the screen.
|
||||
/// </param>
|
||||
private void AddHand(Hand hand, HandLocation buttonLocation)
|
||||
{
|
||||
var buttonTexture = HandTexture(buttonLocation);
|
||||
var storageTexture = _resourceCache.GetTexture("/Textures/Interface/Inventory/back.png");
|
||||
var button = new HandButton(buttonTexture, storageTexture, buttonLocation);
|
||||
var slot = hand.Name;
|
||||
|
||||
button.OnPressed += args => HandKeyBindDown(args, slot);
|
||||
button.OnStoragePressed += args => _OnStoragePressed(args, slot);
|
||||
|
||||
_handsContainer.AddChild(button);
|
||||
|
||||
if (_activeHandRect.Parent == null)
|
||||
{
|
||||
button.AddChild(_activeHandRect);
|
||||
_activeHandRect.SetPositionInParent(1);
|
||||
}
|
||||
|
||||
hand.Button = button;
|
||||
}
|
||||
|
||||
public void RemoveHand(Hand hand)
|
||||
{
|
||||
var button = hand.Button;
|
||||
|
||||
if (button != null)
|
||||
{
|
||||
if (button.Children.Contains(_activeHandRect))
|
||||
{
|
||||
button.RemoveChild(_activeHandRect);
|
||||
}
|
||||
|
||||
_handsContainer.RemoveChild(button);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hands component controlling this gui
|
||||
/// </summary>
|
||||
/// <param name="hands"></param>
|
||||
/// <returns></returns>
|
||||
private bool TryGetHands(out IHandsComponent hands)
|
||||
/// <returns>true if successful and false if failure</returns>
|
||||
private bool TryGetHands(out HandsComponent hands)
|
||||
{
|
||||
hands = default;
|
||||
|
||||
@@ -93,50 +167,63 @@ namespace Content.Client.UserInterface
|
||||
|
||||
UpdateDraw();
|
||||
|
||||
if (!TryGetHands(out var hands))
|
||||
if (!TryGetHands(out var component))
|
||||
{
|
||||
return;
|
||||
|
||||
var left = hands.GetEntity(HandNameLeft);
|
||||
var right = hands.GetEntity(HandNameRight);
|
||||
|
||||
ActiveHandRect.Parent.RemoveChild(ActiveHandRect);
|
||||
var parent = hands.ActiveIndex == HandNameLeft ? _leftButton : _rightButton;
|
||||
parent.AddChild(ActiveHandRect);
|
||||
ActiveHandRect.SetPositionInParent(1);
|
||||
|
||||
if (left != _leftHand)
|
||||
{
|
||||
_leftHand = left;
|
||||
_itemSlotManager.SetItemSlot(_leftButton, _leftHand);
|
||||
}
|
||||
|
||||
if (right != _rightHand)
|
||||
// TODO: Remove button on remove hand
|
||||
|
||||
var hands = component.Hands.OrderByDescending(x => x.Location).ToArray();
|
||||
for (var i = 0; i < hands.Length; i++)
|
||||
{
|
||||
_rightHand = right;
|
||||
_itemSlotManager.SetItemSlot(_rightButton, _rightHand);
|
||||
var hand = hands[i];
|
||||
|
||||
if (hand.Button == null)
|
||||
{
|
||||
AddHand(hand, hand.Location);
|
||||
}
|
||||
|
||||
hand.Button!.Button.Texture = HandTexture(hand.Location);
|
||||
hand.Button!.SetPositionInParent(i);
|
||||
_itemSlotManager.SetItemSlot(hand.Button, hand.Entity);
|
||||
}
|
||||
|
||||
_activeHandRect.Parent?.RemoveChild(_activeHandRect);
|
||||
component.GetHand(component.ActiveIndex)?.Button?.AddChild(_activeHandRect);
|
||||
|
||||
if (hands.Length > 0)
|
||||
{
|
||||
_activeHandRect.SetPositionInParent(1);
|
||||
}
|
||||
|
||||
_leftPanel.SetPositionFirst();
|
||||
_rightPanel.SetPositionLast();
|
||||
}
|
||||
|
||||
private void HandKeyBindDown(GUIBoundKeyEventArgs args, string handIndex)
|
||||
private void HandKeyBindDown(GUIBoundKeyEventArgs args, string slotName)
|
||||
{
|
||||
if (!TryGetHands(out var hands))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Function == ContentKeyFunctions.MouseMiddle)
|
||||
{
|
||||
hands.SendChangeHand(handIndex);
|
||||
hands.SendChangeHand(slotName);
|
||||
args.Handle();
|
||||
return;
|
||||
}
|
||||
|
||||
var entity = hands.GetEntity(handIndex);
|
||||
var entity = hands.GetEntity(slotName);
|
||||
if (entity == null)
|
||||
{
|
||||
if (args.Function == EngineKeyFunctions.UIClick && hands.ActiveIndex != handIndex)
|
||||
if (args.Function == EngineKeyFunctions.UIClick && hands.ActiveIndex != slotName)
|
||||
{
|
||||
hands.SendChangeHand(handIndex);
|
||||
hands.SendChangeHand(slotName);
|
||||
args.Handle();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -148,37 +235,131 @@ namespace Content.Client.UserInterface
|
||||
|
||||
if (args.Function == EngineKeyFunctions.UIClick)
|
||||
{
|
||||
if (hands.ActiveIndex == handIndex)
|
||||
if (hands.ActiveIndex == slotName)
|
||||
{
|
||||
hands.UseActiveHand();
|
||||
}
|
||||
else
|
||||
{
|
||||
hands.AttackByInHand(handIndex);
|
||||
hands.AttackByInHand(slotName);
|
||||
}
|
||||
|
||||
args.Handle();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void _OnStoragePressed(GUIBoundKeyEventArgs args, string handIndex)
|
||||
{
|
||||
if (args.Function != EngineKeyFunctions.UIClick)
|
||||
return;
|
||||
if (!TryGetHands(out var hands))
|
||||
if (args.Function != EngineKeyFunctions.UIClick || !TryGetHands(out var hands))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
hands.ActivateItemInHand(handIndex);
|
||||
}
|
||||
|
||||
private void UpdatePanels()
|
||||
{
|
||||
if (!TryGetHands(out var component))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var hand in component.Hands)
|
||||
{
|
||||
_itemSlotManager.UpdateCooldown(hand.Button, hand.Entity);
|
||||
}
|
||||
|
||||
switch (component.Hands.Count)
|
||||
{
|
||||
case var n when n == 0 && _lastHands != 0:
|
||||
_guiContainer.Visible = false;
|
||||
|
||||
_topPanel.Update(null);
|
||||
_leftPanel.Update(null);
|
||||
_rightPanel.Update(null);
|
||||
|
||||
break;
|
||||
case 1:
|
||||
if (_lastHands != 1)
|
||||
{
|
||||
_guiContainer.Visible = true;
|
||||
|
||||
_topPanel.Update(null);
|
||||
_topPanel.Visible = false;
|
||||
|
||||
_leftPanel.Update(null);
|
||||
_leftPanel.Visible = false;
|
||||
|
||||
_rightPanel.Visible = true;
|
||||
|
||||
if (!_guiContainer.Children.Contains(_rightPanel))
|
||||
{
|
||||
_rightPanel.AddChild(_rightPanel);
|
||||
_rightPanel.SetPositionFirst();
|
||||
}
|
||||
}
|
||||
|
||||
_rightPanel.Update(component.Hands[0].Entity);
|
||||
|
||||
break;
|
||||
case 2:
|
||||
if (_lastHands != 2)
|
||||
{
|
||||
_guiContainer.Visible = true;
|
||||
_topPanel.Update(null);
|
||||
_topPanel.Visible = false;
|
||||
|
||||
_leftPanel.Visible = true;
|
||||
_rightPanel.Visible = true;
|
||||
|
||||
if (_handsColumn.Children.Contains(_topPanel))
|
||||
{
|
||||
_handsColumn.RemoveChild(_topPanel);
|
||||
}
|
||||
}
|
||||
|
||||
_leftPanel.Update(component.Hands[0].Entity);
|
||||
_rightPanel.Update(component.Hands[1].Entity);
|
||||
|
||||
// Order is left, right
|
||||
foreach (var hand in component.Hands)
|
||||
{
|
||||
var tooltip = GetItemPanel(hand);
|
||||
tooltip.Update(hand.Entity);
|
||||
}
|
||||
|
||||
break;
|
||||
case var n when n > 2:
|
||||
if (_lastHands <= 2)
|
||||
{
|
||||
_guiContainer.Visible = true;
|
||||
|
||||
_topPanel.Visible = true;
|
||||
_leftPanel.Visible = false;
|
||||
_rightPanel.Visible = false;
|
||||
|
||||
if (!_handsColumn.Children.Contains(_topPanel))
|
||||
{
|
||||
_handsColumn.AddChild(_topPanel);
|
||||
_topPanel.SetPositionFirst();
|
||||
}
|
||||
}
|
||||
|
||||
_topPanel.Update(component.ActiveHand);
|
||||
_leftPanel.Update(null);
|
||||
_rightPanel.Update(null);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_lastHands = component.Hands.Count;
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
_itemSlotManager.UpdateCooldown(_leftButton, _leftHand);
|
||||
_itemSlotManager.UpdateCooldown(_rightButton, _rightHand);
|
||||
|
||||
_rightStatusPanel.Update(_rightHand);
|
||||
_leftStatusPanel.Update(_leftHand);
|
||||
UpdatePanels();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user