diff --git a/Content.Client/GameObjects/Components/Items/HandsComponent.cs b/Content.Client/GameObjects/Components/Items/HandsComponent.cs
index 2750547ad3..c967a7e4d7 100644
--- a/Content.Client/GameObjects/Components/Items/HandsComponent.cs
+++ b/Content.Client/GameObjects/Components/Items/HandsComponent.cs
@@ -21,7 +21,6 @@ namespace Content.Client.GameObjects.Components.Items
private HandsGui? _gui;
- ///
private readonly List _hands = new List();
[ViewVariables] public IReadOnlyList Hands => _hands;
@@ -90,7 +89,7 @@ namespace Content.Client.GameObjects.Components.Items
{
if (!TryHand(sharedHand.Name, out var hand))
{
- hand = new Hand(sharedHand, Owner.EntityManager);
+ hand = new Hand(this, sharedHand, Owner.EntityManager);
AddHand(hand);
}
else
@@ -102,6 +101,8 @@ namespace Content.Client.GameObjects.Components.Items
: null;
}
+ hand.Enabled = sharedHand.Enabled;
+
UpdateHandSprites(hand);
}
@@ -197,10 +198,35 @@ namespace Content.Client.GameObjects.Components.Items
_gameHud.HandsContainer.AddChild(_gui);
_gui.UpdateHandIcons();
break;
-
case PlayerDetachedMsg _:
_gui?.Parent?.RemoveChild(_gui);
break;
+ case HandEnabledMsg msg:
+ {
+ var hand = GetHand(msg.Name);
+
+ if (hand?.Button == null)
+ {
+ break;
+ }
+
+ hand.Button.Blocked.Visible = false;
+
+ break;
+ }
+ case HandDisabledMsg msg:
+ {
+ var hand = GetHand(msg.Name);
+
+ if (hand?.Button == null)
+ {
+ break;
+ }
+
+ hand.Button.Blocked.Visible = true;
+
+ break;
+ }
}
}
@@ -235,9 +261,11 @@ namespace Content.Client.GameObjects.Components.Items
public class Hand
{
- // TODO: Separate into server hand and client hand
- public Hand(SharedHand hand, IEntityManager manager, HandButton? button = null)
+ private bool _enabled = true;
+
+ public Hand(HandsComponent parent, SharedHand hand, IEntityManager manager, HandButton? button = null)
{
+ Parent = parent;
Index = hand.Index;
Name = hand.Name;
Location = hand.Location;
@@ -252,10 +280,33 @@ namespace Content.Client.GameObjects.Components.Items
Entity = entity;
}
+ private HandsComponent Parent { get; }
public int Index { get; }
public string Name { get; }
public HandLocation Location { get; set; }
public IEntity? Entity { get; set; }
public HandButton? Button { get; set; }
+
+ public bool Enabled
+ {
+ get => _enabled;
+ set
+ {
+ if (_enabled == value)
+ {
+ return;
+ }
+
+ _enabled = value;
+ Parent.Dirty();
+
+ var message = value
+ ? (ComponentMessage) new HandEnabledMsg(Name)
+ : new HandDisabledMsg(Name);
+
+ Parent.HandleMessage(message, Parent);
+ Parent.Owner.SendMessage(Parent, message);
+ }
+ }
}
}
diff --git a/Content.Client/UserInterface/HandButton.cs b/Content.Client/UserInterface/HandButton.cs
index d249cda3bd..af152d5d6b 100644
--- a/Content.Client/UserInterface/HandButton.cs
+++ b/Content.Client/UserInterface/HandButton.cs
@@ -1,15 +1,25 @@
using Content.Shared.GameObjects.Components.Items;
using Robust.Client.Graphics;
+using Robust.Client.UserInterface.Controls;
namespace Content.Client.UserInterface
{
public class HandButton : ItemSlotButton
{
- public HandButton(Texture texture, Texture storageTexture, HandLocation location) : base(texture, storageTexture)
+ public HandButton(Texture texture, Texture storageTexture, Texture blockedTexture, HandLocation location) : base(texture, storageTexture)
{
Location = location;
+
+ AddChild(Blocked = new TextureRect
+ {
+ Texture = blockedTexture,
+ TextureScale = (2, 2),
+ MouseFilter = MouseFilterMode.Stop,
+ Visible = false
+ });
}
public HandLocation Location { get; }
+ public TextureRect Blocked { get; }
}
}
diff --git a/Content.Client/UserInterface/HandsGui.cs b/Content.Client/UserInterface/HandsGui.cs
index 3297fb467b..fca20f2b9a 100644
--- a/Content.Client/UserInterface/HandsGui.cs
+++ b/Content.Client/UserInterface/HandsGui.cs
@@ -111,7 +111,8 @@ namespace Content.Client.UserInterface
{
var buttonTexture = HandTexture(buttonLocation);
var storageTexture = _resourceCache.GetTexture("/Textures/Interface/Inventory/back.png");
- var button = new HandButton(buttonTexture, storageTexture, buttonLocation);
+ var blockedTexture = _resourceCache.GetTexture("/Textures/Interface/Inventory/blocked.png");
+ var button = new HandButton(buttonTexture, storageTexture, blockedTexture, buttonLocation);
var slot = hand.Name;
button.OnPressed += args => HandKeyBindDown(args, slot);
diff --git a/Content.Server/GameObjects/Components/GUI/HandsComponent.cs b/Content.Server/GameObjects/Components/GUI/HandsComponent.cs
index 092076d26d..27246cd39f 100644
--- a/Content.Server/GameObjects/Components/GUI/HandsComponent.cs
+++ b/Content.Server/GameObjects/Components/GUI/HandsComponent.cs
@@ -4,12 +4,10 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.GameObjects.Components.Items.Storage;
-using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.EntitySystems.Click;
using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Shared.GameObjects.Components.Body.Part;
using Content.Shared.GameObjects.Components.Items;
-using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Physics.Pull;
using Robust.Server.GameObjects;
@@ -18,7 +16,6 @@ using Robust.Server.GameObjects.EntitySystemMessages;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
-using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
@@ -26,7 +23,6 @@ using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Players;
using Robust.Shared.ViewVariables;
-using Content.Server.GameObjects.Components.Pulling;
using Robust.Shared.Map;
namespace Content.Server.GameObjects.Components.GUI
@@ -119,7 +115,8 @@ namespace Content.Server.GameObjects.Components.GUI
: GetItem(ActiveHand);
///
- /// Enumerates over the hand keys, returning the active hand first.
+ /// Enumerates over the enabled hand keys,
+ /// returning the active hand first.
///
public IEnumerable ActivePriorityEnumerable()
{
@@ -135,6 +132,11 @@ namespace Content.Server.GameObjects.Components.GUI
continue;
}
+ if (!hand.Enabled)
+ {
+ continue;
+ }
+
yield return hand.Name;
}
}
@@ -205,7 +207,11 @@ namespace Content.Server.GameObjects.Components.GUI
if (mobCheck && !ActionBlockerSystem.CanPickup(Owner))
return false;
- return GetHand(index)?.Container.CanInsert(item.Owner) == true;
+ var hand = GetHand(index);
+
+ return hand != null &&
+ hand.Enabled &&
+ hand.Container.CanInsert(item.Owner) == true;
}
///
@@ -411,7 +417,7 @@ namespace Content.Server.GameObjects.Components.GUI
}
var container = ContainerManagerComponent.Create($"hand {_nextHand++}", Owner);
- var hand = new Hand(name, container);
+ var hand = new Hand(this, name, container);
_hands.Add(hand);
@@ -515,6 +521,53 @@ namespace Content.Server.GameObjects.Components.GUI
return false;
}
+ public override void HandleMessage(ComponentMessage message, IComponent? component)
+ {
+ base.HandleMessage(message, component);
+
+ if (message is PullMessage pullMessage &&
+ pullMessage.Puller.Owner != Owner)
+ {
+ return;
+ }
+
+ switch (message)
+ {
+ case PullAttemptMessage msg:
+ if (!_hands.Any(hand => hand.Enabled))
+ {
+ msg.Cancelled = true;
+ }
+
+ break;
+ case PullStartedMessage _:
+ var firstFreeHand = _hands.FirstOrDefault(hand => hand.Enabled);
+
+ if (firstFreeHand == null)
+ {
+ break;
+ }
+
+ firstFreeHand.Enabled = false;
+
+ break;
+ case PullStoppedMessage _:
+ var firstOccupiedHand = _hands.FirstOrDefault(hand => !hand.Enabled);
+
+ if (firstOccupiedHand == null)
+ {
+ break;
+ }
+
+ firstOccupiedHand.Enabled = true;
+
+ break;
+ case HandDisabledMsg msg:
+ Drop(msg.Name, false);
+ break;
+ }
+ }
+
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession? session = null)
{
base.HandleNetworkMessage(message, channel, session);
@@ -625,34 +678,6 @@ namespace Content.Server.GameObjects.Components.GUI
}
}
- private void AddPullingStatuses(IEntity pulled)
- {
- if (pulled.TryGetComponent(out ServerStatusEffectsComponent? pulledStatus))
- {
- pulledStatus.ChangeStatusEffectIcon(StatusEffect.Pulled,
- "/Textures/Interface/StatusEffects/Pull/pulled.png");
- }
-
- if (Owner.TryGetComponent(out ServerStatusEffectsComponent? ownerStatus))
- {
- ownerStatus.ChangeStatusEffectIcon(StatusEffect.Pulling,
- "/Textures/Interface/StatusEffects/Pull/pulling.png");
- }
- }
-
- private void RemovePullingStatuses(IEntity pulled)
- {
- if (pulled.TryGetComponent(out ServerStatusEffectsComponent? pulledStatus))
- {
- pulledStatus.RemoveStatusEffect(StatusEffect.Pulled);
- }
-
- if (Owner.TryGetComponent(out ServerStatusEffectsComponent? ownerStatus))
- {
- ownerStatus.RemoveStatusEffect(StatusEffect.Pulling);
- }
- }
-
void IBodyPartAdded.BodyPartAdded(BodyPartAddedEventArgs args)
{
if (args.Part.PartType != BodyPartType.Hand)
@@ -676,16 +701,42 @@ namespace Content.Server.GameObjects.Components.GUI
public class Hand : IDisposable
{
- public Hand(string name, ContainerSlot container)
+ private bool _enabled = true;
+
+ public Hand(HandsComponent parent, string name, ContainerSlot container)
{
+ Parent = parent;
Name = name;
Container = container;
}
+ private HandsComponent Parent { get; }
public string Name { get; }
public IEntity? Entity => Container.ContainedEntity;
public ContainerSlot Container { get; }
+ public bool Enabled
+ {
+ get => _enabled;
+ set
+ {
+ if (_enabled == value)
+ {
+ return;
+ }
+
+ _enabled = value;
+ Parent.Dirty();
+
+ var message = value
+ ? (ComponentMessage) new HandEnabledMsg(Name)
+ : new HandDisabledMsg(Name);
+
+ Parent.HandleMessage(message, Parent);
+ Parent.Owner.SendMessage(Parent, message);
+ }
+ }
+
public void Dispose()
{
Container.Shutdown(); // TODO verify this
@@ -693,7 +744,7 @@ namespace Content.Server.GameObjects.Components.GUI
public SharedHand ToShared(int index, HandLocation location)
{
- return new SharedHand(index, Name, Entity?.Uid, location);
+ return new SharedHand(index, Name, Entity?.Uid, location, Enabled);
}
}
diff --git a/Content.Shared/GameObjects/Components/Items/SharedHandsComponent.cs b/Content.Shared/GameObjects/Components/Items/SharedHandsComponent.cs
index dbb31f2883..c8eb3a5255 100644
--- a/Content.Shared/GameObjects/Components/Items/SharedHandsComponent.cs
+++ b/Content.Shared/GameObjects/Components/Items/SharedHandsComponent.cs
@@ -1,13 +1,7 @@
#nullable enable
using System;
-using Content.Shared.GameObjects.Components.Pulling;
-using Content.Shared.Physics.Pull;
-using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
-using Robust.Shared.GameObjects.Components;
-using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization;
-using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Items
{
@@ -24,13 +18,15 @@ namespace Content.Shared.GameObjects.Components.Items
public readonly string Name;
public readonly EntityUid? EntityUid;
public readonly HandLocation Location;
+ public readonly bool Enabled;
- public SharedHand(int index, string name, EntityUid? entityUid, HandLocation location)
+ public SharedHand(int index, string name, EntityUid? entityUid, HandLocation location, bool enabled)
{
Index = index;
Name = name;
EntityUid = entityUid;
Location = location;
+ Enabled = enabled;
}
}
@@ -99,6 +95,28 @@ namespace Content.Shared.GameObjects.Components.Items
}
}
+ [Serializable, NetSerializable]
+ public class HandEnabledMsg : ComponentMessage
+ {
+ public string Name { get; }
+
+ public HandEnabledMsg(string name)
+ {
+ Name = name;
+ }
+ }
+
+ [Serializable, NetSerializable]
+ public class HandDisabledMsg : ComponentMessage
+ {
+ public string Name { get; }
+
+ public HandDisabledMsg(string name)
+ {
+ Name = name;
+ }
+ }
+
public enum HandLocation : byte
{
Left,
diff --git a/Content.Shared/Physics/Pull/PullAttemptMessage.cs b/Content.Shared/Physics/Pull/PullAttemptMessage.cs
new file mode 100644
index 0000000000..8a3a31bab4
--- /dev/null
+++ b/Content.Shared/Physics/Pull/PullAttemptMessage.cs
@@ -0,0 +1,11 @@
+using Robust.Shared.GameObjects.Components;
+
+namespace Content.Shared.Physics.Pull
+{
+ public class PullAttemptMessage : PullMessage
+ {
+ public PullAttemptMessage(IPhysicsComponent puller, IPhysicsComponent pulled) : base(puller, pulled) { }
+
+ public bool Cancelled { get; set; }
+ }
+}
diff --git a/Content.Shared/Physics/Pull/PullController.cs b/Content.Shared/Physics/Pull/PullController.cs
index 5884a0a6ff..b34b571dcc 100644
--- a/Content.Shared/Physics/Pull/PullController.cs
+++ b/Content.Shared/Physics/Pull/PullController.cs
@@ -100,6 +100,22 @@ namespace Content.Shared.Physics.Pull
return false;
}
+ var pullAttempt = new PullAttemptMessage(puller, ControlledComponent);
+
+ puller.Owner.SendMessage(null, pullAttempt);
+
+ if (pullAttempt.Cancelled)
+ {
+ return false;
+ }
+
+ ControlledComponent.Owner.SendMessage(null, pullAttempt);
+
+ if (pullAttempt.Cancelled)
+ {
+ return false;
+ }
+
_puller = puller;
var message = new PullStartedMessage(this, _puller, ControlledComponent);
diff --git a/Resources/Textures/Interface/Inventory/blocked.png b/Resources/Textures/Interface/Inventory/blocked.png
new file mode 100644
index 0000000000..0d6482f99e
Binary files /dev/null and b/Resources/Textures/Interface/Inventory/blocked.png differ