2023-07-08 14:08:32 +10:00
using System.Numerics ;
2022-03-17 20:13:31 +13:00
using Content.Shared.Hands.Components ;
using Content.Shared.Interaction ;
2024-01-14 06:18:47 -04:00
using Content.Shared.Inventory.VirtualItem ;
2024-01-04 00:31:58 -05:00
using Content.Shared.Tag ;
2022-03-17 20:13:31 +13:00
using Robust.Shared.Containers ;
using Robust.Shared.Map ;
namespace Content.Shared.Hands.EntitySystems ;
2023-10-11 02:18:49 -07:00
public abstract partial class SharedHandsSystem
2022-03-17 20:13:31 +13:00
{
2024-01-04 00:31:58 -05:00
[Dependency] private readonly TagSystem _tagSystem = default ! ;
2023-04-23 21:38:52 +02:00
private void InitializeDrop ( )
{
SubscribeLocalEvent < HandsComponent , EntRemovedFromContainerMessage > ( HandleEntityRemoved ) ;
}
protected virtual void HandleEntityRemoved ( EntityUid uid , HandsComponent hands , EntRemovedFromContainerMessage args )
{
if ( ! TryGetHand ( uid , args . Container . ID , out var hand ) )
{
return ;
}
var gotUnequipped = new GotUnequippedHandEvent ( uid , args . Entity , hand ) ;
2023-10-11 02:18:49 -07:00
RaiseLocalEvent ( args . Entity , gotUnequipped ) ;
2023-04-23 21:38:52 +02:00
var didUnequip = new DidUnequipHandEvent ( uid , args . Entity , hand ) ;
2023-10-11 02:18:49 -07:00
RaiseLocalEvent ( uid , didUnequip ) ;
2023-12-25 02:33:32 -05:00
2024-01-14 06:18:47 -04:00
if ( TryComp ( args . Entity , out VirtualItemComponent ? @virtual ) )
_virtualSystem . DeleteVirtualItem ( ( args . Entity , @virtual ) , uid ) ;
2023-04-23 21:38:52 +02:00
}
2024-01-04 00:31:58 -05:00
private bool ShouldIgnoreRestrictions ( EntityUid user )
{
//Checks if the Entity is something that shouldn't care about drop distance or walls ie Aghost
return ! _tagSystem . HasTag ( user , "BypassDropChecks" ) ;
}
2023-11-13 23:43:03 +11:00
/// <summary>
/// Checks whether an entity can drop a given entity. Will return false if they are not holding the entity.
/// </summary>
public bool CanDrop ( EntityUid uid , EntityUid entity , HandsComponent ? handsComp = null , bool checkActionBlocker = true )
{
if ( ! Resolve ( uid , ref handsComp ) )
return false ;
if ( ! IsHolding ( uid , entity , out var hand , handsComp ) )
return false ;
return CanDropHeld ( uid , hand , checkActionBlocker ) ;
}
2022-03-17 20:13:31 +13:00
/// <summary>
/// Checks if the contents of a hand is able to be removed from its container.
/// </summary>
public bool CanDropHeld ( EntityUid uid , Hand hand , bool checkActionBlocker = true )
{
2023-09-11 09:42:41 +10:00
if ( hand . Container ? . ContainedEntity is not { } held )
2022-03-17 20:13:31 +13:00
return false ;
2023-10-11 02:18:49 -07:00
if ( ! ContainerSystem . CanRemove ( held , hand . Container ) )
2022-03-17 20:13:31 +13:00
return false ;
if ( checkActionBlocker & & ! _actionBlocker . CanDrop ( uid ) )
return false ;
return true ;
}
/// <summary>
/// Attempts to drop the item in the currently active hand.
/// </summary>
2023-04-07 11:21:12 -07:00
public bool TryDrop ( EntityUid uid , EntityCoordinates ? targetDropLocation = null , bool checkActionBlocker = true , bool doDropInteraction = true , HandsComponent ? handsComp = null )
2022-03-17 20:13:31 +13:00
{
if ( ! Resolve ( uid , ref handsComp ) )
return false ;
if ( handsComp . ActiveHand = = null )
return false ;
return TryDrop ( uid , handsComp . ActiveHand , targetDropLocation , checkActionBlocker , doDropInteraction , handsComp ) ;
}
/// <summary>
/// Drops an item at the target location.
/// </summary>
2023-04-07 11:21:12 -07:00
public bool TryDrop ( EntityUid uid , EntityUid entity , EntityCoordinates ? targetDropLocation = null , bool checkActionBlocker = true , bool doDropInteraction = true , HandsComponent ? handsComp = null )
2022-03-17 20:13:31 +13:00
{
if ( ! Resolve ( uid , ref handsComp ) )
return false ;
if ( ! IsHolding ( uid , entity , out var hand , handsComp ) )
return false ;
return TryDrop ( uid , hand , targetDropLocation , checkActionBlocker , doDropInteraction , handsComp ) ;
}
/// <summary>
/// Drops a hands contents at the target location.
/// </summary>
2023-04-07 11:21:12 -07:00
public bool TryDrop ( EntityUid uid , Hand hand , EntityCoordinates ? targetDropLocation = null , bool checkActionBlocker = true , bool doDropInteraction = true , HandsComponent ? handsComp = null )
2022-03-17 20:13:31 +13:00
{
if ( ! Resolve ( uid , ref handsComp ) )
return false ;
if ( ! CanDropHeld ( uid , hand , checkActionBlocker ) )
return false ;
var entity = hand . HeldEntity ! . Value ;
DoDrop ( uid , hand , doDropInteraction : doDropInteraction , handsComp ) ;
2023-12-25 02:33:32 -05:00
if ( TerminatingOrDeleted ( entity ) )
return true ;
2022-05-22 08:58:57 +12:00
var itemXform = Transform ( entity ) ;
2023-12-25 02:33:32 -05:00
if ( itemXform . MapUid = = null )
return true ;
var userXform = Transform ( uid ) ;
2023-10-11 02:18:49 -07:00
var isInContainer = ContainerSystem . IsEntityInContainer ( uid ) ;
2022-03-17 20:13:31 +13:00
2022-05-22 08:58:57 +12:00
if ( targetDropLocation = = null | | isInContainer )
2022-03-17 20:13:31 +13:00
{
2022-05-22 08:58:57 +12:00
// If user is in a container, drop item into that container. Otherwise, attach to grid or map.\
2022-03-17 20:13:31 +13:00
// TODO recursively check upwards for containers
2022-05-22 08:58:57 +12:00
if ( ! isInContainer
2023-10-11 02:18:49 -07:00
| | ! ContainerSystem . TryGetContainingContainer ( userXform . ParentUid , uid , out var container , skipExistCheck : true )
2023-12-27 21:30:03 -08:00
| | ! ContainerSystem . Insert ( ( entity , itemXform ) , container ) )
2023-10-11 02:18:49 -07:00
TransformSystem . AttachToGridOrMap ( entity , itemXform ) ;
2022-03-17 20:13:31 +13:00
return true ;
}
2023-10-11 02:18:49 -07:00
var target = targetDropLocation . Value . ToMap ( EntityManager , TransformSystem ) ;
2024-02-28 00:51:20 +11:00
TransformSystem . SetWorldPosition ( itemXform , GetFinalDropCoordinates ( uid , userXform . MapPosition , target ) ) ;
2022-03-17 20:13:31 +13:00
return true ;
}
/// <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.
/// </summary>
2023-09-11 09:42:41 +10:00
public bool TryDropIntoContainer ( EntityUid uid , EntityUid entity , BaseContainer targetContainer , bool checkActionBlocker = true , HandsComponent ? handsComp = null )
2022-03-17 20:13:31 +13:00
{
if ( ! Resolve ( uid , ref handsComp ) )
return false ;
if ( ! IsHolding ( uid , entity , out var hand , handsComp ) )
return false ;
if ( ! CanDropHeld ( uid , hand , checkActionBlocker ) )
return false ;
2023-10-11 02:18:49 -07:00
if ( ! ContainerSystem . CanInsert ( entity , targetContainer ) )
2022-03-17 20:13:31 +13:00
return false ;
DoDrop ( uid , hand , false , handsComp ) ;
2023-12-27 21:30:03 -08:00
ContainerSystem . Insert ( entity , targetContainer ) ;
2022-03-17 20:13:31 +13:00
return true ;
}
/// <summary>
2024-01-04 00:31:58 -05:00
/// Calculates the final location a dropped item will end up at, accounting for max drop range and collision along the targeted drop path, Does a check to see if a user should bypass those checks as well.
2022-03-17 20:13:31 +13:00
/// </summary>
private Vector2 GetFinalDropCoordinates ( EntityUid user , MapCoordinates origin , MapCoordinates target )
{
var dropVector = target . Position - origin . Position ;
2023-07-08 14:08:32 +10:00
var requestedDropDistance = dropVector . Length ( ) ;
2024-01-04 00:31:58 -05:00
var dropLength = dropVector . Length ( ) ;
2022-03-17 20:13:31 +13:00
2024-01-04 00:31:58 -05:00
if ( ShouldIgnoreRestrictions ( user ) )
2022-03-17 20:13:31 +13:00
{
2024-01-04 00:31:58 -05:00
if ( dropVector . Length ( ) > SharedInteractionSystem . InteractionRange )
{
dropVector = dropVector . Normalized ( ) * SharedInteractionSystem . InteractionRange ;
target = new MapCoordinates ( origin . Position + dropVector , target . MapId ) ;
}
2022-03-17 20:13:31 +13:00
2024-01-04 00:31:58 -05:00
dropLength = _interactionSystem . UnobstructedDistance ( origin , target , predicate : e = > e = = user ) ;
}
2022-03-17 20:13:31 +13:00
if ( dropLength < requestedDropDistance )
2023-07-08 14:08:32 +10:00
return origin . Position + dropVector . Normalized ( ) * dropLength ;
2022-03-17 20:13:31 +13:00
return target . Position ;
}
/// <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.
/// </summary>
2023-04-07 11:21:12 -07:00
public virtual void DoDrop ( EntityUid uid , Hand hand , bool doDropInteraction = true , HandsComponent ? handsComp = null )
2022-03-17 20:13:31 +13:00
{
if ( ! Resolve ( uid , ref handsComp ) )
return ;
if ( hand . Container ? . ContainedEntity = = null )
return ;
var entity = hand . Container . ContainedEntity . Value ;
2023-12-09 20:00:53 -05:00
if ( TerminatingOrDeleted ( uid ) | | TerminatingOrDeleted ( entity ) )
return ;
2023-12-27 21:30:03 -08:00
if ( ! ContainerSystem . Remove ( entity , hand . Container ) )
2022-03-17 20:13:31 +13:00
{
2023-06-27 23:56:52 +10:00
Log . Error ( $"Failed to remove {ToPrettyString(entity)} from users hand container when dropping. User: {ToPrettyString(uid)}. Hand: {hand.Name}." ) ;
2022-03-17 20:13:31 +13:00
return ;
}
2023-10-11 02:18:49 -07:00
Dirty ( uid , handsComp ) ;
2022-03-17 20:13:31 +13:00
if ( doDropInteraction )
_interactionSystem . DroppedInteraction ( uid , entity ) ;
if ( hand = = handsComp . ActiveHand )
2023-10-11 02:18:49 -07:00
RaiseLocalEvent ( entity , new HandDeselectedEvent ( uid ) ) ;
2022-03-17 20:13:31 +13:00
}
}