2022-03-17 20:13:31 +13:00
using Content.Shared.Database ;
using Content.Shared.Hands.Components ;
using Content.Shared.Item ;
using Robust.Shared.Containers ;
using Robust.Shared.Map ;
using Robust.Shared.Physics ;
2022-09-14 17:26:26 +10:00
using Robust.Shared.Physics.Components ;
2022-03-17 20:13:31 +13:00
namespace Content.Shared.Hands.EntitySystems ;
public abstract partial class SharedHandsSystem : EntitySystem
{
2023-04-23 21:38:52 +02:00
private void InitializePickup ( )
{
SubscribeLocalEvent < HandsComponent , EntInsertedIntoContainerMessage > ( HandleEntityInserted ) ;
}
protected virtual void HandleEntityInserted ( EntityUid uid , HandsComponent hands , EntInsertedIntoContainerMessage args )
{
if ( ! TryGetHand ( uid , args . Container . ID , out var hand ) )
{
return ;
}
var didEquip = new DidEquipHandEvent ( uid , args . Entity , hand ) ;
RaiseLocalEvent ( uid , didEquip , false ) ;
var gotEquipped = new GotEquippedHandEvent ( uid , args . Entity , hand ) ;
RaiseLocalEvent ( args . Entity , gotEquipped , false ) ;
}
2023-03-06 06:12:08 +13:00
/// <summary>
/// Maximum pickup distance for which the pickup animation plays.
/// </summary>
public const float MaxAnimationRange = 10 ;
2022-03-17 20:13:31 +13:00
/// <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>
2023-03-06 06:12:08 +13:00
public bool TryPickup (
EntityUid uid ,
EntityUid entity ,
string? handName = null ,
bool checkActionBlocker = true ,
bool animateUser = false ,
bool animate = true ,
2023-04-07 11:21:12 -07:00
HandsComponent ? handsComp = null ,
2023-03-06 06:12:08 +13:00
ItemComponent ? item = null )
2022-03-17 20:13:31 +13:00
{
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 ;
2023-09-12 22:34:04 +10:00
return TryPickup ( uid , entity , hand , checkActionBlocker , animate , handsComp , item ) ;
2022-03-17 20:13:31 +13:00
}
/// <summary>
/// Attempts to pick up an item into any empty hand. Prioritizes the currently active hand.
/// </summary>
/// <remarks>
/// If one empty hand fails to pick up the item, this will NOT check other hands. If ever hand-specific item
/// restrictions are added, there a might need to be a TryPickupAllHands or something like that.
/// </remarks>
2023-03-06 06:12:08 +13:00
public bool TryPickupAnyHand (
EntityUid uid ,
EntityUid entity ,
bool checkActionBlocker = true ,
bool animateUser = false ,
bool animate = true ,
2023-04-07 11:21:12 -07:00
HandsComponent ? handsComp = null ,
2023-03-06 06:12:08 +13:00
ItemComponent ? item = null )
2022-03-17 20:13:31 +13:00
{
if ( ! Resolve ( uid , ref handsComp , false ) )
return false ;
if ( ! TryGetEmptyHand ( uid , out var hand , handsComp ) )
return false ;
2023-09-12 22:34:04 +10:00
return TryPickup ( uid , entity , hand , checkActionBlocker , animate , handsComp , item ) ;
2022-03-17 20:13:31 +13:00
}
2023-03-06 06:12:08 +13:00
public bool TryPickup (
EntityUid uid ,
EntityUid entity ,
Hand hand ,
bool checkActionBlocker = true ,
bool animate = true ,
2023-04-07 11:21:12 -07:00
HandsComponent ? handsComp = null ,
2023-03-06 06:12:08 +13:00
ItemComponent ? item = null )
2022-03-17 20:13:31 +13:00
{
if ( ! Resolve ( uid , ref handsComp , false ) )
return false ;
if ( ! Resolve ( entity , ref item , false ) )
return false ;
if ( ! CanPickupToHand ( uid , entity , hand , checkActionBlocker , handsComp , item ) )
return false ;
2023-03-06 06:12:08 +13:00
if ( animate )
2023-01-17 16:23:53 +13:00
{
2023-03-06 06:12:08 +13:00
var xform = Transform ( uid ) ;
var coordinateEntity = xform . ParentUid . IsValid ( ) ? xform . ParentUid : uid ;
2023-07-18 21:44:00 +10:00
var itemXform = Transform ( entity ) ;
2024-02-28 00:51:20 +11:00
var itemPos = itemXform . MapPosition ;
2023-03-06 06:12:08 +13:00
if ( itemPos . MapId = = xform . MapID
2024-02-28 00:51:20 +11:00
& & ( itemPos . Position - xform . MapPosition . Position ) . Length ( ) < = MaxAnimationRange
2023-03-06 06:12:08 +13:00
& & MetaData ( entity ) . VisibilityMask = = MetaData ( uid ) . VisibilityMask ) // Don't animate aghost pickups.
{
var initialPosition = EntityCoordinates . FromMap ( coordinateEntity , itemPos , EntityManager ) ;
2023-09-12 22:34:04 +10:00
_storage . PlayPickupAnimation ( entity , initialPosition , xform . Coordinates , itemXform . LocalRotation , uid ) ;
2023-03-06 06:12:08 +13:00
}
2023-01-17 16:23:53 +13:00
}
2022-03-17 20:13:31 +13:00
DoPickup ( uid , hand , entity , handsComp ) ;
return true ;
}
2023-04-22 14:40:36 +03:00
/// <summary>
/// Tries to pick up an entity into any hand, forcing to drop an item if there are no free hands
/// By default it does check if it's possible to drop items
/// </summary>
public bool TryForcePickupAnyHand ( EntityUid uid , EntityUid entity , bool checkActionBlocker = true , HandsComponent ? handsComp = null , ItemComponent ? item = null )
{
if ( ! Resolve ( uid , ref handsComp , false ) )
return false ;
if ( TryPickupAnyHand ( uid , entity , checkActionBlocker : checkActionBlocker , handsComp : handsComp ) )
return true ;
foreach ( var hand in handsComp . Hands . Values )
{
if ( TryDrop ( uid , hand , checkActionBlocker : checkActionBlocker , handsComp : handsComp ) & &
TryPickup ( uid , entity , hand , checkActionBlocker : checkActionBlocker , handsComp : handsComp ) )
{
return true ;
}
}
return false ;
}
2023-04-07 11:21:12 -07:00
public bool CanPickupAnyHand ( EntityUid uid , EntityUid entity , bool checkActionBlocker = true , HandsComponent ? handsComp = null , ItemComponent ? item = null )
2022-03-17 20:13:31 +13:00
{
if ( ! Resolve ( uid , ref handsComp , false ) )
return false ;
if ( ! TryGetEmptyHand ( uid , out var hand , handsComp ) )
return false ;
return CanPickupToHand ( uid , entity , hand , checkActionBlocker , handsComp , item ) ;
}
/// <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.
/// </summary>
2023-04-07 11:21:12 -07:00
public bool CanPickupToHand ( EntityUid uid , EntityUid entity , Hand hand , bool checkActionBlocker = true , HandsComponent ? handsComp = null , ItemComponent ? item = null )
2022-03-17 20:13:31 +13:00
{
if ( ! Resolve ( uid , ref handsComp , false ) )
return false ;
var handContainer = hand . Container ;
if ( handContainer = = null | | handContainer . ContainedEntity ! = null )
return false ;
2022-07-27 03:53:47 -07:00
if ( ! Resolve ( entity , ref item , false ) )
2022-03-17 20:13:31 +13:00
return false ;
if ( TryComp ( entity , out PhysicsComponent ? physics ) & & physics . BodyType = = BodyType . Static )
return false ;
if ( checkActionBlocker & & ! _actionBlocker . CanPickup ( uid , entity ) )
return false ;
// check can insert (including raising attempt events).
2023-10-11 02:18:49 -07:00
return ContainerSystem . CanInsert ( entity , handContainer ) ;
2022-03-17 20:13:31 +13:00
}
/// <summary>
2022-03-28 17:03:14 +13:00
/// Puts an item into any hand, preferring the active hand, or puts it on the floor.
2022-03-17 20:13:31 +13:00
/// </summary>
2023-03-06 06:12:08 +13:00
public void PickupOrDrop (
EntityUid ? uid ,
EntityUid entity ,
bool checkActionBlocker = true ,
bool animateUser = false ,
bool animate = true ,
2023-04-07 11:21:12 -07:00
HandsComponent ? handsComp = null ,
2023-03-06 06:12:08 +13:00
ItemComponent ? item = null )
2022-03-17 20:13:31 +13:00
{
if ( uid = = null
| | ! Resolve ( uid . Value , ref handsComp , false )
| | ! TryGetEmptyHand ( uid . Value , out var hand , handsComp )
2023-09-12 22:34:04 +10:00
| | ! TryPickup ( uid . Value , entity , hand , checkActionBlocker , animate , handsComp , item ) )
2022-03-17 20:13:31 +13:00
{
// 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.
2023-10-19 12:34:31 -07:00
ContainerSystem . AttachParentToContainerOrGrid ( ( entity , Transform ( entity ) ) ) ;
2022-03-17 20:13:31 +13:00
}
}
/// <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.
/// </summary>
2023-04-07 11:21:12 -07:00
public virtual void DoPickup ( EntityUid uid , Hand hand , EntityUid entity , HandsComponent ? hands = null )
2022-03-17 20:13:31 +13:00
{
if ( ! Resolve ( uid , ref hands ) )
return ;
var handContainer = hand . Container ;
if ( handContainer = = null | | handContainer . ContainedEntity ! = null )
return ;
2023-12-27 21:30:03 -08:00
if ( ! ContainerSystem . Insert ( entity , handContainer ) )
2022-03-17 20:13:31 +13:00
{
2023-06-27 23:56:52 +10:00
Log . Error ( $"Failed to insert {ToPrettyString(entity)} into users hand container when picking up. User: {ToPrettyString(uid)}. Hand: {hand.Name}." ) ;
2022-03-17 20:13:31 +13:00
return ;
}
2022-05-28 23:41:17 -07:00
_adminLogger . Add ( LogType . Pickup , LogImpact . Low , $"{ToPrettyString(uid):user} picked up {ToPrettyString(entity):entity}" ) ;
2022-03-17 20:13:31 +13:00
2023-09-12 22:34:04 +10:00
Dirty ( uid , hands ) ;
2022-03-17 20:13:31 +13:00
if ( hand = = hands . ActiveHand )
RaiseLocalEvent ( entity , new HandSelectedEvent ( uid ) , false ) ;
}
}