2020-07-27 00:54:32 +02:00
using System ;
2021-06-21 02:21:20 -07:00
using System.Collections.Generic ;
using System.Diagnostics.CodeAnalysis ;
using System.Linq ;
using Content.Shared.ActionBlocker ;
using Content.Shared.Interaction ;
2021-10-25 20:06:12 +13:00
using Content.Shared.Item ;
2021-06-21 02:21:20 -07:00
using Robust.Shared.Containers ;
2019-11-13 17:37:46 -05:00
using Robust.Shared.GameObjects ;
2021-07-12 01:32:10 -07:00
using Robust.Shared.GameStates ;
2021-06-21 02:21:20 -07:00
using Robust.Shared.IoC ;
using Robust.Shared.Log ;
2021-02-03 22:07:13 +00:00
using Robust.Shared.Map ;
2021-06-21 02:21:20 -07:00
using Robust.Shared.Maths ;
2022-01-07 19:09:42 +13:00
using Robust.Shared.Physics ;
2019-11-13 17:37:46 -05:00
using Robust.Shared.Serialization ;
2021-06-21 02:21:20 -07:00
using Robust.Shared.Serialization.Manager.Attributes ;
using Robust.Shared.ViewVariables ;
2022-03-02 12:29:42 +13:00
using static Robust . Shared . GameObjects . SharedSpriteComponent ;
2017-09-26 21:27:48 +02:00
2021-06-09 22:19:39 +02:00
namespace Content.Shared.Hands.Components
2017-09-26 21:27:48 +02:00
{
2021-11-08 15:08:24 +01:00
[NetworkedComponent]
public abstract class SharedHandsComponent : Component
2017-09-26 21:27:48 +02:00
{
2021-12-08 17:32:32 +01:00
[Dependency] private readonly IEntityManager _entMan = default ! ;
2021-06-21 02:21:20 -07:00
/// <summary>
/// The name of the currently active hand.
/// </summary>
2022-01-05 17:53:08 +13:00
[ViewVariables]
public string? ActiveHand ;
2021-06-21 02:21:20 -07:00
[ViewVariables]
2022-01-05 17:53:08 +13:00
public List < Hand > Hands = new ( ) ;
2021-06-21 02:21:20 -07:00
/// <summary>
/// The amount of throw impulse per distance the player is from the throw target.
/// </summary>
[DataField("throwForceMultiplier")]
[ViewVariables(VVAccess.ReadWrite)]
2021-07-25 16:58:02 +10:00
public float ThrowForceMultiplier { get ; set ; } = 10f ; //should be tuned so that a thrown item lands about under the player's cursor
2021-06-21 02:21:20 -07:00
/// <summary>
/// Distance after which longer throw targets stop increasing throw impulse.
/// </summary>
[DataField("throwRange")]
[ViewVariables(VVAccess.ReadWrite)]
public float ThrowRange { get ; set ; } = 8f ;
2022-01-05 17:53:08 +13:00
private bool PlayerCanDrop = > EntitySystem . Get < ActionBlockerSystem > ( ) . CanDrop ( Owner ) ;
private bool PlayerCanPickup = > EntitySystem . Get < ActionBlockerSystem > ( ) . CanPickup ( Owner ) ;
2021-10-29 01:34:59 +13:00
2021-06-21 02:21:20 -07:00
public void AddHand ( string handName , HandLocation handLocation )
{
if ( HasHand ( handName ) )
return ;
2022-01-05 17:53:08 +13:00
var container = Owner . EnsureContainer < ContainerSlot > ( handName ) ;
2021-06-21 02:21:20 -07:00
container . OccludesLight = false ;
2021-07-31 03:14:00 +02:00
Hands . Add ( new Hand ( handName , handLocation , container ) ) ;
2021-06-21 02:21:20 -07:00
2022-01-05 17:53:08 +13:00
if ( ActiveHand = = null )
EntitySystem . Get < SharedHandsSystem > ( ) . TrySetActiveHand ( Owner , handName , this ) ;
2021-06-21 02:21:20 -07:00
HandCountChanged ( ) ;
2022-01-05 17:53:08 +13:00
Dirty ( ) ;
2021-06-21 02:21:20 -07:00
}
public void RemoveHand ( string handName )
{
if ( ! TryGetHand ( handName , out var hand ) )
return ;
RemoveHand ( hand ) ;
}
private void RemoveHand ( Hand hand )
{
2021-11-24 00:38:39 +01:00
DropHeldEntityToFloor ( hand ) ;
2021-06-21 02:21:20 -07:00
hand . Container ? . Shutdown ( ) ;
Hands . Remove ( hand ) ;
if ( ActiveHand = = hand . Name )
2022-01-05 17:53:08 +13:00
EntitySystem . Get < SharedHandsSystem > ( ) . TrySetActiveHand ( Owner , Hands . FirstOrDefault ( ) ? . Name , this ) ;
2021-06-21 02:21:20 -07:00
HandCountChanged ( ) ;
2022-01-05 17:53:08 +13:00
Dirty ( ) ;
2021-06-21 02:21:20 -07:00
}
2022-01-05 17:53:08 +13:00
public Hand ? GetActiveHand ( )
2021-07-31 03:14:00 +02:00
{
if ( ActiveHand = = null )
return null ;
return GetHandOrNull ( ActiveHand ) ;
}
2021-06-21 02:21:20 -07:00
public bool HasHand ( string handName )
{
2021-07-31 03:14:00 +02:00
return TryGetHand ( handName , out _ ) ;
2021-06-21 02:21:20 -07:00
}
2021-07-31 03:14:00 +02:00
public Hand ? GetHandOrNull ( string handName )
2021-06-21 02:21:20 -07:00
{
2021-07-31 03:14:00 +02:00
return TryGetHand ( handName , out var hand ) ? hand : null ;
2021-06-21 02:21:20 -07:00
}
2021-07-31 03:14:00 +02:00
public Hand GetHand ( string handName )
2021-06-21 02:21:20 -07:00
{
2021-07-31 03:14:00 +02:00
if ( ! TryGetHand ( handName , out var hand ) )
throw new KeyNotFoundException ( $"Unable to find hand with name {handName}" ) ;
2021-06-21 02:21:20 -07:00
2021-07-31 03:14:00 +02:00
return hand ;
2021-06-21 02:21:20 -07:00
}
2021-07-31 03:14:00 +02:00
public bool TryGetHand ( string handName , [ NotNullWhen ( true ) ] out Hand ? foundHand )
2021-06-21 02:21:20 -07:00
{
2021-07-31 03:14:00 +02:00
foreach ( var hand in Hands )
{
if ( hand . Name = = handName )
{
foundHand = hand ;
return true ;
} ;
}
foundHand = null ;
return false ;
2021-06-21 02:21:20 -07:00
}
2021-07-31 03:14:00 +02:00
public bool TryGetActiveHand ( [ NotNullWhen ( true ) ] out Hand ? activeHand )
2021-06-21 02:21:20 -07:00
{
activeHand = GetActiveHand ( ) ;
return activeHand ! = null ;
}
#region Held Entities
public bool ActiveHandIsHoldingEntity ( )
{
if ( ! TryGetActiveHand ( out var hand ) )
return false ;
2021-12-30 18:27:15 -08:00
return hand . HeldEntity ! = null ;
2021-06-21 02:21:20 -07:00
}
2022-01-05 17:53:08 +13:00
public bool TryGetHeldEntity ( string handName , [ NotNullWhen ( true ) ] out EntityUid ? heldEntity )
2021-06-21 02:21:20 -07:00
{
2021-12-30 18:27:15 -08:00
heldEntity = null ;
2021-06-21 02:21:20 -07:00
if ( ! TryGetHand ( handName , out var hand ) )
return false ;
heldEntity = hand . HeldEntity ;
2022-01-05 17:53:08 +13:00
return heldEntity ! = null ;
2021-06-21 02:21:20 -07:00
}
2021-12-30 18:27:15 -08:00
public bool TryGetActiveHeldEntity ( [ NotNullWhen ( true ) ] out EntityUid ? heldEntity )
2021-06-21 02:21:20 -07:00
{
2021-12-30 18:27:15 -08:00
heldEntity = GetActiveHand ( ) ? . HeldEntity ;
2021-12-20 15:20:27 +01:00
return heldEntity ! = null ;
2021-06-21 02:21:20 -07:00
}
2021-12-04 12:35:33 +01:00
public bool IsHolding ( EntityUid entity )
2021-06-21 02:21:20 -07:00
{
foreach ( var hand in Hands )
{
if ( hand . HeldEntity = = entity )
return true ;
}
return false ;
}
2021-12-04 12:35:33 +01:00
public IEnumerable < EntityUid > GetAllHeldEntities ( )
2021-06-21 02:21:20 -07:00
{
2021-07-31 03:14:00 +02:00
foreach ( var hand in Hands )
2021-06-21 02:21:20 -07:00
{
2022-01-05 17:53:08 +13:00
if ( hand . HeldEntity ! = null )
2021-12-30 18:27:15 -08:00
yield return hand . HeldEntity . Value ;
2021-06-21 02:21:20 -07:00
}
}
2021-09-17 07:16:11 -07:00
/// <summary>
/// Returns the number of hands that have no items in them.
/// </summary>
/// <returns></returns>
public int GetFreeHands ( )
{
int acc = 0 ;
foreach ( var hand in Hands )
{
2021-12-30 18:27:15 -08:00
if ( hand . HeldEntity = = null )
2021-09-17 07:16:11 -07:00
acc + = 1 ;
}
return acc ;
}
2021-12-04 12:35:33 +01:00
public bool TryGetHandHoldingEntity ( EntityUid entity , [ NotNullWhen ( true ) ] out Hand ? handFound )
2021-06-21 02:21:20 -07:00
{
handFound = null ;
foreach ( var hand in Hands )
{
if ( hand . HeldEntity = = entity )
{
handFound = hand ;
return true ;
}
}
return false ;
}
#endregion
#region Dropping
/// <summary>
/// Checks all the conditions relevant to a player being able to drop an item.
/// </summary>
public bool CanDrop ( string handName , bool checkActionBlocker = true )
{
if ( ! TryGetHand ( handName , out var hand ) )
return false ;
if ( ! CanRemoveHeldEntityFromHand ( hand ) )
return false ;
2022-01-05 17:53:08 +13:00
if ( checkActionBlocker & & ! PlayerCanDrop )
2021-06-21 02:21:20 -07:00
return false ;
return true ;
}
/// <summary>
/// Tries to drop the contents of the active hand to the target location.
/// </summary>
2021-11-24 00:38:39 +01:00
public bool TryDropActiveHand ( EntityCoordinates targetDropLocation , bool doMobChecks = true )
2021-06-21 02:21:20 -07:00
{
if ( ! TryGetActiveHand ( out var hand ) )
return false ;
2021-11-24 00:38:39 +01:00
return TryDropHeldEntity ( hand , targetDropLocation , doMobChecks ) ;
2021-06-21 02:21:20 -07:00
}
/// <summary>
/// Tries to drop the contents of a hand to the target location.
/// </summary>
2021-11-24 00:38:39 +01:00
public bool TryDropHand ( string handName , EntityCoordinates targetDropLocation , bool checkActionBlocker = true )
2021-06-21 02:21:20 -07:00
{
if ( ! TryGetHand ( handName , out var hand ) )
return false ;
2021-11-24 00:38:39 +01:00
return TryDropHeldEntity ( hand , targetDropLocation , checkActionBlocker ) ;
2021-06-21 02:21:20 -07:00
}
/// <summary>
/// Tries to drop a held entity to the target location.
/// </summary>
2021-12-04 12:35:33 +01:00
public bool TryDropEntity ( EntityUid entity , EntityCoordinates coords , bool doMobChecks = true )
2021-06-21 02:21:20 -07:00
{
if ( ! TryGetHandHoldingEntity ( entity , out var hand ) )
return false ;
2021-11-24 00:38:39 +01:00
return TryDropHeldEntity ( hand , coords , doMobChecks ) ;
2021-06-21 02:21:20 -07:00
}
/// <summary>
/// Attempts to move the contents of a hand into a container that is not another hand, without dropping it on the floor inbetween.
/// </summary>
public bool TryPutHandIntoContainer ( string handName , BaseContainer targetContainer , bool checkActionBlocker = true )
{
if ( ! TryGetHand ( handName , out var hand ) )
return false ;
if ( ! CanPutHeldEntityIntoContainer ( hand , targetContainer , checkActionBlocker ) )
return false ;
PutHeldEntityIntoContainer ( hand , targetContainer ) ;
return true ;
}
/// <summary>
2021-11-08 15:08:24 +01:00
/// 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.
2021-06-21 02:21:20 -07:00
/// </summary>
2021-12-04 12:35:33 +01:00
public bool Drop ( EntityUid entity , BaseContainer targetContainer , bool checkActionBlocker = true )
2021-06-21 02:21:20 -07:00
{
if ( ! TryGetHandHoldingEntity ( entity , out var hand ) )
return false ;
if ( ! CanPutHeldEntityIntoContainer ( hand , targetContainer , checkActionBlocker ) )
return false ;
PutHeldEntityIntoContainer ( hand , targetContainer ) ;
return true ;
}
/// <summary>
/// Tries to drop the contents of a hand directly under the player.
/// </summary>
2021-11-24 00:38:39 +01:00
public bool Drop ( string handName , bool checkActionBlocker = true )
2021-06-21 02:21:20 -07:00
{
if ( ! TryGetHand ( handName , out var hand ) )
return false ;
2021-12-08 17:32:32 +01:00
return TryDropHeldEntity ( hand , _entMan . GetComponent < TransformComponent > ( Owner ) . Coordinates , checkActionBlocker ) ;
2021-06-21 02:21:20 -07:00
}
/// <summary>
/// Tries to drop a held entity directly under the player.
/// </summary>
2021-12-04 12:35:33 +01:00
public bool Drop ( EntityUid entity , bool checkActionBlocker = true )
2021-06-21 02:21:20 -07:00
{
if ( ! TryGetHandHoldingEntity ( entity , out var hand ) )
return false ;
2021-12-08 17:32:32 +01:00
return TryDropHeldEntity ( hand , _entMan . GetComponent < TransformComponent > ( Owner ) . Coordinates , checkActionBlocker ) ;
2021-06-21 02:21:20 -07:00
}
/// <summary>
/// Tries to remove the item in the active hand, without dropping it.
2021-11-08 15:08:24 +01:00
/// For transferring the held item to another location, like an inventory slot,
/// which shouldn't trigger the drop interaction
/// </summary>
2021-06-21 02:21:20 -07:00
public bool TryDropNoInteraction ( )
{
if ( ! TryGetActiveHand ( out var hand ) )
return false ;
if ( ! CanRemoveHeldEntityFromHand ( hand ) )
return false ;
2022-01-05 17:53:08 +13:00
EntitySystem . Get < SharedHandsSystem > ( ) . RemoveHeldEntityFromHand ( Owner , hand , this ) ;
2021-06-21 02:21:20 -07:00
return true ;
}
/// <summary>
/// Checks if the contents of a hand is able to be removed from its container.
/// </summary>
private bool CanRemoveHeldEntityFromHand ( Hand hand )
{
2021-12-20 15:20:27 +01:00
if ( hand . HeldEntity = = null )
2021-06-21 02:21:20 -07:00
return false ;
2022-01-05 17:53:08 +13:00
if ( ! hand . Container ! . CanRemove ( hand . HeldEntity . Value ) )
2021-06-21 02:21:20 -07:00
return false ;
return true ;
}
/// <summary>
/// Drops a hands contents to the target location.
/// </summary>
2021-11-24 00:38:39 +01:00
public void DropHeldEntity ( Hand hand , EntityCoordinates targetDropLocation )
2021-06-21 02:21:20 -07:00
{
2022-01-05 17:53:08 +13:00
if ( hand . HeldEntity = = null )
2021-06-21 02:21:20 -07:00
return ;
2022-01-05 17:53:08 +13:00
var heldEntity = hand . HeldEntity . Value ;
EntitySystem . Get < SharedHandsSystem > ( ) . RemoveHeldEntityFromHand ( Owner , hand , this ) ;
2021-06-21 02:21:20 -07:00
2021-11-24 00:38:39 +01:00
EntitySystem . Get < SharedInteractionSystem > ( ) . DroppedInteraction ( Owner , heldEntity ) ;
2021-06-21 02:21:20 -07:00
2021-12-08 17:32:32 +01:00
_entMan . GetComponent < TransformComponent > ( heldEntity ) . WorldPosition = GetFinalDropCoordinates ( targetDropLocation ) ;
2021-06-21 02:21:20 -07:00
}
/// <summary>
/// Calculates the final location a dropped item will end up at, accounting for max drop range and collision along the targeted drop path.
/// </summary>
2021-08-25 03:35:37 -07:00
private Vector2 GetFinalDropCoordinates ( EntityCoordinates targetCoords )
2021-06-21 02:21:20 -07:00
{
2021-12-08 17:32:32 +01:00
var origin = _entMan . GetComponent < TransformComponent > ( Owner ) . MapPosition ;
var target = targetCoords . ToMap ( _entMan ) ;
2021-06-21 02:21:20 -07:00
2021-08-25 03:35:37 -07:00
var dropVector = target . Position - origin . Position ;
var requestedDropDistance = dropVector . Length ;
2021-07-13 18:21:09 +10:00
if ( dropVector . Length > SharedInteractionSystem . InteractionRange )
{
dropVector = dropVector . Normalized * SharedInteractionSystem . InteractionRange ;
2021-08-25 03:35:37 -07:00
target = new MapCoordinates ( origin . Position + dropVector , target . MapId ) ;
2021-07-13 18:21:09 +10:00
}
2022-02-17 15:40:03 +13:00
var dropLength = EntitySystem . Get < SharedInteractionSystem > ( ) . UnobstructedDistance ( origin , target , predicate : e = > e = = Owner ) ;
2021-06-21 02:21:20 -07:00
2021-08-25 03:35:37 -07:00
if ( dropLength < requestedDropDistance )
return origin . Position + dropVector . Normalized * dropLength ;
return target . Position ;
2021-06-21 02:21:20 -07:00
}
/// <summary>
/// Tries to drop a hands contents to the target location.
/// </summary>
2021-11-24 00:38:39 +01:00
private bool TryDropHeldEntity ( Hand hand , EntityCoordinates location , bool checkActionBlocker )
2021-06-21 02:21:20 -07:00
{
if ( ! CanRemoveHeldEntityFromHand ( hand ) )
return false ;
2022-01-05 17:53:08 +13:00
if ( checkActionBlocker & & ! PlayerCanDrop )
2021-06-21 02:21:20 -07:00
return false ;
2021-11-24 00:38:39 +01:00
DropHeldEntity ( hand , location ) ;
2021-06-21 02:21:20 -07:00
return true ;
}
/// <summary>
/// Drops the contents of a hand directly under the player.
/// </summary>
2021-11-24 00:38:39 +01:00
private void DropHeldEntityToFloor ( Hand hand )
2021-06-21 02:21:20 -07:00
{
2021-12-08 17:32:32 +01:00
DropHeldEntity ( hand , _entMan . GetComponent < TransformComponent > ( Owner ) . Coordinates ) ;
2021-06-21 02:21:20 -07:00
}
private bool CanPutHeldEntityIntoContainer ( Hand hand , IContainer targetContainer , bool checkActionBlocker )
{
2022-01-05 17:53:08 +13:00
if ( hand . HeldEntity = = null )
2021-06-21 02:21:20 -07:00
return false ;
2022-01-05 17:53:08 +13:00
var heldEntity = hand . HeldEntity . Value ;
if ( checkActionBlocker & & ! PlayerCanDrop )
2021-06-21 02:21:20 -07:00
return false ;
if ( ! targetContainer . CanInsert ( heldEntity ) )
return false ;
return true ;
}
/// <summary>
/// For putting the contents of a hand into a container that is not another hand.
/// </summary>
private void PutHeldEntityIntoContainer ( Hand hand , IContainer targetContainer )
{
2022-01-05 17:53:08 +13:00
if ( hand . HeldEntity = = null )
2021-06-21 02:21:20 -07:00
return ;
2022-01-05 17:53:08 +13:00
var heldEntity = hand . HeldEntity . Value ;
EntitySystem . Get < SharedHandsSystem > ( ) . RemoveHeldEntityFromHand ( Owner , hand , this ) ;
2021-06-21 02:21:20 -07:00
if ( ! targetContainer . Insert ( heldEntity ) )
{
Logger . Error ( $"{nameof(SharedHandsComponent)} on {Owner} could not insert {heldEntity} into {targetContainer}." ) ;
return ;
}
}
#endregion
#region Pickup
2021-12-04 12:35:33 +01:00
public bool CanPickupEntity ( string handName , EntityUid entity , bool checkActionBlocker = true )
2021-06-21 02:21:20 -07:00
{
if ( ! TryGetHand ( handName , out var hand ) )
return false ;
2022-01-05 17:53:08 +13:00
if ( checkActionBlocker & & ! PlayerCanPickup )
2021-06-21 02:21:20 -07:00
return false ;
if ( ! CanInsertEntityIntoHand ( hand , entity ) )
return false ;
return true ;
}
2021-12-04 12:35:33 +01:00
public bool CanPickupEntityToActiveHand ( EntityUid entity , bool checkActionBlocker = true )
2021-06-21 02:21:20 -07:00
{
2021-07-31 03:14:00 +02:00
return ActiveHand ! = null & & CanPickupEntity ( ActiveHand , entity , checkActionBlocker ) ;
2021-06-21 02:21:20 -07:00
}
/// <summary>
/// Tries to pick up an entity to a specific hand.
/// </summary>
2022-01-05 17:53:08 +13:00
public bool TryPickupEntity ( string handName , EntityUid entity , bool checkActionBlocker = true , bool animateUser = false )
2021-06-21 02:21:20 -07:00
{
if ( ! TryGetHand ( handName , out var hand ) )
return false ;
2022-01-05 17:53:08 +13:00
return TryPickupEntity ( hand , entity , checkActionBlocker , animateUser ) ;
2021-06-21 02:21:20 -07:00
}
2022-01-05 17:53:08 +13:00
public bool TryPickupEntityToActiveHand ( EntityUid entity , bool checkActionBlocker = true , bool animateUser = false )
2021-06-21 02:21:20 -07:00
{
2022-01-05 17:53:08 +13:00
return ActiveHand ! = null & & TryPickupEntity ( ActiveHand , entity , checkActionBlocker , animateUser ) ;
2021-06-21 02:21:20 -07:00
}
/// <summary>
/// Checks if an entity can be put into a hand's container.
/// </summary>
2021-12-04 12:35:33 +01:00
protected bool CanInsertEntityIntoHand ( Hand hand , EntityUid entity )
2021-06-21 02:21:20 -07:00
{
2022-01-07 19:09:42 +13:00
var handContainer = hand . Container ;
if ( handContainer = = null ) return false ;
2022-01-05 17:53:08 +13:00
if ( ! _entMan . HasComponent < SharedItemComponent > ( entity ) )
return false ;
2022-01-07 19:09:42 +13:00
if ( _entMan . TryGetComponent ( entity , out IPhysBody ? physics ) & & physics . BodyType = = BodyType . Static )
return false ;
2021-06-21 02:21:20 -07:00
2021-12-29 15:57:20 +11:00
if ( ! handContainer . CanInsert ( entity ) ) return false ;
var @event = new AttemptItemPickupEvent ( ) ;
_entMan . EventBus . RaiseLocalEvent ( entity , @event ) ;
if ( @event . Cancelled ) return false ;
2021-06-21 02:21:20 -07:00
return true ;
}
2022-01-05 17:53:08 +13:00
private bool TryPickupEntity ( Hand hand , EntityUid entity , bool checkActionBlocker = true , bool animateUser = false )
2021-06-21 02:21:20 -07:00
{
if ( ! CanInsertEntityIntoHand ( hand , entity ) )
return false ;
2022-01-05 17:53:08 +13:00
if ( checkActionBlocker & & ! PlayerCanPickup )
2021-06-21 02:21:20 -07:00
return false ;
2022-01-05 17:53:08 +13:00
// animation
var handSys = EntitySystem . Get < SharedHandsSystem > ( ) ;
var coordinateEntity = _entMan . GetComponent < TransformComponent > ( Owner ) . Parent ? . Owner ? ? Owner ;
var initialPosition = EntityCoordinates . FromMap ( coordinateEntity , _entMan . GetComponent < TransformComponent > ( entity ) . MapPosition ) ;
var finalPosition = _entMan . GetComponent < TransformComponent > ( Owner ) . LocalPosition ;
handSys . PickupAnimation ( entity , initialPosition , finalPosition , animateUser ? null : Owner ) ;
handSys . PutEntityIntoHand ( Owner , hand , entity , this ) ;
2022-02-01 19:35:40 -08:00
2021-06-21 02:21:20 -07:00
return true ;
}
#endregion
#region Hand Interactions
/// <summary>
/// Get the name of the hand that a swap hands would result in.
/// </summary>
public bool TryGetSwapHandsResult ( [ NotNullWhen ( true ) ] out string? nextHand )
{
nextHand = null ;
if ( ! TryGetActiveHand ( out var activeHand ) | | Hands . Count = = 1 )
return false ;
var newActiveIndex = Hands . IndexOf ( activeHand ) + 1 ;
var finalHandIndex = Hands . Count - 1 ;
if ( newActiveIndex > finalHandIndex )
newActiveIndex = 0 ;
2021-07-31 03:14:00 +02:00
nextHand = Hands [ newActiveIndex ] . Name ;
2021-06-21 02:21:20 -07:00
return true ;
}
/// <summary>
/// Attempts to interact with the item in a hand using the active held item.
/// </summary>
2022-02-15 17:06:52 +13:00
public void InteractHandWithActiveHand ( string handName )
2021-06-21 02:21:20 -07:00
{
if ( ! TryGetActiveHeldEntity ( out var activeHeldEntity ) )
return ;
if ( ! TryGetHeldEntity ( handName , out var heldEntity ) )
return ;
if ( activeHeldEntity = = heldEntity )
return ;
2022-02-15 17:06:52 +13:00
EntitySystem . Get < SharedInteractionSystem > ( )
2021-12-30 18:27:15 -08:00
. InteractUsing ( Owner , activeHeldEntity . Value , heldEntity . Value , EntityCoordinates . Invalid ) ;
2021-06-21 02:21:20 -07:00
}
2021-10-25 20:06:12 +13:00
public void ActivateItem ( bool altInteract = false )
2021-06-21 02:21:20 -07:00
{
if ( ! TryGetActiveHeldEntity ( out var heldEntity ) )
return ;
2022-01-05 02:23:01 +13:00
var sys = EntitySystem . Get < SharedInteractionSystem > ( ) ;
if ( altInteract )
sys . AltInteract ( Owner , heldEntity . Value ) ;
else
2022-02-15 17:06:52 +13:00
sys . UseInHandInteraction ( Owner , heldEntity . Value ) ;
2021-06-21 02:21:20 -07:00
}
public void ActivateHeldEntity ( string handName )
{
if ( ! TryGetHeldEntity ( handName , out var heldEntity ) )
return ;
2021-10-25 20:06:12 +13:00
EntitySystem . Get < SharedInteractionSystem > ( )
2022-02-15 17:06:52 +13:00
. InteractionActivate ( Owner , heldEntity . Value ) ;
2021-06-21 02:21:20 -07:00
}
/// <summary>
/// Moves an entity from one hand to the active hand.
/// </summary>
public bool TryMoveHeldEntityToActiveHand ( string handName , bool checkActionBlocker = true )
{
if ( ! TryGetHand ( handName , out var hand ) | | ! TryGetActiveHand ( out var activeHand ) )
return false ;
if ( ! TryGetHeldEntity ( handName , out var heldEntity ) )
return false ;
2021-12-30 18:27:15 -08:00
if ( ! CanInsertEntityIntoHand ( activeHand , heldEntity . Value ) | | ! CanRemoveHeldEntityFromHand ( hand ) )
2021-06-21 02:21:20 -07:00
return false ;
2022-01-05 17:53:08 +13:00
if ( checkActionBlocker & & ( ! PlayerCanDrop | | ! PlayerCanPickup ) )
2021-06-21 02:21:20 -07:00
return false ;
2022-01-05 17:53:08 +13:00
EntitySystem . Get < SharedHandsSystem > ( ) . RemoveHeldEntityFromHand ( Owner , hand , this ) ;
EntitySystem . Get < SharedHandsSystem > ( ) . PutEntityIntoHand ( Owner , activeHand , heldEntity . Value , this ) ;
2021-06-21 02:21:20 -07:00
return true ;
}
#endregion
private void HandCountChanged ( )
{
2021-12-08 17:32:32 +01:00
_entMan . EventBus . RaiseEvent ( EventSource . Local , new HandCountChangedEvent ( Owner ) ) ;
2021-06-21 02:21:20 -07:00
}
2021-10-25 20:06:12 +13:00
/// <summary>
/// Tries to pick up an entity into the active hand. If it cannot, tries to pick up the entity into each other hand.
/// </summary>
public bool PutInHand ( SharedItemComponent item , bool checkActionBlocker = true )
{
2022-01-05 17:53:08 +13:00
return PutInHand ( item . Owner , checkActionBlocker ) ;
2021-10-25 20:06:12 +13:00
}
2021-12-30 22:56:10 +01:00
/// <summary>
2022-01-05 17:53:08 +13:00
/// Puts an item any hand, prefering the active hand, or puts it on the floor under the player.
2021-12-30 22:56:10 +01:00
/// </summary>
2022-01-05 17:53:08 +13:00
public void PutInHandOrDrop ( EntityUid entity , bool checkActionBlocker = true )
2021-12-30 22:56:10 +01:00
{
2022-01-05 17:53:08 +13:00
if ( ! PutInHand ( entity , checkActionBlocker ) )
_entMan . GetComponent < TransformComponent > ( entity ) . Coordinates = _entMan . GetComponent < TransformComponent > ( Owner ) . Coordinates ;
2021-12-30 22:56:10 +01:00
}
2022-01-05 17:53:08 +13:00
public void PutInHandOrDrop ( SharedItemComponent item , bool checkActionBlocker = true )
2021-10-25 20:06:12 +13:00
{
2022-01-05 17:53:08 +13:00
PutInHandOrDrop ( item . Owner , checkActionBlocker ) ;
2021-10-25 20:06:12 +13:00
}
2022-01-05 17:53:08 +13:00
2021-06-21 02:21:20 -07:00
/// <summary>
/// Tries to pick up an entity into the active hand. If it cannot, tries to pick up the entity into each other hand.
/// </summary>
2022-01-05 17:53:08 +13:00
public bool PutInHand ( EntityUid entity , bool checkActionBlocker = true )
2021-06-21 02:21:20 -07:00
{
2022-01-05 17:53:08 +13:00
return PutInHand ( entity , GetActiveHand ( ) , checkActionBlocker ) ;
2021-06-21 02:21:20 -07:00
}
/// <summary>
/// Tries to pick up an entity into the priority hand, if provided. If it cannot, tries to pick up the entity into each other hand.
/// </summary>
2021-12-04 12:35:33 +01:00
public bool TryPutInAnyHand ( EntityUid entity , string? priorityHandName = null , bool checkActionBlocker = true )
2021-06-21 02:21:20 -07:00
{
Hand ? priorityHand = null ;
if ( priorityHandName ! = null )
2021-07-31 03:14:00 +02:00
priorityHand = GetHandOrNull ( priorityHandName ) ;
2021-06-21 02:21:20 -07:00
2022-01-05 17:53:08 +13:00
return PutInHand ( entity , priorityHand , checkActionBlocker ) ;
2021-06-21 02:21:20 -07:00
}
/// <summary>
/// Tries to pick up an entity into the priority hand, if provided. If it cannot, tries to pick up the entity into each other hand.
/// </summary>
2022-01-05 17:53:08 +13:00
private bool PutInHand ( EntityUid entity , Hand ? priorityHand = null , bool checkActionBlocker = true )
2021-06-21 02:21:20 -07:00
{
if ( priorityHand ! = null )
{
if ( TryPickupEntity ( priorityHand , entity , checkActionBlocker ) )
return true ;
}
foreach ( var hand in Hands )
{
if ( TryPickupEntity ( hand , entity , checkActionBlocker ) )
return true ;
}
return false ;
}
2022-01-05 17:53:08 +13:00
/// <summary>
/// Checks if any hand can pick up an item.
/// </summary>
public bool CanPutInHand ( SharedItemComponent item , bool mobCheck = true )
{
var entity = item . Owner ;
if ( mobCheck & & ! PlayerCanPickup )
return false ;
2021-06-21 02:21:20 -07:00
2022-01-05 17:53:08 +13:00
foreach ( var hand in Hands )
{
if ( CanInsertEntityIntoHand ( hand , entity ) )
return true ;
}
return false ;
}
2021-06-21 02:21:20 -07:00
}
2021-10-29 01:34:59 +13:00
2022-01-05 17:53:08 +13:00
[Serializable, NetSerializable]
2022-02-16 00:23:23 -07:00
public sealed class Hand
2021-06-21 02:21:20 -07:00
{
[ViewVariables]
2021-07-31 03:14:00 +02:00
public string Name { get ; }
2021-06-21 02:21:20 -07:00
[ViewVariables]
2021-07-31 03:14:00 +02:00
public HandLocation Location { get ; }
2021-06-21 02:21:20 -07:00
/// <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.
/// </summary>
2022-01-05 17:53:08 +13:00
[ViewVariables, NonSerialized]
public ContainerSlot ? Container ;
2021-06-21 02:21:20 -07:00
[ViewVariables]
2022-01-05 17:53:08 +13:00
public EntityUid ? HeldEntity = > Container ? . ContainedEntity ;
2021-06-21 02:21:20 -07:00
2022-01-05 17:53:08 +13:00
public bool IsEmpty = > HeldEntity = = null ;
2021-07-31 03:14:00 +02:00
2022-01-05 17:53:08 +13:00
public Hand ( string name , HandLocation location , ContainerSlot ? container = null )
2021-06-21 02:21:20 -07:00
{
Name = name ;
Location = location ;
Container = container ;
}
2020-07-25 15:11:16 +02:00
}
2018-05-27 10:13:33 +02:00
[Serializable, NetSerializable]
2021-07-31 03:14:00 +02:00
public sealed class HandsComponentState : ComponentState
2017-09-26 21:27:48 +02:00
{
2022-01-05 17:53:08 +13:00
public List < Hand > Hands { get ; }
2021-06-21 02:21:20 -07:00
public string? ActiveHand { get ; }
2017-09-26 21:27:48 +02:00
2022-01-05 17:53:08 +13:00
public HandsComponentState ( List < Hand > hands , string? activeHand = null )
2017-09-26 21:27:48 +02:00
{
Hands = hands ;
2021-06-21 02:21:20 -07:00
ActiveHand = activeHand ;
2017-09-26 21:27:48 +02:00
}
}
2018-04-22 06:11:38 -05:00
/// <summary>
2020-01-17 18:41:47 -08:00
/// A message that calls the use interaction on an item in hand, presumed for now the interaction will occur only on the active hand.
2018-04-22 06:11:38 -05:00
/// </summary>
[Serializable, NetSerializable]
2021-07-31 03:14:00 +02:00
public sealed class UseInHandMsg : EntityEventArgs
2018-04-22 06:11:38 -05:00
{
}
2018-11-21 20:58:11 +01:00
2020-01-17 18:41:47 -08:00
/// <summary>
2021-06-21 02:21:20 -07:00
/// A message that calls the activate interaction on the item in the specified hand.
2020-01-17 18:41:47 -08:00
/// </summary>
[Serializable, NetSerializable]
2022-02-16 00:23:23 -07:00
public sealed class ActivateInHandMsg : EntityEventArgs
2020-01-17 18:41:47 -08:00
{
2021-06-21 02:21:20 -07:00
public string HandName { get ; }
2020-01-17 18:41:47 -08:00
2021-06-21 02:21:20 -07:00
public ActivateInHandMsg ( string handName )
2020-01-17 18:41:47 -08:00
{
2021-06-21 02:21:20 -07:00
HandName = handName ;
2020-01-17 18:41:47 -08:00
}
}
2021-06-21 02:21:20 -07:00
/// <summary>
/// Uses the item in the active hand on the item in the specified hand.
/// </summary>
2018-11-21 20:58:11 +01:00
[Serializable, NetSerializable]
2022-02-16 00:23:23 -07:00
public sealed class ClientInteractUsingInHandMsg : EntityEventArgs
2018-11-21 20:58:11 +01:00
{
2021-06-21 02:21:20 -07:00
public string HandName { get ; }
2018-11-21 20:58:11 +01:00
2021-07-31 03:14:00 +02:00
public ClientInteractUsingInHandMsg ( string handName )
2018-11-21 20:58:11 +01:00
{
2021-06-21 02:21:20 -07:00
HandName = handName ;
2018-11-21 20:58:11 +01:00
}
}
2019-08-31 17:49:18 -07:00
2021-06-21 02:21:20 -07:00
/// <summary>
/// Moves an item from one hand to the active hand.
/// </summary>
2019-08-31 17:49:18 -07:00
[Serializable, NetSerializable]
2022-02-16 00:23:23 -07:00
public sealed class MoveItemFromHandMsg : EntityEventArgs
2019-08-31 17:49:18 -07:00
{
2021-06-21 02:21:20 -07:00
public string HandName { get ; }
2019-08-31 17:49:18 -07:00
2021-06-21 02:21:20 -07:00
public MoveItemFromHandMsg ( string handName )
2019-08-31 17:49:18 -07:00
{
2021-06-21 02:21:20 -07:00
HandName = handName ;
2020-10-28 10:16:40 +01:00
}
}
2021-06-21 02:21:20 -07:00
/// <summary>
/// What side of the body this hand is on.
/// </summary>
2020-07-25 15:11:16 +02:00
public enum HandLocation : byte
{
Left ,
Middle ,
Right
}
2021-02-03 22:07:13 +00:00
2022-02-16 00:23:23 -07:00
public sealed class HandCountChangedEvent : EntityEventArgs
2021-06-21 02:21:20 -07:00
{
2021-12-04 12:35:33 +01:00
public HandCountChangedEvent ( EntityUid sender )
2021-06-21 02:21:20 -07:00
{
Sender = sender ;
}
2021-12-04 12:35:33 +01:00
public EntityUid Sender { get ; }
2021-06-21 02:21:20 -07:00
}
2017-09-26 21:27:48 +02:00
}