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,4 +1,5 @@
using Content.Shared.GameObjects;
using Content.Client.GameObjects.Components.Items;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.GameObjects.Components.Items;
using Robust.Client.Graphics;

View File

@@ -1,215 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Content.Client.Interfaces.GameObjects;
using Content.Client.UserInterface;
using Content.Shared.GameObjects;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects
{
[RegisterComponent]
[ComponentReference(typeof(IHandsComponent))]
public class HandsComponent : SharedHandsComponent, IHandsComponent
{
private HandsGui _gui;
#pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud;
#pragma warning restore 649
[ViewVariables] private readonly Dictionary<string, IEntity> _hands = new Dictionary<string, IEntity>();
[ViewVariables] public string ActiveIndex { get; private set; }
[ViewVariables] private ISpriteComponent _sprite;
[ViewVariables] public IEntity ActiveHand => GetEntity(ActiveIndex);
public override void OnRemove()
{
base.OnRemove();
_gui?.Dispose();
}
public override void Initialize()
{
base.Initialize();
if (Owner.TryGetComponent(out _sprite))
{
foreach (var slot in _hands.Keys)
{
_sprite.LayerMapReserveBlank($"hand-{slot}");
_setHand(slot, _hands[slot]);
}
}
}
public IEntity GetEntity(string index)
{
if (!string.IsNullOrEmpty(index) && _hands.TryGetValue(index, out var entity))
{
return entity;
}
return null;
}
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
if (curState == null)
return;
var cast = (HandsComponentState) curState;
foreach (var (slot, uid) in cast.Hands)
{
IEntity entity = null;
try
{
entity = Owner.EntityManager.GetEntity(uid);
}
catch
{
// Nothing.
}
_hands[slot] = entity;
_setHand(slot, entity);
}
foreach (var slot in _hands.Keys.ToList())
{
if (!cast.Hands.ContainsKey(slot))
{
_hands[slot] = null;
_setHand(slot, null);
}
}
ActiveIndex = cast.ActiveIndex;
_gui?.UpdateHandIcons();
RefreshInHands();
}
private void _setHand(string hand, IEntity entity)
{
if (_sprite == null)
{
return;
}
if (entity == null)
{
_sprite.LayerSetVisible($"hand-{hand}", false);
return;
}
SetInHands(hand, entity);
}
private void SetInHands(string hand, IEntity entity)
{
if (entity == null)
{
_sprite.LayerSetVisible($"hand-{hand}", false);
return;
}
if (!entity.TryGetComponent(out ItemComponent item)) return;
var maybeInhands = item.GetInHandStateInfo(hand);
if (!maybeInhands.HasValue)
{
_sprite.LayerSetVisible($"hand-{hand}", false);
}
else
{
var (rsi, state) = maybeInhands.Value;
_sprite.LayerSetVisible($"hand-{hand}", true);
_sprite.LayerSetState($"hand-{hand}", state, rsi);
}
}
public void RefreshInHands()
{
if (!Initialized) return;
foreach (var (hand, entity) in _hands)
{
SetInHands(hand, entity);
}
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataReadWriteFunction(
"hands",
new List<string>(),
hands => hands.ForEach(slot => _hands.Add(slot, null)),
() => _hands.Keys.ToList());
serializer.DataField(this, x => ActiveIndex, "defaultHand", _hands.Keys.LastOrDefault());
}
public override void HandleMessage(ComponentMessage message, IComponent component)
{
base.HandleMessage(message, component);
switch (message)
{
case PlayerAttachedMsg _:
if (_gui == null)
{
_gui = new HandsGui();
}
else
{
_gui.Parent?.RemoveChild(_gui);
}
_gameHud.HandsContainer.AddChild(_gui);
_gui.UpdateHandIcons();
break;
case PlayerDetachedMsg _:
_gui.Parent?.RemoveChild(_gui);
break;
}
}
public void SendChangeHand(string index)
{
SendNetworkMessage(new ClientChangedHandMsg(index));
}
public void AttackByInHand(string index)
{
SendNetworkMessage(new ClientAttackByInHandMsg(index));
}
public void UseActiveHand()
{
if (GetEntity(ActiveIndex) != null)
{
SendNetworkMessage(new UseInHandMsg());
}
}
public void ActivateItemInHand(string handIndex)
{
if (GetEntity(handIndex) == null)
return;
SendNetworkMessage(new ActivateInHandMsg(handIndex));
}
}
}

