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:
@@ -16,7 +16,6 @@ using Robust.Client.UserInterface;
|
|||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Timing;
|
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Client.Hands.Systems
|
namespace Content.Client.Hands.Systems
|
||||||
@@ -27,16 +26,13 @@ namespace Content.Client.Hands.Systems
|
|||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly IUserInterfaceManager _ui = default!;
|
[Dependency] private readonly IUserInterfaceManager _ui = default!;
|
||||||
|
|
||||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
|
||||||
[Dependency] private readonly StrippableSystem _stripSys = default!;
|
[Dependency] private readonly StrippableSystem _stripSys = default!;
|
||||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||||
[Dependency] private readonly ExamineSystem _examine = default!;
|
[Dependency] private readonly ExamineSystem _examine = default!;
|
||||||
[Dependency] private readonly DisplacementMapSystem _displacement = default!;
|
[Dependency] private readonly DisplacementMapSystem _displacement = default!;
|
||||||
|
|
||||||
public event Action<string, HandLocation>? OnPlayerAddHand;
|
|
||||||
public event Action<string>? OnPlayerRemoveHand;
|
|
||||||
public event Action<string?>? OnPlayerSetActiveHand;
|
public event Action<string?>? OnPlayerSetActiveHand;
|
||||||
public event Action<HandsComponent>? OnPlayerHandsAdded;
|
public event Action<Entity<HandsComponent>>? OnPlayerHandsAdded;
|
||||||
public event Action? OnPlayerHandsRemoved;
|
public event Action? OnPlayerHandsRemoved;
|
||||||
public event Action<string, EntityUid>? OnPlayerItemAdded;
|
public event Action<string, EntityUid>? OnPlayerItemAdded;
|
||||||
public event Action<string, EntityUid>? OnPlayerItemRemoved;
|
public event Action<string, EntityUid>? OnPlayerItemRemoved;
|
||||||
@@ -58,67 +54,28 @@ namespace Content.Client.Hands.Systems
|
|||||||
}
|
}
|
||||||
|
|
||||||
#region StateHandling
|
#region StateHandling
|
||||||
private void HandleComponentState(EntityUid uid, HandsComponent component, ref ComponentHandleState args)
|
private void HandleComponentState(Entity<HandsComponent> ent, ref ComponentHandleState args)
|
||||||
{
|
{
|
||||||
if (args.Current is not HandsComponentState state)
|
if (args.Current is not HandsComponentState state)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var handsModified = component.Hands.Count != state.Hands.Count;
|
var newHands = state.Hands.Keys.Except(ent.Comp.Hands.Keys); // hands that were added between states
|
||||||
// we need to check that, even if we have the same amount, that the individual hands didn't change.
|
var oldHands = ent.Comp.Hands.Keys.Except(state.Hands.Keys); // hands that were removed between states
|
||||||
if (!handsModified)
|
|
||||||
|
foreach (var handId in oldHands)
|
||||||
{
|
{
|
||||||
foreach (var hand in component.Hands.Values)
|
RemoveHand(ent.AsNullable(), handId);
|
||||||
{
|
|
||||||
if (state.Hands.Contains(hand))
|
|
||||||
continue;
|
|
||||||
handsModified = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var manager = EnsureComp<ContainerManagerComponent>(uid);
|
foreach (var handId in state.SortedHands.Intersect(newHands))
|
||||||
|
|
||||||
if (handsModified)
|
|
||||||
{
|
{
|
||||||
List<Hand> addedHands = new();
|
AddHand(ent.AsNullable(), handId, state.Hands[handId]);
|
||||||
foreach (var hand in state.Hands)
|
|
||||||
{
|
|
||||||
if (component.Hands.ContainsKey(hand.Name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var container = _containerSystem.EnsureContainer<ContainerSlot>(uid, hand.Name, manager);
|
|
||||||
var newHand = new Hand(hand.Name, hand.Location, container);
|
|
||||||
component.Hands.Add(hand.Name, newHand);
|
|
||||||
addedHands.Add(newHand);
|
|
||||||
}
|
}
|
||||||
|
ent.Comp.SortedHands = new (state.SortedHands);
|
||||||
|
|
||||||
foreach (var name in component.Hands.Keys)
|
SetActiveHand(ent.AsNullable(), state.ActiveHandId);
|
||||||
{
|
|
||||||
if (!state.HandNames.Contains(name))
|
|
||||||
{
|
|
||||||
RemoveHand(uid, name, component);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
component.SortedHands.Clear();
|
_stripSys.UpdateUi(ent);
|
||||||
component.SortedHands.AddRange(state.HandNames);
|
|
||||||
var sorted = addedHands.OrderBy(hand => component.SortedHands.IndexOf(hand.Name));
|
|
||||||
|
|
||||||
foreach (var hand in sorted)
|
|
||||||
{
|
|
||||||
AddHand(uid, hand, component);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_stripSys.UpdateUi(uid);
|
|
||||||
|
|
||||||
if (component.ActiveHand == null && state.ActiveHand == null)
|
|
||||||
return; //edge case
|
|
||||||
|
|
||||||
if (component.ActiveHand != null && state.ActiveHand != component.ActiveHand.Name)
|
|
||||||
{
|
|
||||||
SetActiveHand(uid, component.Hands[state.ActiveHand!], component);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -129,47 +86,52 @@ namespace Content.Client.Hands.Systems
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
OnPlayerHandsAdded?.Invoke(hands);
|
OnPlayerHandsAdded?.Invoke(hands.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void DoDrop(EntityUid uid, Hand hand, bool doDropInteraction = true, HandsComponent? hands = null, bool log = true)
|
public override void DoDrop(Entity<HandsComponent?> ent,
|
||||||
|
string handId,
|
||||||
|
bool doDropInteraction = true,
|
||||||
|
bool log = true)
|
||||||
{
|
{
|
||||||
base.DoDrop(uid, hand, doDropInteraction, hands, log);
|
base.DoDrop(ent, handId, doDropInteraction, log);
|
||||||
|
|
||||||
if (TryComp(hand.HeldEntity, out SpriteComponent? sprite))
|
if (TryGetHeldItem(ent, handId, out var held) && TryComp(held, out SpriteComponent? sprite))
|
||||||
sprite.RenderOrder = EntityManager.CurrentTick.Value;
|
sprite.RenderOrder = EntityManager.CurrentTick.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityUid? GetActiveHandEntity()
|
public EntityUid? GetActiveHandEntity()
|
||||||
{
|
{
|
||||||
return TryGetPlayerHands(out var hands) ? hands.ActiveHandEntity : null;
|
return TryGetPlayerHands(out var hands) ? GetActiveItem(hands.Value.AsNullable()) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the hands component of the local player
|
/// Get the hands component of the local player
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TryGetPlayerHands([NotNullWhen(true)] out HandsComponent? hands)
|
public bool TryGetPlayerHands([NotNullWhen(true)] out Entity<HandsComponent>? hands)
|
||||||
{
|
{
|
||||||
var player = _playerManager.LocalEntity;
|
var player = _playerManager.LocalEntity;
|
||||||
hands = null;
|
hands = null;
|
||||||
return player != null && TryComp(player.Value, out hands);
|
if (player == null || !TryComp<HandsComponent>(player.Value, out var handsComp))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
hands = (player.Value, handsComp);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when a user clicked on their hands GUI
|
/// Called when a user clicked on their hands GUI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void UIHandClick(HandsComponent hands, string handName)
|
public void UIHandClick(Entity<HandsComponent> ent, string handName)
|
||||||
{
|
{
|
||||||
if (!hands.Hands.TryGetValue(handName, out var pressedHand))
|
var hands = ent.Comp;
|
||||||
|
if (hands.ActiveHandId == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (hands.ActiveHand == null)
|
var pressedEntity = GetHeldItem(ent.AsNullable(), handName);
|
||||||
return;
|
var activeEntity = GetActiveItem(ent.AsNullable());
|
||||||
|
|
||||||
var pressedEntity = pressedHand.HeldEntity;
|
if (handName == hands.ActiveHandId && activeEntity != null)
|
||||||
var activeEntity = hands.ActiveHand.HeldEntity;
|
|
||||||
|
|
||||||
if (pressedHand == hands.ActiveHand && activeEntity != null)
|
|
||||||
{
|
{
|
||||||
// use item in hand
|
// use item in hand
|
||||||
// it will always be attack_self() in my heart.
|
// it will always be attack_self() in my heart.
|
||||||
@@ -177,24 +139,24 @@ namespace Content.Client.Hands.Systems
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pressedHand != hands.ActiveHand && pressedEntity == null)
|
if (handName != hands.ActiveHandId && pressedEntity == null)
|
||||||
{
|
{
|
||||||
// change active hand
|
// change active hand
|
||||||
EntityManager.RaisePredictiveEvent(new RequestSetHandEvent(handName));
|
EntityManager.RaisePredictiveEvent(new RequestSetHandEvent(handName));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pressedHand != hands.ActiveHand && pressedEntity != null && activeEntity != null)
|
if (handName != hands.ActiveHandId && pressedEntity != null && activeEntity != null)
|
||||||
{
|
{
|
||||||
// use active item on held item
|
// use active item on held item
|
||||||
EntityManager.RaisePredictiveEvent(new RequestHandInteractUsingEvent(pressedHand.Name));
|
EntityManager.RaisePredictiveEvent(new RequestHandInteractUsingEvent(handName));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pressedHand != hands.ActiveHand && pressedEntity != null && activeEntity == null)
|
if (handName != hands.ActiveHandId && pressedEntity != null && activeEntity == null)
|
||||||
{
|
{
|
||||||
// move the item to the active hand
|
// move the item to the active hand
|
||||||
EntityManager.RaisePredictiveEvent(new RequestMoveHandItemEvent(pressedHand.Name));
|
EntityManager.RaisePredictiveEvent(new RequestMoveHandItemEvent(handName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,13 +172,12 @@ namespace Content.Client.Hands.Systems
|
|||||||
public void UIInventoryExamine(string handName)
|
public void UIInventoryExamine(string handName)
|
||||||
{
|
{
|
||||||
if (!TryGetPlayerHands(out var hands) ||
|
if (!TryGetPlayerHands(out var hands) ||
|
||||||
!hands.Hands.TryGetValue(handName, out var hand) ||
|
!TryGetHeldItem(hands.Value.AsNullable(), handName, out var heldEntity))
|
||||||
hand.HeldEntity is not { Valid: true } entity)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_examine.DoExamine(entity);
|
_examine.DoExamine(heldEntity.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -226,13 +187,12 @@ namespace Content.Client.Hands.Systems
|
|||||||
public void UIHandOpenContextMenu(string handName)
|
public void UIHandOpenContextMenu(string handName)
|
||||||
{
|
{
|
||||||
if (!TryGetPlayerHands(out var hands) ||
|
if (!TryGetPlayerHands(out var hands) ||
|
||||||
!hands.Hands.TryGetValue(handName, out var hand) ||
|
!TryGetHeldItem(hands.Value.AsNullable(), handName, out var heldEntity))
|
||||||
hand.HeldEntity is not { Valid: true } entity)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ui.GetUIController<VerbMenuUIController>().OpenVerbMenu(entity);
|
_ui.GetUIController<VerbMenuUIController>().OpenVerbMenu(heldEntity.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UIHandAltActivateItem(string handName)
|
public void UIHandAltActivateItem(string handName)
|
||||||
@@ -246,60 +206,67 @@ namespace Content.Client.Hands.Systems
|
|||||||
{
|
{
|
||||||
base.HandleEntityInserted(uid, hands, args);
|
base.HandleEntityInserted(uid, hands, args);
|
||||||
|
|
||||||
if (!hands.Hands.TryGetValue(args.Container.ID, out var hand))
|
if (!hands.Hands.ContainsKey(args.Container.ID))
|
||||||
return;
|
return;
|
||||||
UpdateHandVisuals(uid, args.Entity, hand);
|
|
||||||
|
UpdateHandVisuals(uid, args.Entity, args.Container.ID);
|
||||||
_stripSys.UpdateUi(uid);
|
_stripSys.UpdateUi(uid);
|
||||||
|
|
||||||
if (uid != _playerManager.LocalEntity)
|
if (uid != _playerManager.LocalEntity)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
OnPlayerItemAdded?.Invoke(hand.Name, args.Entity);
|
OnPlayerItemAdded?.Invoke(args.Container.ID, args.Entity);
|
||||||
|
|
||||||
if (HasComp<VirtualItemComponent>(args.Entity))
|
if (HasComp<VirtualItemComponent>(args.Entity))
|
||||||
OnPlayerHandBlocked?.Invoke(hand.Name);
|
OnPlayerHandBlocked?.Invoke(args.Container.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void HandleEntityRemoved(EntityUid uid, HandsComponent hands, EntRemovedFromContainerMessage args)
|
protected override void HandleEntityRemoved(EntityUid uid, HandsComponent hands, EntRemovedFromContainerMessage args)
|
||||||
{
|
{
|
||||||
base.HandleEntityRemoved(uid, hands, args);
|
base.HandleEntityRemoved(uid, hands, args);
|
||||||
|
|
||||||
if (!hands.Hands.TryGetValue(args.Container.ID, out var hand))
|
if (!hands.Hands.ContainsKey(args.Container.ID))
|
||||||
return;
|
return;
|
||||||
UpdateHandVisuals(uid, args.Entity, hand);
|
|
||||||
|
UpdateHandVisuals(uid, args.Entity, args.Container.ID);
|
||||||
_stripSys.UpdateUi(uid);
|
_stripSys.UpdateUi(uid);
|
||||||
|
|
||||||
if (uid != _playerManager.LocalEntity)
|
if (uid != _playerManager.LocalEntity)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
OnPlayerItemRemoved?.Invoke(hand.Name, args.Entity);
|
OnPlayerItemRemoved?.Invoke(args.Container.ID, args.Entity);
|
||||||
|
|
||||||
if (HasComp<VirtualItemComponent>(args.Entity))
|
if (HasComp<VirtualItemComponent>(args.Entity))
|
||||||
OnPlayerHandUnblocked?.Invoke(hand.Name);
|
OnPlayerHandUnblocked?.Invoke(args.Container.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update the players sprite with new in-hand visuals.
|
/// Update the players sprite with new in-hand visuals.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateHandVisuals(EntityUid uid, EntityUid held, Hand hand, HandsComponent? handComp = null, SpriteComponent? sprite = null)
|
private void UpdateHandVisuals(Entity<HandsComponent?, SpriteComponent?> ent, EntityUid held, string handId)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handComp, ref sprite, false))
|
if (!Resolve(ent, ref ent.Comp1, ref ent.Comp2, false))
|
||||||
|
return;
|
||||||
|
var handComp = ent.Comp1;
|
||||||
|
var sprite = ent.Comp2;
|
||||||
|
|
||||||
|
if (!TryGetHand((ent, handComp), handId, out var hand))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// visual update might involve changes to the entity's effective sprite -> need to update hands GUI.
|
// visual update might involve changes to the entity's effective sprite -> need to update hands GUI.
|
||||||
if (uid == _playerManager.LocalEntity)
|
if (ent == _playerManager.LocalEntity)
|
||||||
OnPlayerItemAdded?.Invoke(hand.Name, held);
|
OnPlayerItemAdded?.Invoke(handId, held);
|
||||||
|
|
||||||
if (!handComp.ShowInHands)
|
if (!handComp.ShowInHands)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Remove old layers. We could also just set them to invisible, but as items may add arbitrary layers, this
|
// Remove old layers. We could also just set them to invisible, but as items may add arbitrary layers, this
|
||||||
// may eventually bloat the player with lots of layers.
|
// may eventually bloat the player with lots of layers.
|
||||||
if (handComp.RevealedLayers.TryGetValue(hand.Location, out var revealedLayers))
|
if (handComp.RevealedLayers.TryGetValue(hand.Value.Location, out var revealedLayers))
|
||||||
{
|
{
|
||||||
foreach (var key in revealedLayers)
|
foreach (var key in revealedLayers)
|
||||||
{
|
{
|
||||||
_sprite.RemoveLayer((uid, sprite), key);
|
_sprite.RemoveLayer((ent, sprite), key);
|
||||||
}
|
}
|
||||||
|
|
||||||
revealedLayers.Clear();
|
revealedLayers.Clear();
|
||||||
@@ -307,22 +274,22 @@ namespace Content.Client.Hands.Systems
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
revealedLayers = new();
|
revealedLayers = new();
|
||||||
handComp.RevealedLayers[hand.Location] = revealedLayers;
|
handComp.RevealedLayers[hand.Value.Location] = revealedLayers;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hand.HeldEntity == null)
|
if (HandIsEmpty((ent, handComp), handId))
|
||||||
{
|
{
|
||||||
// the held item was removed.
|
// the held item was removed.
|
||||||
RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers), true);
|
RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(ent, revealedLayers), true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ev = new GetInhandVisualsEvent(uid, hand.Location);
|
var ev = new GetInhandVisualsEvent(ent, hand.Value.Location);
|
||||||
RaiseLocalEvent(held, ev);
|
RaiseLocalEvent(held, ev);
|
||||||
|
|
||||||
if (ev.Layers.Count == 0)
|
if (ev.Layers.Count == 0)
|
||||||
{
|
{
|
||||||
RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers), true);
|
RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(ent, revealedLayers), true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,7 +302,7 @@ namespace Content.Client.Hands.Systems
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var index = _sprite.LayerMapReserve((uid, sprite), key);
|
var index = _sprite.LayerMapReserve((ent, sprite), key);
|
||||||
|
|
||||||
// In case no RSI is given, use the item's base RSI as a default. This cuts down on a lot of unnecessary yaml entries.
|
// In case no RSI is given, use the item's base RSI as a default. This cuts down on a lot of unnecessary yaml entries.
|
||||||
if (layerData.RsiPath == null
|
if (layerData.RsiPath == null
|
||||||
@@ -343,35 +310,34 @@ namespace Content.Client.Hands.Systems
|
|||||||
&& sprite[index].Rsi == null)
|
&& sprite[index].Rsi == null)
|
||||||
{
|
{
|
||||||
if (TryComp<ItemComponent>(held, out var itemComponent) && itemComponent.RsiPath != null)
|
if (TryComp<ItemComponent>(held, out var itemComponent) && itemComponent.RsiPath != null)
|
||||||
_sprite.LayerSetRsi((uid, sprite), index, new ResPath(itemComponent.RsiPath));
|
_sprite.LayerSetRsi((ent, sprite), index, new ResPath(itemComponent.RsiPath));
|
||||||
else if (TryComp(held, out SpriteComponent? clothingSprite))
|
else if (TryComp(held, out SpriteComponent? clothingSprite))
|
||||||
_sprite.LayerSetRsi((uid, sprite), index, clothingSprite.BaseRSI);
|
_sprite.LayerSetRsi((ent, sprite), index, clothingSprite.BaseRSI);
|
||||||
}
|
}
|
||||||
|
|
||||||
_sprite.LayerSetData((uid, sprite), index, layerData);
|
_sprite.LayerSetData((ent, sprite), index, layerData);
|
||||||
|
|
||||||
// Add displacement maps
|
// Add displacement maps
|
||||||
var displacement = hand.Location switch
|
var displacement = hand.Value.Location switch
|
||||||
{
|
{
|
||||||
HandLocation.Left => handComp.LeftHandDisplacement,
|
HandLocation.Left => handComp.LeftHandDisplacement,
|
||||||
HandLocation.Right => handComp.RightHandDisplacement,
|
HandLocation.Right => handComp.RightHandDisplacement,
|
||||||
_ => handComp.HandDisplacement
|
_ => handComp.HandDisplacement
|
||||||
};
|
};
|
||||||
|
|
||||||
if (displacement is not null && _displacement.TryAddDisplacement(displacement, (uid, sprite), index, key, out var displacementKey))
|
if (displacement is not null && _displacement.TryAddDisplacement(displacement, (ent, sprite), index, key, out var displacementKey))
|
||||||
revealedLayers.Add(displacementKey);
|
revealedLayers.Add(displacementKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers), true);
|
RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(ent, revealedLayers), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnVisualsChanged(EntityUid uid, HandsComponent component, VisualsChangedEvent args)
|
private void OnVisualsChanged(EntityUid uid, HandsComponent component, VisualsChangedEvent args)
|
||||||
{
|
{
|
||||||
// update hands visuals if this item is in a hand (rather then inventory or other container).
|
// update hands visuals if this item is in a hand (rather then inventory or other container).
|
||||||
if (component.Hands.TryGetValue(args.ContainerId, out var hand))
|
if (!component.Hands.ContainsKey(args.ContainerId))
|
||||||
{
|
return;
|
||||||
UpdateHandVisuals(uid, GetEntity(args.Item), hand, component);
|
UpdateHandVisuals((uid, component), GetEntity(args.Item), args.ContainerId);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -379,7 +345,7 @@ namespace Content.Client.Hands.Systems
|
|||||||
|
|
||||||
private void HandlePlayerAttached(EntityUid uid, HandsComponent component, LocalPlayerAttachedEvent args)
|
private void HandlePlayerAttached(EntityUid uid, HandsComponent component, LocalPlayerAttachedEvent args)
|
||||||
{
|
{
|
||||||
OnPlayerHandsAdded?.Invoke(component);
|
OnPlayerHandsAdded?.Invoke((uid, component));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandlePlayerDetached(EntityUid uid, HandsComponent component, LocalPlayerDetachedEvent args)
|
private void HandlePlayerDetached(EntityUid uid, HandsComponent component, LocalPlayerDetachedEvent args)
|
||||||
@@ -390,7 +356,7 @@ namespace Content.Client.Hands.Systems
|
|||||||
private void OnHandsStartup(EntityUid uid, HandsComponent component, ComponentStartup args)
|
private void OnHandsStartup(EntityUid uid, HandsComponent component, ComponentStartup args)
|
||||||
{
|
{
|
||||||
if (_playerManager.LocalEntity == uid)
|
if (_playerManager.LocalEntity == uid)
|
||||||
OnPlayerHandsAdded?.Invoke(component);
|
OnPlayerHandsAdded?.Invoke((uid, component));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnHandsShutdown(EntityUid uid, HandsComponent component, ComponentShutdown args)
|
private void OnHandsShutdown(EntityUid uid, HandsComponent component, ComponentShutdown args)
|
||||||
@@ -400,36 +366,6 @@ namespace Content.Client.Hands.Systems
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private void AddHand(EntityUid uid, Hand newHand, HandsComponent? handsComp = null)
|
|
||||||
{
|
|
||||||
AddHand(uid, newHand.Name, newHand.Location, handsComp);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void AddHand(EntityUid uid, string handName, HandLocation handLocation, HandsComponent? handsComp = null)
|
|
||||||
{
|
|
||||||
base.AddHand(uid, handName, handLocation, handsComp);
|
|
||||||
|
|
||||||
if (uid == _playerManager.LocalEntity)
|
|
||||||
OnPlayerAddHand?.Invoke(handName, handLocation);
|
|
||||||
|
|
||||||
if (handsComp == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (handsComp.ActiveHand == null)
|
|
||||||
SetActiveHand(uid, handsComp.Hands[handName], handsComp);
|
|
||||||
}
|
|
||||||
public override void RemoveHand(EntityUid uid, string handName, HandsComponent? handsComp = null)
|
|
||||||
{
|
|
||||||
if (uid == _playerManager.LocalEntity && handsComp != null &&
|
|
||||||
handsComp.Hands.ContainsKey(handName) && uid ==
|
|
||||||
_playerManager.LocalEntity)
|
|
||||||
{
|
|
||||||
OnPlayerRemoveHand?.Invoke(handName);
|
|
||||||
}
|
|
||||||
|
|
||||||
base.RemoveHand(uid, handName, handsComp);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnHandActivated(Entity<HandsComponent>? ent)
|
private void OnHandActivated(Entity<HandsComponent>? ent)
|
||||||
{
|
{
|
||||||
if (ent is not { } hand)
|
if (ent is not { } hand)
|
||||||
@@ -438,13 +374,7 @@ namespace Content.Client.Hands.Systems
|
|||||||
if (_playerManager.LocalEntity != hand.Owner)
|
if (_playerManager.LocalEntity != hand.Owner)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (hand.Comp.ActiveHand == null)
|
OnPlayerSetActiveHand?.Invoke(hand.Comp.ActiveHandId);
|
||||||
{
|
|
||||||
OnPlayerSetActiveHand?.Invoke(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
OnPlayerSetActiveHand?.Invoke(hand.Comp.ActiveHand.Name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Client.Examine;
|
using Content.Client.Examine;
|
||||||
|
using Content.Client.Hands.Systems;
|
||||||
using Content.Client.Strip;
|
using Content.Client.Strip;
|
||||||
using Content.Client.Stylesheets;
|
using Content.Client.Stylesheets;
|
||||||
using Content.Client.UserInterface.Controls;
|
using Content.Client.UserInterface.Controls;
|
||||||
@@ -34,6 +35,7 @@ namespace Content.Client.Inventory
|
|||||||
[Dependency] private readonly IUserInterfaceManager _ui = default!;
|
[Dependency] private readonly IUserInterfaceManager _ui = default!;
|
||||||
|
|
||||||
private readonly ExamineSystem _examine;
|
private readonly ExamineSystem _examine;
|
||||||
|
private readonly HandsSystem _hands;
|
||||||
private readonly InventorySystem _inv;
|
private readonly InventorySystem _inv;
|
||||||
private readonly SharedCuffableSystem _cuffable;
|
private readonly SharedCuffableSystem _cuffable;
|
||||||
private readonly StrippableSystem _strippable;
|
private readonly StrippableSystem _strippable;
|
||||||
@@ -65,6 +67,7 @@ namespace Content.Client.Inventory
|
|||||||
public StrippableBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
public StrippableBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||||
{
|
{
|
||||||
_examine = EntMan.System<ExamineSystem>();
|
_examine = EntMan.System<ExamineSystem>();
|
||||||
|
_hands = EntMan.System<HandsSystem>();
|
||||||
_inv = EntMan.System<InventorySystem>();
|
_inv = EntMan.System<InventorySystem>();
|
||||||
_cuffable = EntMan.System<SharedCuffableSystem>();
|
_cuffable = EntMan.System<SharedCuffableSystem>();
|
||||||
_strippable = EntMan.System<StrippableSystem>();
|
_strippable = EntMan.System<StrippableSystem>();
|
||||||
@@ -120,28 +123,28 @@ namespace Content.Client.Inventory
|
|||||||
{
|
{
|
||||||
// good ol hands shit code. there is a GuiHands comparer that does the same thing... but these are hands
|
// good ol hands shit code. there is a GuiHands comparer that does the same thing... but these are hands
|
||||||
// and not gui hands... which are different...
|
// and not gui hands... which are different...
|
||||||
foreach (var hand in handsComp.Hands.Values)
|
foreach (var (id, hand) in handsComp.Hands)
|
||||||
{
|
{
|
||||||
if (hand.Location != HandLocation.Right)
|
if (hand.Location != HandLocation.Right)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
AddHandButton(hand);
|
AddHandButton((Owner, handsComp), id, hand);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var hand in handsComp.Hands.Values)
|
foreach (var (id, hand) in handsComp.Hands)
|
||||||
{
|
{
|
||||||
if (hand.Location != HandLocation.Middle)
|
if (hand.Location != HandLocation.Middle)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
AddHandButton(hand);
|
AddHandButton((Owner, handsComp), id, hand);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var hand in handsComp.Hands.Values)
|
foreach (var (id, hand) in handsComp.Hands)
|
||||||
{
|
{
|
||||||
if (hand.Location != HandLocation.Left)
|
if (hand.Location != HandLocation.Left)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
AddHandButton(hand);
|
AddHandButton((Owner, handsComp), id, hand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,20 +180,21 @@ namespace Content.Client.Inventory
|
|||||||
_strippingMenu.SetSize = new Vector2(horizontalMenuSize, verticalMenuSize);
|
_strippingMenu.SetSize = new Vector2(horizontalMenuSize, verticalMenuSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddHandButton(Hand hand)
|
private void AddHandButton(Entity<HandsComponent> ent, string handId, Hand hand)
|
||||||
{
|
{
|
||||||
var button = new HandButton(hand.Name, hand.Location);
|
var button = new HandButton(handId, hand.Location);
|
||||||
|
|
||||||
button.Pressed += SlotPressed;
|
button.Pressed += SlotPressed;
|
||||||
|
|
||||||
if (EntMan.TryGetComponent<VirtualItemComponent>(hand.HeldEntity, out var virt))
|
var heldEntity = _hands.GetHeldItem(ent.AsNullable(), handId);
|
||||||
|
if (EntMan.TryGetComponent<VirtualItemComponent>(heldEntity, out var virt))
|
||||||
{
|
{
|
||||||
button.Blocked = true;
|
button.Blocked = true;
|
||||||
if (EntMan.TryGetComponent<CuffableComponent>(Owner, out var cuff) && _cuffable.GetAllCuffs(cuff).Contains(virt.BlockingEntity))
|
if (EntMan.TryGetComponent<CuffableComponent>(Owner, out var cuff) && _cuffable.GetAllCuffs(cuff).Contains(virt.BlockingEntity))
|
||||||
button.BlockedRect.MouseFilter = MouseFilterMode.Ignore;
|
button.BlockedRect.MouseFilter = MouseFilterMode.Ignore;
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateEntityIcon(button, hand.HeldEntity);
|
UpdateEntityIcon(button, heldEntity);
|
||||||
_strippingMenu!.HandsContainer.AddChild(button);
|
_strippingMenu!.HandsContainer.AddChild(button);
|
||||||
LayoutContainer.SetPosition(button, new Vector2i(_handCount, 0) * (SlotControl.DefaultButtonSize + ButtonSeparation));
|
LayoutContainer.SetPosition(button, new Vector2i(_handCount, 0) * (SlotControl.DefaultButtonSize + ButtonSeparation));
|
||||||
_handCount++;
|
_handCount++;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Client.Gameplay;
|
using Content.Client.Gameplay;
|
||||||
|
using Content.Client.Hands.Systems;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.RCD.Components;
|
using Content.Shared.RCD.Components;
|
||||||
@@ -17,6 +18,7 @@ public sealed class AlignRCDConstruction : PlacementMode
|
|||||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
private readonly SharedMapSystem _mapSystem;
|
private readonly SharedMapSystem _mapSystem;
|
||||||
|
private readonly HandsSystem _handsSystem;
|
||||||
private readonly RCDSystem _rcdSystem;
|
private readonly RCDSystem _rcdSystem;
|
||||||
private readonly SharedTransformSystem _transformSystem;
|
private readonly SharedTransformSystem _transformSystem;
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
@@ -34,6 +36,7 @@ public sealed class AlignRCDConstruction : PlacementMode
|
|||||||
{
|
{
|
||||||
IoCManager.InjectDependencies(this);
|
IoCManager.InjectDependencies(this);
|
||||||
_mapSystem = _entityManager.System<SharedMapSystem>();
|
_mapSystem = _entityManager.System<SharedMapSystem>();
|
||||||
|
_handsSystem = _entityManager.System<HandsSystem>();
|
||||||
_rcdSystem = _entityManager.System<RCDSystem>();
|
_rcdSystem = _entityManager.System<RCDSystem>();
|
||||||
_transformSystem = _entityManager.System<SharedTransformSystem>();
|
_transformSystem = _entityManager.System<SharedTransformSystem>();
|
||||||
|
|
||||||
@@ -88,11 +91,9 @@ public sealed class AlignRCDConstruction : PlacementMode
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Determine if player is carrying an RCD in their active hand
|
// Determine if player is carrying an RCD in their active hand
|
||||||
if (!_entityManager.TryGetComponent<HandsComponent>(player, out var hands))
|
if (!_handsSystem.TryGetActiveItem(player.Value, out var heldEntity))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var heldEntity = hands.ActiveHand?.HeldEntity;
|
|
||||||
|
|
||||||
if (!_entityManager.TryGetComponent<RCDComponent>(heldEntity, out var rcd))
|
if (!_entityManager.TryGetComponent<RCDComponent>(heldEntity, out var rcd))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Content.Client.Hands.Systems;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.RCD;
|
using Content.Shared.RCD;
|
||||||
@@ -15,6 +16,7 @@ public sealed class RCDConstructionGhostSystem : EntitySystem
|
|||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly IPlacementManager _placementManager = default!;
|
[Dependency] private readonly IPlacementManager _placementManager = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||||
|
[Dependency] private readonly HandsSystem _hands = default!;
|
||||||
|
|
||||||
private string _placementMode = typeof(AlignRCDConstruction).Name;
|
private string _placementMode = typeof(AlignRCDConstruction).Name;
|
||||||
private Direction _placementDirection = default;
|
private Direction _placementDirection = default;
|
||||||
@@ -33,12 +35,11 @@ public sealed class RCDConstructionGhostSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Determine if player is carrying an RCD in their active hand
|
// Determine if player is carrying an RCD in their active hand
|
||||||
var player = _playerManager.LocalSession?.AttachedEntity;
|
if (_playerManager.LocalSession?.AttachedEntity is not { } player)
|
||||||
|
|
||||||
if (!TryComp<HandsComponent>(player, out var hands))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var heldEntity = hands.ActiveHand?.HeldEntity;
|
if (!_hands.TryGetActiveItem(player, out var heldEntity))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!TryComp<RCDComponent>(heldEntity, out var rcd))
|
if (!TryComp<RCDComponent>(heldEntity, out var rcd))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -68,11 +68,15 @@ public sealed class TrayScannerSystem : SharedTrayScannerSystem
|
|||||||
|
|
||||||
foreach (var hand in _hands.EnumerateHands(player.Value))
|
foreach (var hand in _hands.EnumerateHands(player.Value))
|
||||||
{
|
{
|
||||||
if (!scannerQuery.TryGetComponent(hand.HeldEntity, out var heldScanner) || !heldScanner.Enabled)
|
if (!_hands.TryGetHeldItem(player.Value, hand, out var heldEntity))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!scannerQuery.TryGetComponent(heldEntity, out var heldScanner) || !heldScanner.Enabled)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
range = MathF.Max(heldScanner.Range, range);
|
range = MathF.Max(heldScanner.Range, range);
|
||||||
canSee = true;
|
canSee = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
inRange = new HashSet<Entity<SubFloorHideComponent>>();
|
inRange = new HashSet<Entity<SubFloorHideComponent>>();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Content.Shared.Hands.Components;
|
|||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
using Content.Shared.Inventory.VirtualItem;
|
using Content.Shared.Inventory.VirtualItem;
|
||||||
using Content.Shared.Timing;
|
using Content.Shared.Timing;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controllers;
|
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, int> _handContainerIndices = new();
|
||||||
private readonly Dictionary<string, HandButton> _handLookup = new();
|
private readonly Dictionary<string, HandButton> _handLookup = new();
|
||||||
private HandsComponent? _playerHandsComponent;
|
private HandsComponent? _playerHandsComponent;
|
||||||
private HandButton? _activeHand = null;
|
private HandButton? _activeHand;
|
||||||
|
|
||||||
// We only have two item status controls (left and right hand),
|
// We only have two item status controls (left and right hand),
|
||||||
// but we may have more than two hands.
|
// but we may have more than two hands.
|
||||||
@@ -38,7 +39,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
|
|||||||
private HandButton? _statusHandLeft;
|
private HandButton? _statusHandLeft;
|
||||||
private HandButton? _statusHandRight;
|
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>();
|
private HotbarGui? HandsGui => UIManager.GetActiveUIWidgetOrNull<HotbarGui>();
|
||||||
|
|
||||||
@@ -48,7 +49,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
|
|||||||
_handsSystem.OnPlayerItemAdded += OnItemAdded;
|
_handsSystem.OnPlayerItemAdded += OnItemAdded;
|
||||||
_handsSystem.OnPlayerItemRemoved += OnItemRemoved;
|
_handsSystem.OnPlayerItemRemoved += OnItemRemoved;
|
||||||
_handsSystem.OnPlayerSetActiveHand += SetActiveHand;
|
_handsSystem.OnPlayerSetActiveHand += SetActiveHand;
|
||||||
_handsSystem.OnPlayerRemoveHand += RemoveHand;
|
_handsSystem.OnPlayerRemoveHand += OnRemoveHand;
|
||||||
_handsSystem.OnPlayerHandsAdded += LoadPlayerHands;
|
_handsSystem.OnPlayerHandsAdded += LoadPlayerHands;
|
||||||
_handsSystem.OnPlayerHandsRemoved += UnloadPlayerHands;
|
_handsSystem.OnPlayerHandsRemoved += UnloadPlayerHands;
|
||||||
_handsSystem.OnPlayerHandBlocked += HandBlocked;
|
_handsSystem.OnPlayerHandBlocked += HandBlocked;
|
||||||
@@ -61,28 +62,35 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
|
|||||||
_handsSystem.OnPlayerItemAdded -= OnItemAdded;
|
_handsSystem.OnPlayerItemAdded -= OnItemAdded;
|
||||||
_handsSystem.OnPlayerItemRemoved -= OnItemRemoved;
|
_handsSystem.OnPlayerItemRemoved -= OnItemRemoved;
|
||||||
_handsSystem.OnPlayerSetActiveHand -= SetActiveHand;
|
_handsSystem.OnPlayerSetActiveHand -= SetActiveHand;
|
||||||
_handsSystem.OnPlayerRemoveHand -= RemoveHand;
|
_handsSystem.OnPlayerRemoveHand -= OnRemoveHand;
|
||||||
_handsSystem.OnPlayerHandsAdded -= LoadPlayerHands;
|
_handsSystem.OnPlayerHandsAdded -= LoadPlayerHands;
|
||||||
_handsSystem.OnPlayerHandsRemoved -= UnloadPlayerHands;
|
_handsSystem.OnPlayerHandsRemoved -= UnloadPlayerHands;
|
||||||
_handsSystem.OnPlayerHandBlocked -= HandBlocked;
|
_handsSystem.OnPlayerHandBlocked -= HandBlocked;
|
||||||
_handsSystem.OnPlayerHandUnblocked -= HandUnblocked;
|
_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);
|
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)
|
private void HandPressed(GUIBoundKeyEventArgs args, SlotControl hand)
|
||||||
{
|
{
|
||||||
if (_playerHandsComponent == null)
|
if (!_handsSystem.TryGetPlayerHands(out var hands))
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (args.Function == EngineKeyFunctions.UIClick)
|
if (args.Function == EngineKeyFunctions.UIClick)
|
||||||
{
|
{
|
||||||
_handsSystem.UIHandClick(_playerHandsComponent, hand.SlotName);
|
_handsSystem.UIHandClick(hands.Value, hand.SlotName);
|
||||||
args.Handle();
|
args.Handle();
|
||||||
}
|
}
|
||||||
else if (args.Function == EngineKeyFunctions.UseSecondary)
|
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);
|
DebugTools.Assert(_playerHandsComponent == null);
|
||||||
if (HandsGui != null)
|
if (HandsGui != null)
|
||||||
HandsGui.Visible = true;
|
HandsGui.Visible = true;
|
||||||
|
|
||||||
_playerHandsComponent = handsComp;
|
_playerHandsComponent = handsComp;
|
||||||
foreach (var (name, hand) in handsComp.Hands)
|
foreach (var (name, hand) in handsComp.Comp.Hands)
|
||||||
{
|
{
|
||||||
var handButton = AddHand(name, hand.Location);
|
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.SetEntity(virt.BlockingEntity);
|
||||||
handButton.Blocked = true;
|
handButton.Blocked = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
handButton.SetEntity(hand.HeldEntity);
|
handButton.SetEntity(held);
|
||||||
handButton.Blocked = false;
|
handButton.Blocked = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var activeHand = handsComp.ActiveHand;
|
if (handsComp.Comp.ActiveHandId == null)
|
||||||
if (activeHand == null)
|
|
||||||
return;
|
return;
|
||||||
SetActiveHand(activeHand.Name);
|
SetActiveHand(handsComp.Comp.ActiveHandId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandBlocked(string handName)
|
private void HandBlocked(string handName)
|
||||||
@@ -260,19 +268,21 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
|
|||||||
if (HandsGui != null &&
|
if (HandsGui != null &&
|
||||||
_playerHandsComponent != null &&
|
_playerHandsComponent != null &&
|
||||||
_player.LocalSession?.AttachedEntity is { } playerEntity &&
|
_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)
|
if (foldedLocation == HandUILocation.Left)
|
||||||
{
|
{
|
||||||
_statusHandLeft = handControl;
|
_statusHandLeft = handControl;
|
||||||
HandsGui.UpdatePanelEntityLeft(hand.HeldEntity);
|
HandsGui.UpdatePanelEntityLeft(heldEnt);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Middle or right
|
// Middle or right
|
||||||
_statusHandRight = handControl;
|
_statusHandRight = handControl;
|
||||||
HandsGui.UpdatePanelEntityRight(hand.HeldEntity);
|
HandsGui.UpdatePanelEntityRight(heldEnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
HandsGui.SetHighlightHand(foldedLocation);
|
HandsGui.SetHighlightHand(foldedLocation);
|
||||||
@@ -292,7 +302,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
|
|||||||
button.Pressed += HandPressed;
|
button.Pressed += HandPressed;
|
||||||
|
|
||||||
if (!_handLookup.TryAdd(handName, button))
|
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)
|
if (HandsGui != null)
|
||||||
{
|
{
|
||||||
@@ -362,6 +372,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
|
|||||||
RemoveHand(handName, out _);
|
RemoveHand(handName, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[PublicAPI]
|
||||||
private bool RemoveHand(string handName, out HandButton? handButton)
|
private bool RemoveHand(string handName, out HandButton? handButton)
|
||||||
{
|
{
|
||||||
if (!_handLookup.TryGetValue(handName, out handButton))
|
if (!_handLookup.TryGetValue(handName, out handButton))
|
||||||
@@ -377,7 +388,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered<GameplaySt
|
|||||||
_statusHandRight = null;
|
_statusHandRight = null;
|
||||||
|
|
||||||
_handLookup.Remove(handName);
|
_handLookup.Remove(handName);
|
||||||
handButton.Dispose();
|
handButton.Orphan();
|
||||||
UpdateVisibleStatusPanels();
|
UpdateVisibleStatusPanels();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -329,9 +329,8 @@ public sealed class InventoryUIController : UIController, IOnStateEntered<Gamepl
|
|||||||
var player = _playerUid;
|
var player = _playerUid;
|
||||||
|
|
||||||
if (!control.MouseIsHovering ||
|
if (!control.MouseIsHovering ||
|
||||||
_playerInventory == null ||
|
player == null ||
|
||||||
!_entities.TryGetComponent<HandsComponent>(player, out var hands) ||
|
!_handsSystem.TryGetActiveItem(player.Value, out var held) ||
|
||||||
hands.ActiveHandEntity is not { } held ||
|
|
||||||
!_entities.TryGetComponent(held, out SpriteComponent? sprite) ||
|
!_entities.TryGetComponent(held, out SpriteComponent? sprite) ||
|
||||||
!_inventorySystem.TryGetSlotContainer(player.Value, control.SlotName, out var container, out var slotDef))
|
!_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
|
// Set green / red overlay at 50% transparency
|
||||||
var hoverEntity = _entities.SpawnEntity("hoverentity", MapCoordinates.Nullspace);
|
var hoverEntity = _entities.SpawnEntity("hoverentity", MapCoordinates.Nullspace);
|
||||||
var hoverSprite = _entities.GetComponent<SpriteComponent>(hoverEntity);
|
var hoverSprite = _entities.GetComponent<SpriteComponent>(hoverEntity);
|
||||||
var fits = _inventorySystem.CanEquip(player.Value, held, control.SlotName, out _, slotDef) &&
|
var fits = _inventorySystem.CanEquip(player.Value, held.Value, control.SlotName, out _, slotDef) &&
|
||||||
_container.CanInsert(held, container);
|
_container.CanInsert(held.Value, container);
|
||||||
|
|
||||||
if (!fits && _entities.TryGetComponent<StorageComponent>(container.ContainedEntity, out var storage))
|
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))
|
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)
|
if (!slot.InsertOnInteract)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!itemSlotsSys.CanInsert(container.ContainedEntity.Value, held, null, slot))
|
if (!itemSlotsSys.CanInsert(container.ContainedEntity.Value, held.Value, null, slot))
|
||||||
continue;
|
continue;
|
||||||
fits = true;
|
fits = true;
|
||||||
break;
|
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));
|
_sprite.SetColor((hoverEntity, hoverSprite), fits ? new Color(0, 255, 0, 127) : new Color(255, 0, 0, 127));
|
||||||
|
|
||||||
control.HoverSpriteView.SetEntity(hoverEntity);
|
control.HoverSpriteView.SetEntity(hoverEntity);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public sealed class RetractableItemActionTest : InteractionTest
|
|||||||
await Server.WaitAssertion(() =>
|
await Server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
// Make sure the player's hand starts empty
|
// Make sure the player's hand starts empty
|
||||||
var heldItem = Hands.ActiveHandEntity;
|
var heldItem = handsSystem.GetActiveItem((playerUid, Hands));
|
||||||
Assert.That(heldItem, Is.Null, $"Player is holding an item ({SEntMan.ToPrettyString(heldItem)}) at start of test.");
|
Assert.That(heldItem, Is.Null, $"Player is holding an item ({SEntMan.ToPrettyString(heldItem)}) at start of test.");
|
||||||
|
|
||||||
// Inspect the action prototype to find the item it spawns
|
// Inspect the action prototype to find the item it spawns
|
||||||
@@ -43,14 +43,14 @@ public sealed class RetractableItemActionTest : InteractionTest
|
|||||||
var actionEnt = actionsSystem.GetAction(actionUid);
|
var actionEnt = actionsSystem.GetAction(actionUid);
|
||||||
|
|
||||||
// Make sure the player's hand is still empty
|
// Make sure the player's hand is still empty
|
||||||
heldItem = Hands.ActiveHandEntity;
|
heldItem = handsSystem.GetActiveItem((playerUid, Hands));
|
||||||
Assert.That(heldItem, Is.Null, $"Player is holding an item ({SEntMan.ToPrettyString(heldItem)}) after adding action.");
|
Assert.That(heldItem, Is.Null, $"Player is holding an item ({SEntMan.ToPrettyString(heldItem)}) after adding action.");
|
||||||
|
|
||||||
// Activate the arm blade
|
// Activate the arm blade
|
||||||
actionsSystem.PerformAction(ToServer(Player), actionEnt!.Value);
|
actionsSystem.PerformAction(ToServer(Player), actionEnt!.Value);
|
||||||
|
|
||||||
// Make sure the player is now holding the expected item
|
// Make sure the player is now holding the expected item
|
||||||
heldItem = Hands.ActiveHandEntity;
|
heldItem = handsSystem.GetActiveItem((playerUid, Hands));
|
||||||
Assert.That(heldItem, Is.Not.Null, $"Expected player to be holding {spawnedProtoId} but was holding nothing.");
|
Assert.That(heldItem, Is.Not.Null, $"Expected player to be holding {spawnedProtoId} but was holding nothing.");
|
||||||
AssertPrototype(spawnedProtoId, SEntMan.GetNetEntity(heldItem));
|
AssertPrototype(spawnedProtoId, SEntMan.GetNetEntity(heldItem));
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ public sealed class RetractableItemActionTest : InteractionTest
|
|||||||
actionsSystem.PerformAction(ToServer(Player), actionEnt.Value);
|
actionsSystem.PerformAction(ToServer(Player), actionEnt.Value);
|
||||||
|
|
||||||
// Make sure the player's hand is empty again
|
// Make sure the player's hand is empty again
|
||||||
heldItem = Hands.ActiveHandEntity;
|
heldItem = handsSystem.GetActiveItem((playerUid, Hands));
|
||||||
Assert.That(heldItem, Is.Null, $"Player is still holding an item ({SEntMan.ToPrettyString(heldItem)}) after second use.");
|
Assert.That(heldItem, Is.Null, $"Player is still holding an item ({SEntMan.ToPrettyString(heldItem)}) after second use.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -293,9 +293,9 @@ namespace Content.IntegrationTests.Tests.Buckle
|
|||||||
Assert.That(buckle.Buckled);
|
Assert.That(buckle.Buckled);
|
||||||
|
|
||||||
// With items in all hands
|
// With items in all hands
|
||||||
foreach (var hand in hands.Hands.Values)
|
foreach (var hand in hands.Hands.Keys)
|
||||||
{
|
{
|
||||||
Assert.That(hand.HeldEntity, Is.Not.Null);
|
Assert.That(handsSys.GetHeldItem((human, hands), hand), Is.Not.Null);
|
||||||
}
|
}
|
||||||
|
|
||||||
var bodySystem = entityManager.System<BodySystem>();
|
var bodySystem = entityManager.System<BodySystem>();
|
||||||
@@ -316,9 +316,9 @@ namespace Content.IntegrationTests.Tests.Buckle
|
|||||||
Assert.That(buckle.Buckled);
|
Assert.That(buckle.Buckled);
|
||||||
|
|
||||||
// Now with no item in any hand
|
// Now with no item in any hand
|
||||||
foreach (var hand in hands.Hands.Values)
|
foreach (var hand in hands.Hands.Keys)
|
||||||
{
|
{
|
||||||
Assert.That(hand.HeldEntity, Is.Null);
|
Assert.That(handsSys.GetHeldItem((human, hands), hand), Is.Null);
|
||||||
}
|
}
|
||||||
|
|
||||||
buckleSystem.Unbuckle(human, human);
|
buckleSystem.Unbuckle(human, human);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Content.Client.Chemistry.UI;
|
using Content.Client.Chemistry.UI;
|
||||||
using Content.IntegrationTests.Tests.Interaction;
|
using Content.IntegrationTests.Tests.Interaction;
|
||||||
using Content.Shared.Chemistry;
|
using Content.Shared.Chemistry;
|
||||||
using Content.Server.Chemistry.Components;
|
|
||||||
using Content.Shared.Containers.ItemSlots;
|
using Content.Shared.Containers.ItemSlots;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Chemistry;
|
namespace Content.IntegrationTests.Tests.Chemistry;
|
||||||
@@ -19,7 +18,7 @@ public sealed class DispenserTest : InteractionTest
|
|||||||
|
|
||||||
// Insert beaker
|
// Insert beaker
|
||||||
await InteractUsing("Beaker");
|
await InteractUsing("Beaker");
|
||||||
Assert.That(Hands.ActiveHandEntity, Is.Null);
|
Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null);
|
||||||
|
|
||||||
// Open BUI
|
// Open BUI
|
||||||
await Interact();
|
await Interact();
|
||||||
@@ -29,18 +28,18 @@ public sealed class DispenserTest : InteractionTest
|
|||||||
await SendBui(ReagentDispenserUiKey.Key, ev);
|
await SendBui(ReagentDispenserUiKey.Key, ev);
|
||||||
|
|
||||||
// Beaker is back in the player's hands
|
// Beaker is back in the player's hands
|
||||||
Assert.That(Hands.ActiveHandEntity, Is.Not.Null);
|
Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Not.Null);
|
||||||
AssertPrototype("Beaker", SEntMan.GetNetEntity(Hands.ActiveHandEntity));
|
AssertPrototype("Beaker", SEntMan.GetNetEntity(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands))));
|
||||||
|
|
||||||
// Re-insert the beaker
|
// Re-insert the beaker
|
||||||
await Interact();
|
await Interact();
|
||||||
Assert.That(Hands.ActiveHandEntity, Is.Null);
|
Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null);
|
||||||
|
|
||||||
// Re-eject using the button directly instead of sending a BUI event. This test is really just a test of the
|
// Re-eject using the button directly instead of sending a BUI event. This test is really just a test of the
|
||||||
// bui/window helper methods.
|
// bui/window helper methods.
|
||||||
await ClickControl<ReagentDispenserWindow>(nameof(ReagentDispenserWindow.EjectButton));
|
await ClickControl<ReagentDispenserWindow>(nameof(ReagentDispenserWindow.EjectButton));
|
||||||
await RunTicks(5);
|
await RunTicks(5);
|
||||||
Assert.That(Hands.ActiveHandEntity, Is.Not.Null);
|
Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Not.Null);
|
||||||
AssertPrototype("Beaker", SEntMan.GetNetEntity(Hands.ActiveHandEntity));
|
AssertPrototype("Beaker", SEntMan.GetNetEntity(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ public sealed class SuicideCommandTests
|
|||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
var item = entManager.SpawnEntity("SharpTestObject", transformSystem.GetMapCoordinates(player));
|
var item = entManager.SpawnEntity("SharpTestObject", transformSystem.GetMapCoordinates(player));
|
||||||
Assert.That(handsSystem.TryPickup(player, item, handsComponent.ActiveHand!));
|
Assert.That(handsSystem.TryPickup(player, item, handsComponent.ActiveHandId!));
|
||||||
entManager.TryGetComponent<ExecutionComponent>(item, out var executionComponent);
|
entManager.TryGetComponent<ExecutionComponent>(item, out var executionComponent);
|
||||||
Assert.That(executionComponent, Is.Not.EqualTo(null));
|
Assert.That(executionComponent, Is.Not.EqualTo(null));
|
||||||
});
|
});
|
||||||
@@ -342,7 +342,7 @@ public sealed class SuicideCommandTests
|
|||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
var item = entManager.SpawnEntity("MixedDamageTestObject", transformSystem.GetMapCoordinates(player));
|
var item = entManager.SpawnEntity("MixedDamageTestObject", transformSystem.GetMapCoordinates(player));
|
||||||
Assert.That(handsSystem.TryPickup(player, item, handsComponent.ActiveHand!));
|
Assert.That(handsSystem.TryPickup(player, item, handsComponent.ActiveHandId!));
|
||||||
entManager.TryGetComponent<ExecutionComponent>(item, out var executionComponent);
|
entManager.TryGetComponent<ExecutionComponent>(item, out var executionComponent);
|
||||||
Assert.That(executionComponent, Is.Not.EqualTo(null));
|
Assert.That(executionComponent, Is.Not.EqualTo(null));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ public sealed class WallConstruction : InteractionTest
|
|||||||
{
|
{
|
||||||
await StartConstruction(Wall);
|
await StartConstruction(Wall);
|
||||||
await InteractUsing(Steel, 2);
|
await InteractUsing(Steel, 2);
|
||||||
Assert.That(Hands.ActiveHandEntity, Is.Null);
|
Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null);
|
||||||
ClientAssertPrototype(Girder, Target);
|
ClientAssertPrototype(Girder, Target);
|
||||||
await InteractUsing(Steel, 2);
|
await InteractUsing(Steel, 2);
|
||||||
Assert.That(Hands.ActiveHandEntity, Is.Null);
|
Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null);
|
||||||
AssertPrototype(WallSolid);
|
AssertPrototype(WallSolid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,20 +53,20 @@ public sealed class HandTests
|
|||||||
var xform = entMan.GetComponent<TransformComponent>(player);
|
var xform = entMan.GetComponent<TransformComponent>(player);
|
||||||
item = entMan.SpawnEntity("Crowbar", tSys.GetMapCoordinates(player, xform: xform));
|
item = entMan.SpawnEntity("Crowbar", tSys.GetMapCoordinates(player, xform: xform));
|
||||||
hands = entMan.GetComponent<HandsComponent>(player);
|
hands = entMan.GetComponent<HandsComponent>(player);
|
||||||
sys.TryPickup(player, item, hands.ActiveHand!);
|
sys.TryPickup(player, item, hands.ActiveHandId!);
|
||||||
});
|
});
|
||||||
|
|
||||||
// run ticks here is important, as errors may happen within the container system's frame update methods.
|
// run ticks here is important, as errors may happen within the container system's frame update methods.
|
||||||
await pair.RunTicksSync(5);
|
await pair.RunTicksSync(5);
|
||||||
Assert.That(hands.ActiveHandEntity, Is.EqualTo(item));
|
Assert.That(sys.GetActiveItem((player, hands)), Is.EqualTo(item));
|
||||||
|
|
||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
sys.TryDrop(player, item, null!);
|
sys.TryDrop(player, item);
|
||||||
});
|
});
|
||||||
|
|
||||||
await pair.RunTicksSync(5);
|
await pair.RunTicksSync(5);
|
||||||
Assert.That(hands.ActiveHandEntity, Is.Null);
|
Assert.That(sys.GetActiveItem((player, hands)), Is.Null);
|
||||||
|
|
||||||
await server.WaitPost(() => mapSystem.DeleteMap(data.MapId));
|
await server.WaitPost(() => mapSystem.DeleteMap(data.MapId));
|
||||||
await pair.CleanReturnAsync();
|
await pair.CleanReturnAsync();
|
||||||
@@ -105,10 +105,10 @@ public sealed class HandTests
|
|||||||
player = playerMan.Sessions.First().AttachedEntity!.Value;
|
player = playerMan.Sessions.First().AttachedEntity!.Value;
|
||||||
tSys.PlaceNextTo(player, item);
|
tSys.PlaceNextTo(player, item);
|
||||||
hands = entMan.GetComponent<HandsComponent>(player);
|
hands = entMan.GetComponent<HandsComponent>(player);
|
||||||
sys.TryPickup(player, item, hands.ActiveHand!);
|
sys.TryPickup(player, item, hands.ActiveHandId!);
|
||||||
});
|
});
|
||||||
await pair.RunTicksSync(5);
|
await pair.RunTicksSync(5);
|
||||||
Assert.That(hands.ActiveHandEntity, Is.EqualTo(item));
|
Assert.That(sys.GetActiveItem((player, hands)), Is.EqualTo(item));
|
||||||
|
|
||||||
// Open then close the box to place the player, who is holding the crowbar, inside of it
|
// Open then close the box to place the player, who is holding the crowbar, inside of it
|
||||||
var storage = server.System<EntityStorageSystem>();
|
var storage = server.System<EntityStorageSystem>();
|
||||||
@@ -125,12 +125,12 @@ public sealed class HandTests
|
|||||||
// with the item not being in the player's hands
|
// with the item not being in the player's hands
|
||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
sys.TryDrop(player, item, null!);
|
sys.TryDrop(player, item);
|
||||||
});
|
});
|
||||||
await pair.RunTicksSync(5);
|
await pair.RunTicksSync(5);
|
||||||
var xform = entMan.GetComponent<TransformComponent>(player);
|
var xform = entMan.GetComponent<TransformComponent>(player);
|
||||||
var itemXform = entMan.GetComponent<TransformComponent>(item);
|
var itemXform = entMan.GetComponent<TransformComponent>(item);
|
||||||
Assert.That(hands.ActiveHandEntity, Is.Not.EqualTo(item));
|
Assert.That(sys.GetActiveItem((player, hands)), Is.Not.EqualTo(item));
|
||||||
Assert.That(containerSystem.IsInSameOrNoContainer((player, xform), (item, itemXform)));
|
Assert.That(containerSystem.IsInSameOrNoContainer((player, xform), (item, itemXform)));
|
||||||
|
|
||||||
await server.WaitPost(() => mapSystem.DeleteMap(map.MapId));
|
await server.WaitPost(() => mapSystem.DeleteMap(map.MapId));
|
||||||
|
|||||||
@@ -120,18 +120,18 @@ public abstract partial class InteractionTest
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected async Task DeleteHeldEntity()
|
protected async Task DeleteHeldEntity()
|
||||||
{
|
{
|
||||||
if (Hands.ActiveHandEntity is { } held)
|
if (HandSys.GetActiveItem((ToServer(Player), Hands)) is { } held)
|
||||||
{
|
{
|
||||||
await Server.WaitPost(() =>
|
await Server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
Assert.That(HandSys.TryDrop(SEntMan.GetEntity(Player), null, false, true, Hands));
|
Assert.That(HandSys.TryDrop((SEntMan.GetEntity(Player), Hands), null, false, true));
|
||||||
SEntMan.DeleteEntity(held);
|
SEntMan.DeleteEntity(held);
|
||||||
SLogger.Debug($"Deleting held entity");
|
SLogger.Debug($"Deleting held entity");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await RunTicks(1);
|
await RunTicks(1);
|
||||||
Assert.That(Hands.ActiveHandEntity, Is.Null);
|
Assert.That(HandSys.GetActiveItem((ToServer(Player), Hands)), Is.Null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -152,7 +152,7 @@ public abstract partial class InteractionTest
|
|||||||
/// <param name="enableToggleable">Whether or not to automatically enable any toggleable items</param>
|
/// <param name="enableToggleable">Whether or not to automatically enable any toggleable items</param>
|
||||||
protected async Task<NetEntity> PlaceInHands(EntitySpecifier entity, bool enableToggleable = true)
|
protected async Task<NetEntity> PlaceInHands(EntitySpecifier entity, bool enableToggleable = true)
|
||||||
{
|
{
|
||||||
if (Hands.ActiveHand == null)
|
if (Hands.ActiveHandId == null)
|
||||||
{
|
{
|
||||||
Assert.Fail("No active hand");
|
Assert.Fail("No active hand");
|
||||||
return default;
|
return default;
|
||||||
@@ -169,7 +169,7 @@ public abstract partial class InteractionTest
|
|||||||
{
|
{
|
||||||
var playerEnt = SEntMan.GetEntity(Player);
|
var playerEnt = SEntMan.GetEntity(Player);
|
||||||
|
|
||||||
Assert.That(HandSys.TryPickup(playerEnt, item, Hands.ActiveHand, false, false, Hands));
|
Assert.That(HandSys.TryPickup(playerEnt, item, Hands.ActiveHandId, false, false, false, Hands));
|
||||||
|
|
||||||
// turn on welders
|
// turn on welders
|
||||||
if (enableToggleable && SEntMan.TryGetComponent(item, out itemToggle) && !itemToggle.Activated)
|
if (enableToggleable && SEntMan.TryGetComponent(item, out itemToggle) && !itemToggle.Activated)
|
||||||
@@ -179,7 +179,7 @@ public abstract partial class InteractionTest
|
|||||||
});
|
});
|
||||||
|
|
||||||
await RunTicks(1);
|
await RunTicks(1);
|
||||||
Assert.That(Hands.ActiveHandEntity, Is.EqualTo(item));
|
Assert.That(HandSys.GetActiveItem((ToServer(Player), Hands)), Is.EqualTo(item));
|
||||||
if (enableToggleable && itemToggle != null)
|
if (enableToggleable && itemToggle != null)
|
||||||
Assert.That(itemToggle.Activated);
|
Assert.That(itemToggle.Activated);
|
||||||
|
|
||||||
@@ -193,7 +193,7 @@ public abstract partial class InteractionTest
|
|||||||
{
|
{
|
||||||
entity ??= Target;
|
entity ??= Target;
|
||||||
|
|
||||||
if (Hands.ActiveHand == null)
|
if (Hands.ActiveHandId == null)
|
||||||
{
|
{
|
||||||
Assert.Fail("No active hand");
|
Assert.Fail("No active hand");
|
||||||
return;
|
return;
|
||||||
@@ -212,11 +212,11 @@ public abstract partial class InteractionTest
|
|||||||
|
|
||||||
await Server.WaitPost(() =>
|
await Server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
Assert.That(HandSys.TryPickup(SEntMan.GetEntity(Player), uid.Value, Hands.ActiveHand, false, false, Hands, item));
|
Assert.That(HandSys.TryPickup(ToServer(Player), uid.Value, Hands.ActiveHandId, false, false, false, Hands, item));
|
||||||
});
|
});
|
||||||
|
|
||||||
await RunTicks(1);
|
await RunTicks(1);
|
||||||
Assert.That(Hands.ActiveHandEntity, Is.EqualTo(uid));
|
Assert.That(HandSys.GetActiveItem((ToServer(Player), Hands)), Is.EqualTo(uid));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -224,7 +224,7 @@ public abstract partial class InteractionTest
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected async Task Drop()
|
protected async Task Drop()
|
||||||
{
|
{
|
||||||
if (Hands.ActiveHandEntity == null)
|
if (HandSys.GetActiveItem((ToServer(Player), Hands)) == null)
|
||||||
{
|
{
|
||||||
Assert.Fail("Not holding any entity to drop");
|
Assert.Fail("Not holding any entity to drop");
|
||||||
return;
|
return;
|
||||||
@@ -232,11 +232,11 @@ public abstract partial class InteractionTest
|
|||||||
|
|
||||||
await Server.WaitPost(() =>
|
await Server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
Assert.That(HandSys.TryDrop(SEntMan.GetEntity(Player), handsComp: Hands));
|
Assert.That(HandSys.TryDrop((ToServer(Player), Hands)));
|
||||||
});
|
});
|
||||||
|
|
||||||
await RunTicks(1);
|
await RunTicks(1);
|
||||||
Assert.That(Hands.ActiveHandEntity, Is.Null);
|
Assert.That(HandSys.GetActiveItem((ToServer(Player), Hands)), Is.Null);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Interact
|
#region Interact
|
||||||
@@ -246,7 +246,7 @@ public abstract partial class InteractionTest
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected async Task UseInHand()
|
protected async Task UseInHand()
|
||||||
{
|
{
|
||||||
if (Hands.ActiveHandEntity is not { } target)
|
if (HandSys.GetActiveItem((ToServer(Player), Hands)) is not { } target)
|
||||||
{
|
{
|
||||||
Assert.Fail("Not holding any entity");
|
Assert.Fail("Not holding any entity");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Client.Construction;
|
using Content.Client.Construction;
|
||||||
using Content.Client.Examine;
|
using Content.Client.Examine;
|
||||||
using Content.Client.Gameplay;
|
using Content.Client.Gameplay;
|
||||||
using Content.IntegrationTests.Pair;
|
using Content.IntegrationTests.Pair;
|
||||||
using Content.Server.Body.Systems;
|
|
||||||
using Content.Server.Hands.Systems;
|
using Content.Server.Hands.Systems;
|
||||||
using Content.Server.Stack;
|
using Content.Server.Stack;
|
||||||
using Content.Server.Tools;
|
using Content.Server.Tools;
|
||||||
using Content.Shared.Body.Part;
|
|
||||||
using Content.Shared.DoAfter;
|
using Content.Shared.DoAfter;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
@@ -135,10 +132,13 @@ public abstract partial class InteractionTest
|
|||||||
- type: entity
|
- type: entity
|
||||||
id: InteractionTestMob
|
id: InteractionTestMob
|
||||||
components:
|
components:
|
||||||
- type: Body
|
|
||||||
prototype: Aghost
|
|
||||||
- type: DoAfter
|
- type: DoAfter
|
||||||
- type: Hands
|
- type: Hands
|
||||||
|
hands:
|
||||||
|
hand_right: # only one hand, so that they do not accidentally pick up deconstruction products
|
||||||
|
location: Right
|
||||||
|
sortedHands:
|
||||||
|
- hand_right
|
||||||
- type: ComplexInteraction
|
- type: ComplexInteraction
|
||||||
- type: MindContainer
|
- type: MindContainer
|
||||||
- type: Stripping
|
- type: Stripping
|
||||||
@@ -230,20 +230,6 @@ public abstract partial class InteractionTest
|
|||||||
SEntMan.DeleteEntity(old.Value);
|
SEntMan.DeleteEntity(old.Value);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ensure that the player only has one hand, so that they do not accidentally pick up deconstruction products
|
|
||||||
await Server.WaitPost(() =>
|
|
||||||
{
|
|
||||||
// I lost an hour of my life trying to track down how the hell interaction tests were breaking
|
|
||||||
// so greatz to this. Just make your own body prototype!
|
|
||||||
var bodySystem = SEntMan.System<BodySystem>();
|
|
||||||
var hands = bodySystem.GetBodyChildrenOfType(SEntMan.GetEntity(Player), BodyPartType.Hand).ToArray();
|
|
||||||
|
|
||||||
for (var i = 1; i < hands.Length; i++)
|
|
||||||
{
|
|
||||||
SEntMan.DeleteEntity(hands[i].Id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Change UI state to in-game.
|
// Change UI state to in-game.
|
||||||
var state = Client.ResolveDependency<IStateManager>();
|
var state = Client.ResolveDependency<IStateManager>();
|
||||||
await Client.WaitPost(() => state.RequestStateChange<GameplayState>());
|
await Client.WaitPost(() => state.RequestStateChange<GameplayState>());
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public sealed class TileConstructionTests : InteractionTest
|
|||||||
await SetTile(null);
|
await SetTile(null);
|
||||||
await InteractUsing(Rod);
|
await InteractUsing(Rod);
|
||||||
await AssertTile(Lattice);
|
await AssertTile(Lattice);
|
||||||
Assert.That(Hands.ActiveHandEntity, Is.Null);
|
Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null);
|
||||||
await InteractUsing(Cut);
|
await InteractUsing(Cut);
|
||||||
await AssertTile(null);
|
await AssertTile(null);
|
||||||
await AssertEntityLookup((Rod, 1));
|
await AssertEntityLookup((Rod, 1));
|
||||||
@@ -49,7 +49,7 @@ public sealed class TileConstructionTests : InteractionTest
|
|||||||
AssertGridCount(1);
|
AssertGridCount(1);
|
||||||
|
|
||||||
// Cut lattice
|
// Cut lattice
|
||||||
Assert.That(Hands.ActiveHandEntity, Is.Null);
|
Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null);
|
||||||
await InteractUsing(Cut);
|
await InteractUsing(Cut);
|
||||||
await AssertTile(null);
|
await AssertTile(null);
|
||||||
AssertGridCount(0);
|
AssertGridCount(0);
|
||||||
@@ -83,13 +83,13 @@ public sealed class TileConstructionTests : InteractionTest
|
|||||||
|
|
||||||
// Lattice -> Plating
|
// Lattice -> Plating
|
||||||
await InteractUsing(FloorItem);
|
await InteractUsing(FloorItem);
|
||||||
Assert.That(Hands.ActiveHandEntity, Is.Null);
|
Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null);
|
||||||
await AssertTile(Plating);
|
await AssertTile(Plating);
|
||||||
AssertGridCount(1);
|
AssertGridCount(1);
|
||||||
|
|
||||||
// Plating -> Tile
|
// Plating -> Tile
|
||||||
await InteractUsing(FloorItem);
|
await InteractUsing(FloorItem);
|
||||||
Assert.That(Hands.ActiveHandEntity, Is.Null);
|
Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null);
|
||||||
await AssertTile(Floor);
|
await AssertTile(Floor);
|
||||||
AssertGridCount(1);
|
AssertGridCount(1);
|
||||||
|
|
||||||
|
|||||||
@@ -42,13 +42,12 @@ public sealed class StripAllCommand : LocalizedEntityCommands
|
|||||||
|
|
||||||
if (EntityManager.TryGetComponent<HandsComponent>(targetEntity, out var hands))
|
if (EntityManager.TryGetComponent<HandsComponent>(targetEntity, out var hands))
|
||||||
{
|
{
|
||||||
foreach (var hand in _handsSystem.EnumerateHands(targetEntity.Value, hands))
|
foreach (var hand in _handsSystem.EnumerateHands((targetEntity.Value, hands)))
|
||||||
{
|
{
|
||||||
_handsSystem.TryDrop(targetEntity.Value,
|
_handsSystem.TryDrop((targetEntity.Value, hands),
|
||||||
hand,
|
hand,
|
||||||
checkActionBlocker: false,
|
checkActionBlocker: false,
|
||||||
doDropInteraction: false,
|
doDropInteraction: false);
|
||||||
handsComp: hands);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -433,9 +433,9 @@ public sealed class AdminSystem : EntitySystem
|
|||||||
|
|
||||||
if (TryComp(entity, out HandsComponent? hands))
|
if (TryComp(entity, out HandsComponent? hands))
|
||||||
{
|
{
|
||||||
foreach (var hand in _hands.EnumerateHands(entity, hands))
|
foreach (var hand in _hands.EnumerateHands((entity, hands)))
|
||||||
{
|
{
|
||||||
_hands.TryDrop(entity, hand, checkActionBlocker: false, doDropInteraction: false, handsComp: hands);
|
_hands.TryDrop((entity, hands), hand, checkActionBlocker: false, doDropInteraction: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -820,7 +820,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
}
|
}
|
||||||
else if (TryComp<HandsComponent>(target, out var hands))
|
else if (TryComp<HandsComponent>(target, out var hands))
|
||||||
{
|
{
|
||||||
foreach (var held in _handsSystem.EnumerateHeld(target, hands))
|
foreach (var held in _handsSystem.EnumerateHeld((target, hands)))
|
||||||
{
|
{
|
||||||
if (HasComp<AccessComponent>(held))
|
if (HasComp<AccessComponent>(held))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -97,8 +97,7 @@ public sealed class CryostorageSystem : SharedCryostorageSystem
|
|||||||
EntityUid? entity = null;
|
EntityUid? entity = null;
|
||||||
if (args.Type == CryostorageRemoveItemBuiMessage.RemovalType.Hand)
|
if (args.Type == CryostorageRemoveItemBuiMessage.RemovalType.Hand)
|
||||||
{
|
{
|
||||||
if (_hands.TryGetHand(cryoContained, args.Key, out var hand))
|
entity = _hands.GetHeldItem(cryoContained, args.Key);
|
||||||
entity = hand.HeldEntity;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -320,10 +319,10 @@ public sealed class CryostorageSystem : SharedCryostorageSystem
|
|||||||
|
|
||||||
foreach (var hand in _hands.EnumerateHands(uid))
|
foreach (var hand in _hands.EnumerateHands(uid))
|
||||||
{
|
{
|
||||||
if (hand.HeldEntity == null)
|
if (!_hands.TryGetHeldItem(uid, hand, out var heldEntity))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
data.HeldItems.Add(hand.Name, Name(hand.HeldEntity.Value));
|
data.HeldItems.Add(hand, Name(heldEntity.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Content.Server.Atmos.EntitySystems;
|
using Content.Server.Atmos.EntitySystems;
|
||||||
using Content.Server.Botany.Components;
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Server.Hands.Systems;
|
||||||
using Content.Server.Kitchen.Components;
|
using Content.Server.Kitchen.Components;
|
||||||
using Content.Server.Popups;
|
using Content.Server.Popups;
|
||||||
using Content.Shared.Chemistry.EntitySystems;
|
using Content.Shared.Chemistry.EntitySystems;
|
||||||
@@ -37,6 +38,7 @@ public sealed class PlantHolderSystem : EntitySystem
|
|||||||
[Dependency] private readonly MutationSystem _mutation = default!;
|
[Dependency] private readonly MutationSystem _mutation = default!;
|
||||||
[Dependency] private readonly AppearanceSystem _appearance = default!;
|
[Dependency] private readonly AppearanceSystem _appearance = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
|
[Dependency] private readonly HandsSystem _hands = default!;
|
||||||
[Dependency] private readonly PopupSystem _popup = default!;
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
|
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
|
||||||
@@ -706,9 +708,9 @@ public sealed class PlantHolderSystem : EntitySystem
|
|||||||
|
|
||||||
if (component.Harvest && !component.Dead)
|
if (component.Harvest && !component.Dead)
|
||||||
{
|
{
|
||||||
if (TryComp<HandsComponent>(user, out var hands))
|
if (_hands.TryGetActiveItem(user, out var activeItem))
|
||||||
{
|
{
|
||||||
if (!_botany.CanHarvest(component.Seed, hands.ActiveHandEntity))
|
if (!_botany.CanHarvest(component.Seed, activeItem))
|
||||||
{
|
{
|
||||||
_popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), user);
|
_popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), user);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using Content.Server.Ghost;
|
using Content.Server.Ghost;
|
||||||
|
using Content.Server.Hands.Systems;
|
||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.Chat;
|
using Content.Shared.Chat;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Hands.Components;
|
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Interaction.Events;
|
using Content.Shared.Interaction.Events;
|
||||||
using Content.Shared.Item;
|
using Content.Shared.Item;
|
||||||
@@ -22,6 +22,7 @@ public sealed class SuicideSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!;
|
[Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!;
|
||||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||||
|
[Dependency] private readonly HandsSystem _hands = default!;
|
||||||
[Dependency] private readonly TagSystem _tagSystem = default!;
|
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
@@ -116,10 +117,9 @@ public sealed class SuicideSystem : EntitySystem
|
|||||||
var suicideByEnvironmentEvent = new SuicideByEnvironmentEvent(victim);
|
var suicideByEnvironmentEvent = new SuicideByEnvironmentEvent(victim);
|
||||||
|
|
||||||
// Try to suicide by raising an event on the held item
|
// Try to suicide by raising an event on the held item
|
||||||
if (EntityManager.TryGetComponent(victim, out HandsComponent? handsComponent)
|
if (_hands.TryGetActiveItem(victim.Owner, out var item))
|
||||||
&& handsComponent.ActiveHandEntity is { } item)
|
|
||||||
{
|
{
|
||||||
RaiseLocalEvent(item, suicideByEnvironmentEvent);
|
RaiseLocalEvent(item.Value, suicideByEnvironmentEvent);
|
||||||
if (suicideByEnvironmentEvent.Handled)
|
if (suicideByEnvironmentEvent.Handled)
|
||||||
{
|
{
|
||||||
args.Handled = suicideByEnvironmentEvent.Handled;
|
args.Handled = suicideByEnvironmentEvent.Handled;
|
||||||
|
|||||||
@@ -471,7 +471,7 @@ namespace Content.Server.Construction
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!_actionBlocker.CanInteract(user, null)
|
if (!_actionBlocker.CanInteract(user, null)
|
||||||
|| !EntityManager.TryGetComponent(user, out HandsComponent? hands) || hands.ActiveHandEntity == null)
|
|| !EntityManager.TryGetComponent(user, out HandsComponent? hands) || _handsSystem.GetActiveItem((user, hands)) == null)
|
||||||
{
|
{
|
||||||
Cleanup();
|
Cleanup();
|
||||||
return;
|
return;
|
||||||
@@ -496,7 +496,7 @@ namespace Content.Server.Construction
|
|||||||
|
|
||||||
var valid = false;
|
var valid = false;
|
||||||
|
|
||||||
if (hands.ActiveHandEntity is not {Valid: true} holding)
|
if (_handsSystem.GetActiveItem((user, hands)) is not {Valid: true} holding)
|
||||||
{
|
{
|
||||||
Cleanup();
|
Cleanup();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Server.Inventory;
|
|
||||||
using Content.Server.Stack;
|
using Content.Server.Stack;
|
||||||
using Content.Server.Stunnable;
|
using Content.Server.Stunnable;
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
@@ -10,9 +9,7 @@ using Content.Shared.Explosion;
|
|||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Hands.EntitySystems;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
using Content.Shared.Inventory.VirtualItem;
|
|
||||||
using Content.Shared.Movement.Pulling.Components;
|
using Content.Shared.Movement.Pulling.Components;
|
||||||
using Content.Shared.Movement.Pulling.Events;
|
|
||||||
using Content.Shared.Movement.Pulling.Systems;
|
using Content.Shared.Movement.Pulling.Systems;
|
||||||
using Content.Shared.Stacks;
|
using Content.Shared.Stacks;
|
||||||
using Content.Shared.Standing;
|
using Content.Shared.Standing;
|
||||||
@@ -24,7 +21,6 @@ using Robust.Shared.Physics.Components;
|
|||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Utility;
|
|
||||||
|
|
||||||
namespace Content.Server.Hands.Systems
|
namespace Content.Server.Hands.Systems
|
||||||
{
|
{
|
||||||
@@ -87,10 +83,9 @@ namespace Content.Server.Hands.Systems
|
|||||||
if (ent.Comp.DisableExplosionRecursion)
|
if (ent.Comp.DisableExplosionRecursion)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var hand in ent.Comp.Hands.Values)
|
foreach (var held in EnumerateHeld(ent.AsNullable()))
|
||||||
{
|
{
|
||||||
if (hand.HeldEntity is { } uid)
|
args.Contents.Add(held);
|
||||||
args.Contents.Add(uid);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +107,7 @@ namespace Content.Server.Hands.Systems
|
|||||||
args.Handled = true; // no shove/stun.
|
args.Handled = true; // no shove/stun.
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleBodyPartAdded(EntityUid uid, HandsComponent component, ref BodyPartAddedEvent args)
|
private void HandleBodyPartAdded(Entity<HandsComponent> ent, ref BodyPartAddedEvent args)
|
||||||
{
|
{
|
||||||
if (args.Part.Comp.PartType != BodyPartType.Hand)
|
if (args.Part.Comp.PartType != BodyPartType.Hand)
|
||||||
return;
|
return;
|
||||||
@@ -127,7 +122,7 @@ namespace Content.Server.Hands.Systems
|
|||||||
_ => throw new ArgumentOutOfRangeException(nameof(args.Part.Comp.Symmetry))
|
_ => throw new ArgumentOutOfRangeException(nameof(args.Part.Comp.Symmetry))
|
||||||
};
|
};
|
||||||
|
|
||||||
AddHand(uid, args.Slot, location);
|
AddHand(ent.AsNullable(), args.Slot, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleBodyPartRemoved(EntityUid uid, HandsComponent component, ref BodyPartRemovedEvent args)
|
private void HandleBodyPartRemoved(EntityUid uid, HandsComponent component, ref BodyPartRemovedEvent args)
|
||||||
@@ -155,8 +150,8 @@ namespace Content.Server.Hands.Systems
|
|||||||
{
|
{
|
||||||
if (ContainerSystem.IsEntityInContainer(player) ||
|
if (ContainerSystem.IsEntityInContainer(player) ||
|
||||||
!TryComp(player, out HandsComponent? hands) ||
|
!TryComp(player, out HandsComponent? hands) ||
|
||||||
hands.ActiveHandEntity is not { } throwEnt ||
|
!TryGetActiveItem((player, hands), out var throwEnt) ||
|
||||||
!_actionBlockerSystem.CanThrow(player, throwEnt))
|
!_actionBlockerSystem.CanThrow(player, throwEnt.Value))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (_timing.CurTime < hands.NextThrowTime)
|
if (_timing.CurTime < hands.NextThrowTime)
|
||||||
@@ -165,7 +160,7 @@ namespace Content.Server.Hands.Systems
|
|||||||
|
|
||||||
if (EntityManager.TryGetComponent(throwEnt, out StackComponent? stack) && stack.Count > 1 && stack.ThrowIndividually)
|
if (EntityManager.TryGetComponent(throwEnt, out StackComponent? stack) && stack.Count > 1 && stack.ThrowIndividually)
|
||||||
{
|
{
|
||||||
var splitStack = _stackSystem.Split(throwEnt, 1, EntityManager.GetComponent<TransformComponent>(player).Coordinates, stack);
|
var splitStack = _stackSystem.Split(throwEnt.Value, 1, EntityManager.GetComponent<TransformComponent>(player).Coordinates, stack);
|
||||||
|
|
||||||
if (splitStack is not {Valid: true})
|
if (splitStack is not {Valid: true})
|
||||||
return false;
|
return false;
|
||||||
@@ -185,14 +180,14 @@ namespace Content.Server.Hands.Systems
|
|||||||
|
|
||||||
// Let other systems change the thrown entity (useful for virtual items)
|
// Let other systems change the thrown entity (useful for virtual items)
|
||||||
// or the throw strength.
|
// or the throw strength.
|
||||||
var ev = new BeforeThrowEvent(throwEnt, direction, throwSpeed, player);
|
var ev = new BeforeThrowEvent(throwEnt.Value, direction, throwSpeed, player);
|
||||||
RaiseLocalEvent(player, ref ev);
|
RaiseLocalEvent(player, ref ev);
|
||||||
|
|
||||||
if (ev.Cancelled)
|
if (ev.Cancelled)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// This can grief the above event so we raise it afterwards
|
// This can grief the above event so we raise it afterwards
|
||||||
if (IsHolding(player, throwEnt, out _, hands) && !TryDrop(player, throwEnt, handsComp: hands))
|
if (IsHolding((player, hands), throwEnt, out _) && !TryDrop(player, throwEnt.Value))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
_throwingSystem.TryThrow(ev.ItemUid, ev.Direction, ev.ThrowSpeed, ev.PlayerUid, compensateFriction: !HasComp<LandAtCursorComponent>(ev.ItemUid));
|
_throwingSystem.TryThrow(ev.ItemUid, ev.Direction, ev.ThrowSpeed, ev.PlayerUid, compensateFriction: !HasComp<LandAtCursorComponent>(ev.ItemUid));
|
||||||
@@ -207,20 +202,20 @@ namespace Content.Server.Hands.Systems
|
|||||||
var spreadMaxAngle = Angle.FromDegrees(DropHeldItemsSpread);
|
var spreadMaxAngle = Angle.FromDegrees(DropHeldItemsSpread);
|
||||||
|
|
||||||
var fellEvent = new FellDownEvent(entity);
|
var fellEvent = new FellDownEvent(entity);
|
||||||
RaiseLocalEvent(entity, fellEvent, false);
|
RaiseLocalEvent(entity, fellEvent);
|
||||||
|
|
||||||
foreach (var hand in entity.Comp.Hands.Values)
|
foreach (var hand in entity.Comp.Hands.Keys)
|
||||||
{
|
{
|
||||||
if (hand.HeldEntity is not EntityUid held)
|
if (!TryGetHeldItem(entity.AsNullable(), hand, out var heldEntity))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var throwAttempt = new FellDownThrowAttemptEvent(entity);
|
var throwAttempt = new FellDownThrowAttemptEvent(entity);
|
||||||
RaiseLocalEvent(hand.HeldEntity.Value, ref throwAttempt);
|
RaiseLocalEvent(heldEntity.Value, ref throwAttempt);
|
||||||
|
|
||||||
if (throwAttempt.Cancelled)
|
if (throwAttempt.Cancelled)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!TryDrop(entity, hand, null, checkActionBlocker: false, handsComp: entity.Comp))
|
if (!TryDrop(entity.AsNullable(), hand, checkActionBlocker: false))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Rotate the item's throw vector a bit for each item
|
// Rotate the item's throw vector a bit for each item
|
||||||
@@ -231,12 +226,12 @@ namespace Content.Server.Hands.Systems
|
|||||||
itemVelocity *= _random.NextFloat(1f);
|
itemVelocity *= _random.NextFloat(1f);
|
||||||
// Heavier objects don't get thrown as far
|
// Heavier objects don't get thrown as far
|
||||||
// If the item doesn't have a physics component, it isn't going to get thrown anyway, but we'll assume infinite mass
|
// If the item doesn't have a physics component, it isn't going to get thrown anyway, but we'll assume infinite mass
|
||||||
itemVelocity *= _physicsQuery.TryComp(held, out var heldPhysics) ? heldPhysics.InvMass : 0;
|
itemVelocity *= _physicsQuery.TryComp(heldEntity, out var heldPhysics) ? heldPhysics.InvMass : 0;
|
||||||
// Throw at half the holder's intentional throw speed and
|
// Throw at half the holder's intentional throw speed and
|
||||||
// vary the speed a little to make it look more interesting
|
// vary the speed a little to make it look more interesting
|
||||||
var throwSpeed = entity.Comp.BaseThrowspeed * _random.NextFloat(0.45f, 0.55f);
|
var throwSpeed = entity.Comp.BaseThrowspeed * _random.NextFloat(0.45f, 0.55f);
|
||||||
|
|
||||||
_throwingSystem.TryThrow(held,
|
_throwingSystem.TryThrow(heldEntity.Value,
|
||||||
itemVelocity,
|
itemVelocity,
|
||||||
throwSpeed,
|
throwSpeed,
|
||||||
entity,
|
entity,
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public sealed class HotPotatoSystem : SharedHotPotatoSystem
|
|||||||
if (!TryComp<HandsComponent>(hitEntity, out var hands))
|
if (!TryComp<HandsComponent>(hitEntity, out var hands))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!_hands.IsHolding(hitEntity, uid, out _, hands) && _hands.TryForcePickupAnyHand(hitEntity, uid, handsComp: hands))
|
if (!_hands.IsHolding((hitEntity, hands), uid, out _) && _hands.TryForcePickupAnyHand(hitEntity, uid, handsComp: hands))
|
||||||
{
|
{
|
||||||
_popup.PopupEntity(Loc.GetString("hot-potato-passed",
|
_popup.PopupEntity(Loc.GetString("hot-potato-passed",
|
||||||
("from", args.User), ("to", hitEntity)), uid, PopupType.Medium);
|
("from", args.User), ("to", hitEntity)), uid, PopupType.Medium);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Shared.Hands.Components;
|
using Content.Server.Hands.Systems;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.NPC.HTN.Preconditions;
|
namespace Content.Server.NPC.HTN.Preconditions;
|
||||||
@@ -18,14 +18,18 @@ public sealed partial class ActiveHandComponentPrecondition : HTNPrecondition
|
|||||||
|
|
||||||
public override bool IsMet(NPCBlackboard blackboard)
|
public override bool IsMet(NPCBlackboard blackboard)
|
||||||
{
|
{
|
||||||
if (!blackboard.TryGetValue<Hand>(NPCBlackboard.ActiveHand, out var hand, _entManager) || hand.HeldEntity == null)
|
if (!blackboard.TryGetValue<EntityUid>(NPCBlackboard.Owner, out var owner, _entManager) ||
|
||||||
|
!blackboard.TryGetValue<string>(NPCBlackboard.ActiveHand, out var hand, _entManager))
|
||||||
{
|
{
|
||||||
return Invert;
|
return Invert;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_entManager.System<HandsSystem>().TryGetHeldItem(owner, hand, out var entity))
|
||||||
|
return Invert;
|
||||||
|
|
||||||
foreach (var comp in Components)
|
foreach (var comp in Components)
|
||||||
{
|
{
|
||||||
var hasComp = _entManager.HasComponent(hand.HeldEntity, comp.Value.Component.GetType());
|
var hasComp = _entManager.HasComponent(entity, comp.Value.Component.GetType());
|
||||||
|
|
||||||
if (!hasComp ||
|
if (!hasComp ||
|
||||||
Invert && hasComp)
|
Invert && hasComp)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Shared.Hands.Components;
|
using Content.Server.Hands.Systems;
|
||||||
|
|
||||||
namespace Content.Server.NPC.HTN.Preconditions;
|
namespace Content.Server.NPC.HTN.Preconditions;
|
||||||
|
|
||||||
@@ -11,11 +11,12 @@ public sealed partial class ActiveHandEntityPrecondition : HTNPrecondition
|
|||||||
|
|
||||||
public override bool IsMet(NPCBlackboard blackboard)
|
public override bool IsMet(NPCBlackboard blackboard)
|
||||||
{
|
{
|
||||||
if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out Hand? activeHand, _entManager))
|
if (!blackboard.TryGetValue(NPCBlackboard.Owner, out EntityUid owner, _entManager) ||
|
||||||
|
!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out string? activeHand, _entManager))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return activeHand.HeldEntity != null;
|
return !_entManager.System<HandsSystem>().HandIsEmpty(owner, activeHand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public sealed partial class DropOperator : HTNOperator
|
|||||||
|
|
||||||
public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
|
public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
|
||||||
{
|
{
|
||||||
if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out Hand? activeHand, _entManager))
|
if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out string? activeHand, _entManager))
|
||||||
{
|
{
|
||||||
return HTNOperatorStatus.Finished;
|
return HTNOperatorStatus.Finished;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Content.Server.Interaction;
|
using Content.Server.Hands.Systems;
|
||||||
using Content.Shared.Access.Systems;
|
using Content.Shared.Access.Systems;
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Inventory;
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
@@ -152,6 +151,8 @@ public sealed partial class NPCBlackboard : IEnumerable<KeyValuePair<string, obj
|
|||||||
value = default;
|
value = default;
|
||||||
EntityUid owner;
|
EntityUid owner;
|
||||||
|
|
||||||
|
var handSys = entManager.System<HandsSystem>();
|
||||||
|
|
||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case Access:
|
case Access:
|
||||||
@@ -168,25 +169,24 @@ public sealed partial class NPCBlackboard : IEnumerable<KeyValuePair<string, obj
|
|||||||
case ActiveHand:
|
case ActiveHand:
|
||||||
{
|
{
|
||||||
if (!TryGetValue(Owner, out owner, entManager) ||
|
if (!TryGetValue(Owner, out owner, entManager) ||
|
||||||
!entManager.TryGetComponent<HandsComponent>(owner, out var hands) ||
|
handSys.GetActiveHand(owner) is not { } activeHand)
|
||||||
hands.ActiveHand == null)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = hands.ActiveHand;
|
value = activeHand;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case ActiveHandFree:
|
case ActiveHandFree:
|
||||||
{
|
{
|
||||||
if (!TryGetValue(Owner, out owner, entManager) ||
|
if (!TryGetValue(Owner, out owner, entManager) ||
|
||||||
!entManager.TryGetComponent<HandsComponent>(owner, out var hands) ||
|
!entManager.TryGetComponent<HandsComponent>(owner, out var hands) ||
|
||||||
hands.ActiveHand == null)
|
handSys.GetActiveHand(owner) is not { } activeHand)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = hands.ActiveHand.IsEmpty;
|
value = handSys.HandIsEmpty((owner, hands), activeHand);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case CanMove:
|
case CanMove:
|
||||||
@@ -204,16 +204,16 @@ public sealed partial class NPCBlackboard : IEnumerable<KeyValuePair<string, obj
|
|||||||
{
|
{
|
||||||
if (!TryGetValue(Owner, out owner, entManager) ||
|
if (!TryGetValue(Owner, out owner, entManager) ||
|
||||||
!entManager.TryGetComponent<HandsComponent>(owner, out var hands) ||
|
!entManager.TryGetComponent<HandsComponent>(owner, out var hands) ||
|
||||||
hands.ActiveHand == null)
|
handSys.GetActiveHand(owner) is null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var handos = new List<string>();
|
var handos = new List<string>();
|
||||||
|
|
||||||
foreach (var (id, hand) in hands.Hands)
|
foreach (var id in hands.Hands.Keys)
|
||||||
{
|
{
|
||||||
if (!hand.IsEmpty)
|
if (!handSys.HandIsEmpty((owner, hands), id))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
handos.Add(id);
|
handos.Add(id);
|
||||||
@@ -226,16 +226,16 @@ public sealed partial class NPCBlackboard : IEnumerable<KeyValuePair<string, obj
|
|||||||
{
|
{
|
||||||
if (!TryGetValue(Owner, out owner, entManager) ||
|
if (!TryGetValue(Owner, out owner, entManager) ||
|
||||||
!entManager.TryGetComponent<HandsComponent>(owner, out var hands) ||
|
!entManager.TryGetComponent<HandsComponent>(owner, out var hands) ||
|
||||||
hands.ActiveHand == null)
|
handSys.GetActiveHand(owner) is null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var handos = new List<string>();
|
var handos = new List<string>();
|
||||||
|
|
||||||
foreach (var (id, hand) in hands.Hands)
|
foreach (var id in hands.Hands.Keys)
|
||||||
{
|
{
|
||||||
if (!hand.IsEmpty)
|
if (!handSys.HandIsEmpty((owner, hands), id))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
handos.Add(id);
|
handos.Add(id);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Content.Server.Atmos.Components;
|
using Content.Server.Atmos.Components;
|
||||||
using Content.Server.Fluids.EntitySystems;
|
using Content.Server.Fluids.EntitySystems;
|
||||||
|
using Content.Server.Hands.Systems;
|
||||||
using Content.Server.NPC.Queries;
|
using Content.Server.NPC.Queries;
|
||||||
using Content.Server.NPC.Queries.Considerations;
|
using Content.Server.NPC.Queries.Considerations;
|
||||||
using Content.Server.NPC.Queries.Curves;
|
using Content.Server.NPC.Queries.Curves;
|
||||||
@@ -44,6 +45,7 @@ public sealed class NPCUtilitySystem : EntitySystem
|
|||||||
[Dependency] private readonly DrinkSystem _drink = default!;
|
[Dependency] private readonly DrinkSystem _drink = default!;
|
||||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||||
[Dependency] private readonly FoodSystem _food = default!;
|
[Dependency] private readonly FoodSystem _food = default!;
|
||||||
|
[Dependency] private readonly HandsSystem _hands = default!;
|
||||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||||
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
|
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
|
||||||
@@ -256,8 +258,9 @@ public sealed class NPCUtilitySystem : EntitySystem
|
|||||||
}
|
}
|
||||||
case TargetAmmoMatchesCon:
|
case TargetAmmoMatchesCon:
|
||||||
{
|
{
|
||||||
if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out Hand? activeHand, EntityManager) ||
|
if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out string? activeHand, EntityManager) ||
|
||||||
!TryComp<BallisticAmmoProviderComponent>(activeHand.HeldEntity, out var heldGun))
|
!_hands.TryGetHeldItem(owner, activeHand, out var heldEntity) ||
|
||||||
|
!TryComp<BallisticAmmoProviderComponent>(heldEntity, out var heldGun))
|
||||||
{
|
{
|
||||||
return 0f;
|
return 0f;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ public sealed class FTLDiskCommand : LocalizedCommands
|
|||||||
|
|
||||||
if (_entManager.TryGetComponent<StorageComponent>(cdCaseUid, out var storage) && storageSystem.Insert(cdCaseUid, cdUid, out _, storageComp: storage, playSound: false))
|
if (_entManager.TryGetComponent<StorageComponent>(cdCaseUid, out var storage) && storageSystem.Insert(cdCaseUid, cdUid, out _, storageComp: storage, playSound: false))
|
||||||
{
|
{
|
||||||
if (_entManager.TryGetComponent<HandsComponent>(entity, out var handsComponent) && handsSystem.TryGetEmptyHand(entity, out var emptyHand, handsComponent))
|
if (_entManager.TryGetComponent<HandsComponent>(entity, out var handsComponent) && handsSystem.TryGetEmptyHand((entity, handsComponent), out var emptyHand))
|
||||||
{
|
{
|
||||||
handsSystem.TryPickup(entity, cdCaseUid, emptyHand, checkActionBlocker: false, handsComp: handsComponent);
|
handsSystem.TryPickup(entity, cdCaseUid, emptyHand, checkActionBlocker: false, handsComp: handsComponent);
|
||||||
}
|
}
|
||||||
@@ -161,7 +161,7 @@ public sealed class FTLDiskCommand : LocalizedCommands
|
|||||||
{
|
{
|
||||||
_entManager.DeleteEntity(cdCaseUid); // something went wrong so just yeet the chaf
|
_entManager.DeleteEntity(cdCaseUid); // something went wrong so just yeet the chaf
|
||||||
|
|
||||||
if (_entManager.TryGetComponent<HandsComponent>(entity, out var handsComponent) && handsSystem.TryGetEmptyHand(entity, out var emptyHand, handsComponent))
|
if (_entManager.TryGetComponent<HandsComponent>(entity, out var handsComponent) && handsSystem.TryGetEmptyHand((entity, handsComponent), out var emptyHand))
|
||||||
{
|
{
|
||||||
handsSystem.TryPickup(entity, cdUid, emptyHand, checkActionBlocker: false, handsComp: handsComponent);
|
handsSystem.TryPickup(entity, cdUid, emptyHand, checkActionBlocker: false, handsComp: handsComponent);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,8 +217,8 @@ public sealed partial class BorgSystem
|
|||||||
|
|
||||||
var handId = $"{uid}-item{component.HandCounter}";
|
var handId = $"{uid}-item{component.HandCounter}";
|
||||||
component.HandCounter++;
|
component.HandCounter++;
|
||||||
_hands.AddHand(chassis, handId, HandLocation.Middle, hands);
|
_hands.AddHand((chassis, hands), handId, HandLocation.Middle);
|
||||||
_hands.DoPickup(chassis, hands.Hands[handId], item, hands);
|
_hands.DoPickup(chassis, handId, item, hands);
|
||||||
EnsureComp<UnremoveableComponent>(item);
|
EnsureComp<UnremoveableComponent>(item);
|
||||||
component.ProvidedItems.Add(handId, item);
|
component.ProvidedItems.Add(handId, item);
|
||||||
}
|
}
|
||||||
@@ -239,7 +239,7 @@ public sealed partial class BorgSystem
|
|||||||
foreach (var (hand, item) in component.ProvidedItems)
|
foreach (var (hand, item) in component.ProvidedItems)
|
||||||
{
|
{
|
||||||
QueueDel(item);
|
QueueDel(item);
|
||||||
_hands.RemoveHand(chassis, hand, hands);
|
_hands.RemoveHand(chassis, hand);
|
||||||
}
|
}
|
||||||
component.ProvidedItems.Clear();
|
component.ProvidedItems.Clear();
|
||||||
return;
|
return;
|
||||||
@@ -252,7 +252,7 @@ public sealed partial class BorgSystem
|
|||||||
RemComp<UnremoveableComponent>(item);
|
RemComp<UnremoveableComponent>(item);
|
||||||
_container.Insert(item, component.ProvidedContainer);
|
_container.Insert(item, component.ProvidedContainer);
|
||||||
}
|
}
|
||||||
_hands.RemoveHand(chassis, handId, hands);
|
_hands.RemoveHand(chassis, handId);
|
||||||
}
|
}
|
||||||
component.ProvidedItems.Clear();
|
component.ProvidedItems.Clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Content.Server.Hands.Systems;
|
||||||
using Content.Server.Popups;
|
using Content.Server.Popups;
|
||||||
using Content.Server.Tabletop.Components;
|
using Content.Server.Tabletop.Components;
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
@@ -22,6 +23,7 @@ namespace Content.Server.Tabletop
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||||
[Dependency] private readonly EyeSystem _eye = default!;
|
[Dependency] private readonly EyeSystem _eye = default!;
|
||||||
|
[Dependency] private readonly HandsSystem _hands = default!;
|
||||||
[Dependency] private readonly ViewSubscriberSystem _viewSubscriberSystem = default!;
|
[Dependency] private readonly ViewSubscriberSystem _viewSubscriberSystem = default!;
|
||||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||||
@@ -84,18 +86,13 @@ namespace Content.Server.Tabletop
|
|||||||
if (component.Session is not { } session)
|
if (component.Session is not { } session)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (hands.ActiveHand == null)
|
if (!_hands.TryGetActiveItem(uid, out var handEnt))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (hands.ActiveHand.HeldEntity == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var handEnt = hands.ActiveHand.HeldEntity.Value;
|
|
||||||
|
|
||||||
if (!TryComp<ItemComponent>(handEnt, out var item))
|
if (!TryComp<ItemComponent>(handEnt, out var item))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var meta = MetaData(handEnt);
|
var meta = MetaData(handEnt.Value);
|
||||||
var protoId = meta.EntityPrototype?.ID;
|
var protoId = meta.EntityPrototype?.ID;
|
||||||
|
|
||||||
var hologram = Spawn(protoId, session.Position.Offset(-1, 0));
|
var hologram = Spawn(protoId, session.Position.Offset(-1, 0));
|
||||||
|
|||||||
@@ -90,9 +90,9 @@ public sealed class InnateToolSystem : EntitySystem
|
|||||||
|
|
||||||
if (TryComp<HandsComponent>(uid, out var hands))
|
if (TryComp<HandsComponent>(uid, out var hands))
|
||||||
{
|
{
|
||||||
foreach (var hand in hands.Hands)
|
foreach (var hand in hands.Hands.Keys)
|
||||||
{
|
{
|
||||||
_sharedHandsSystem.TryDrop(uid, hand.Value, checkActionBlocker: false, handsComp: hands);
|
_sharedHandsSystem.TryDrop((uid, hands), hand, checkActionBlocker: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Server.Administration.Managers;
|
using Content.Server.Administration.Managers;
|
||||||
|
using Content.Server.Hands.Systems;
|
||||||
using Content.Server.Popups;
|
using Content.Server.Popups;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Hands.Components;
|
|
||||||
using Content.Shared.Inventory.VirtualItem;
|
using Content.Shared.Inventory.VirtualItem;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
@@ -14,6 +14,7 @@ namespace Content.Server.Verbs
|
|||||||
public sealed class VerbSystem : SharedVerbSystem
|
public sealed class VerbSystem : SharedVerbSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||||
|
[Dependency] private readonly HandsSystem _hands = default!;
|
||||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||||
[Dependency] private readonly IAdminManager _adminMgr = default!;
|
[Dependency] private readonly IAdminManager _adminMgr = default!;
|
||||||
|
|
||||||
@@ -91,8 +92,7 @@ namespace Content.Server.Verbs
|
|||||||
{
|
{
|
||||||
// first get the held item. again.
|
// first get the held item. again.
|
||||||
EntityUid? holding = null;
|
EntityUid? holding = null;
|
||||||
if (TryComp(user, out HandsComponent? hands) &&
|
if (_hands.GetActiveItem(user) is { } heldEntity)
|
||||||
hands.ActiveHandEntity is EntityUid heldEntity)
|
|
||||||
{
|
{
|
||||||
holding = heldEntity;
|
holding = heldEntity;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Content.Server.Hands.Systems;
|
|||||||
using Content.Server.Storage.EntitySystems;
|
using Content.Server.Storage.EntitySystems;
|
||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Hands.Components;
|
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Storage;
|
using Content.Shared.Storage;
|
||||||
@@ -40,24 +39,20 @@ public sealed class StorageVoiceControlSystem : EntitySystem
|
|||||||
if (!TryComp<StorageComponent>(ent, out var storage))
|
if (!TryComp<StorageComponent>(ent, out var storage))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Get the hands component
|
|
||||||
if (!TryComp<HandsComponent>(args.Source, out var hands))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// If the player has something in their hands, try to insert it into the storage
|
// If the player has something in their hands, try to insert it into the storage
|
||||||
if (hands.ActiveHand != null && hands.ActiveHand.HeldEntity.HasValue)
|
if (_hands.TryGetActiveItem(ent.Owner, out var activeItem))
|
||||||
{
|
{
|
||||||
// Disallow insertion and provide a reason why if the person decides to insert the item into itself
|
// Disallow insertion and provide a reason why if the person decides to insert the item into itself
|
||||||
if (ent.Owner.Equals(hands.ActiveHand.HeldEntity.Value))
|
if (ent.Owner.Equals(activeItem.Value))
|
||||||
{
|
{
|
||||||
_popup.PopupEntity(Loc.GetString("comp-storagevoicecontrol-self-insert", ("entity", hands.ActiveHand.HeldEntity.Value)), ent, args.Source);
|
_popup.PopupEntity(Loc.GetString("comp-storagevoicecontrol-self-insert", ("entity", activeItem.Value)), ent, args.Source);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_storage.CanInsert(ent, hands.ActiveHand.HeldEntity.Value, out var failedReason))
|
if (_storage.CanInsert(ent, activeItem.Value, out var failedReason))
|
||||||
{
|
{
|
||||||
// We adminlog before insertion, otherwise the logger will attempt to pull info on an entity that no longer is present and throw an exception
|
// We adminlog before insertion, otherwise the logger will attempt to pull info on an entity that no longer is present and throw an exception
|
||||||
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.Source)} inserted {ToPrettyString(hands.ActiveHand.HeldEntity.Value)} into {ToPrettyString(ent)} via voice control");
|
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.Source)} inserted {ToPrettyString(activeItem.Value)} into {ToPrettyString(ent)} via voice control");
|
||||||
_storage.Insert(ent, hands.ActiveHand.HeldEntity.Value, out _);
|
_storage.Insert(ent, activeItem.Value, out _);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -67,7 +62,7 @@ public sealed class StorageVoiceControlSystem : EntitySystem
|
|||||||
_popup.PopupEntity(Loc.GetString(failedReason), ent, args.Source);
|
_popup.PopupEntity(Loc.GetString(failedReason), ent, args.Source);
|
||||||
_adminLogger.Add(LogType.Action,
|
_adminLogger.Add(LogType.Action,
|
||||||
LogImpact.Low,
|
LogImpact.Low,
|
||||||
$"{ToPrettyString(args.Source)} failed to insert {ToPrettyString(hands.ActiveHand.HeldEntity.Value)} into {ToPrettyString(ent)} via voice control");
|
$"{ToPrettyString(args.Source)} failed to insert {ToPrettyString(activeItem.Value)} into {ToPrettyString(ent)} via voice control");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -80,7 +75,7 @@ public sealed class StorageVoiceControlSystem : EntitySystem
|
|||||||
// E.g "go go s" would give you the screwdriver because "screwdriver" contains "s"
|
// E.g "go go s" would give you the screwdriver because "screwdriver" contains "s"
|
||||||
if (Name(item).Contains(args.MessageWithoutPhrase))
|
if (Name(item).Contains(args.MessageWithoutPhrase))
|
||||||
{
|
{
|
||||||
ExtractItemFromStorage(ent, item, args.Source, hands);
|
ExtractItemFromStorage(ent, item, args.Source);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,16 +87,14 @@ public sealed class StorageVoiceControlSystem : EntitySystem
|
|||||||
/// <param name="ent">The entity with the <see cref="StorageVoiceControlComponent"/></param>
|
/// <param name="ent">The entity with the <see cref="StorageVoiceControlComponent"/></param>
|
||||||
/// <param name="item">The entity to be extracted from the attached storage</param>
|
/// <param name="item">The entity to be extracted from the attached storage</param>
|
||||||
/// <param name="source">The entity wearing the item</param>
|
/// <param name="source">The entity wearing the item</param>
|
||||||
/// <param name="hands">The <see cref="HandsComponent"/> of the person wearing the item</param>
|
|
||||||
private void ExtractItemFromStorage(Entity<StorageVoiceControlComponent> ent,
|
private void ExtractItemFromStorage(Entity<StorageVoiceControlComponent> ent,
|
||||||
EntityUid item,
|
EntityUid item,
|
||||||
EntityUid source,
|
EntityUid source)
|
||||||
HandsComponent hands)
|
|
||||||
{
|
{
|
||||||
_container.RemoveEntity(ent, item);
|
_container.RemoveEntity(ent, item);
|
||||||
_adminLogger.Add(LogType.Action,
|
_adminLogger.Add(LogType.Action,
|
||||||
LogImpact.Low,
|
LogImpact.Low,
|
||||||
$"{ToPrettyString(source)} retrieved {ToPrettyString(item)} from {ToPrettyString(ent)} via voice control");
|
$"{ToPrettyString(source)} retrieved {ToPrettyString(item)} from {ToPrettyString(ent)} via voice control");
|
||||||
_hands.TryPickup(source, item, handsComp: hands);
|
_hands.TryPickup(source, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Content.Server.Construction;
|
using Content.Server.Construction;
|
||||||
using Content.Server.Construction.Components;
|
using Content.Server.Construction.Components;
|
||||||
|
using Content.Server.Hands.Systems;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Shared.DoAfter;
|
using Content.Shared.DoAfter;
|
||||||
using Content.Shared.GameTicking;
|
using Content.Shared.GameTicking;
|
||||||
@@ -24,6 +25,7 @@ public sealed class WiresSystem : SharedWiresSystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||||
|
[Dependency] private readonly HandsSystem _hands = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||||
@@ -405,19 +407,13 @@ public sealed class WiresSystem : SharedWiresSystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var activeHand = handsComponent.ActiveHand;
|
if (!_hands.TryGetActiveItem((player, handsComponent), out var heldEntity))
|
||||||
|
|
||||||
if (activeHand == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (activeHand.HeldEntity == null)
|
if (!EntityManager.TryGetComponent(heldEntity, out ToolComponent? tool))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var activeHandEntity = activeHand.HeldEntity.Value;
|
TryDoWireAction(uid, player, heldEntity.Value, args.Id, args.Action, component, tool);
|
||||||
if (!EntityManager.TryGetComponent(activeHandEntity, out ToolComponent? tool))
|
|
||||||
return;
|
|
||||||
|
|
||||||
TryDoWireAction(uid, player, activeHandEntity, args.Id, args.Action, component, tool);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDoAfter(EntityUid uid, WiresComponent component, WireDoAfterEvent args)
|
private void OnDoAfter(EntityUid uid, WiresComponent component, WireDoAfterEvent args)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using Content.Shared.Access.Components;
|
|||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.PDA;
|
using Content.Shared.PDA;
|
||||||
@@ -21,6 +21,7 @@ public abstract class SharedIdCardSystem : EntitySystem
|
|||||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] private readonly SharedAccessSystem _access = default!;
|
[Dependency] private readonly SharedAccessSystem _access = default!;
|
||||||
|
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||||
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
|
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
@@ -83,8 +84,7 @@ public abstract class SharedIdCardSystem : EntitySystem
|
|||||||
public bool TryFindIdCard(EntityUid uid, out Entity<IdCardComponent> idCard)
|
public bool TryFindIdCard(EntityUid uid, out Entity<IdCardComponent> idCard)
|
||||||
{
|
{
|
||||||
// check held item?
|
// check held item?
|
||||||
if (TryComp(uid, out HandsComponent? hands) &&
|
if (_hands.GetActiveItem(uid) is { } heldItem &&
|
||||||
hands.ActiveHandEntity is EntityUid heldItem &&
|
|
||||||
TryGetIdCard(heldItem, out idCard))
|
TryGetIdCard(heldItem, out idCard))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ public sealed partial class BlockingSystem : EntitySystem
|
|||||||
if (!handQuery.TryGetComponent(args.Performer, out var hands))
|
if (!handQuery.TryGetComponent(args.Performer, out var hands))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var shields = _handsSystem.EnumerateHeld(args.Performer, hands).ToArray();
|
var shields = _handsSystem.EnumerateHeld((args.Performer, hands)).ToArray();
|
||||||
|
|
||||||
foreach (var shield in shields)
|
foreach (var shield in shields)
|
||||||
{
|
{
|
||||||
@@ -277,7 +277,7 @@ public sealed partial class BlockingSystem : EntitySystem
|
|||||||
if (!handQuery.TryGetComponent(user, out var hands))
|
if (!handQuery.TryGetComponent(user, out var hands))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var shields = _handsSystem.EnumerateHeld(user, hands).ToArray();
|
var shields = _handsSystem.EnumerateHeld((user, hands)).ToArray();
|
||||||
|
|
||||||
foreach (var shield in shields)
|
foreach (var shield in shields)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -947,12 +947,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
|||||||
if (!entity.Comp.HeldOnly)
|
if (!entity.Comp.HeldOnly)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (TryComp(examiner, out HandsComponent? handsComp))
|
return Hands.IsHolding(examiner, entity, out _);
|
||||||
{
|
|
||||||
return Hands.IsHolding(examiner, entity, out _, handsComp);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMapInit(Entity<SolutionContainerManagerComponent> entity, ref MapInitEvent args)
|
private void OnMapInit(Entity<SolutionContainerManagerComponent> entity, ref MapInitEvent args)
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ namespace Content.Shared.Containers.ItemSlots
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Drop the held item onto the floor. Return if the user cannot drop.
|
// Drop the held item onto the floor. Return if the user cannot drop.
|
||||||
if (!_handsSystem.TryDrop(args.User, args.Used, handsComp: hands))
|
if (!_handsSystem.TryDrop(args.User, args.Used))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
slots.Sort(SortEmpty);
|
slots.Sort(SortEmpty);
|
||||||
@@ -395,17 +395,17 @@ namespace Content.Shared.Containers.ItemSlots
|
|||||||
if (!Resolve(user, ref hands, false))
|
if (!Resolve(user, ref hands, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (hands.ActiveHand?.HeldEntity is not { } held)
|
if (!_handsSystem.TryGetActiveItem((uid, hands), out var held))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!CanInsert(uid, held, user, slot))
|
if (!CanInsert(uid, held.Value, user, slot))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// hands.Drop(item) checks CanDrop action blocker
|
// hands.Drop(item) checks CanDrop action blocker
|
||||||
if (!_handsSystem.TryDrop(user, hands.ActiveHand))
|
if (!_handsSystem.TryDrop(user, hands.ActiveHandId!))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Insert(uid, slot, held, user, excludeUserAudio: excludeUserAudio);
|
Insert(uid, slot, held.Value, user, excludeUserAudio: excludeUserAudio);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,16 +428,14 @@ namespace Content.Shared.Containers.ItemSlots
|
|||||||
if (!Resolve(ent, ref ent.Comp, false))
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
TryComp(user, out HandsComponent? handsComp);
|
|
||||||
|
|
||||||
if (!TryGetAvailableSlot(ent,
|
if (!TryGetAvailableSlot(ent,
|
||||||
item,
|
item,
|
||||||
user == null ? null : (user.Value, handsComp),
|
user,
|
||||||
out var itemSlot,
|
out var itemSlot,
|
||||||
emptyOnly: true))
|
emptyOnly: true))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (user != null && !_handsSystem.TryDrop(user.Value, item, handsComp: handsComp))
|
if (user != null && !_handsSystem.TryDrop(user.Value, item))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Insert(ent, itemSlot, item, user, excludeUserAudio: excludeUserAudio);
|
Insert(ent, itemSlot, item, user, excludeUserAudio: excludeUserAudio);
|
||||||
@@ -466,7 +464,7 @@ namespace Content.Shared.Containers.ItemSlots
|
|||||||
&& Resolve(user, ref user.Comp)
|
&& Resolve(user, ref user.Comp)
|
||||||
&& _handsSystem.IsHolding(user, item))
|
&& _handsSystem.IsHolding(user, item))
|
||||||
{
|
{
|
||||||
if (!_handsSystem.CanDrop(user, item, user.Comp))
|
if (!_handsSystem.CanDrop(user, item))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -397,6 +397,10 @@ namespace Content.Shared.Cuffs
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnHandCountChanged(Entity<CuffableComponent> ent, ref HandCountChangedEvent message)
|
private void OnHandCountChanged(Entity<CuffableComponent> ent, ref HandCountChangedEvent message)
|
||||||
{
|
{
|
||||||
|
// TODO: either don't store a container ref, or make it actually nullable.
|
||||||
|
if (ent.Comp.Container == default!)
|
||||||
|
return;
|
||||||
|
|
||||||
var dirty = false;
|
var dirty = false;
|
||||||
var handCount = CompOrNull<HandsComponent>(ent.Owner)?.Count ?? 0;
|
var handCount = CompOrNull<HandsComponent>(ent.Owner)?.Count ?? 0;
|
||||||
|
|
||||||
@@ -431,19 +435,19 @@ namespace Content.Shared.Cuffs
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var freeHands = 0;
|
var freeHands = 0;
|
||||||
foreach (var hand in _hands.EnumerateHands(uid, handsComponent))
|
foreach (var hand in _hands.EnumerateHands((uid, handsComponent)))
|
||||||
{
|
{
|
||||||
if (hand.HeldEntity == null)
|
if (!_hands.TryGetHeldItem((uid, handsComponent), hand, out var held))
|
||||||
{
|
{
|
||||||
freeHands++;
|
freeHands++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this entity removable? (it might be an existing handcuff blocker)
|
// Is this entity removable? (it might be an existing handcuff blocker)
|
||||||
if (HasComp<UnremoveableComponent>(hand.HeldEntity))
|
if (HasComp<UnremoveableComponent>(held))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
_hands.DoDrop(uid, hand, true, handsComponent);
|
_hands.DoDrop(uid, hand, true);
|
||||||
freeHands++;
|
freeHands++;
|
||||||
if (freeHands == 2)
|
if (freeHands == 2)
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ public abstract class SharedDisposalUnitSystem : EntitySystem
|
|||||||
Category = VerbCategory.Insert,
|
Category = VerbCategory.Insert,
|
||||||
Act = () =>
|
Act = () =>
|
||||||
{
|
{
|
||||||
_handsSystem.TryDropIntoContainer(args.User, args.Using.Value, component.Container, checkActionBlocker: false, args.Hands);
|
_handsSystem.TryDropIntoContainer((args.User, args.Hands), args.Using.Value, component.Container, checkActionBlocker: false);
|
||||||
_adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Using.Value)} into {ToPrettyString(uid)}");
|
_adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Using.Value)} into {ToPrettyString(uid)}");
|
||||||
AfterInsert(uid, component, args.Using.Value, args.User);
|
AfterInsert(uid, component, args.Using.Value, args.User);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
// If the user changes which hand is active at all, interrupt the do-after
|
// If the user changes which hand is active at all, interrupt the do-after
|
||||||
if (args.BreakOnHandChange && hands.ActiveHand?.Name != doAfter.InitialHand)
|
if (args.BreakOnHandChange && hands.ActiveHandId != doAfter.InitialHand)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -221,8 +221,8 @@ public abstract partial class SharedDoAfterSystem : EntitySystem
|
|||||||
if (!TryComp(args.User, out HandsComponent? handsComponent))
|
if (!TryComp(args.User, out HandsComponent? handsComponent))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
doAfter.InitialHand = handsComponent.ActiveHand?.Name;
|
doAfter.InitialHand = handsComponent.ActiveHandId;
|
||||||
doAfter.InitialItem = handsComponent.ActiveHandEntity;
|
doAfter.InitialItem = _hands.GetActiveItem((args.User, handsComponent));
|
||||||
}
|
}
|
||||||
|
|
||||||
doAfter.NetInitialItem = GetNetEntity(doAfter.InitialItem);
|
doAfter.NetInitialItem = GetNetEntity(doAfter.InitialItem);
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ public sealed class DeployFoldableSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!TryComp(args.User, out HandsComponent? hands)
|
if (!TryComp(args.User, out HandsComponent? hands)
|
||||||
|| !_hands.TryDrop(args.User, args.Used, targetDropLocation: args.ClickLocation, handsComp: hands))
|
|| !_hands.TryDrop((args.User, hands), args.Used, targetDropLocation: args.ClickLocation))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!_foldable.TrySetFolded(ent, foldable, false))
|
if (!_foldable.TrySetFolded(ent, foldable, false))
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using Content.Shared.Hands.EntitySystems;
|
|
||||||
|
|
||||||
namespace Content.Shared.Hands.Components;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// These helpers exist to make getting basic information out of the hands component more convenient, without
|
|
||||||
/// needing to resolve hands system or something like that.
|
|
||||||
/// </summary>
|
|
||||||
public static class HandHelpers
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Returns true if any hand is free. This is a LinQ method, not a property, so
|
|
||||||
/// cache it instead of accessing this multiple times.
|
|
||||||
/// </summary>
|
|
||||||
public static bool IsAnyHandFree(this HandsComponent component) => component.Hands.Values.Any(hand => hand.IsEmpty);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the number of hands that are not currently holding anything. This is a LinQ method, not a property, so
|
|
||||||
/// cache it instead of accessing this multiple times.
|
|
||||||
/// </summary>
|
|
||||||
public static int CountFreeHands(this HandsComponent component) => component.Hands.Values.Count(hand => hand.IsEmpty);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the number of hands that are not currently holding anything. This is a LinQ method, not a property, so
|
|
||||||
/// cache it instead of accessing this multiple times.
|
|
||||||
/// </summary>
|
|
||||||
public static int CountFreeableHands(this Entity<HandsComponent> component, SharedHandsSystem system)
|
|
||||||
{
|
|
||||||
return system.CountFreeableHands(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get a list of hands that are currently holding nothing. This is a LinQ method, not a property, so cache
|
|
||||||
/// it instead of accessing this multiple times.
|
|
||||||
/// </summary>
|
|
||||||
public static IEnumerable<Hand> GetFreeHands(this HandsComponent component) => component.Hands.Values.Where(hand => !hand.IsEmpty);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get a list of hands that are currently holding nothing. This is a LinQ method, not a property, so cache
|
|
||||||
/// it instead of accessing this multiple times.
|
|
||||||
/// </summary>
|
|
||||||
public static IEnumerable<string> GetFreeHandNames(this HandsComponent component) => GetFreeHands(component).Select(hand => hand.Name);
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using Content.Shared.DisplacementMap;
|
using Content.Shared.DisplacementMap;
|
||||||
using Content.Shared.Hands.EntitySystems;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Robust.Shared.Containers;
|
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
@@ -13,50 +12,50 @@ public sealed partial class HandsComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The currently active hand.
|
/// The currently active hand.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables]
|
[DataField]
|
||||||
public Hand? ActiveHand;
|
public string? ActiveHandId;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The item currently held in the active hand.
|
/// Dictionary relating a unique hand ID corresponding to a container slot on the attached entity to a class containing information about the Hand itself.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables]
|
[DataField]
|
||||||
public EntityUid? ActiveHandEntity => ActiveHand?.HeldEntity;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public Dictionary<string, Hand> Hands = new();
|
public Dictionary<string, Hand> Hands = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of hands
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
public int Count => Hands.Count;
|
public int Count => Hands.Count;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List of hand-names. These are keys for <see cref="Hands"/>. The order of this list determines the order in which hands are iterated over.
|
/// List of hand-names. These are keys for <see cref="Hands"/>. The order of this list determines the order in which hands are iterated over.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
public List<string> SortedHands = new();
|
public List<string> SortedHands = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If true, the items in the hands won't be affected by explosions.
|
/// If true, the items in the hands won't be affected by explosions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public bool DisableExplosionRecursion = false;
|
public bool DisableExplosionRecursion;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Modifies the speed at which items are thrown.
|
/// Modifies the speed at which items are thrown.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
public float BaseThrowspeed = 11f;
|
||||||
public float BaseThrowspeed { get; set; } = 11f;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Distance after which longer throw targets stop increasing throw impulse.
|
/// Distance after which longer throw targets stop increasing throw impulse.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("throwRange")]
|
[DataField]
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
public float ThrowRange = 8f;
|
||||||
public float ThrowRange { get; set; } = 8f;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether or not to add in-hand sprites for held items. Some entities (e.g., drones) don't want these.
|
/// Whether or not to add in-hand sprites for held items. Some entities (e.g., drones) don't want these.
|
||||||
/// Used by the client.
|
/// Used by the client.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("showInHands")]
|
[DataField]
|
||||||
public bool ShowInHands = true;
|
public bool ShowInHands = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -68,14 +67,13 @@ public sealed partial class HandsComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time at which throws will be allowed again.
|
/// The time at which throws will be allowed again.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField, AutoPausedField]
|
||||||
[AutoPausedField]
|
|
||||||
public TimeSpan NextThrowTime;
|
public TimeSpan NextThrowTime;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The minimum time inbetween throws.
|
/// The minimum time inbetween throws.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
public TimeSpan ThrowCooldown = TimeSpan.FromSeconds(0.5f);
|
public TimeSpan ThrowCooldown = TimeSpan.FromSeconds(0.5f);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -103,48 +101,37 @@ public sealed partial class HandsComponent : Component
|
|||||||
public bool CanBeStripped = true;
|
public bool CanBeStripped = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[DataDefinition]
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class Hand //TODO: This should definitely be a struct - Jezi
|
public partial record struct Hand
|
||||||
{
|
{
|
||||||
[ViewVariables]
|
[DataField]
|
||||||
public string Name { get; }
|
public HandLocation Location = HandLocation.Right;
|
||||||
|
|
||||||
[ViewVariables]
|
public Hand()
|
||||||
public HandLocation Location { get; }
|
{
|
||||||
|
|
||||||
/// <summary>
|
}
|
||||||
/// The container used to hold the contents of this hand. Nullable because the client must get the containers via <see cref="ContainerManagerComponent"/>,
|
|
||||||
/// which may not be synced with the server when the client hands are created.
|
public Hand(HandLocation location)
|
||||||
/// </summary>
|
|
||||||
[ViewVariables, NonSerialized]
|
|
||||||
public ContainerSlot? Container;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public EntityUid? HeldEntity => Container?.ContainedEntity;
|
|
||||||
|
|
||||||
public bool IsEmpty => HeldEntity == null;
|
|
||||||
|
|
||||||
public Hand(string name, HandLocation location, ContainerSlot? container = null)
|
|
||||||
{
|
{
|
||||||
Name = name;
|
|
||||||
Location = location;
|
Location = location;
|
||||||
Container = container;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class HandsComponentState : ComponentState
|
public sealed class HandsComponentState : ComponentState
|
||||||
{
|
{
|
||||||
public readonly List<Hand> Hands;
|
public readonly Dictionary<string, Hand> Hands;
|
||||||
public readonly List<string> HandNames;
|
public readonly List<string> SortedHands;
|
||||||
public readonly string? ActiveHand;
|
public readonly string? ActiveHandId;
|
||||||
|
|
||||||
public HandsComponentState(HandsComponent handComp)
|
public HandsComponentState(HandsComponent handComp)
|
||||||
{
|
{
|
||||||
// cloning lists because of test networking.
|
// cloning lists because of test networking.
|
||||||
Hands = new(handComp.Hands.Values);
|
Hands = new(handComp.Hands);
|
||||||
HandNames = new(handComp.SortedHands);
|
SortedHands = new(handComp.SortedHands);
|
||||||
ActiveHand = handComp.ActiveHand?.Name;
|
ActiveHandId = handComp.ActiveHandId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ namespace Content.Shared.Hands.EntitySystems;
|
|||||||
// These functions are mostly unused except for some AI operator stuff
|
// These functions are mostly unused except for some AI operator stuff
|
||||||
// Nothing stops them from being used in general. If they ever get used elsewhere, then this file probably needs to be renamed.
|
// Nothing stops them from being used in general. If they ever get used elsewhere, then this file probably needs to be renamed.
|
||||||
|
|
||||||
public abstract partial class SharedHandsSystem : EntitySystem
|
public abstract partial class SharedHandsSystem
|
||||||
{
|
{
|
||||||
public bool TrySelect(EntityUid uid, EntityUid? entity, HandsComponent? handsComp = null)
|
public bool TrySelect(EntityUid uid, EntityUid? entity, HandsComponent? handsComp = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handsComp, false))
|
if (!Resolve(uid, ref handsComp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!IsHolding(uid, entity, out var hand, handsComp))
|
if (!IsHolding((uid, handsComp), entity, out var hand))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
SetActiveHand(uid, hand, handsComp);
|
SetActiveHand((uid, handsComp), hand);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,9 +26,12 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
if (!Resolve(uid, ref handsComp, false))
|
if (!Resolve(uid, ref handsComp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
foreach (var hand in handsComp.Hands.Values)
|
foreach (var hand in handsComp.Hands.Keys)
|
||||||
{
|
{
|
||||||
if (TryComp(hand.HeldEntity, out component))
|
if (!TryGetHeldItem((uid, handsComp), hand, out var held))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (TryComp(held, out component))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Content.Shared.Tag;
|
|||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Shared.Hands.EntitySystems;
|
namespace Content.Shared.Hands.EntitySystems;
|
||||||
|
|
||||||
@@ -28,10 +29,10 @@ public abstract partial class SharedHandsSystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var gotUnequipped = new GotUnequippedHandEvent(uid, args.Entity, hand);
|
var gotUnequipped = new GotUnequippedHandEvent(uid, args.Entity, hand.Value);
|
||||||
RaiseLocalEvent(args.Entity, gotUnequipped);
|
RaiseLocalEvent(args.Entity, gotUnequipped);
|
||||||
|
|
||||||
var didUnequip = new DidUnequipHandEvent(uid, args.Entity, hand);
|
var didUnequip = new DidUnequipHandEvent(uid, args.Entity, hand.Value);
|
||||||
RaiseLocalEvent(uid, didUnequip);
|
RaiseLocalEvent(uid, didUnequip);
|
||||||
|
|
||||||
if (TryComp(args.Entity, out VirtualItemComponent? @virtual))
|
if (TryComp(args.Entity, out VirtualItemComponent? @virtual))
|
||||||
@@ -47,26 +48,29 @@ public abstract partial class SharedHandsSystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks whether an entity can drop a given entity. Will return false if they are not holding the entity.
|
/// Checks whether an entity can drop a given entity. Will return false if they are not holding the entity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool CanDrop(EntityUid uid, EntityUid entity, HandsComponent? handsComp = null, bool checkActionBlocker = true)
|
public bool CanDrop(Entity<HandsComponent?> ent, EntityUid entity, bool checkActionBlocker = true)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handsComp))
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!IsHolding(uid, entity, out var hand, handsComp))
|
if (!IsHolding(ent, entity, out var hand))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return CanDropHeld(uid, hand, checkActionBlocker);
|
return CanDropHeld(ent, hand, checkActionBlocker);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the contents of a hand is able to be removed from its container.
|
/// Checks if the contents of a hand is able to be removed from its container.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool CanDropHeld(EntityUid uid, Hand hand, bool checkActionBlocker = true)
|
public bool CanDropHeld(EntityUid uid, string handId, bool checkActionBlocker = true)
|
||||||
{
|
{
|
||||||
if (hand.Container?.ContainedEntity is not {} held)
|
if (!ContainerSystem.TryGetContainer(uid, handId, out var container))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!ContainerSystem.CanRemove(held, hand.Container))
|
if (container.ContainedEntities.FirstOrNull() is not {} held)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ContainerSystem.CanRemove(held, container))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (checkActionBlocker && !_actionBlocker.CanDrop(uid))
|
if (checkActionBlocker && !_actionBlocker.CanDrop(uid))
|
||||||
@@ -78,98 +82,100 @@ public abstract partial class SharedHandsSystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to drop the item in the currently active hand.
|
/// Attempts to drop the item in the currently active hand.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TryDrop(EntityUid uid, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true, HandsComponent? handsComp = null)
|
public bool TryDrop(Entity<HandsComponent?> ent, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handsComp))
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (handsComp.ActiveHand == null)
|
if (ent.Comp.ActiveHandId == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return TryDrop(uid, handsComp.ActiveHand, targetDropLocation, checkActionBlocker, doDropInteraction, handsComp);
|
return TryDrop(ent, ent.Comp.ActiveHandId, targetDropLocation, checkActionBlocker, doDropInteraction);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Drops an item at the target location.
|
/// Drops an item at the target location.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TryDrop(EntityUid uid, EntityUid entity, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true, HandsComponent? handsComp = null)
|
public bool TryDrop(Entity<HandsComponent?> ent, EntityUid entity, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handsComp))
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!IsHolding(uid, entity, out var hand, handsComp))
|
if (!IsHolding(ent, entity, out var hand))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return TryDrop(uid, hand, targetDropLocation, checkActionBlocker, doDropInteraction, handsComp);
|
return TryDrop(ent, hand, targetDropLocation, checkActionBlocker, doDropInteraction);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Drops a hands contents at the target location.
|
/// Drops a hands contents at the target location.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TryDrop(EntityUid uid, Hand hand, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true, HandsComponent? handsComp = null)
|
public bool TryDrop(Entity<HandsComponent?> ent, string handId, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handsComp))
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!CanDropHeld(uid, hand, checkActionBlocker))
|
if (!CanDropHeld(ent, handId, checkActionBlocker))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var entity = hand.HeldEntity!.Value;
|
if (!TryGetHeldItem(ent, handId, out var entity))
|
||||||
|
return false;
|
||||||
|
|
||||||
// if item is a fake item (like with pulling), just delete it rather than bothering with trying to drop it into the world
|
// if item is a fake item (like with pulling), just delete it rather than bothering with trying to drop it into the world
|
||||||
if (TryComp(entity, out VirtualItemComponent? @virtual))
|
if (TryComp(entity, out VirtualItemComponent? @virtual))
|
||||||
_virtualSystem.DeleteVirtualItem((entity, @virtual), uid);
|
_virtualSystem.DeleteVirtualItem((entity.Value, @virtual), ent);
|
||||||
|
|
||||||
if (TerminatingOrDeleted(entity))
|
if (TerminatingOrDeleted(entity))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var itemXform = Transform(entity);
|
var itemXform = Transform(entity.Value);
|
||||||
if (itemXform.MapUid == null)
|
if (itemXform.MapUid == null)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var userXform = Transform(uid);
|
var userXform = Transform(ent);
|
||||||
var isInContainer = ContainerSystem.IsEntityOrParentInContainer(uid, xform: userXform);
|
var isInContainer = ContainerSystem.IsEntityOrParentInContainer(ent, xform: userXform);
|
||||||
|
|
||||||
// if the user is in a container, drop the item inside the container
|
// if the user is in a container, drop the item inside the container
|
||||||
if (isInContainer) {
|
if (isInContainer)
|
||||||
TransformSystem.DropNextTo((entity, itemXform), (uid, userXform));
|
{
|
||||||
|
TransformSystem.DropNextTo((entity.Value, itemXform), (ent, userXform));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// drop the item with heavy calculations from their hands and place it at the calculated interaction range position
|
// drop the item with heavy calculations from their hands and place it at the calculated interaction range position
|
||||||
// The DoDrop is handle if there's no drop target
|
// The DoDrop is handle if there's no drop target
|
||||||
DoDrop(uid, hand, doDropInteraction: doDropInteraction, handsComp);
|
DoDrop(ent, handId, doDropInteraction: doDropInteraction);
|
||||||
|
|
||||||
// if there's no drop location stop here
|
// if there's no drop location stop here
|
||||||
if (targetDropLocation == null)
|
if (targetDropLocation == null)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// otherwise, also move dropped item and rotate it properly according to grid/map
|
// otherwise, also move dropped item and rotate it properly according to grid/map
|
||||||
var (itemPos, itemRot) = TransformSystem.GetWorldPositionRotation(entity);
|
var (itemPos, itemRot) = TransformSystem.GetWorldPositionRotation(entity.Value);
|
||||||
var origin = new MapCoordinates(itemPos, itemXform.MapID);
|
var origin = new MapCoordinates(itemPos, itemXform.MapID);
|
||||||
var target = TransformSystem.ToMapCoordinates(targetDropLocation.Value);
|
var target = TransformSystem.ToMapCoordinates(targetDropLocation.Value);
|
||||||
TransformSystem.SetWorldPositionRotation(entity, GetFinalDropCoordinates(uid, origin, target, entity), itemRot);
|
TransformSystem.SetWorldPositionRotation(entity.Value, GetFinalDropCoordinates(ent, origin, target, entity.Value), itemRot);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to move a held item from a hand into a container that is not another hand, without dropping it on the floor in-between.
|
/// Attempts to move a held item from a hand into a container that is not another hand, without dropping it on the floor in-between.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TryDropIntoContainer(EntityUid uid, EntityUid entity, BaseContainer targetContainer, bool checkActionBlocker = true, HandsComponent? handsComp = null)
|
public bool TryDropIntoContainer(Entity<HandsComponent?> ent, EntityUid entity, BaseContainer targetContainer, bool checkActionBlocker = true)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handsComp))
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!IsHolding(uid, entity, out var hand, handsComp))
|
if (!IsHolding(ent, entity, out var hand))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!CanDropHeld(uid, hand, checkActionBlocker))
|
if (!CanDropHeld(ent, hand, checkActionBlocker))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!ContainerSystem.CanInsert(entity, targetContainer))
|
if (!ContainerSystem.CanInsert(entity, targetContainer))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
DoDrop(uid, hand, false, handsComp);
|
DoDrop(ent, hand, false);
|
||||||
ContainerSystem.Insert(entity, targetContainer);
|
ContainerSystem.Insert(entity, targetContainer);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -202,34 +208,38 @@ public abstract partial class SharedHandsSystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes the contents of a hand from its container. Assumes that the removal is allowed. In general, you should not be calling this directly.
|
/// Removes the contents of a hand from its container. Assumes that the removal is allowed. In general, you should not be calling this directly.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void DoDrop(EntityUid uid, Hand hand, bool doDropInteraction = true, HandsComponent? handsComp = null, bool log = true)
|
public virtual void DoDrop(Entity<HandsComponent?> ent,
|
||||||
|
string handId,
|
||||||
|
bool doDropInteraction = true,
|
||||||
|
bool log = true)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handsComp))
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (hand.Container?.ContainedEntity == null)
|
if (!ContainerSystem.TryGetContainer(ent, handId, out var container))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var entity = hand.Container.ContainedEntity.Value;
|
if (!TryGetHeldItem(ent, handId, out var entity))
|
||||||
|
|
||||||
if (TerminatingOrDeleted(uid) || TerminatingOrDeleted(entity))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!ContainerSystem.Remove(entity, hand.Container))
|
if (TerminatingOrDeleted(ent) || TerminatingOrDeleted(entity))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!ContainerSystem.Remove(entity.Value, container))
|
||||||
{
|
{
|
||||||
Log.Error($"Failed to remove {ToPrettyString(entity)} from users hand container when dropping. User: {ToPrettyString(uid)}. Hand: {hand.Name}.");
|
Log.Error($"Failed to remove {ToPrettyString(entity)} from users hand container when dropping. User: {ToPrettyString(ent)}. Hand: {handId}.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Dirty(uid, handsComp);
|
Dirty(ent);
|
||||||
|
|
||||||
if (doDropInteraction)
|
if (doDropInteraction)
|
||||||
_interactionSystem.DroppedInteraction(uid, entity);
|
_interactionSystem.DroppedInteraction(ent, entity.Value);
|
||||||
|
|
||||||
if (log)
|
if (log)
|
||||||
_adminLogger.Add(LogType.Drop, LogImpact.Low, $"{ToPrettyString(uid):user} dropped {ToPrettyString(entity):entity}");
|
_adminLogger.Add(LogType.Drop, LogImpact.Low, $"{ToPrettyString(ent):user} dropped {ToPrettyString(entity):entity}");
|
||||||
|
|
||||||
if (hand == handsComp.ActiveHand)
|
if (handId == ent.Comp.ActiveHandId)
|
||||||
RaiseLocalEvent(entity, new HandDeselectedEvent(uid));
|
RaiseLocalEvent(entity.Value, new HandDeselectedEvent(ent));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,20 +97,20 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
if (!_actionBlocker.CanInteract(session.AttachedEntity.Value, null))
|
if (!_actionBlocker.CanInteract(session.AttachedEntity.Value, null))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (component.ActiveHand == null || component.Hands.Count < 2)
|
if (component.ActiveHandId == null || component.Hands.Count < 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var currentIndex = component.SortedHands.IndexOf(component.ActiveHand.Name);
|
var currentIndex = component.SortedHands.IndexOf(component.ActiveHandId);
|
||||||
var newActiveIndex = (currentIndex + (reverse ? -1 : 1) + component.Hands.Count) % component.Hands.Count;
|
var newActiveIndex = (currentIndex + (reverse ? -1 : 1) + component.Hands.Count) % component.Hands.Count;
|
||||||
var nextHand = component.SortedHands[newActiveIndex];
|
var nextHand = component.SortedHands[newActiveIndex];
|
||||||
|
|
||||||
TrySetActiveHand(session.AttachedEntity.Value, nextHand, component);
|
TrySetActiveHand((session.AttachedEntity.Value, component), nextHand);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool DropPressed(ICommonSession? session, EntityCoordinates coords, EntityUid netEntity)
|
private bool DropPressed(ICommonSession? session, EntityCoordinates coords, EntityUid netEntity)
|
||||||
{
|
{
|
||||||
if (TryComp(session?.AttachedEntity, out HandsComponent? hands) && hands.ActiveHand != null)
|
if (TryComp(session?.AttachedEntity, out HandsComponent? hands) && hands.ActiveHandId != null)
|
||||||
TryDrop(session.AttachedEntity.Value, hands.ActiveHand, coords, handsComp: hands);
|
TryDrop((session.AttachedEntity.Value, hands), hands.ActiveHandId, coords);
|
||||||
|
|
||||||
// always send to server.
|
// always send to server.
|
||||||
return false;
|
return false;
|
||||||
@@ -122,14 +122,14 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
if (!Resolve(uid, ref handsComp, false))
|
if (!Resolve(uid, ref handsComp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Hand? hand;
|
var hand = handName;
|
||||||
if (handName == null || !handsComp.Hands.TryGetValue(handName, out hand))
|
if (!TryGetHand(uid, hand, out _))
|
||||||
hand = handsComp.ActiveHand;
|
hand = handsComp.ActiveHandId;
|
||||||
|
|
||||||
if (hand?.HeldEntity is not { } held)
|
if (!TryGetHeldItem((uid, handsComp), hand, out var held))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return _interactionSystem.InteractionActivate(uid, held);
|
return _interactionSystem.InteractionActivate(uid, held.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryInteractHandWithActiveHand(EntityUid uid, string handName, HandsComponent? handsComp = null)
|
public bool TryInteractHandWithActiveHand(EntityUid uid, string handName, HandsComponent? handsComp = null)
|
||||||
@@ -137,16 +137,13 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
if (!Resolve(uid, ref handsComp, false))
|
if (!Resolve(uid, ref handsComp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (handsComp.ActiveHandEntity == null)
|
if (!TryGetActiveItem((uid, handsComp), out var activeHeldItem))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!handsComp.Hands.TryGetValue(handName, out var hand))
|
if (!TryGetHeldItem((uid, handsComp), handName, out var held))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (hand.HeldEntity == null)
|
_interactionSystem.InteractUsing(uid, activeHeldItem.Value, held.Value, Transform(held.Value).Coordinates);
|
||||||
return false;
|
|
||||||
|
|
||||||
_interactionSystem.InteractUsing(uid, handsComp.ActiveHandEntity.Value, hand.HeldEntity.Value, Transform(hand.HeldEntity.Value).Coordinates);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,17 +152,16 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
if (!Resolve(uid, ref handsComp, false))
|
if (!Resolve(uid, ref handsComp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Hand? hand;
|
var hand = handName;
|
||||||
if (handName == null || !handsComp.Hands.TryGetValue(handName, out hand))
|
if (!TryGetHand(uid, hand, out _))
|
||||||
hand = handsComp.ActiveHand;
|
hand = handsComp.ActiveHandId;
|
||||||
|
|
||||||
if (hand?.HeldEntity is not { } held)
|
if (!TryGetHeldItem((uid, handsComp), hand, out var held))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (altInteract)
|
if (altInteract)
|
||||||
return _interactionSystem.AltInteract(uid, held);
|
return _interactionSystem.AltInteract(uid, held.Value);
|
||||||
else
|
return _interactionSystem.UseInHandInteraction(uid, held.Value);
|
||||||
return _interactionSystem.UseInHandInteraction(uid, held);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -176,22 +172,20 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
if (!Resolve(uid, ref handsComp))
|
if (!Resolve(uid, ref handsComp))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (handsComp.ActiveHand == null || !handsComp.ActiveHand.IsEmpty)
|
if (handsComp.ActiveHandId == null || !HandIsEmpty((uid, handsComp), handsComp.ActiveHandId))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!handsComp.Hands.TryGetValue(handName, out var hand))
|
if (!TryGetHeldItem((uid, handsComp), handName, out var entity))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!CanDropHeld(uid, hand, checkActionBlocker))
|
if (!CanDropHeld(uid, handName, checkActionBlocker))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var entity = hand.HeldEntity!.Value;
|
if (!CanPickupToHand(uid, entity.Value, handsComp.ActiveHandId, checkActionBlocker, handsComp))
|
||||||
|
|
||||||
if (!CanPickupToHand(uid, entity, handsComp.ActiveHand, checkActionBlocker, handsComp))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
DoDrop(uid, hand, false, handsComp, log:false);
|
DoDrop(uid, handName, false, log: false);
|
||||||
DoPickup(uid, handsComp.ActiveHand, entity, handsComp, log: false);
|
DoPickup(uid, handsComp.ActiveHandId, entity.Value, handsComp, log: false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,19 +194,19 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
if (args.Handled)
|
if (args.Handled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (component.ActiveHandEntity.HasValue)
|
if (TryGetActiveItem((uid, component), out var activeHeldItem))
|
||||||
{
|
{
|
||||||
// allow for the item to return a different entity, e.g. virtual items
|
// allow for the item to return a different entity, e.g. virtual items
|
||||||
RaiseLocalEvent(component.ActiveHandEntity.Value, ref args);
|
RaiseLocalEvent(activeHeldItem.Value, ref args);
|
||||||
}
|
}
|
||||||
|
|
||||||
args.Used ??= component.ActiveHandEntity;
|
args.Used ??= activeHeldItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Actually shows all items/clothing/etc.
|
//TODO: Actually shows all items/clothing/etc.
|
||||||
private void HandleExamined(EntityUid examinedUid, HandsComponent handsComp, ExaminedEvent args)
|
private void HandleExamined(EntityUid examinedUid, HandsComponent handsComp, ExaminedEvent args)
|
||||||
{
|
{
|
||||||
var heldItemNames = EnumerateHeld(examinedUid, handsComp)
|
var heldItemNames = EnumerateHeld((examinedUid, handsComp))
|
||||||
.Where(entity => !HasComp<VirtualItemComponent>(entity))
|
.Where(entity => !HasComp<VirtualItemComponent>(entity))
|
||||||
.Select(item => FormattedMessage.EscapeText(Identity.Name(item, EntityManager)))
|
.Select(item => FormattedMessage.EscapeText(Identity.Name(item, EntityManager)))
|
||||||
.Select(itemName => Loc.GetString("comp-hands-examine-wrapper", ("item", itemName)))
|
.Select(itemName => Loc.GetString("comp-hands-examine-wrapper", ("item", itemName)))
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
using Content.Shared.Clothing.Components;
|
using System.Diagnostics;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Item;
|
using Content.Shared.Item;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Physics;
|
using Robust.Shared.Physics;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Shared.Hands.EntitySystems;
|
namespace Content.Shared.Hands.EntitySystems;
|
||||||
|
|
||||||
public abstract partial class SharedHandsSystem : EntitySystem
|
public abstract partial class SharedHandsSystem
|
||||||
{
|
{
|
||||||
private void InitializePickup()
|
private void InitializePickup()
|
||||||
{
|
{
|
||||||
@@ -23,11 +23,11 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var didEquip = new DidEquipHandEvent(uid, args.Entity, hand);
|
var didEquip = new DidEquipHandEvent(uid, args.Entity, hand.Value);
|
||||||
RaiseLocalEvent(uid, didEquip, false);
|
RaiseLocalEvent(uid, didEquip);
|
||||||
|
|
||||||
var gotEquipped = new GotEquippedHandEvent(uid, args.Entity, hand);
|
var gotEquipped = new GotEquippedHandEvent(uid, args.Entity, hand.Value);
|
||||||
RaiseLocalEvent(args.Entity, gotEquipped, false);
|
RaiseLocalEvent(args.Entity, gotEquipped);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -35,32 +35,6 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const float MaxAnimationRange = 10;
|
public const float MaxAnimationRange = 10;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to pick up an entity to a specific hand. If no explicit hand is specified, defaults to using the currently active hand.
|
|
||||||
/// </summary>
|
|
||||||
public bool TryPickup(
|
|
||||||
EntityUid uid,
|
|
||||||
EntityUid entity,
|
|
||||||
string? handName = null,
|
|
||||||
bool checkActionBlocker = true,
|
|
||||||
bool animateUser = false,
|
|
||||||
bool animate = true,
|
|
||||||
HandsComponent? handsComp = null,
|
|
||||||
ItemComponent? item = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref handsComp, false))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var hand = handsComp.ActiveHand;
|
|
||||||
if (handName != null && !handsComp.Hands.TryGetValue(handName, out hand))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (hand == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return TryPickup(uid, entity, hand, checkActionBlocker, animate, handsComp, item);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to pick up an item into any empty hand. Prioritizes the currently active hand.
|
/// Attempts to pick up an item into any empty hand. Prioritizes the currently active hand.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -80,17 +54,21 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
if (!Resolve(uid, ref handsComp, false))
|
if (!Resolve(uid, ref handsComp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!TryGetEmptyHand(uid, out var hand, handsComp))
|
if (!TryGetEmptyHand((uid, handsComp), out var hand))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return TryPickup(uid, entity, hand, checkActionBlocker, animate, handsComp, item);
|
return TryPickup(uid, entity, hand, checkActionBlocker, animateUser, animate, handsComp, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to pick up an entity to a specific hand. If no explicit hand is specified, defaults to using the currently active hand.
|
||||||
|
/// </summary>
|
||||||
public bool TryPickup(
|
public bool TryPickup(
|
||||||
EntityUid uid,
|
EntityUid uid,
|
||||||
EntityUid entity,
|
EntityUid entity,
|
||||||
Hand hand,
|
string? handId = null,
|
||||||
bool checkActionBlocker = true,
|
bool checkActionBlocker = true,
|
||||||
|
bool animateUser = false,
|
||||||
bool animate = true,
|
bool animate = true,
|
||||||
HandsComponent? handsComp = null,
|
HandsComponent? handsComp = null,
|
||||||
ItemComponent? item = null)
|
ItemComponent? item = null)
|
||||||
@@ -98,10 +76,15 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
if (!Resolve(uid, ref handsComp, false))
|
if (!Resolve(uid, ref handsComp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
handId ??= handsComp.ActiveHandId;
|
||||||
|
|
||||||
|
if (handId == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!Resolve(entity, ref item, false))
|
if (!Resolve(entity, ref item, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!CanPickupToHand(uid, entity, hand, checkActionBlocker, handsComp, item))
|
if (!CanPickupToHand(uid, entity, handId, checkActionBlocker, handsComp, item))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (animate)
|
if (animate)
|
||||||
@@ -119,7 +102,7 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
_storage.PlayPickupAnimation(entity, initialPosition, xform.Coordinates, itemXform.LocalRotation, uid);
|
_storage.PlayPickupAnimation(entity, initialPosition, xform.Coordinates, itemXform.LocalRotation, uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DoPickup(uid, hand, entity, handsComp);
|
DoPickup(uid, handId, entity, handsComp);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -129,20 +112,20 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
/// By default it does check if it's possible to drop items.
|
/// By default it does check if it's possible to drop items.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TryForcePickup(
|
public bool TryForcePickup(
|
||||||
EntityUid uid,
|
Entity<HandsComponent?> ent,
|
||||||
EntityUid entity,
|
EntityUid entity,
|
||||||
Hand hand,
|
string hand,
|
||||||
bool checkActionBlocker = true,
|
bool checkActionBlocker = true,
|
||||||
bool animate = true,
|
bool animate = true,
|
||||||
HandsComponent? handsComp = null,
|
HandsComponent? handsComp = null,
|
||||||
ItemComponent? item = null)
|
ItemComponent? item = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handsComp, false))
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
TryDrop(uid, hand, checkActionBlocker: checkActionBlocker, handsComp: handsComp);
|
TryDrop(ent, hand, checkActionBlocker: checkActionBlocker);
|
||||||
|
|
||||||
return TryPickup(uid, entity, hand, checkActionBlocker, animate, handsComp, item);
|
return TryPickup(ent, entity, hand, checkActionBlocker, animate: animate, handsComp: handsComp, item: item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -157,9 +140,9 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
if (TryPickupAnyHand(uid, entity, checkActionBlocker: checkActionBlocker, handsComp: handsComp))
|
if (TryPickupAnyHand(uid, entity, checkActionBlocker: checkActionBlocker, handsComp: handsComp))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
foreach (var hand in handsComp.Hands.Values)
|
foreach (var hand in handsComp.Hands.Keys)
|
||||||
{
|
{
|
||||||
if (TryDrop(uid, hand, checkActionBlocker: checkActionBlocker, handsComp: handsComp) &&
|
if (TryDrop((uid, handsComp), hand, checkActionBlocker: checkActionBlocker) &&
|
||||||
TryPickup(uid, entity, hand, checkActionBlocker: checkActionBlocker, handsComp: handsComp))
|
TryPickup(uid, entity, hand, checkActionBlocker: checkActionBlocker, handsComp: handsComp))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -173,7 +156,7 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
if (!Resolve(uid, ref handsComp, false))
|
if (!Resolve(uid, ref handsComp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!TryGetEmptyHand(uid, out var hand, handsComp))
|
if (!TryGetEmptyHand((uid, handsComp), out var hand))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return CanPickupToHand(uid, entity, hand, checkActionBlocker, handsComp, item);
|
return CanPickupToHand(uid, entity, hand, checkActionBlocker, handsComp, item);
|
||||||
@@ -182,13 +165,15 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks whether a given item will fit into a specific user's hand. Unless otherwise specified, this will also check the general CanPickup action blocker.
|
/// Checks whether a given item will fit into a specific user's hand. Unless otherwise specified, this will also check the general CanPickup action blocker.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool CanPickupToHand(EntityUid uid, EntityUid entity, Hand hand, bool checkActionBlocker = true, HandsComponent? handsComp = null, ItemComponent? item = null)
|
public bool CanPickupToHand(EntityUid uid, EntityUid entity, string handId, bool checkActionBlocker = true, HandsComponent? handsComp = null, ItemComponent? item = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handsComp, false))
|
if (!Resolve(uid, ref handsComp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var handContainer = hand.Container;
|
if (!ContainerSystem.TryGetContainer(uid, handId, out var handContainer))
|
||||||
if (handContainer == null || handContainer.ContainedEntity != null)
|
return false;
|
||||||
|
|
||||||
|
if (handContainer.ContainedEntities.FirstOrNull() != null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!Resolve(entity, ref item, false))
|
if (!Resolve(entity, ref item, false))
|
||||||
@@ -231,8 +216,7 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
if (uid == null
|
if (uid == null
|
||||||
|| !Resolve(uid.Value, ref handsComp, false)
|
|| !Resolve(uid.Value, ref handsComp, false)
|
||||||
|| !TryGetEmptyHand(uid.Value, out var hand, handsComp)
|
|| !TryPickupAnyHand(uid.Value, entity, checkActionBlocker, animateUser, animate, handsComp, item))
|
||||||
|| !TryPickup(uid.Value, entity, hand, checkActionBlocker, animate, handsComp, item))
|
|
||||||
{
|
{
|
||||||
// TODO make this check upwards for any container, and parent to that.
|
// TODO make this check upwards for any container, and parent to that.
|
||||||
// Currently this just checks the direct parent, so items can still teleport through containers.
|
// Currently this just checks the direct parent, so items can still teleport through containers.
|
||||||
@@ -248,18 +232,20 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Puts an entity into the player's hand, assumes that the insertion is allowed. In general, you should not be calling this function directly.
|
/// Puts an entity into the player's hand, assumes that the insertion is allowed. In general, you should not be calling this function directly.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void DoPickup(EntityUid uid, Hand hand, EntityUid entity, HandsComponent? hands = null, bool log = true)
|
public virtual void DoPickup(EntityUid uid, string hand, EntityUid entity, HandsComponent? hands = null, bool log = true)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref hands))
|
if (!Resolve(uid, ref hands))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var handContainer = hand.Container;
|
if (!ContainerSystem.TryGetContainer(uid, hand, out var handContainer))
|
||||||
if (handContainer == null || handContainer.ContainedEntity != null)
|
return;
|
||||||
|
|
||||||
|
if (handContainer.ContainedEntities.FirstOrNull() != null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!ContainerSystem.Insert(entity, handContainer))
|
if (!ContainerSystem.Insert(entity, handContainer))
|
||||||
{
|
{
|
||||||
Log.Error($"Failed to insert {ToPrettyString(entity)} into users hand container when picking up. User: {ToPrettyString(uid)}. Hand: {hand.Name}.");
|
Log.Error($"Failed to insert {ToPrettyString(entity)} into users hand container when picking up. User: {ToPrettyString(uid)}. Hand: {hand}.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +256,7 @@ public abstract partial class SharedHandsSystem : EntitySystem
|
|||||||
|
|
||||||
Dirty(uid, hands);
|
Dirty(uid, hands);
|
||||||
|
|
||||||
if (hand == hands.ActiveHand)
|
if (hand == hands.ActiveHandId)
|
||||||
RaiseLocalEvent(entity, new HandSelectedEvent(uid), false);
|
RaiseLocalEvent(entity, new HandSelectedEvent(uid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public abstract partial class SharedHandsSystem
|
|||||||
{
|
{
|
||||||
var ev = new HeldRelayedEvent<T>(args);
|
var ev = new HeldRelayedEvent<T>(args);
|
||||||
|
|
||||||
foreach (var held in EnumerateHeld(entity, entity.Comp))
|
foreach (var held in EnumerateHeld(entity.AsNullable()))
|
||||||
{
|
{
|
||||||
RaiseLocalEvent(held, ref ev);
|
RaiseLocalEvent(held, ref ev);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using Content.Shared.Inventory.VirtualItem;
|
|||||||
using Content.Shared.Storage.EntitySystems;
|
using Content.Shared.Storage.EntitySystems;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.Input.Binding;
|
using Robust.Shared.Input.Binding;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Shared.Hands.EntitySystems;
|
namespace Content.Shared.Hands.EntitySystems;
|
||||||
|
|
||||||
@@ -23,6 +24,8 @@ public abstract partial class SharedHandsSystem
|
|||||||
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
|
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
|
||||||
[Dependency] private readonly SharedVirtualItemSystem _virtualSystem = default!;
|
[Dependency] private readonly SharedVirtualItemSystem _virtualSystem = default!;
|
||||||
|
|
||||||
|
public event Action<Entity<HandsComponent>, string, HandLocation>? OnPlayerAddHand;
|
||||||
|
public event Action<Entity<HandsComponent>, string>? OnPlayerRemoveHand;
|
||||||
protected event Action<Entity<HandsComponent>?>? OnHandSetActive;
|
protected event Action<Entity<HandsComponent>?>? OnHandSetActive;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
@@ -33,6 +36,9 @@ public abstract partial class SharedHandsSystem
|
|||||||
InitializeDrop();
|
InitializeDrop();
|
||||||
InitializePickup();
|
InitializePickup();
|
||||||
InitializeRelay();
|
InitializeRelay();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<HandsComponent, ComponentInit>(OnInit);
|
||||||
|
SubscribeLocalEvent<HandsComponent, MapInitEvent>(OnMapInit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown()
|
public override void Shutdown()
|
||||||
@@ -41,71 +47,94 @@ public abstract partial class SharedHandsSystem
|
|||||||
CommandBinds.Unregister<SharedHandsSystem>();
|
CommandBinds.Unregister<SharedHandsSystem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void AddHand(EntityUid uid, string handName, HandLocation handLocation, HandsComponent? handsComp = null)
|
private void OnInit(Entity<HandsComponent> ent, ref ComponentInit args)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handsComp, false))
|
var container = EnsureComp<ContainerManagerComponent>(ent);
|
||||||
return;
|
foreach (var id in ent.Comp.Hands.Keys)
|
||||||
|
{
|
||||||
if (handsComp.Hands.ContainsKey(handName))
|
ContainerSystem.EnsureContainer<ContainerSlot>(ent, id, container);
|
||||||
return;
|
}
|
||||||
|
|
||||||
var container = ContainerSystem.EnsureContainer<ContainerSlot>(uid, handName);
|
|
||||||
container.OccludesLight = false;
|
|
||||||
|
|
||||||
var newHand = new Hand(handName, handLocation, container);
|
|
||||||
handsComp.Hands.Add(handName, newHand);
|
|
||||||
handsComp.SortedHands.Add(handName);
|
|
||||||
|
|
||||||
if (handsComp.ActiveHand == null)
|
|
||||||
SetActiveHand(uid, newHand, handsComp);
|
|
||||||
|
|
||||||
RaiseLocalEvent(uid, new HandCountChangedEvent(uid));
|
|
||||||
Dirty(uid, handsComp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void RemoveHand(EntityUid uid, string handName, HandsComponent? handsComp = null)
|
private void OnMapInit(Entity<HandsComponent> ent, ref MapInitEvent args)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handsComp, false))
|
if (ent.Comp.ActiveHandId == null)
|
||||||
|
SetActiveHand(ent.AsNullable(), ent.Comp.SortedHands.FirstOrDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a hand with the given container id and supplied location to the specified entity.
|
||||||
|
/// </summary>
|
||||||
|
public void AddHand(Entity<HandsComponent?> ent, string handName, HandLocation handLocation)
|
||||||
|
{
|
||||||
|
AddHand(ent, handName, new Hand(handLocation));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a hand with the given container id and supplied hand definition to the given entity.
|
||||||
|
/// </summary>
|
||||||
|
public void AddHand(Entity<HandsComponent?> ent, string handName, Hand hand)
|
||||||
|
{
|
||||||
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!handsComp.Hands.Remove(handName, out var hand))
|
if (ent.Comp.Hands.ContainsKey(handName))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
handsComp.SortedHands.Remove(hand.Name);
|
var container = ContainerSystem.EnsureContainer<ContainerSlot>(ent, handName);
|
||||||
TryDrop(uid, hand, null, false, true, handsComp);
|
container.OccludesLight = false;
|
||||||
if (hand.Container != null)
|
|
||||||
ContainerSystem.ShutdownContainer(hand.Container);
|
|
||||||
|
|
||||||
if (handsComp.ActiveHand == hand)
|
ent.Comp.Hands.Add(handName, hand);
|
||||||
TrySetActiveHand(uid, handsComp.SortedHands.FirstOrDefault(), handsComp);
|
ent.Comp.SortedHands.Add(handName);
|
||||||
|
Dirty(ent);
|
||||||
|
|
||||||
RaiseLocalEvent(uid, new HandCountChangedEvent(uid));
|
OnPlayerAddHand?.Invoke((ent, ent.Comp), handName, hand.Location);
|
||||||
Dirty(uid, handsComp);
|
|
||||||
|
if (ent.Comp.ActiveHandId == null)
|
||||||
|
SetActiveHand(ent, handName);
|
||||||
|
|
||||||
|
RaiseLocalEvent(ent, new HandCountChangedEvent(ent));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the specified hand from the specified entity
|
||||||
|
/// </summary>
|
||||||
|
public virtual void RemoveHand(Entity<HandsComponent?> ent, string handName)
|
||||||
|
{
|
||||||
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
OnPlayerRemoveHand?.Invoke((ent, ent.Comp), handName);
|
||||||
|
|
||||||
|
TryDrop(ent, handName, null, false);
|
||||||
|
|
||||||
|
if (!ent.Comp.Hands.Remove(handName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ContainerSystem.TryGetContainer(ent, handName, out var container))
|
||||||
|
ContainerSystem.ShutdownContainer(container);
|
||||||
|
|
||||||
|
ent.Comp.SortedHands.Remove(handName);
|
||||||
|
if (ent.Comp.ActiveHandId == handName)
|
||||||
|
TrySetActiveHand(ent, ent.Comp.SortedHands.FirstOrDefault());
|
||||||
|
|
||||||
|
RaiseLocalEvent(ent, new HandCountChangedEvent(ent));
|
||||||
|
Dirty(ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets rid of all the entity's hands.
|
/// Gets rid of all the entity's hands.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="uid"></param>
|
public void RemoveHands(Entity<HandsComponent?> ent)
|
||||||
/// <param name="handsComp"></param>
|
|
||||||
public void RemoveHands(EntityUid uid, HandsComponent? handsComp = null)
|
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handsComp))
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
RemoveHands(uid, EnumerateHands(uid), handsComp);
|
var handIds = new List<string>(ent.Comp.Hands.Keys);
|
||||||
|
foreach (var handId in handIds)
|
||||||
|
{
|
||||||
|
RemoveHand(ent, handId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveHands(EntityUid uid, IEnumerable<Hand> hands, HandsComponent handsComp)
|
|
||||||
{
|
|
||||||
if (!hands.Any())
|
|
||||||
return;
|
|
||||||
|
|
||||||
var hand = hands.First();
|
|
||||||
RemoveHand(uid, hand.Name, handsComp);
|
|
||||||
|
|
||||||
// Repeats it for any additional hands.
|
|
||||||
RemoveHands(uid, hands, handsComp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleSetHand(RequestSetHandEvent msg, EntitySessionEventArgs eventArgs)
|
private void HandleSetHand(RequestSetHandEvent msg, EntitySessionEventArgs eventArgs)
|
||||||
@@ -119,15 +148,15 @@ public abstract partial class SharedHandsSystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get any empty hand. Prioritizes the currently active hand.
|
/// Get any empty hand. Prioritizes the currently active hand.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TryGetEmptyHand(EntityUid uid, [NotNullWhen(true)] out Hand? emptyHand, HandsComponent? handComp = null)
|
public bool TryGetEmptyHand(Entity<HandsComponent?> ent, [NotNullWhen(true)] out string? emptyHand)
|
||||||
{
|
{
|
||||||
emptyHand = null;
|
emptyHand = null;
|
||||||
if (!Resolve(uid, ref handComp, false))
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
foreach (var hand in EnumerateHands(uid, handComp))
|
foreach (var hand in EnumerateHands(ent))
|
||||||
{
|
{
|
||||||
if (hand.IsEmpty)
|
if (HandIsEmpty(ent, hand))
|
||||||
{
|
{
|
||||||
emptyHand = hand;
|
emptyHand = hand;
|
||||||
return true;
|
return true;
|
||||||
@@ -137,28 +166,20 @@ public abstract partial class SharedHandsSystem
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetActiveHand(Entity<HandsComponent?> entity, [NotNullWhen(true)] out Hand? hand)
|
/// <summary>
|
||||||
{
|
/// Attempts to retrieve the item held in the entity's active hand.
|
||||||
if (!Resolve(entity, ref entity.Comp, false))
|
/// </summary>
|
||||||
{
|
|
||||||
hand = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
hand = entity.Comp.ActiveHand;
|
|
||||||
return hand != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetActiveItem(Entity<HandsComponent?> entity, [NotNullWhen(true)] out EntityUid? item)
|
public bool TryGetActiveItem(Entity<HandsComponent?> entity, [NotNullWhen(true)] out EntityUid? item)
|
||||||
{
|
|
||||||
if (!TryGetActiveHand(entity, out var hand))
|
|
||||||
{
|
{
|
||||||
item = null;
|
item = null;
|
||||||
|
if (!Resolve(entity, ref entity.Comp, false))
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
item = hand.HeldEntity;
|
if (!TryGetHeldItem(entity, entity.Comp.ActiveHandId, out var held))
|
||||||
return item != null;
|
return false;
|
||||||
|
|
||||||
|
item = held;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -174,55 +195,73 @@ public abstract partial class SharedHandsSystem
|
|||||||
return item.Value;
|
return item.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Hand? GetActiveHand(Entity<HandsComponent?> entity)
|
/// <summary>
|
||||||
|
/// Gets the current active hand's Id for the specified entity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entity"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public string? GetActiveHand(Entity<HandsComponent?> entity)
|
||||||
{
|
{
|
||||||
if (!Resolve(entity, ref entity.Comp))
|
if (!Resolve(entity, ref entity.Comp, false))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return entity.Comp.ActiveHand;
|
return entity.Comp.ActiveHandId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current active hand's held entity for the specified entity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entity"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public EntityUid? GetActiveItem(Entity<HandsComponent?> entity)
|
public EntityUid? GetActiveItem(Entity<HandsComponent?> entity)
|
||||||
{
|
{
|
||||||
return GetActiveHand(entity)?.HeldEntity;
|
if (!Resolve(entity, ref entity.Comp, false))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return GetHeldItem(entity, entity.Comp.ActiveHandId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ActiveHandIsEmpty(Entity<HandsComponent?> entity)
|
||||||
|
{
|
||||||
|
return GetActiveItem(entity) == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enumerate over hands, starting with the currently active hand.
|
/// Enumerate over hands, starting with the currently active hand.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<Hand> EnumerateHands(EntityUid uid, HandsComponent? handsComp = null)
|
public IEnumerable<string> EnumerateHands(Entity<HandsComponent?> ent)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handsComp, false))
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
if (handsComp.ActiveHand != null)
|
if (ent.Comp.ActiveHandId != null)
|
||||||
yield return handsComp.ActiveHand;
|
yield return ent.Comp.ActiveHandId;
|
||||||
|
|
||||||
foreach (var name in handsComp.SortedHands)
|
foreach (var name in ent.Comp.SortedHands)
|
||||||
{
|
{
|
||||||
if (name != handsComp.ActiveHand?.Name)
|
if (name != ent.Comp.ActiveHandId)
|
||||||
yield return handsComp.Hands[name];
|
yield return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enumerate over held items, starting with the item in the currently active hand (if there is one).
|
/// Enumerate over held items, starting with the item in the currently active hand (if there is one).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<EntityUid> EnumerateHeld(EntityUid uid, HandsComponent? handsComp = null)
|
public IEnumerable<EntityUid> EnumerateHeld(Entity<HandsComponent?> ent)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handsComp, false))
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
if (handsComp.ActiveHandEntity != null)
|
if (TryGetActiveItem(ent, out var activeHeld))
|
||||||
yield return handsComp.ActiveHandEntity.Value;
|
yield return activeHeld.Value;
|
||||||
|
|
||||||
foreach (var name in handsComp.SortedHands)
|
foreach (var name in ent.Comp.SortedHands)
|
||||||
{
|
{
|
||||||
if (name == handsComp.ActiveHand?.Name)
|
if (name == ent.Comp.ActiveHandId)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (handsComp.Hands[name].HeldEntity is { } held)
|
if (TryGetHeldItem(ent, name, out var held))
|
||||||
yield return held;
|
yield return held.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,18 +270,17 @@ public abstract partial class SharedHandsSystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>True if the active hand was set to a NEW value. Setting it to the same value returns false and does
|
/// <returns>True if the active hand was set to a NEW value. Setting it to the same value returns false and does
|
||||||
/// not trigger interactions.</returns>
|
/// not trigger interactions.</returns>
|
||||||
public virtual bool TrySetActiveHand(EntityUid uid, string? name, HandsComponent? handComp = null)
|
public bool TrySetActiveHand(Entity<HandsComponent?> ent, string? name)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handComp))
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (name == handComp.ActiveHand?.Name)
|
if (name == ent.Comp.ActiveHandId)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Hand? hand = null;
|
if (name != null && !ent.Comp.Hands.ContainsKey(name))
|
||||||
if (name != null && !handComp.Hands.TryGetValue(name, out hand))
|
|
||||||
return false;
|
return false;
|
||||||
return SetActiveHand(uid, hand, handComp);
|
return SetActiveHand(ent, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -250,50 +288,50 @@ public abstract partial class SharedHandsSystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>True if the active hand was set to a NEW value. Setting it to the same value returns false and does
|
/// <returns>True if the active hand was set to a NEW value. Setting it to the same value returns false and does
|
||||||
/// not trigger interactions.</returns>
|
/// not trigger interactions.</returns>
|
||||||
public bool SetActiveHand(EntityUid uid, Hand? hand, HandsComponent? handComp = null)
|
public bool SetActiveHand(Entity<HandsComponent?> ent, string? handId)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref handComp))
|
if (!Resolve(ent, ref ent.Comp))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (hand == handComp.ActiveHand)
|
if (handId == ent.Comp.ActiveHandId)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (handComp.ActiveHand?.HeldEntity is { } held)
|
if (TryGetHeldItem(ent, handId, out var oldHeld))
|
||||||
RaiseLocalEvent(held, new HandDeselectedEvent(uid));
|
RaiseLocalEvent(oldHeld.Value, new HandDeselectedEvent(ent));
|
||||||
|
|
||||||
if (hand == null)
|
if (handId == null)
|
||||||
{
|
{
|
||||||
handComp.ActiveHand = null;
|
ent.Comp.ActiveHandId = null;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
handComp.ActiveHand = hand;
|
ent.Comp.ActiveHandId = handId;
|
||||||
OnHandSetActive?.Invoke((uid, handComp));
|
OnHandSetActive?.Invoke((ent, ent.Comp));
|
||||||
|
|
||||||
if (hand.HeldEntity != null)
|
if (TryGetHeldItem(ent, handId, out var newHeld))
|
||||||
RaiseLocalEvent(hand.HeldEntity.Value, new HandSelectedEvent(uid));
|
RaiseLocalEvent(newHeld.Value, new HandSelectedEvent(ent));
|
||||||
|
|
||||||
Dirty(uid, handComp);
|
Dirty(ent);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsHolding(Entity<HandsComponent?> entity, [NotNullWhen(true)] EntityUid? item)
|
public bool IsHolding(Entity<HandsComponent?> entity, [NotNullWhen(true)] EntityUid? item)
|
||||||
{
|
{
|
||||||
return IsHolding(entity, item, out _, entity);
|
return IsHolding(entity, item, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsHolding(EntityUid uid, [NotNullWhen(true)] EntityUid? entity, [NotNullWhen(true)] out Hand? inHand, HandsComponent? handsComp = null)
|
public bool IsHolding(Entity<HandsComponent?> ent, [NotNullWhen(true)] EntityUid? entity, [NotNullWhen(true)] out string? inHand)
|
||||||
{
|
{
|
||||||
inHand = null;
|
inHand = null;
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!Resolve(uid, ref handsComp, false))
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
foreach (var hand in handsComp.Hands.Values)
|
foreach (var hand in ent.Comp.Hands.Keys)
|
||||||
{
|
{
|
||||||
if (hand.HeldEntity == entity)
|
if (GetHeldItem(ent, hand) == entity)
|
||||||
{
|
{
|
||||||
inHand = hand;
|
inHand = hand;
|
||||||
return true;
|
return true;
|
||||||
@@ -303,23 +341,89 @@ public abstract partial class SharedHandsSystem
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetHand(EntityUid handsUid, string handId, [NotNullWhen(true)] out Hand? hand,
|
/// <summary>
|
||||||
HandsComponent? hands = null)
|
/// Attempts to retrieve the associated hand struct corresponding to a hand ID on a given entity.
|
||||||
|
/// </summary>
|
||||||
|
public bool TryGetHand(Entity<HandsComponent?> ent, [NotNullWhen(true)] string? handId, [NotNullWhen(true)] out Hand? hand)
|
||||||
{
|
{
|
||||||
hand = null;
|
hand = null;
|
||||||
|
|
||||||
if (!Resolve(handsUid, ref hands))
|
if (handId == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return hands.Hands.TryGetValue(handId, out hand);
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ent.Comp.Hands.TryGetValue(handId, out var handsHand))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
hand = handsHand;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the item currently held in the entity's specified hand. Returns null if no hands are present or there is no item.
|
||||||
|
/// </summary>
|
||||||
|
public EntityUid? GetHeldItem(Entity<HandsComponent?> ent, string? handId)
|
||||||
|
{
|
||||||
|
TryGetHeldItem(ent, handId, out var held);
|
||||||
|
return held;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the item currently held in the entity's specified hand. Returns false if no hands are present or there is no item.
|
||||||
|
/// </summary>
|
||||||
|
public bool TryGetHeldItem(Entity<HandsComponent?> ent, string? handId, [NotNullWhen(true)] out EntityUid? held)
|
||||||
|
{
|
||||||
|
held = null;
|
||||||
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Sanity check to make sure this is actually a hand.
|
||||||
|
if (handId == null || !ent.Comp.Hands.ContainsKey(handId))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ContainerSystem.TryGetContainer(ent, handId, out var container))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
held = container.ContainedEntities.FirstOrNull();
|
||||||
|
return held != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HandIsEmpty(Entity<HandsComponent?> ent, string handId)
|
||||||
|
{
|
||||||
|
return GetHeldItem(ent, handId) == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHandCount(Entity<HandsComponent?> ent)
|
||||||
|
{
|
||||||
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return ent.Comp.Hands.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CountFreeHands(Entity<HandsComponent?> ent)
|
||||||
|
{
|
||||||
|
if (!Resolve(ent, ref ent.Comp, false))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
var free = 0;
|
||||||
|
foreach (var name in ent.Comp.Hands.Keys)
|
||||||
|
{
|
||||||
|
if (HandIsEmpty(ent, name))
|
||||||
|
free++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return free;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int CountFreeableHands(Entity<HandsComponent> hands)
|
public int CountFreeableHands(Entity<HandsComponent> hands)
|
||||||
{
|
{
|
||||||
var freeable = 0;
|
var freeable = 0;
|
||||||
foreach (var hand in hands.Comp.Hands.Values)
|
foreach (var name in hands.Comp.Hands.Keys)
|
||||||
{
|
{
|
||||||
if (hand.IsEmpty || CanDropHeld(hands, hand))
|
if (HandIsEmpty(hands.AsNullable(), name) || CanDropHeld(hands, name))
|
||||||
freeable++;
|
freeable++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using Content.Shared.Database;
|
|||||||
using Content.Shared.Ghost;
|
using Content.Shared.Ghost;
|
||||||
using Content.Shared.Hands;
|
using Content.Shared.Hands;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
using Content.Shared.Interaction.Components;
|
using Content.Shared.Interaction.Components;
|
||||||
using Content.Shared.Interaction.Events;
|
using Content.Shared.Interaction.Events;
|
||||||
@@ -58,6 +59,7 @@ namespace Content.Shared.Interaction
|
|||||||
[Dependency] private readonly ISharedChatManager _chat = default!;
|
[Dependency] private readonly ISharedChatManager _chat = default!;
|
||||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||||
|
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||||
[Dependency] private readonly PullingSystem _pullSystem = default!;
|
[Dependency] private readonly PullingSystem _pullSystem = default!;
|
||||||
[Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!;
|
[Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!;
|
||||||
@@ -343,7 +345,7 @@ namespace Content.Shared.Interaction
|
|||||||
public bool CombatModeCanHandInteract(EntityUid user, EntityUid? target)
|
public bool CombatModeCanHandInteract(EntityUid user, EntityUid? target)
|
||||||
{
|
{
|
||||||
// Always allow attack in these cases
|
// Always allow attack in these cases
|
||||||
if (target == null || !_handsQuery.TryComp(user, out var hands) || hands.ActiveHand?.HeldEntity is not null)
|
if (target == null || !_handsQuery.TryComp(user, out var hands) || _hands.GetActiveItem((user, hands)) is not null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Only eat input if:
|
// Only eat input if:
|
||||||
|
|||||||
@@ -64,10 +64,10 @@ public sealed class SmartEquipSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// early out if we don't have any hands or a valid inventory slot
|
// early out if we don't have any hands or a valid inventory slot
|
||||||
if (!TryComp<HandsComponent>(uid, out var hands) || hands.ActiveHand == null)
|
if (!TryComp<HandsComponent>(uid, out var hands) || hands.ActiveHandId == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var handItem = hands.ActiveHand.HeldEntity;
|
var handItem = _hands.GetActiveItem((uid, hands));
|
||||||
|
|
||||||
// can the user interact, and is the item interactable? e.g. virtual items
|
// can the user interact, and is the item interactable? e.g. virtual items
|
||||||
if (!_actionBlocker.CanInteract(uid, handItem))
|
if (!_actionBlocker.CanInteract(uid, handItem))
|
||||||
@@ -80,7 +80,7 @@ public sealed class SmartEquipSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// early out if we have an item and cant drop it at all
|
// early out if we have an item and cant drop it at all
|
||||||
if (handItem != null && !_hands.CanDropHeld(uid, hands.ActiveHand))
|
if (hands.ActiveHandId != null && !_hands.CanDropHeld(uid, hands.ActiveHandId))
|
||||||
{
|
{
|
||||||
_popup.PopupClient(Loc.GetString("smart-equip-cant-drop"), uid, uid);
|
_popup.PopupClient(Loc.GetString("smart-equip-cant-drop"), uid, uid);
|
||||||
return;
|
return;
|
||||||
@@ -121,7 +121,7 @@ public sealed class SmartEquipSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_hands.TryDrop(uid, hands.ActiveHand, handsComp: hands);
|
_hands.TryDrop((uid, hands), hands.ActiveHandId!);
|
||||||
_inventory.TryEquip(uid, handItem.Value, equipmentSlot, predicted: true, checkDoafter:true);
|
_inventory.TryEquip(uid, handItem.Value, equipmentSlot, predicted: true, checkDoafter:true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -149,7 +149,7 @@ public sealed class SmartEquipSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_hands.TryDrop(uid, hands.ActiveHand, handsComp: hands);
|
_hands.TryDrop((uid, hands), hands.ActiveHandId!);
|
||||||
_storage.Insert(slotItem, handItem.Value, out var stacked, out _);
|
_storage.Insert(slotItem, handItem.Value, out var stacked, out _);
|
||||||
|
|
||||||
// if the hand item stacked with the things in inventory, but there's no more space left for the rest
|
// if the hand item stacked with the things in inventory, but there's no more space left for the rest
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public abstract partial class InventorySystem
|
|||||||
if (!TryComp(actor, out InventoryComponent? inventory) || !TryComp<HandsComponent>(actor, out var hands))
|
if (!TryComp(actor, out InventoryComponent? inventory) || !TryComp<HandsComponent>(actor, out var hands))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var held = hands.ActiveHandEntity;
|
var held = _handsSystem.GetActiveItem((actor, hands));
|
||||||
TryGetSlotEntity(actor, ev.Slot, out var itemUid, inventory);
|
TryGetSlotEntity(actor, ev.Slot, out var itemUid, inventory);
|
||||||
|
|
||||||
// attempt to perform some interaction
|
// attempt to perform some interaction
|
||||||
@@ -115,7 +115,7 @@ public abstract partial class InventorySystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_handsSystem.CanDropHeld(actor, hands.ActiveHand!, checkActionBlocker: false))
|
if (!_handsSystem.CanDropHeld(actor, hands.ActiveHandId!, checkActionBlocker: false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
RaiseLocalEvent(held.Value, new HandDeselectedEvent(actor));
|
RaiseLocalEvent(held.Value, new HandDeselectedEvent(actor));
|
||||||
|
|||||||
@@ -16,12 +16,9 @@ public partial class InventorySystem
|
|||||||
{
|
{
|
||||||
if (Resolve(user.Owner, ref user.Comp1, false))
|
if (Resolve(user.Owner, ref user.Comp1, false))
|
||||||
{
|
{
|
||||||
foreach (var hand in user.Comp1.Hands.Values)
|
foreach (var held in _handsSystem.EnumerateHeld(user))
|
||||||
{
|
{
|
||||||
if (hand.HeldEntity == null)
|
yield return held;
|
||||||
continue;
|
|
||||||
|
|
||||||
yield return hand.HeldEntity.Value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Content.Shared.Hands;
|
using Content.Shared.Hands;
|
||||||
using Content.Shared.Hands.Components;
|
|
||||||
using Content.Shared.Hands.EntitySystems;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Interaction.Events;
|
using Content.Shared.Interaction.Events;
|
||||||
@@ -8,7 +7,6 @@ using Content.Shared.Inventory.Events;
|
|||||||
using Content.Shared.Item;
|
using Content.Shared.Item;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.Network;
|
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Shared.Inventory.VirtualItem;
|
namespace Content.Shared.Inventory.VirtualItem;
|
||||||
@@ -88,9 +86,9 @@ public abstract class SharedVirtualItemSystem : EntitySystem
|
|||||||
|
|
||||||
// if the user is holding the real item the virtual item points to,
|
// if the user is holding the real item the virtual item points to,
|
||||||
// we allow them to use it in the interaction
|
// we allow them to use it in the interaction
|
||||||
foreach (var hand in _handsSystem.EnumerateHands(args.User))
|
foreach (var held in _handsSystem.EnumerateHeld(args.User))
|
||||||
{
|
{
|
||||||
if (hand.HeldEntity == ent.Comp.BlockingEntity)
|
if (held == ent.Comp.BlockingEntity)
|
||||||
{
|
{
|
||||||
args.Used = ent.Comp.BlockingEntity;
|
args.Used = ent.Comp.BlockingEntity;
|
||||||
return;
|
return;
|
||||||
@@ -112,7 +110,7 @@ public abstract class SharedVirtualItemSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="TrySpawnVirtualItemInHand(Robust.Shared.GameObjects.EntityUid,Robust.Shared.GameObjects.EntityUid,bool)"/>
|
/// <inheritdoc cref="TrySpawnVirtualItemInHand(Robust.Shared.GameObjects.EntityUid,Robust.Shared.GameObjects.EntityUid,bool)"/>
|
||||||
public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem, bool dropOthers = false, Hand? empty = null)
|
public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem, bool dropOthers = false, string? empty = null)
|
||||||
{
|
{
|
||||||
virtualItem = null;
|
virtualItem = null;
|
||||||
if (empty == null && !_handsSystem.TryGetEmptyHand(user, out empty))
|
if (empty == null && !_handsSystem.TryGetEmptyHand(user, out empty))
|
||||||
@@ -122,7 +120,7 @@ public abstract class SharedVirtualItemSystem : EntitySystem
|
|||||||
|
|
||||||
foreach (var hand in _handsSystem.EnumerateHands(user))
|
foreach (var hand in _handsSystem.EnumerateHands(user))
|
||||||
{
|
{
|
||||||
if (hand.HeldEntity is not { } held)
|
if (!_handsSystem.TryGetHeldItem(user, hand, out var held))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (held == blockingEnt)
|
if (held == blockingEnt)
|
||||||
@@ -155,11 +153,11 @@ public abstract class SharedVirtualItemSystem : EntitySystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void DeleteInHandsMatching(EntityUid user, EntityUid matching)
|
public void DeleteInHandsMatching(EntityUid user, EntityUid matching)
|
||||||
{
|
{
|
||||||
foreach (var hand in _handsSystem.EnumerateHands(user))
|
foreach (var held in _handsSystem.EnumerateHeld(user))
|
||||||
{
|
{
|
||||||
if (TryComp(hand.HeldEntity, out VirtualItemComponent? virt) && virt.BlockingEntity == matching)
|
if (TryComp(held, out VirtualItemComponent? virt) && virt.BlockingEntity == matching)
|
||||||
{
|
{
|
||||||
DeleteVirtualItem((hand.HeldEntity.Value, virt), user);
|
DeleteVirtualItem((held, virt), user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Content.Shared.Hands;
|
using Content.Shared.Hands;
|
||||||
using Content.Shared.Hands.Components;
|
|
||||||
using Content.Shared.Hands.EntitySystems;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.Inventory.VirtualItem;
|
using Content.Shared.Inventory.VirtualItem;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
@@ -38,7 +37,7 @@ public sealed class MultiHandedItemSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnAttemptPickup(Entity<MultiHandedItemComponent> ent, ref GettingPickedUpAttemptEvent args)
|
private void OnAttemptPickup(Entity<MultiHandedItemComponent> ent, ref GettingPickedUpAttemptEvent args)
|
||||||
{
|
{
|
||||||
if (TryComp<HandsComponent>(args.User, out var hands) && hands.CountFreeHands() >= ent.Comp.HandsNeeded)
|
if (_hands.CountFreeHands(ent.Owner) >= ent.Comp.HandsNeeded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
args.Cancel();
|
args.Cancel();
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ public abstract class SharedItemSystem : EntitySystem
|
|||||||
if (args.Handled)
|
if (args.Handled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
args.Handled = _handsSystem.TryPickup(args.User, uid, animateUser: false);
|
args.Handled = _handsSystem.TryPickup(args.User, uid, null, animateUser: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddPickupVerb(EntityUid uid, ItemComponent component, GetVerbsEvent<InteractionVerb> args)
|
private void AddPickupVerb(EntityUid uid, ItemComponent component, GetVerbsEvent<InteractionVerb> args)
|
||||||
|
|||||||
@@ -434,7 +434,7 @@ public abstract class SharedMagicSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
EntityUid? wand = null;
|
EntityUid? wand = null;
|
||||||
foreach (var item in _hands.EnumerateHeld(ev.Performer, handsComp))
|
foreach (var item in _hands.EnumerateHeld((ev.Performer, handsComp)))
|
||||||
{
|
{
|
||||||
if (!_tag.HasTag(item, ev.WandTag))
|
if (!_tag.HasTag(item, ev.WandTag))
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -109,16 +109,12 @@ public sealed class PullingSystem : EntitySystem
|
|||||||
|
|
||||||
// Try find hand that is doing this pull.
|
// Try find hand that is doing this pull.
|
||||||
// and clear it.
|
// and clear it.
|
||||||
foreach (var hand in component.Hands.Values)
|
foreach (var held in _handsSystem.EnumerateHeld((uid, component)))
|
||||||
{
|
|
||||||
if (hand.HeldEntity == null
|
|
||||||
|| !TryComp(hand.HeldEntity, out VirtualItemComponent? virtualItem)
|
|
||||||
|| virtualItem.BlockingEntity != args.PulledUid)
|
|
||||||
{
|
{
|
||||||
|
if (!TryComp(held, out VirtualItemComponent? virtualItem) || virtualItem.BlockingEntity != args.PulledUid)
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
_handsSystem.TryDrop(args.PullerUid, hand, handsComp: component);
|
_handsSystem.TryDrop((args.PullerUid, component), held);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Content.Shared.Clothing.Components;
|
|||||||
using Content.Shared.CombatMode;
|
using Content.Shared.CombatMode;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Inventory.Events;
|
using Content.Shared.Inventory.Events;
|
||||||
using Content.Shared.Item.ItemToggle;
|
using Content.Shared.Item.ItemToggle;
|
||||||
@@ -19,6 +20,7 @@ public abstract class SharedNinjaGlovesSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] private readonly SharedCombatModeSystem _combatMode = default!;
|
[Dependency] private readonly SharedCombatModeSystem _combatMode = default!;
|
||||||
|
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||||
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
||||||
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
@@ -128,8 +130,7 @@ public abstract class SharedNinjaGlovesSystem : EntitySystem
|
|||||||
target = args.Target;
|
target = args.Target;
|
||||||
return _timing.IsFirstTimePredicted
|
return _timing.IsFirstTimePredicted
|
||||||
&& !_combatMode.IsInCombatMode(uid)
|
&& !_combatMode.IsInCombatMode(uid)
|
||||||
&& TryComp<HandsComponent>(uid, out var hands)
|
&& _hands.GetActiveItem(uid) == null
|
||||||
&& hands.ActiveHandEntity == null
|
|
||||||
&& _interaction.InRangeUnobstructed(uid, target);
|
&& _interaction.InRangeUnobstructed(uid, target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -459,7 +459,7 @@ public sealed class FoodSystem : EntitySystem
|
|||||||
|
|
||||||
var usedTypes = UtensilType.None;
|
var usedTypes = UtensilType.None;
|
||||||
|
|
||||||
foreach (var item in _hands.EnumerateHeld(user, hands))
|
foreach (var item in _hands.EnumerateHeld((user, hands)))
|
||||||
{
|
{
|
||||||
// Is utensil?
|
// Is utensil?
|
||||||
if (!TryComp<UtensilComponent>(item, out var utensil))
|
if (!TryComp<UtensilComponent>(item, out var utensil))
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using Content.Shared.Construction;
|
|||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.DoAfter;
|
using Content.Shared.DoAfter;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Maps;
|
using Content.Shared.Maps;
|
||||||
using Content.Shared.Physics;
|
using Content.Shared.Physics;
|
||||||
@@ -35,6 +35,7 @@ public sealed class RCDSystem : EntitySystem
|
|||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
[Dependency] private readonly SharedChargesSystem _sharedCharges = default!;
|
[Dependency] private readonly SharedChargesSystem _sharedCharges = default!;
|
||||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||||
|
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||||
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
[Dependency] private readonly TurfSystem _turf = default!;
|
[Dependency] private readonly TurfSystem _turf = default!;
|
||||||
@@ -296,11 +297,10 @@ public sealed class RCDSystem : EntitySystem
|
|||||||
var uid = GetEntity(ev.NetEntity);
|
var uid = GetEntity(ev.NetEntity);
|
||||||
|
|
||||||
// Determine if player that send the message is carrying the specified RCD in their active hand
|
// Determine if player that send the message is carrying the specified RCD in their active hand
|
||||||
if (session.SenderSession.AttachedEntity == null)
|
if (session.SenderSession.AttachedEntity is not { } player)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!TryComp<HandsComponent>(session.SenderSession.AttachedEntity, out var hands) ||
|
if (_hands.GetActiveItem(player) != uid)
|
||||||
uid != hands.ActiveHand?.HeldEntity)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!TryComp<RCDComponent>(uid, out var rcd))
|
if (!TryComp<RCDComponent>(uid, out var rcd))
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Content.Shared.Actions;
|
using Content.Shared.Actions;
|
||||||
using Content.Shared.Cuffs;
|
using Content.Shared.Cuffs;
|
||||||
using Content.Shared.Hands;
|
using Content.Shared.Hands;
|
||||||
using Content.Shared.Hands.Components;
|
|
||||||
using Content.Shared.Hands.EntitySystems;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.Interaction.Components;
|
using Content.Shared.Interaction.Components;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
@@ -42,7 +41,7 @@ public sealed class RetractableItemActionSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnRetractableItemAction(Entity<RetractableItemActionComponent> ent, ref OnRetractableItemActionEvent args)
|
private void OnRetractableItemAction(Entity<RetractableItemActionComponent> ent, ref OnRetractableItemActionEvent args)
|
||||||
{
|
{
|
||||||
if (_hands.GetActiveHand(args.Performer) is not { } userHand)
|
if (_hands.GetActiveHand(args.Performer) is not { } activeHand)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_actions.GetAction(ent.Owner) is not { } action)
|
if (_actions.GetAction(ent.Owner) is not { } action)
|
||||||
@@ -55,7 +54,9 @@ public sealed class RetractableItemActionSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Don't allow to summon an item if holding an unremoveable item unless that item is summoned by the action.
|
// Don't allow to summon an item if holding an unremoveable item unless that item is summoned by the action.
|
||||||
if (userHand.HeldEntity != null && !_hands.IsHolding(args.Performer, ent.Comp.ActionItemUid) && !_hands.CanDropHeld(args.Performer, userHand, false))
|
if (_hands.GetActiveItem(ent.Owner) != null
|
||||||
|
&& !_hands.IsHolding(args.Performer, ent.Comp.ActionItemUid)
|
||||||
|
&& !_hands.CanDropHeld(args.Performer, activeHand, false))
|
||||||
{
|
{
|
||||||
_popups.PopupClient(Loc.GetString("retractable-item-hand-cannot-drop"), args.Performer, args.Performer);
|
_popups.PopupClient(Loc.GetString("retractable-item-hand-cannot-drop"), args.Performer, args.Performer);
|
||||||
return;
|
return;
|
||||||
@@ -67,7 +68,7 @@ public sealed class RetractableItemActionSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SummonRetractableItem(args.Performer, ent.Comp.ActionItemUid.Value, userHand, ent.Owner);
|
SummonRetractableItem(args.Performer, ent.Comp.ActionItemUid.Value, activeHand, ent.Owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
@@ -93,7 +94,7 @@ public sealed class RetractableItemActionSystem : EntitySystem
|
|||||||
if (action.Comp.AttachedEntity == null)
|
if (action.Comp.AttachedEntity == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_hands.GetActiveHand(action.Comp.AttachedEntity.Value) is not { } userHand)
|
if (_hands.GetActiveHand(action.Comp.AttachedEntity.Value) is not { })
|
||||||
return;
|
return;
|
||||||
|
|
||||||
RetractRetractableItem(action.Comp.AttachedEntity.Value, ent, action.Owner);
|
RetractRetractableItem(action.Comp.AttachedEntity.Value, ent, action.Owner);
|
||||||
@@ -128,7 +129,7 @@ public sealed class RetractableItemActionSystem : EntitySystem
|
|||||||
_audio.PlayPredicted(action.Comp.RetractSounds, holder, holder);
|
_audio.PlayPredicted(action.Comp.RetractSounds, holder, holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SummonRetractableItem(EntityUid holder, EntityUid item, Hand hand, Entity<RetractableItemActionComponent?> action)
|
private void SummonRetractableItem(EntityUid holder, EntityUid item, string hand, Entity<RetractableItemActionComponent?> action)
|
||||||
{
|
{
|
||||||
if (!Resolve(action, ref action.Comp, false))
|
if (!Resolve(action, ref action.Comp, false))
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ namespace Content.Shared.Stacks
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This is shit code until hands get fixed and give an easy way to enumerate over items, starting with the currently active item.
|
// This is shit code until hands get fixed and give an easy way to enumerate over items, starting with the currently active item.
|
||||||
foreach (var held in Hands.EnumerateHeld(user, hands))
|
foreach (var held in Hands.EnumerateHeld((user, hands)))
|
||||||
{
|
{
|
||||||
TryMergeStacks(item, held, out _, donorStack: itemStack);
|
TryMergeStacks(item, held, out _, donorStack: itemStack);
|
||||||
|
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ public abstract class SharedStationSpawningSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
var inhandEntity = EntityManager.SpawnEntity(prototype, coords);
|
var inhandEntity = EntityManager.SpawnEntity(prototype, coords);
|
||||||
|
|
||||||
if (_handsSystem.TryGetEmptyHand(entity, out var emptyHand, handsComponent))
|
if (_handsSystem.TryGetEmptyHand((entity, handsComponent), out var emptyHand))
|
||||||
{
|
{
|
||||||
_handsSystem.TryPickup(entity, inhandEntity, emptyHand, checkActionBlocker: false, handsComp: handsComponent);
|
_handsSystem.TryPickup(entity, inhandEntity, emptyHand, checkActionBlocker: false, handsComp: handsComponent);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -689,7 +689,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// If the user's active hand is empty, try pick up the item.
|
// If the user's active hand is empty, try pick up the item.
|
||||||
if (player.Comp.ActiveHandEntity == null)
|
if (!_sharedHandsSystem.TryGetActiveItem(player.AsNullable(), out var activeItem))
|
||||||
{
|
{
|
||||||
_adminLog.Add(
|
_adminLog.Add(
|
||||||
LogType.Storage,
|
LogType.Storage,
|
||||||
@@ -709,11 +709,11 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
_adminLog.Add(
|
_adminLog.Add(
|
||||||
LogType.Storage,
|
LogType.Storage,
|
||||||
LogImpact.Low,
|
LogImpact.Low,
|
||||||
$"{ToPrettyString(player):player} is interacting with {ToPrettyString(item):item} while it is stored in {ToPrettyString(storage):storage} using {ToPrettyString(player.Comp.ActiveHandEntity):used}");
|
$"{ToPrettyString(player):player} is interacting with {ToPrettyString(item):item} while it is stored in {ToPrettyString(storage):storage} using {ToPrettyString(activeItem):used}");
|
||||||
|
|
||||||
// Else, interact using the held item
|
// Else, interact using the held item
|
||||||
if (_interactionSystem.InteractUsing(player,
|
if (_interactionSystem.InteractUsing(player,
|
||||||
player.Comp.ActiveHandEntity.Value,
|
activeItem.Value,
|
||||||
item,
|
item,
|
||||||
Transform(item).Coordinates,
|
Transform(item).Coordinates,
|
||||||
checkCanInteract: false))
|
checkCanInteract: false))
|
||||||
@@ -1208,10 +1208,10 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
if (!Resolve(ent.Owner, ref ent.Comp)
|
if (!Resolve(ent.Owner, ref ent.Comp)
|
||||||
|| !Resolve(player.Owner, ref player.Comp)
|
|| !Resolve(player.Owner, ref player.Comp)
|
||||||
|| player.Comp.ActiveHandEntity == null)
|
|| !_sharedHandsSystem.TryGetActiveItem(player, out var activeItem))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var toInsert = player.Comp.ActiveHandEntity;
|
var toInsert = activeItem;
|
||||||
|
|
||||||
if (!CanInsert(ent, toInsert.Value, out var reason, ent.Comp))
|
if (!CanInsert(ent, toInsert.Value, out var reason, ent.Comp))
|
||||||
{
|
{
|
||||||
@@ -1219,7 +1219,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_sharedHandsSystem.CanDrop(player, toInsert.Value, player.Comp))
|
if (!_sharedHandsSystem.CanDrop(player, toInsert.Value))
|
||||||
{
|
{
|
||||||
_popupSystem.PopupClient(Loc.GetString("comp-storage-cant-drop", ("entity", toInsert.Value)), ent, player);
|
_popupSystem.PopupClient(Loc.GetString("comp-storage-cant-drop", ("entity", toInsert.Value)), ent, player);
|
||||||
return false;
|
return false;
|
||||||
@@ -1933,7 +1933,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
|
|
||||||
if (held)
|
if (held)
|
||||||
{
|
{
|
||||||
if (!_sharedHandsSystem.IsHolding(player, itemUid, out _))
|
if (!_sharedHandsSystem.IsHolding(player.AsNullable(), itemUid, out _))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -104,8 +104,8 @@ public abstract class SharedStrippableSystem : EntitySystem
|
|||||||
|
|
||||||
var hasEnt = _inventorySystem.TryGetSlotEntity(strippable, args.Slot, out var held, inventory);
|
var hasEnt = _inventorySystem.TryGetSlotEntity(strippable, args.Slot, out var held, inventory);
|
||||||
|
|
||||||
if (userHands.ActiveHandEntity != null && !hasEnt)
|
if (_handsSystem.GetActiveItem((user, userHands)) is { } activeItem && !hasEnt)
|
||||||
StartStripInsertInventory((user, userHands), strippable.Owner, userHands.ActiveHandEntity.Value, args.Slot);
|
StartStripInsertInventory((user, userHands), strippable.Owner, activeItem, args.Slot);
|
||||||
else if (hasEnt)
|
else if (hasEnt)
|
||||||
StartStripRemoveInventory(user, strippable.Owner, held!.Value, args.Slot);
|
StartStripRemoveInventory(user, strippable.Owner, held!.Value, args.Slot);
|
||||||
}
|
}
|
||||||
@@ -124,11 +124,10 @@ public abstract class SharedStrippableSystem : EntitySystem
|
|||||||
if (!target.Comp.CanBeStripped)
|
if (!target.Comp.CanBeStripped)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!_handsSystem.TryGetHand(target.Owner, handId, out var handSlot))
|
var heldEntity = _handsSystem.GetHeldItem(target.Owner, handId);
|
||||||
return;
|
|
||||||
|
|
||||||
// Is the target a handcuff?
|
// Is the target a handcuff?
|
||||||
if (TryComp<VirtualItemComponent>(handSlot.HeldEntity, out var virtualItem) &&
|
if (TryComp<VirtualItemComponent>(heldEntity, out var virtualItem) &&
|
||||||
TryComp<CuffableComponent>(target.Owner, out var cuffable) &&
|
TryComp<CuffableComponent>(target.Owner, out var cuffable) &&
|
||||||
_cuffableSystem.GetAllCuffs(cuffable).Contains(virtualItem.BlockingEntity))
|
_cuffableSystem.GetAllCuffs(cuffable).Contains(virtualItem.BlockingEntity))
|
||||||
{
|
{
|
||||||
@@ -136,10 +135,10 @@ public abstract class SharedStrippableSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.Comp.ActiveHandEntity != null && handSlot.HeldEntity == null)
|
if (_handsSystem.GetActiveItem(user.AsNullable()) is { } activeItem && heldEntity == null)
|
||||||
StartStripInsertHand(user, target, user.Comp.ActiveHandEntity.Value, handId, targetStrippable);
|
StartStripInsertHand(user, target, activeItem, handId, targetStrippable);
|
||||||
else if (handSlot.HeldEntity != null)
|
else if (heldEntity != null)
|
||||||
StartStripRemoveHand(user, target, handSlot.HeldEntity.Value, handId, targetStrippable);
|
StartStripRemoveHand(user, target, heldEntity.Value, handId, targetStrippable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -154,16 +153,10 @@ public abstract class SharedStrippableSystem : EntitySystem
|
|||||||
if (!Resolve(user, ref user.Comp))
|
if (!Resolve(user, ref user.Comp))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (user.Comp.ActiveHand == null)
|
if (!_handsSystem.TryGetActiveItem(user, out var activeItem) || activeItem != held)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (user.Comp.ActiveHandEntity == null)
|
if (!_handsSystem.CanDropHeld(user, user.Comp.ActiveHandId!))
|
||||||
return false;
|
|
||||||
|
|
||||||
if (user.Comp.ActiveHandEntity != held)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!_handsSystem.CanDropHeld(user, user.Comp.ActiveHand))
|
|
||||||
{
|
{
|
||||||
_popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop"));
|
_popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop"));
|
||||||
return false;
|
return false;
|
||||||
@@ -210,10 +203,14 @@ public abstract class SharedStrippableSystem : EntitySystem
|
|||||||
var (time, stealth) = GetStripTimeModifiers(user, target, held, slotDef.StripTime);
|
var (time, stealth) = GetStripTimeModifiers(user, target, held, slotDef.StripTime);
|
||||||
|
|
||||||
if (!stealth)
|
if (!stealth)
|
||||||
|
{
|
||||||
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-insert",
|
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-insert",
|
||||||
("user", Identity.Entity(user, EntityManager)),
|
("user", Identity.Entity(user, EntityManager)),
|
||||||
("item", user.Comp.ActiveHandEntity!.Value)),
|
("item", _handsSystem.GetActiveItem((user, user.Comp))!.Value)),
|
||||||
target, target, PopupType.Large);
|
target,
|
||||||
|
target,
|
||||||
|
PopupType.Large);
|
||||||
|
}
|
||||||
|
|
||||||
var prefix = stealth ? "stealthily " : "";
|
var prefix = stealth ? "stealthily " : "";
|
||||||
_adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}place the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s {slot} slot");
|
_adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}place the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s {slot} slot");
|
||||||
@@ -246,7 +243,7 @@ public abstract class SharedStrippableSystem : EntitySystem
|
|||||||
if (!CanStripInsertInventory(user, target, held, slot))
|
if (!CanStripInsertInventory(user, target, held, slot))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!_handsSystem.TryDrop(user, handsComp: user.Comp))
|
if (!_handsSystem.TryDrop(user))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_inventorySystem.TryEquip(user, target, held, slot, triggerHandContact: true);
|
_inventorySystem.TryEquip(user, target, held, slot, triggerHandContact: true);
|
||||||
@@ -305,10 +302,15 @@ public abstract class SharedStrippableSystem : EntitySystem
|
|||||||
if (IsStripHidden(slotDef, user))
|
if (IsStripHidden(slotDef, user))
|
||||||
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-hidden", ("slot", slot)), target, target, PopupType.Large);
|
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-hidden", ("slot", slot)), target, target, PopupType.Large);
|
||||||
else
|
else
|
||||||
|
{
|
||||||
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner",
|
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner",
|
||||||
("user", Identity.Entity(user, EntityManager)),
|
("user", Identity.Entity(user, EntityManager)),
|
||||||
("item", item)),
|
("item", item)),
|
||||||
target, target, PopupType.Large);
|
target,
|
||||||
|
target,
|
||||||
|
PopupType.Large);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var prefix = stealth ? "stealthily " : "";
|
var prefix = stealth ? "stealthily " : "";
|
||||||
@@ -368,23 +370,16 @@ public abstract class SharedStrippableSystem : EntitySystem
|
|||||||
if (!target.Comp.CanBeStripped)
|
if (!target.Comp.CanBeStripped)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (user.Comp.ActiveHand == null)
|
if (!_handsSystem.TryGetActiveItem(user, out var activeItem) || activeItem != held)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (user.Comp.ActiveHandEntity == null)
|
if (!_handsSystem.CanDropHeld(user, user.Comp.ActiveHandId!))
|
||||||
return false;
|
|
||||||
|
|
||||||
if (user.Comp.ActiveHandEntity != held)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!_handsSystem.CanDropHeld(user, user.Comp.ActiveHand))
|
|
||||||
{
|
{
|
||||||
_popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop"));
|
_popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_handsSystem.TryGetHand(target, handName, out var handSlot, target.Comp) ||
|
if (!_handsSystem.CanPickupToHand(target, activeItem.Value, handName, checkActionBlocker: false, target.Comp))
|
||||||
!_handsSystem.CanPickupToHand(target, user.Comp.ActiveHandEntity.Value, handSlot, checkActionBlocker: false, target.Comp))
|
|
||||||
{
|
{
|
||||||
_popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-put-message", ("owner", Identity.Entity(target, EntityManager))));
|
_popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-put-message", ("owner", Identity.Entity(target, EntityManager))));
|
||||||
return false;
|
return false;
|
||||||
@@ -414,10 +409,15 @@ public abstract class SharedStrippableSystem : EntitySystem
|
|||||||
var (time, stealth) = GetStripTimeModifiers(user, target, null, targetStrippable.HandStripDelay);
|
var (time, stealth) = GetStripTimeModifiers(user, target, null, targetStrippable.HandStripDelay);
|
||||||
|
|
||||||
if (!stealth)
|
if (!stealth)
|
||||||
|
{
|
||||||
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-insert-hand",
|
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-insert-hand",
|
||||||
("user", Identity.Entity(user, EntityManager)),
|
("user", Identity.Entity(user, EntityManager)),
|
||||||
("item", user.Comp.ActiveHandEntity!.Value)),
|
("item", _handsSystem.GetActiveItem(user)!.Value)),
|
||||||
target, target, PopupType.Large);
|
target,
|
||||||
|
target,
|
||||||
|
PopupType.Large);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
var prefix = stealth ? "stealthily " : "";
|
var prefix = stealth ? "stealthily " : "";
|
||||||
_adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}place the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s hands");
|
_adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}place the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s hands");
|
||||||
@@ -452,7 +452,7 @@ public abstract class SharedStrippableSystem : EntitySystem
|
|||||||
if (!CanStripInsertHand(user, target, held, handName))
|
if (!CanStripInsertHand(user, target, held, handName))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_handsSystem.TryDrop(user, checkActionBlocker: false, handsComp: user.Comp);
|
_handsSystem.TryDrop(user, checkActionBlocker: false);
|
||||||
_handsSystem.TryPickup(target, held, handName, checkActionBlocker: false, animateUser: stealth, animate: !stealth, handsComp: target.Comp);
|
_handsSystem.TryPickup(target, held, handName, checkActionBlocker: false, animateUser: stealth, animate: !stealth, handsComp: target.Comp);
|
||||||
_adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):actor} has placed the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s hands");
|
_adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):actor} has placed the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s hands");
|
||||||
|
|
||||||
@@ -474,22 +474,22 @@ public abstract class SharedStrippableSystem : EntitySystem
|
|||||||
if (!target.Comp.CanBeStripped)
|
if (!target.Comp.CanBeStripped)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!_handsSystem.TryGetHand(target, handName, out var handSlot, target.Comp))
|
if (!_handsSystem.TryGetHand(target, handName, out _))
|
||||||
{
|
{
|
||||||
_popupSystem.PopupCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Identity.Entity(target, EntityManager))));
|
_popupSystem.PopupCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Identity.Entity(target, EntityManager))));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasComp<VirtualItemComponent>(handSlot.HeldEntity))
|
if (!_handsSystem.TryGetHeldItem(target, handName, out var heldEntity))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (handSlot.HeldEntity == null)
|
if (HasComp<VirtualItemComponent>(heldEntity))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (handSlot.HeldEntity != item)
|
if (heldEntity != item)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!_handsSystem.CanDropHeld(target, handSlot, false))
|
if (!_handsSystem.CanDropHeld(target, handName, false))
|
||||||
{
|
{
|
||||||
_popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop-message", ("owner", Identity.Entity(target, EntityManager))));
|
_popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop-message", ("owner", Identity.Entity(target, EntityManager))));
|
||||||
return false;
|
return false;
|
||||||
@@ -519,10 +519,13 @@ public abstract class SharedStrippableSystem : EntitySystem
|
|||||||
var (time, stealth) = GetStripTimeModifiers(user, target, null, targetStrippable.HandStripDelay);
|
var (time, stealth) = GetStripTimeModifiers(user, target, null, targetStrippable.HandStripDelay);
|
||||||
|
|
||||||
if (!stealth)
|
if (!stealth)
|
||||||
|
{
|
||||||
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner",
|
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner",
|
||||||
("user", Identity.Entity(user, EntityManager)),
|
("user", Identity.Entity(user, EntityManager)),
|
||||||
("item", item)),
|
("item", item)),
|
||||||
target, target);
|
target,
|
||||||
|
target);
|
||||||
|
}
|
||||||
|
|
||||||
var prefix = stealth ? "stealthily " : "";
|
var prefix = stealth ? "stealthily " : "";
|
||||||
_adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}strip the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s hands");
|
_adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}strip the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s hands");
|
||||||
@@ -560,7 +563,7 @@ public abstract class SharedStrippableSystem : EntitySystem
|
|||||||
if (!CanStripRemoveHand(user, target, item, handName))
|
if (!CanStripRemoveHand(user, target, item, handName))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_handsSystem.TryDrop(target, item, checkActionBlocker: false, handsComp: target.Comp);
|
_handsSystem.TryDrop(target, item, checkActionBlocker: false);
|
||||||
_handsSystem.PickupOrDrop(user, item, animateUser: stealth, animate: !stealth, handsComp: user.Comp);
|
_handsSystem.PickupOrDrop(user, item, animateUser: stealth, animate: !stealth, handsComp: user.Comp);
|
||||||
_adminLogger.Add(LogType.Stripping, LogImpact.High, $"{ToPrettyString(user):actor} has stripped the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s hands");
|
_adminLogger.Add(LogType.Stripping, LogImpact.High, $"{ToPrettyString(user):actor} has stripped the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s hands");
|
||||||
|
|
||||||
@@ -580,15 +583,19 @@ public abstract class SharedStrippableSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
if ( ev.Event.InsertOrRemove && !CanStripInsertInventory((entity.Owner, entity.Comp), args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName) ||
|
if ( ev.Event.InsertOrRemove && !CanStripInsertInventory((entity.Owner, entity.Comp), args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName) ||
|
||||||
!ev.Event.InsertOrRemove && !CanStripRemoveInventory(entity.Owner, args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName))
|
!ev.Event.InsertOrRemove && !CanStripRemoveInventory(entity.Owner, args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName))
|
||||||
|
{
|
||||||
ev.Cancel();
|
ev.Cancel();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ( ev.Event.InsertOrRemove && !CanStripInsertHand((entity.Owner, entity.Comp), args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName) ||
|
if ( ev.Event.InsertOrRemove && !CanStripInsertHand((entity.Owner, entity.Comp), args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName) ||
|
||||||
!ev.Event.InsertOrRemove && !CanStripRemoveHand(entity.Owner, args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName))
|
!ev.Event.InsertOrRemove && !CanStripRemoveHand(entity.Owner, args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName))
|
||||||
|
{
|
||||||
ev.Cancel();
|
ev.Cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnStrippableDoAfterFinished(Entity<HandsComponent> entity, ref StrippableDoAfterEvent ev)
|
private void OnStrippableDoAfterFinished(Entity<HandsComponent> entity, ref StrippableDoAfterEvent ev)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -108,10 +108,10 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
|||||||
|
|
||||||
if (component.InHandsOnly)
|
if (component.InHandsOnly)
|
||||||
{
|
{
|
||||||
if (!_hands.IsHolding(args.User, uid, out var hand, args.Hands))
|
if (!_hands.IsHolding((args.User, args.Hands), uid, out var hand ))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (component.RequireActiveHand && args.Hands.ActiveHand != hand)
|
if (component.RequireActiveHand && args.Hands.ActiveHandId != hand)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,10 +202,10 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
|||||||
if (!TryComp(user, out HandsComponent? hands))
|
if (!TryComp(user, out HandsComponent? hands))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!_hands.IsHolding(user, uiEntity, out var hand, hands))
|
if (!_hands.IsHolding((user, hands), uiEntity, out var hand))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (aui.RequireActiveHand && hands.ActiveHand != hand)
|
if (aui.RequireActiveHand && hands.ActiveHandId != hand)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ using Content.Shared.Database;
|
|||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Content.Shared.Hands;
|
using Content.Shared.Hands;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
@@ -51,6 +52,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
|||||||
[Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!;
|
[Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!;
|
||||||
[Dependency] protected readonly ActionBlockerSystem Blocker = default!;
|
[Dependency] protected readonly ActionBlockerSystem Blocker = default!;
|
||||||
[Dependency] protected readonly DamageableSystem Damageable = default!;
|
[Dependency] protected readonly DamageableSystem Damageable = default!;
|
||||||
|
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||||
[Dependency] private readonly MeleeSoundSystem _meleeSound = default!;
|
[Dependency] private readonly MeleeSoundSystem _meleeSound = default!;
|
||||||
[Dependency] protected readonly MobStateSystem MobState = default!;
|
[Dependency] protected readonly MobStateSystem MobState = default!;
|
||||||
@@ -288,15 +290,14 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use inhands entity if we got one.
|
// Use inhands entity if we got one.
|
||||||
if (EntityManager.TryGetComponent(entity, out HandsComponent? hands) &&
|
if (_hands.TryGetActiveItem(entity, out var held))
|
||||||
hands.ActiveHandEntity is { } held)
|
|
||||||
{
|
{
|
||||||
// Make sure the entity is a weapon AND it doesn't need
|
// Make sure the entity is a weapon AND it doesn't need
|
||||||
// to be equipped to be used (E.g boxing gloves).
|
// to be equipped to be used (E.g boxing gloves).
|
||||||
if (EntityManager.TryGetComponent(held, out melee) &&
|
if (EntityManager.TryGetComponent(held, out melee) &&
|
||||||
!melee.MustBeEquippedToUse)
|
!melee.MustBeEquippedToUse)
|
||||||
{
|
{
|
||||||
weaponUid = held;
|
weaponUid = held.Value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -858,9 +859,9 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
|||||||
|
|
||||||
EntityUid? inTargetHand = null;
|
EntityUid? inTargetHand = null;
|
||||||
|
|
||||||
if (targetHandsComponent?.ActiveHand is { IsEmpty: false })
|
if (_hands.TryGetActiveItem(target.Value, out var activeHeldEntity))
|
||||||
{
|
{
|
||||||
inTargetHand = targetHandsComponent.ActiveHand.HeldEntity!.Value;
|
inTargetHand = activeHeldEntity.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
var attemptEvent = new DisarmAttemptEvent(target.Value, user, inTargetHand);
|
var attemptEvent = new DisarmAttemptEvent(target.Value, user, inTargetHand);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Shared.CombatMode;
|
using Content.Shared.CombatMode;
|
||||||
using Content.Shared.Hands;
|
using Content.Shared.Hands;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Movement.Events;
|
using Content.Shared.Movement.Events;
|
||||||
using Content.Shared.Physics;
|
using Content.Shared.Physics;
|
||||||
@@ -25,6 +25,7 @@ public abstract class SharedGrapplingGunSystem : EntitySystem
|
|||||||
[Dependency] private readonly INetManager _netManager = default!;
|
[Dependency] private readonly INetManager _netManager = default!;
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
|
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||||
[Dependency] private readonly SharedJointSystem _joints = default!;
|
[Dependency] private readonly SharedJointSystem _joints = default!;
|
||||||
[Dependency] private readonly SharedGunSystem _gun = default!;
|
[Dependency] private readonly SharedGunSystem _gun = default!;
|
||||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||||
@@ -80,9 +81,11 @@ public abstract class SharedGrapplingGunSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnGrapplingReel(RequestGrapplingReelMessage msg, EntitySessionEventArgs args)
|
private void OnGrapplingReel(RequestGrapplingReelMessage msg, EntitySessionEventArgs args)
|
||||||
{
|
{
|
||||||
var player = args.SenderSession.AttachedEntity;
|
if (args.SenderSession.AttachedEntity is not { } player)
|
||||||
if (!TryComp<HandsComponent>(player, out var hands) ||
|
return;
|
||||||
!TryComp<GrapplingGunComponent>(hands.ActiveHandEntity, out var grappling))
|
|
||||||
|
if (!_hands.TryGetActiveItem(player, out var activeItem) ||
|
||||||
|
!TryComp<GrapplingGunComponent>(activeItem, out var grappling))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -94,7 +97,7 @@ public abstract class SharedGrapplingGunSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetReeling(hands.ActiveHandEntity.Value, grappling, msg.Reeling, player.Value);
|
SetReeling(activeItem.Value, grappling, msg.Reeling, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnWeightlessMove(ref CanWeightlessMoveEvent ev)
|
private void OnWeightlessMove(ref CanWeightlessMoveEvent ev)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
using Content.Shared.Buckle.Components;
|
using Content.Shared.Buckle.Components;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Mobs.Systems;
|
using Content.Shared.Mobs.Systems;
|
||||||
using Content.Shared.Movement.Events;
|
using Content.Shared.Movement.Events;
|
||||||
@@ -23,6 +24,7 @@ public abstract partial class SharedTetherGunSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly INetManager _netManager = default!;
|
[Dependency] private readonly INetManager _netManager = default!;
|
||||||
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
||||||
|
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||||
[Dependency] private readonly MobStateSystem _mob = default!;
|
[Dependency] private readonly MobStateSystem _mob = default!;
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
@@ -139,14 +141,14 @@ public abstract partial class SharedTetherGunSystem : EntitySystem
|
|||||||
gunUid = null;
|
gunUid = null;
|
||||||
gun = null;
|
gun = null;
|
||||||
|
|
||||||
if (!TryComp<HandsComponent>(user, out var hands) ||
|
if (!_hands.TryGetActiveItem(user, out var activeItem) ||
|
||||||
!TryComp(hands.ActiveHandEntity, out gun) ||
|
!TryComp(activeItem, out gun) ||
|
||||||
_container.IsEntityInContainer(user))
|
_container.IsEntityInContainer(user))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
gunUid = hands.ActiveHandEntity.Value;
|
gunUid = activeItem.Value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ using Content.Shared.Damage;
|
|||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Gravity;
|
using Content.Shared.Gravity;
|
||||||
using Content.Shared.Hands;
|
using Content.Shared.Hands;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Projectiles;
|
using Content.Shared.Projectiles;
|
||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
@@ -49,6 +49,7 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
[Dependency] protected readonly ISharedAdminLogManager Logs = default!;
|
[Dependency] protected readonly ISharedAdminLogManager Logs = default!;
|
||||||
[Dependency] protected readonly DamageableSystem Damageable = default!;
|
[Dependency] protected readonly DamageableSystem Damageable = default!;
|
||||||
[Dependency] protected readonly ExamineSystemShared Examine = default!;
|
[Dependency] protected readonly ExamineSystemShared Examine = default!;
|
||||||
|
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||||
[Dependency] private readonly ItemSlotsSystem _slots = default!;
|
[Dependency] private readonly ItemSlotsSystem _slots = default!;
|
||||||
[Dependency] private readonly RechargeBasicEntityAmmoSystem _recharge = default!;
|
[Dependency] private readonly RechargeBasicEntityAmmoSystem _recharge = default!;
|
||||||
[Dependency] protected readonly SharedActionsSystem Actions = default!;
|
[Dependency] protected readonly SharedActionsSystem Actions = default!;
|
||||||
@@ -173,8 +174,7 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
gunEntity = default;
|
gunEntity = default;
|
||||||
gunComp = null;
|
gunComp = null;
|
||||||
|
|
||||||
if (EntityManager.TryGetComponent(entity, out HandsComponent? hands) &&
|
if (_hands.GetActiveItem(entity) is { } held &&
|
||||||
hands.ActiveHandEntity is { } held &&
|
|
||||||
TryComp(held, out GunComponent? gun))
|
TryComp(held, out GunComponent? gun))
|
||||||
{
|
{
|
||||||
gunEntity = held;
|
gunEntity = held;
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public abstract class SharedWieldableSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnDeselectWieldable(EntityUid uid, WieldableComponent component, HandDeselectedEvent args)
|
private void OnDeselectWieldable(EntityUid uid, WieldableComponent component, HandDeselectedEvent args)
|
||||||
{
|
{
|
||||||
if (_hands.EnumerateHands(args.User).Count() > 2)
|
if (_hands.GetHandCount(uid) > 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TryUnwield(uid, component, args.User);
|
TryUnwield(uid, component, args.User);
|
||||||
@@ -168,7 +168,7 @@ public abstract class SharedWieldableSystem : EntitySystem
|
|||||||
if (args.Hands == null || !args.CanAccess || !args.CanInteract)
|
if (args.Hands == null || !args.CanAccess || !args.CanInteract)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!_hands.IsHolding(args.User, uid, out _, args.Hands))
|
if (!_hands.IsHolding((args.User, args.Hands), uid, out _))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// TODO VERB TOOLTIPS Make CanWield or some other function return string, set as verb tooltip and disable
|
// TODO VERB TOOLTIPS Make CanWield or some other function return string, set as verb tooltip and disable
|
||||||
@@ -252,7 +252,7 @@ public abstract class SharedWieldableSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Is it.. actually in one of their hands?
|
// Is it.. actually in one of their hands?
|
||||||
if (!_hands.IsHolding(user, uid, out _, hands))
|
if (!_hands.IsHolding((user, hands), uid, out _))
|
||||||
{
|
{
|
||||||
if (!quiet)
|
if (!quiet)
|
||||||
_popup.PopupClient(Loc.GetString("wieldable-component-not-in-hands", ("item", uid)), user, user);
|
_popup.PopupClient(Loc.GetString("wieldable-component-not-in-hands", ("item", uid)), user, user);
|
||||||
@@ -373,7 +373,7 @@ public abstract class SharedWieldableSystem : EntitySystem
|
|||||||
/// <param name="force">If this is true we will bypass UnwieldAttemptEvent.</param>
|
/// <param name="force">If this is true we will bypass UnwieldAttemptEvent.</param>
|
||||||
public void UnwieldAll(Entity<HandsComponent?> wielder, bool force = false)
|
public void UnwieldAll(Entity<HandsComponent?> wielder, bool force = false)
|
||||||
{
|
{
|
||||||
foreach (var held in _hands.EnumerateHeld(wielder.Owner, wielder.Comp))
|
foreach (var held in _hands.EnumerateHeld(wielder))
|
||||||
{
|
{
|
||||||
if (TryComp<WieldableComponent>(held, out var wieldable))
|
if (TryComp<WieldableComponent>(held, out var wieldable))
|
||||||
TryUnwield(held, wieldable, wielder, force);
|
TryUnwield(held, wieldable, wielder, force);
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
- type: body
|
|
||||||
id: Aghost
|
|
||||||
name: "aghost"
|
|
||||||
root: torso
|
|
||||||
slots:
|
|
||||||
torso:
|
|
||||||
part: TorsoHuman
|
|
||||||
connections:
|
|
||||||
- right_arm
|
|
||||||
- left_arm
|
|
||||||
right_arm:
|
|
||||||
part: RightArmHuman
|
|
||||||
connections:
|
|
||||||
- right_hand
|
|
||||||
left_arm:
|
|
||||||
part: LeftArmHuman
|
|
||||||
connections:
|
|
||||||
- left_hand
|
|
||||||
right_hand:
|
|
||||||
part: RightHandHuman
|
|
||||||
left_hand:
|
|
||||||
part: LeftHandHuman
|
|
||||||
@@ -22,6 +22,14 @@
|
|||||||
canInteract: true
|
canInteract: true
|
||||||
- type: GhostHearing
|
- type: GhostHearing
|
||||||
- type: Hands
|
- type: Hands
|
||||||
|
hands:
|
||||||
|
hand_right:
|
||||||
|
location: Right
|
||||||
|
hand_left:
|
||||||
|
location: Left
|
||||||
|
sortedHands:
|
||||||
|
- hand_right
|
||||||
|
- hand_left
|
||||||
- type: ComplexInteraction
|
- type: ComplexInteraction
|
||||||
- type: Puller
|
- type: Puller
|
||||||
needsHands: false
|
needsHands: false
|
||||||
@@ -29,8 +37,6 @@
|
|||||||
- type: Physics
|
- type: Physics
|
||||||
ignorePaused: true
|
ignorePaused: true
|
||||||
bodyType: Kinematic
|
bodyType: Kinematic
|
||||||
- type: Body
|
|
||||||
prototype: Aghost
|
|
||||||
- type: Access
|
- type: Access
|
||||||
groups:
|
groups:
|
||||||
- AllAccess
|
- AllAccess
|
||||||
|
|||||||
Reference in New Issue
Block a user