HandsSystem Refactor (#38438)

* checkpoint

* pt 2

* pt... i forgot

* pt 4

* patch

* More test fixes

* optimization!!!

* the REAL hand system

* fix RetractableItemActionSystem.cs oversight

* the review

* test

* remove test usage of body prototype

* Update Content.IntegrationTests/Tests/Interaction/InteractionTest.cs

Co-authored-by: Tayrtahn <tayrtahn@gmail.com>

* hellcode

* hellcode 2

* Minor cleanup

* test

* Chasing the last of the bugs

* changes

---------

Co-authored-by: Tayrtahn <tayrtahn@gmail.com>
This commit is contained in:
Nemanja
2025-06-25 09:13:03 -04:00
committed by GitHub
parent 6cffa8aabe
commit 524725d378
79 changed files with 849 additions and 897 deletions

View File

@@ -7,6 +7,7 @@ using Content.Shared.Hands.Components;
using Content.Shared.Input;
using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Timing;
using JetBrains.Annotations;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers;
@@ -28,7 +29,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
private readonly Dictionary<string, int> _handContainerIndices = new();
private readonly Dictionary<string, HandButton> _handLookup = new();
private HandsComponent? _playerHandsComponent;
private HandButton? _activeHand = null;
private HandButton? _activeHand;
// We only have two item status controls (left and right hand),
// but we may have more than two hands.
@@ -38,7 +39,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
private HandButton? _statusHandLeft;
private HandButton? _statusHandRight;
private int _backupSuffix = 0; //this is used when autogenerating container names if they don't have names
private int _backupSuffix; //this is used when autogenerating container names if they don't have names
private HotbarGui? HandsGui => UIManager.GetActiveUIWidgetOrNull<HotbarGui>();
@@ -48,7 +49,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
_handsSystem.OnPlayerItemAdded += OnItemAdded;
_handsSystem.OnPlayerItemRemoved += OnItemRemoved;
_handsSystem.OnPlayerSetActiveHand += SetActiveHand;
_handsSystem.OnPlayerRemoveHand += RemoveHand;
_handsSystem.OnPlayerRemoveHand += OnRemoveHand;
_handsSystem.OnPlayerHandsAdded += LoadPlayerHands;
_handsSystem.OnPlayerHandsRemoved += UnloadPlayerHands;
_handsSystem.OnPlayerHandBlocked += HandBlocked;
@@ -61,28 +62,35 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
_handsSystem.OnPlayerItemAdded -= OnItemAdded;
_handsSystem.OnPlayerItemRemoved -= OnItemRemoved;
_handsSystem.OnPlayerSetActiveHand -= SetActiveHand;
_handsSystem.OnPlayerRemoveHand -= RemoveHand;
_handsSystem.OnPlayerRemoveHand -= OnRemoveHand;
_handsSystem.OnPlayerHandsAdded -= LoadPlayerHands;
_handsSystem.OnPlayerHandsRemoved -= UnloadPlayerHands;
_handsSystem.OnPlayerHandBlocked -= HandBlocked;
_handsSystem.OnPlayerHandUnblocked -= HandUnblocked;
}
private void OnAddHand(string name, HandLocation location)
private void OnAddHand(Entity<HandsComponent> entity, string name, HandLocation location)
{
if (entity.Owner != _player.LocalEntity)
return;
AddHand(name, location);
}
private void OnRemoveHand(Entity<HandsComponent> entity, string name)
{
if (entity.Owner != _player.LocalEntity)
return;
RemoveHand(name);
}
private void HandPressed(GUIBoundKeyEventArgs args, SlotControl hand)
{
if (_playerHandsComponent == null)
{
if (!_handsSystem.TryGetPlayerHands(out var hands))
return;
}
if (args.Function == EngineKeyFunctions.UIClick)
{
_handsSystem.UIHandClick(_playerHandsComponent, hand.SlotName);
_handsSystem.UIHandClick(hands.Value, hand.SlotName);
args.Handle();
}
else if (args.Function == EngineKeyFunctions.UseSecondary)
@@ -122,33 +130,33 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
}
}
private void LoadPlayerHands(HandsComponent handsComp)
private void LoadPlayerHands(Entity<HandsComponent> handsComp)
{
DebugTools.Assert(_playerHandsComponent == null);
if (HandsGui != null)
HandsGui.Visible = true;
_playerHandsComponent = handsComp;
foreach (var (name, hand) in handsComp.Hands)
foreach (var (name, hand) in handsComp.Comp.Hands)
{
var handButton = AddHand(name, hand.Location);
if (_entities.TryGetComponent(hand.HeldEntity, out VirtualItemComponent? virt))
if (_handsSystem.TryGetHeldItem(handsComp.AsNullable(), name, out var held) &&
_entities.TryGetComponent(held, out VirtualItemComponent? virt))
{
handButton.SetEntity(virt.BlockingEntity);
handButton.Blocked = true;
}
else
{
handButton.SetEntity(hand.HeldEntity);
handButton.SetEntity(held);
handButton.Blocked = false;
}
}
var activeHand = handsComp.ActiveHand;
if (activeHand == null)
if (handsComp.Comp.ActiveHandId == null)
return;
SetActiveHand(activeHand.Name);
SetActiveHand(handsComp.Comp.ActiveHandId);
}
private void HandBlocked(string handName)
@@ -260,19 +268,21 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
if (HandsGui != null &&
_playerHandsComponent != null &&
_player.LocalSession?.AttachedEntity is { } playerEntity &&
_handsSystem.TryGetHand(playerEntity, handName, out var hand, _playerHandsComponent))
_handsSystem.TryGetHand((playerEntity, _playerHandsComponent), handName, out var hand))
{
var foldedLocation = hand.Location.GetUILocation();
var heldEnt = _handsSystem.GetHeldItem((playerEntity, _playerHandsComponent), handName);
var foldedLocation = hand.Value.Location.GetUILocation();
if (foldedLocation == HandUILocation.Left)
{
_statusHandLeft = handControl;
HandsGui.UpdatePanelEntityLeft(hand.HeldEntity);
HandsGui.UpdatePanelEntityLeft(heldEnt);
}
else
{
// Middle or right
_statusHandRight = handControl;
HandsGui.UpdatePanelEntityRight(hand.HeldEntity);
HandsGui.UpdatePanelEntityRight(heldEnt);
}
HandsGui.SetHighlightHand(foldedLocation);
@@ -292,7 +302,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
button.Pressed += HandPressed;
if (!_handLookup.TryAdd(handName, button))
throw new Exception("Tried to add hand with duplicate name to UI. Name:" + handName);
return _handLookup[handName];
if (HandsGui != null)
{
@@ -362,6 +372,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
RemoveHand(handName, out _);
}
[PublicAPI]
private bool RemoveHand(string handName, out HandButton? handButton)
{
if (!_handLookup.TryGetValue(handName, out handButton))
@@ -377,7 +388,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
_statusHandRight = null;
_handLookup.Remove(handName);
handButton.Dispose();
handButton.Orphan();
UpdateVisibleStatusPanels();
return true;
}

View File

@@ -329,9 +329,8 @@ public sealed class InventoryUIController : UIController, IOnStateEntered<Gamepl
var player = _playerUid;
if (!control.MouseIsHovering ||
_playerInventory == null ||
!_entities.TryGetComponent<HandsComponent>(player, out var hands) ||
hands.ActiveHandEntity is not { } held ||
player == null ||
!_handsSystem.TryGetActiveItem(player.Value, out var held) ||
!_entities.TryGetComponent(held, out SpriteComponent? sprite) ||
!_inventorySystem.TryGetSlotContainer(player.Value, control.SlotName, out var container, out var slotDef))
{
@@ -342,12 +341,12 @@ public sealed class InventoryUIController : UIController, IOnStateEntered<Gamepl
// Set green / red overlay at 50% transparency
var hoverEntity = _entities.SpawnEntity("hoverentity", MapCoordinates.Nullspace);
var hoverSprite = _entities.GetComponent<SpriteComponent>(hoverEntity);
var fits = _inventorySystem.CanEquip(player.Value, held, control.SlotName, out _, slotDef) &&
_container.CanInsert(held, container);
var fits = _inventorySystem.CanEquip(player.Value, held.Value, control.SlotName, out _, slotDef) &&
_container.CanInsert(held.Value, container);
if (!fits && _entities.TryGetComponent<StorageComponent>(container.ContainedEntity, out var storage))
{
fits = _entities.System<StorageSystem>().CanInsert(container.ContainedEntity.Value, held, out _, storage);
fits = _entities.System<StorageSystem>().CanInsert(container.ContainedEntity.Value, held.Value, out _, storage);
}
else if (!fits && _entities.TryGetComponent<ItemSlotsComponent>(container.ContainedEntity, out var itemSlots))
{
@@ -357,14 +356,14 @@ public sealed class InventoryUIController : UIController, IOnStateEntered<Gamepl
if (!slot.InsertOnInteract)
continue;
if (!itemSlotsSys.CanInsert(container.ContainedEntity.Value, held, null, slot))
if (!itemSlotsSys.CanInsert(container.ContainedEntity.Value, held.Value, null, slot))
continue;
fits = true;
break;
}
}
_sprite.CopySprite((held, sprite), (hoverEntity, hoverSprite));
_sprite.CopySprite((held.Value, sprite), (hoverEntity, hoverSprite));
_sprite.SetColor((hoverEntity, hoverSprite), fits ? new Color(0, 255, 0, 127) : new Color(255, 0, 0, 127));
control.HoverSpriteView.SetEntity(hoverEntity);