View File

@@ -0,0 +1,256 @@
#nullable enable
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Client.UserInterface;
using Content.Shared.GameObjects.Components.Items;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects.Components.Items
{
[RegisterComponent]
public class HandsComponent : SharedHandsComponent
{
private HandsGui? _gui;
#pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud = default!;
#pragma warning restore 649
private readonly List<Hand> _hands = new List<Hand>();
[ViewVariables] public IReadOnlyList<Hand> Hands => _hands;
[ViewVariables] public string? ActiveIndex { get; private set; }
[ViewVariables] private ISpriteComponent? _sprite;
[ViewVariables] public IEntity? ActiveHand => GetEntity(ActiveIndex);
private void AddHand(Hand hand)
{
_hands.Insert(hand.Index, hand);
}
public Hand? GetHand(string? name)
{
return Hands.FirstOrDefault(hand => hand.Name == name);
}
private bool TryHand(string name, [MaybeNullWhen(false)] out Hand hand)
{
return (hand = GetHand(name)) != null;
}
public IEntity? GetEntity(string? handName)
{
if (handName == null)
{
return null;
}
return GetHand(handName)?.Entity;
}
public override void OnRemove()
{
base.OnRemove();
_gui?.Dispose();
}
public override void Initialize()
{
base.Initialize();
if (Owner.TryGetComponent(out _sprite))
{
foreach (var hand in _hands)
{
_sprite.LayerMapReserveBlank($"hand-{hand.Name}");
UpdateHandSprites(hand);
}
}
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
if (curState == null)
{
return;
}
var cast = (HandsComponentState) curState;
foreach (var sharedHand in cast.Hands)
{
if (!TryHand(sharedHand.Name, out var hand))
{
hand = new Hand(sharedHand, Owner.EntityManager);
AddHand(hand);
}
else
{
hand.Location = sharedHand.Location;
hand.Entity = sharedHand.EntityUid.HasValue
? Owner.EntityManager.GetEntity(sharedHand.EntityUid.Value)
: null;
}
UpdateHandSprites(hand);
}
foreach (var currentHand in _hands.ToList())
{
if (cast.Hands.All(newHand => newHand.Name != currentHand.Name))
{
_hands.Remove(currentHand);
_gui?.RemoveHand(currentHand);
HideHand(currentHand);
}
}
ActiveIndex = cast.ActiveIndex;
_gui?.UpdateHandIcons();
RefreshInHands();
}
private void HideHand(Hand hand)
{
_sprite?.LayerSetVisible($"hand-{hand.Name}", false);
}
private void UpdateHandSprites(Hand hand)
{
if (_sprite == null)
{
return;
}
var entity = hand.Entity;
var name = hand.Name;
if (entity == null)
{
_sprite.LayerSetVisible($"hand-{name}", false);
return;
}
if (!entity.TryGetComponent(out ItemComponent item)) return;
var maybeInHands = item.GetInHandStateInfo(name);
if (!maybeInHands.HasValue)
{
_sprite.LayerSetVisible($"hand-{name}", false);
}
else
{
var (rsi, state) = maybeInHands.Value;
_sprite.LayerSetVisible($"hand-{name}", true);
_sprite.LayerSetState($"hand-{name}", state, rsi);
}
}
public void RefreshInHands()
{
if (!Initialized) return;
foreach (var hand in _hands)
{
UpdateHandSprites(hand);
}
}
protected override void Startup()
{
ActiveIndex = _hands.LastOrDefault()?.Name;
}
public override void HandleMessage(ComponentMessage message, IComponent? component)
{
base.HandleMessage(message, component);
switch (message)
{
case PlayerAttachedMsg _:
if (_gui == null)
{
_gui = new HandsGui();
}
else
{
_gui.Parent?.RemoveChild(_gui);
}
_gameHud.HandsContainer.AddChild(_gui);
_gui.UpdateHandIcons();
break;
case PlayerDetachedMsg _:
_gui?.Parent?.RemoveChild(_gui);
break;
}
}
public void SendChangeHand(string index)
{
SendNetworkMessage(new ClientChangedHandMsg(index));
}
public void AttackByInHand(string index)
{
SendNetworkMessage(new ClientAttackByInHandMsg(index));
}
public void UseActiveHand()
{
if (GetEntity(ActiveIndex) != null)
{
SendNetworkMessage(new UseInHandMsg());
}
}
public void ActivateItemInHand(string handIndex)
{
if (GetEntity(handIndex) == null)
{
return;
}
SendNetworkMessage(new ActivateInHandMsg(handIndex));
}
}
public class Hand
{
// TODO: Separate into server hand and client hand
public Hand(SharedHand hand, IEntityManager manager, HandButton? button = null)
{
Index = hand.Index;
Name = hand.Name;
Location = hand.Location;
Button = button;
if (!hand.EntityUid.HasValue)
{
return;
}
manager.TryGetEntity(hand.EntityUid.Value, out var entity);
Entity = entity;
}
public int Index { get; }
public string Name { get; }
public HandLocation Location { get; set; }
public IEntity? Entity { get; set; }
public HandButton? Button { get; set; }
}
}

