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:
DrSmugleaf
2020-07-25 15:11:16 +02:00
committed by GitHub
parent 3a4ad42c80
commit 4b4e83d2bf
74 changed files with 1106 additions and 558 deletions

View File

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