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 ;
namespace Content.Shared.Hands.EntitySystems ;
public abstract partial class SharedHandsSystem : EntitySystem
{
/// <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 , SharedHandsComponent ? handsComp = null , SharedItemComponent ? 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 , animateUser , handsComp , item ) ;
}
/// <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>
public bool TryPickupAnyHand ( EntityUid uid , EntityUid entity , bool checkActionBlocker = true , bool animateUser = false , SharedHandsComponent ? handsComp = null , SharedItemComponent ? item = null )
{
if ( ! Resolve ( uid , ref handsComp , false ) )
return false ;
if ( ! TryGetEmptyHand ( uid , out var hand , handsComp ) )
return false ;
return TryPickup ( uid , entity , hand , checkActionBlocker , animateUser , handsComp , item ) ;
}
public bool TryPickup ( EntityUid uid , EntityUid entity , Hand hand , bool checkActionBlocker = true , bool animateUser = false , SharedHandsComponent ? handsComp = null , SharedItemComponent ? item = null )
{
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 ;
// animation
var xform = Transform ( uid ) ;
var coordinateEntity = xform . ParentUid . IsValid ( ) ? xform . ParentUid : uid ;
var initialPosition = EntityCoordinates . FromMap ( EntityManager , coordinateEntity , Transform ( entity ) . MapPosition ) ;
PickupAnimation ( entity , initialPosition , xform . LocalPosition , animateUser ? null : uid ) ;
DoPickup ( uid , hand , entity , handsComp ) ;
return true ;
}
public bool CanPickupAnyHand ( EntityUid uid , EntityUid entity , bool checkActionBlocker = true , SharedHandsComponent ? handsComp = null , SharedItemComponent ? item = null )
{
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>
public bool CanPickupToHand ( EntityUid uid , EntityUid entity , Hand hand , bool checkActionBlocker = true , SharedHandsComponent ? handsComp = null , SharedItemComponent ? item = null )
{
if ( ! Resolve ( uid , ref handsComp , false ) )
return false ;
var handContainer = hand . Container ;
if ( handContainer = = null | | handContainer . ContainedEntity ! = null )
return false ;
2022-04-23 15:31:45 +12:00
if ( ! Resolve ( entity , ref item , false ) | | ! item . CanPickup )
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).
return handContainer . CanInsert ( entity , EntityManager ) ;
}
/// <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>
public void PickupOrDrop ( EntityUid ? uid , EntityUid entity , bool checkActionBlocker = true , bool animateUser = false , SharedHandsComponent ? handsComp = null , SharedItemComponent ? item = null )
{
if ( uid = = null
| | ! Resolve ( uid . Value , ref handsComp , false )
| | ! TryGetEmptyHand ( uid . Value , out var hand , handsComp )
| | ! TryPickup ( uid . Value , entity , hand , checkActionBlocker , animateUser , handsComp , item ) )
{
// 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.
Transform ( entity ) . AttachParentToContainerOrGrid ( EntityManager ) ;
}
}
/// <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>
public virtual void DoPickup ( EntityUid uid , Hand hand , EntityUid entity , SharedHandsComponent ? hands = null )
{
if ( ! Resolve ( uid , ref hands ) )
return ;
var handContainer = hand . Container ;
if ( handContainer = = null | | handContainer . ContainedEntity ! = null )
return ;
if ( ! handContainer . Insert ( entity , EntityManager ) )
{
Logger . Error ( $"{nameof(SharedHandsComponent)} on {uid} could not insert {entity} into {handContainer}." ) ;
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
Dirty ( hands ) ;
var didEquip = new DidEquipHandEvent ( uid , entity , hand ) ;
RaiseLocalEvent ( uid , didEquip , false ) ;
var gotEquipped = new GotEquippedHandEvent ( uid , entity , hand ) ;
2022-06-22 09:53:41 +10:00
RaiseLocalEvent ( entity , gotEquipped , true ) ;
2022-03-17 20:13:31 +13:00
// TODO this should REALLY be a cancellable thing, not a handled event.
// If one of the interactions resulted in the item being dropped, return early.
if ( gotEquipped . Handled )
return ;
if ( hand = = hands . ActiveHand )
RaiseLocalEvent ( entity , new HandSelectedEvent ( uid ) , false ) ;
}
public abstract void PickupAnimation ( EntityUid item , EntityCoordinates initialPosition , Vector2 finalPosition ,
EntityUid ? exclude ) ;
}