View File

@@ -14,7 +14,7 @@ using Robust.Shared.Serialization;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects
namespace Content.Client.GameObjects.Components.Items
{
[RegisterComponent]
[ComponentReference(typeof(IItemComponent))]

View File

@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using Content.Client.GameObjects.Components.Items;
using Content.Shared.GameObjects.Components.Storage;
using Content.Client.Interfaces.GameObjects;
using Content.Client.Interfaces.GameObjects.Components.Interaction;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.GameObjects.Components;
@@ -137,7 +137,7 @@ namespace Content.Client.GameObjects.Components.Storage
{
var controlledEntity = IoCManager.Resolve<IPlayerManager>().LocalPlayer.ControlledEntity;
if (controlledEntity.TryGetComponent(out IHandsComponent hands))
if (controlledEntity.TryGetComponent(out HandsComponent hands))
{
StorageEntity.SendNetworkMessage(new InsertEntityMessage());
}
@@ -250,7 +250,7 @@ namespace Content.Client.GameObjects.Components.Storage
{
var controlledEntity = IoCManager.Resolve<IPlayerManager>().LocalPlayer.ControlledEntity;
if (controlledEntity.TryGetComponent(out IHandsComponent hands))
if (controlledEntity.TryGetComponent(out HandsComponent hands))
{
StorageEntity.SendNetworkMessage(new InsertEntityMessage());
}

View File

@@ -1,6 +1,6 @@
using System;
using Content.Client.GameObjects.Components.Items;
using Content.Client.GameObjects.Components.Weapons.Ranged;
using Content.Client.Interfaces.GameObjects;
using Content.Shared.GameObjects.Components.Weapons.Ranged;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Interfaces.Graphics.ClientEye;
@@ -11,7 +11,6 @@ using Robust.Shared.Input;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Log;
namespace Content.Client.GameObjects.EntitySystems
{
@@ -48,7 +47,7 @@ namespace Content.Client.GameObjects.EntitySystems
{
return;
}
var state = _inputSystem.CmdStates.GetState(EngineKeyFunctions.Use);
if (!_combatModeSystem.IsInCombatMode() || state != BoundKeyState.Down)
{
@@ -58,7 +57,7 @@ namespace Content.Client.GameObjects.EntitySystems
}
var entity = _playerManager.LocalPlayer.ControlledEntity;
if (entity == null || !entity.TryGetComponent(out IHandsComponent hands))
if (entity == null || !entity.TryGetComponent(out HandsComponent hands))
{
return;
}

View File

@@ -1,19 +0,0 @@
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Client.Interfaces.GameObjects
{
// HYPER SIMPLE HANDS API CLIENT SIDE.
// To allow for showing the HUD, mostly.
public interface IHandsComponent
{
IEntity GetEntity(string index);
string ActiveIndex { get; }
IEntity ActiveHand { get; }
void SendChangeHand(string index);
void AttackByInHand(string index);
void UseActiveHand();
void ActivateItemInHand(string handIndex);
void RefreshInHands();
}
}

View File

@@ -0,0 +1,15 @@
using Content.Shared.GameObjects.Components.Items;
using Robust.Client.Graphics;
namespace Content.Client.UserInterface
{
public class HandButton : ItemSlotButton
{
public HandButton(Texture texture, Texture storageTexture, HandLocation location) : base(texture, storageTexture)
{
Location = location;
}
public HandLocation Location { get; }
}
}

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

View File

@@ -1,15 +1,14 @@
using System;
using Content.Client.UserInterface;
using Content.Shared.Input;
using Content.Shared.GameObjects.Components.Items;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects
namespace Content.Client.UserInterface
{
public sealed class ItemSlotButton : MarginContainer
public class ItemSlotButton : MarginContainer
{
public TextureRect Button { get; }
public SpriteView SpriteView { get; }

View File

@@ -1,7 +1,11 @@
#nullable enable
using System;
using System.Collections.Generic;
using Content.Client.GameObjects.Components;
using Content.Client.UserInterface.Stylesheets;
using Content.Client.Utility;
using Content.Shared.GameObjects.Components.Items;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
@@ -26,22 +30,17 @@ namespace Content.Client.UserInterface
private readonly PanelContainer _panel;
[ViewVariables]
private IEntity _entity;
private IEntity? _entity;
public ItemStatusPanel(bool isRightHand)
public ItemStatusPanel(Texture texture, StyleBox.Margin margin)
{
// isRightHand means on the LEFT of the screen.
// Keep that in mind.
var panel = new StyleBoxTexture
{
Texture = ResC.GetTexture(isRightHand
? "/Textures/Interface/Nano/item_status_right.svg.96dpi.png"
: "/Textures/Interface/Nano/item_status_left.svg.96dpi.png")
Texture = texture
};
panel.SetContentMarginOverride(StyleBox.Margin.Vertical, 4);
panel.SetContentMarginOverride(StyleBox.Margin.Horizontal, 6);
panel.SetPatchMargin((isRightHand ? StyleBox.Margin.Left : StyleBox.Margin.Right) | StyleBox.Margin.Top,
13);
panel.SetPatchMargin(margin, 13);
AddChild(_panel = new PanelContainer
{
@@ -67,7 +66,42 @@ namespace Content.Client.UserInterface
SizeFlagsVertical = SizeFlags.ShrinkEnd;
}
public void Update(IEntity entity)
/// <summary>
/// Creates a new instance of <see cref="ItemStatusPanel"/>
/// based on whether or not it is being created for the right
/// or left hand.
/// </summary>
/// <param name="location">
/// The location of the hand that this panel is for
/// </param>
/// <returns>the new <see cref="ItemStatusPanel"/> instance</returns>
public static ItemStatusPanel FromSide(HandLocation location)
{
string texture;
StyleBox.Margin margin;
switch (location)
{
case HandLocation.Left:
texture = "/Textures/Interface/Nano/item_status_right.svg.96dpi.png";
margin = StyleBox.Margin.Left | StyleBox.Margin.Top;
break;
case HandLocation.Middle:
texture = "/Textures/Interface/Nano/item_status_left.svg.96dpi.png";
margin = StyleBox.Margin.Right | StyleBox.Margin.Top;
break;
case HandLocation.Right:
texture = "/Textures/Interface/Nano/item_status_left.svg.96dpi.png";
margin = StyleBox.Margin.Right | StyleBox.Margin.Top;
break;
default:
throw new ArgumentOutOfRangeException(nameof(location), location, null);
}
return new ItemStatusPanel(ResC.GetTexture(texture), margin);
}
public void Update(IEntity? entity)
{
if (entity == null)
{
@@ -105,7 +139,7 @@ namespace Content.Client.UserInterface
ClearOldStatus();
foreach (var statusComponent in _entity.GetAllComponents<IItemStatus>())
foreach (var statusComponent in _entity!.GetAllComponents<IItemStatus>())
{
var control = statusComponent.MakeControl();
_statusContents.AddChild(control);
@@ -114,9 +148,10 @@ namespace Content.Client.UserInterface
}
}
// TODO: Depending on if its a two-hand panel or not
protected override Vector2 CalculateMinimumSize()
{
return Vector2.ComponentMax(base.CalculateMinimumSize(), (150, 00));
return Vector2.ComponentMax(base.CalculateMinimumSize(), (150, 0));
}
}
}