2020-06-21 18:31:56 -07:00
using System ;
2018-02-05 13:57:26 -06:00
using System.Linq ;
2019-06-30 00:01:41 +02:00
using Content.Server.GameObjects.Components.Mobs ;
2020-01-22 17:08:14 -05:00
using Content.Server.GameObjects.Components.Timing ;
2019-05-16 15:51:26 +02:00
using Content.Server.Interfaces.GameObjects ;
2020-07-06 14:27:03 -07:00
using Content.Server.Interfaces.GameObjects.Components.Interaction ;
2020-05-23 02:27:31 -07:00
using Content.Server.Utility ;
2020-04-21 14:38:35 +02:00
using Content.Shared.GameObjects.Components.Inventory ;
2020-07-06 14:27:03 -07:00
using Content.Shared.GameObjects.EntitySystemMessages ;
2020-06-13 16:10:26 +02:00
using Content.Shared.GameObjects.EntitySystems ;
2018-08-16 15:57:11 -07:00
using Content.Shared.Input ;
2019-05-16 15:51:26 +02:00
using JetBrains.Annotations ;
2020-06-18 22:52:44 +10:00
using Robust.Server.GameObjects ;
2019-04-15 21:11:38 -06:00
using Robust.Server.GameObjects.EntitySystems ;
using Robust.Server.Interfaces.Player ;
2020-07-07 01:00:29 +02:00
using Robust.Shared.Containers ;
2019-05-16 15:51:26 +02:00
using Robust.Shared.GameObjects ;
2020-01-11 14:12:20 -08:00
using Robust.Shared.GameObjects.Components ;
2019-05-16 15:51:26 +02:00
using Robust.Shared.Input ;
2020-05-31 14:32:05 -07:00
using Robust.Shared.Input.Binding ;
2019-05-16 15:51:26 +02:00
using Robust.Shared.Interfaces.GameObjects ;
2019-04-15 21:11:38 -06:00
using Robust.Shared.Interfaces.GameObjects.Components ;
2019-04-20 16:20:18 -07:00
using Robust.Shared.Interfaces.Map ;
using Robust.Shared.IoC ;
2019-05-16 15:51:26 +02:00
using Robust.Shared.Log ;
using Robust.Shared.Map ;
2020-02-07 08:54:56 -08:00
using Robust.Shared.Maths ;
2019-04-15 21:11:38 -06:00
using Robust.Shared.Players ;
2018-02-05 13:57:26 -06:00
2020-07-06 14:27:03 -07:00
namespace Content.Server.GameObjects.EntitySystems.Click
2018-02-05 13:57:26 -06:00
{
/// <summary>
/// Governs interactions during clicking on entities
/// </summary>
2019-05-16 15:51:26 +02:00
[UsedImplicitly]
2020-04-22 00:58:31 +10:00
public sealed class InteractionSystem : SharedInteractionSystem
2018-02-05 13:57:26 -06:00
{
2019-04-20 16:20:18 -07:00
#pragma warning disable 649
[Dependency] private readonly IMapManager _mapManager ;
#pragma warning restore 649
2018-08-16 15:57:11 -07:00
public override void Initialize ( )
{
2020-07-06 14:27:03 -07:00
SubscribeNetworkEvent < DragDropMessage > ( HandleDragDropMessage ) ;
2020-05-31 14:32:05 -07:00
CommandBinds . Builder
. Bind ( EngineKeyFunctions . Use ,
2020-06-18 22:52:44 +10:00
new PointerInputCmdHandler ( HandleClientUseItemInHand ) )
2020-05-31 14:32:05 -07:00
. Bind ( ContentKeyFunctions . WideAttack ,
new PointerInputCmdHandler ( HandleWideAttack ) )
. Bind ( ContentKeyFunctions . ActivateItemInWorld ,
new PointerInputCmdHandler ( HandleActivateItemInWorld ) )
. Register < InteractionSystem > ( ) ;
2018-08-22 01:19:47 -07:00
}
2020-05-31 14:32:05 -07:00
public override void Shutdown ( )
{
CommandBinds . Unregister < InteractionSystem > ( ) ;
base . Shutdown ( ) ;
}
2020-02-28 17:52:18 +01:00
2020-07-06 14:27:03 -07:00
private void HandleDragDropMessage ( DragDropMessage msg , EntitySessionEventArgs args )
{
var performer = args . SenderSession . AttachedEntity ;
if ( ! EntityManager . TryGetEntity ( msg . Dropped , out var dropped ) ) return ;
if ( ! EntityManager . TryGetEntity ( msg . Target , out var target ) ) return ;
var interactionArgs = new DragDropEventArgs ( performer , msg . DropLocation , dropped , target ) ;
// must be in range of both the target and the object they are drag / dropping
if ( ! InteractionChecks . InRangeUnobstructed ( interactionArgs ) ) return ;
// trigger dragdrops on the dropped entity
foreach ( var dragDrop in dropped . GetAllComponents < IDragDrop > ( ) )
{
if ( dragDrop . DragDrop ( interactionArgs ) ) return ;
}
// trigger dragdropons on the targeted entity
foreach ( var dragDropOn in target . GetAllComponents < IDragDropOn > ( ) )
{
if ( dragDropOn . DragDropOn ( interactionArgs ) ) return ;
}
}
2019-09-17 16:08:45 -07:00
private bool HandleActivateItemInWorld ( ICommonSession session , GridCoordinates coords , EntityUid uid )
2018-08-22 01:19:47 -07:00
{
2019-05-16 15:51:26 +02:00
if ( ! EntityManager . TryGetEntity ( uid , out var used ) )
2019-09-17 16:08:45 -07:00
return false ;
2018-08-22 01:19:47 -07:00
var playerEnt = ( ( IPlayerSession ) session ) . AttachedEntity ;
2019-05-16 15:51:26 +02:00
if ( playerEnt = = null | | ! playerEnt . IsValid ( ) )
{
2019-09-17 16:08:45 -07:00
return false ;
2019-05-16 15:51:26 +02:00
}
2018-08-22 01:19:47 -07:00
2019-05-16 15:51:26 +02:00
if ( ! playerEnt . Transform . GridPosition . InRange ( _mapManager , used . Transform . GridPosition , InteractionRange ) )
{
2019-09-17 16:08:45 -07:00
return false ;
2019-05-16 15:51:26 +02:00
}
InteractionActivate ( playerEnt , used ) ;
2019-09-17 16:08:45 -07:00
return true ;
2019-05-16 15:51:26 +02:00
}
2018-08-22 01:19:47 -07:00
2020-01-17 18:41:47 -08:00
/// <summary>
/// Activates the Activate behavior of an object
/// Verifies that the user is capable of doing the use interaction first
/// </summary>
/// <param name="user"></param>
/// <param name="used"></param>
public void TryInteractionActivate ( IEntity user , IEntity used )
{
if ( user ! = null & & used ! = null & & ActionBlockerSystem . CanUse ( user ) )
{
InteractionActivate ( user , used ) ;
}
}
2019-05-16 15:51:26 +02:00
private void InteractionActivate ( IEntity user , IEntity used )
{
var activateMsg = new ActivateInWorldMessage ( user , used ) ;
2020-02-19 17:08:59 -08:00
RaiseLocalEvent ( activateMsg ) ;
2019-05-16 15:51:26 +02:00
if ( activateMsg . Handled )
{
2019-04-20 16:18:16 -07:00
return ;
2019-05-16 15:51:26 +02:00
}
2019-04-20 16:18:16 -07:00
if ( ! used . TryGetComponent ( out IActivate activateComp ) )
2019-05-16 15:51:26 +02:00
{
2019-04-20 16:18:16 -07:00
return ;
2019-05-16 15:51:26 +02:00
}
2019-04-20 16:18:16 -07:00
2020-05-23 02:27:31 -07:00
// all activates should only fire when in range / unbostructed
2020-05-23 17:23:25 +02:00
var activateEventArgs = new ActivateEventArgs { User = user , Target = used } ;
2020-05-23 02:27:31 -07:00
if ( InteractionChecks . InRangeUnobstructed ( activateEventArgs ) )
{
activateComp . Activate ( activateEventArgs ) ;
}
2018-08-16 15:57:11 -07:00
}
2020-05-03 14:26:49 +02:00
private bool HandleWideAttack ( ICommonSession session , GridCoordinates coords , EntityUid uid )
2018-08-16 15:57:11 -07:00
{
2018-10-25 17:29:33 -07:00
// client sanitization
2019-05-16 15:51:26 +02:00
if ( ! _mapManager . GridExists ( coords . GridID ) )
2018-10-25 17:29:33 -07:00
{
2018-11-11 11:32:05 -08:00
Logger . InfoS ( "system.interaction" , $"Invalid Coordinates: client={session}, coords={coords}" ) ;
2019-09-17 16:08:45 -07:00
return true ;
2018-11-11 11:32:05 -08:00
}
if ( uid . IsClientSide ( ) )
{
2019-05-16 15:51:26 +02:00
Logger . WarningS ( "system.interaction" ,
2020-01-26 03:38:51 +01:00
$"Client sent attack with client-side entity. Session={session}, Uid={uid}" ) ;
2019-09-17 16:08:45 -07:00
return true ;
2018-10-25 17:29:33 -07:00
}
2019-06-30 00:01:41 +02:00
var userEntity = ( ( IPlayerSession ) session ) . AttachedEntity ;
2019-09-06 10:13:48 -07:00
if ( userEntity = = null | | ! userEntity . IsValid ( ) )
{
2019-09-17 16:08:45 -07:00
return true ;
2019-09-06 10:13:48 -07:00
}
2019-06-30 00:01:41 +02:00
if ( userEntity . TryGetComponent ( out CombatModeComponent combatMode ) & & combatMode . IsInCombatMode )
{
2020-01-11 14:12:20 -08:00
DoAttack ( userEntity , coords ) ;
2019-06-30 00:01:41 +02:00
}
2020-01-26 03:38:51 +01:00
return true ;
}
2020-06-18 22:52:44 +10:00
/// <summary>
/// Entity will try and use their active hand at the target location.
/// Don't use for players
/// </summary>
/// <param name="entity"></param>
/// <param name="coords"></param>
/// <param name="uid"></param>
internal void UseItemInHand ( IEntity entity , GridCoordinates coords , EntityUid uid )
{
if ( entity . HasComponent < BasicActorComponent > ( ) )
{
throw new InvalidOperationException ( ) ;
}
if ( entity . TryGetComponent ( out CombatModeComponent combatMode ) & & combatMode . IsInCombatMode )
{
DoAttack ( entity , coords ) ;
}
else
{
UserInteraction ( entity , coords , uid ) ;
}
}
private bool HandleClientUseItemInHand ( ICommonSession session , GridCoordinates coords , EntityUid uid )
2020-01-26 03:38:51 +01:00
{
// client sanitization
if ( ! _mapManager . GridExists ( coords . GridID ) )
2019-06-30 00:01:41 +02:00
{
2020-01-26 03:38:51 +01:00
Logger . InfoS ( "system.interaction" , $"Invalid Coordinates: client={session}, coords={coords}" ) ;
return true ;
2019-06-30 00:01:41 +02:00
}
2019-09-17 16:08:45 -07:00
2020-01-26 03:38:51 +01:00
if ( uid . IsClientSide ( ) )
{
Logger . WarningS ( "system.interaction" ,
$"Client sent interaction with client-side entity. Session={session}, Uid={uid}" ) ;
return true ;
}
var userEntity = ( ( IPlayerSession ) session ) . AttachedEntity ;
if ( userEntity = = null | | ! userEntity . IsValid ( ) )
{
return true ;
}
UserInteraction ( userEntity , coords , uid ) ;
2019-09-17 16:08:45 -07:00
return true ;
2018-08-16 15:57:11 -07:00
}
2019-01-18 11:40:30 +01:00
private void UserInteraction ( IEntity player , GridCoordinates coordinates , EntityUid clickedUid )
2018-02-05 13:57:26 -06:00
{
2019-05-16 15:51:26 +02:00
// Get entity clicked upon from UID if valid UID, if not assume no entity clicked upon and null
2018-08-16 15:57:11 -07:00
if ( ! EntityManager . TryGetEntity ( clickedUid , out var attacked ) )
2019-05-16 15:51:26 +02:00
{
2018-08-22 01:19:47 -07:00
attacked = null ;
2019-05-16 15:51:26 +02:00
}
2018-05-10 19:21:15 +02:00
2019-05-16 15:51:26 +02:00
// Verify player has a transform component
2018-08-02 08:29:55 +02:00
if ( ! player . TryGetComponent < ITransformComponent > ( out var playerTransform ) )
2018-03-09 10:59:03 -06:00
{
return ;
}
2019-05-16 15:51:26 +02:00
// Verify player is on the same map as the entity he clicked on
2019-11-26 14:16:36 -08:00
if ( _mapManager . GetGrid ( coordinates . GridID ) . ParentMapId ! = playerTransform . MapID )
2018-02-05 13:57:26 -06:00
{
2019-05-16 15:51:26 +02:00
Logger . WarningS ( "system.interaction" ,
$"Player named {player.Name} clicked on a map he isn't located on" ) ;
2018-02-05 13:57:26 -06:00
return ;
}
2019-05-16 15:51:26 +02:00
// Verify player has a hand, and find what object he is currently holding in his active hand
2018-03-09 10:59:03 -06:00
if ( ! player . TryGetComponent < IHandsComponent > ( out var hands ) )
2018-02-05 13:57:26 -06:00
{
return ;
}
2018-03-09 10:59:03 -06:00
2018-11-11 11:32:05 -08:00
var item = hands . GetActiveHand ? . Owner ;
2018-03-09 10:59:03 -06:00
2020-05-23 15:33:01 +02:00
if ( ActionBlockerSystem . CanChangeDirection ( player ) )
{
var diff = coordinates . ToMapPos ( _mapManager ) - playerTransform . MapPosition . Position ;
if ( diff . LengthSquared > 0.01f )
{
playerTransform . LocalRotation = new Angle ( diff ) ;
}
}
2020-05-13 19:26:39 +02:00
2020-07-07 01:40:08 +02:00
if ( ! ActionBlockerSystem . CanInteract ( player ) )
{
return ;
}
// In a container where the attacked entity is not the container's owner and either the attacked entity is null, not contained or in a different container
if ( ContainerHelpers . TryGetContainer ( player , out var playerContainer ) & &
attacked ! = playerContainer . Owner & &
( attacked = = null | |
! ContainerHelpers . TryGetContainer ( attacked , out var attackedContainer ) | |
attackedContainer ! = playerContainer ) )
2018-02-05 13:57:26 -06:00
{
2018-03-09 10:59:03 -06:00
return ;
}
2019-05-16 15:51:26 +02:00
// TODO: Check if client should be able to see that object to click on it in the first place
// Clicked on empty space behavior, try using ranged attack
if ( attacked = = null )
2018-03-09 10:59:03 -06:00
{
2019-05-16 15:51:26 +02:00
if ( item ! = null )
{
2020-05-23 17:23:25 +02:00
// After attack: Check if we clicked on an empty location, if so the only interaction we can do is AfterInteract
InteractAfter ( player , item , coordinates ) ;
2019-05-16 15:51:26 +02:00
}
2018-04-22 06:11:38 -05:00
return ;
}
2019-05-16 15:51:26 +02:00
// Verify attacked object is on the map if we managed to click on it somehow
if ( ! attacked . Transform . IsMapTransform )
2018-04-22 06:11:38 -05:00
{
2019-05-16 15:51:26 +02:00
Logger . WarningS ( "system.interaction" ,
$"Player named {player.Name} clicked on object {attacked.Name} that isn't currently on the map somehow" ) ;
2018-02-05 13:57:26 -06:00
return ;
}
2020-05-23 17:23:25 +02:00
// RangedInteract/AfterInteract: Check distance between user and clicked item, if too large parse it in the ranged function
2019-05-16 15:51:26 +02:00
// TODO: have range based upon the item being used? or base it upon some variables of the player himself?
var distance = ( playerTransform . WorldPosition - attacked . Transform . WorldPosition ) . LengthSquared ;
if ( distance > InteractionRangeSquared )
2018-02-05 13:57:26 -06:00
{
2018-05-10 19:21:15 +02:00
if ( item ! = null )
2018-03-09 10:59:03 -06:00
{
2018-08-16 15:57:11 -07:00
RangedInteraction ( player , item , attacked , coordinates ) ;
2018-03-09 10:59:03 -06:00
return ;
}
2019-05-16 15:51:26 +02:00
2020-05-23 17:23:25 +02:00
return ; // Add some form of ranged InteractHand here if you need it someday, or perhaps just ways to modify the range of InteractHand
2018-02-05 13:57:26 -06:00
}
2018-05-10 19:21:15 +02:00
2019-05-16 15:51:26 +02:00
// We are close to the nearby object and the object isn't contained in our active hand
2020-05-23 17:23:25 +02:00
// InteractUsing/AfterInteract: We will either use the item on the nearby object
2018-03-09 10:59:03 -06:00
if ( item ! = null )
2018-02-05 13:57:26 -06:00
{
2018-08-16 15:57:11 -07:00
Interaction ( player , item , attacked , coordinates ) ;
2018-02-05 13:57:26 -06:00
}
2020-05-23 17:23:25 +02:00
// InteractHand/Activate: Since our hand is empty we will use InteractHand/Activate
2018-02-05 13:57:26 -06:00
else
{
2018-03-09 10:59:03 -06:00
Interaction ( player , attacked ) ;
}
}
/// <summary>
2020-05-23 17:23:25 +02:00
/// We didn't click on any entity, try doing an AfterInteract on the click location
2018-03-09 10:59:03 -06:00
/// </summary>
2020-05-23 17:23:25 +02:00
private void InteractAfter ( IEntity user , IEntity weapon , GridCoordinates clickLocation )
2018-03-09 10:59:03 -06:00
{
2020-07-06 14:27:03 -07:00
var message = new AfterInteractMessage ( user , weapon , null , clickLocation ) ;
2020-02-19 17:08:59 -08:00
RaiseLocalEvent ( message ) ;
2019-05-16 15:51:26 +02:00
if ( message . Handled )
{
2019-04-20 16:18:16 -07:00
return ;
2019-05-16 15:51:26 +02:00
}
2019-04-20 16:18:16 -07:00
2020-05-23 17:23:25 +02:00
var afterInteracts = weapon . GetAllComponents < IAfterInteract > ( ) . ToList ( ) ;
var afterInteractEventArgs = new AfterInteractEventArgs { User = user , ClickLocation = clickLocation } ;
2018-04-05 17:32:51 -05:00
2020-05-23 17:23:25 +02:00
foreach ( var afterInteract in afterInteracts )
2018-03-09 10:59:03 -06:00
{
2020-05-23 17:23:25 +02:00
afterInteract . AfterInteract ( afterInteractEventArgs ) ;
2018-02-05 13:57:26 -06:00
}
}
/// <summary>
/// Uses a weapon/object on an entity
2020-05-23 17:23:25 +02:00
/// Finds components with the InteractUsing interface and calls their function
2018-02-05 13:57:26 -06:00
/// </summary>
2019-05-16 15:51:26 +02:00
public void Interaction ( IEntity user , IEntity weapon , IEntity attacked , GridCoordinates clickLocation )
2018-02-05 13:57:26 -06:00
{
2020-07-06 14:27:03 -07:00
var attackMsg = new InteractUsingMessage ( user , weapon , attacked , clickLocation ) ;
2020-02-19 17:08:59 -08:00
RaiseLocalEvent ( attackMsg ) ;
2019-05-16 15:51:26 +02:00
if ( attackMsg . Handled )
{
2019-04-20 16:18:16 -07:00
return ;
2019-05-16 15:51:26 +02:00
}
2019-04-20 16:18:16 -07:00
2020-05-23 17:23:25 +02:00
var attackBys = attacked . GetAllComponents < IInteractUsing > ( ) . ToList ( ) ;
var attackByEventArgs = new InteractUsingEventArgs
2019-05-16 15:51:26 +02:00
{
2020-05-23 17:23:25 +02:00
User = user , ClickLocation = clickLocation , Using = weapon , Target = attacked
2019-05-16 15:51:26 +02:00
} ;
2018-02-05 13:57:26 -06:00
2020-05-23 02:27:31 -07:00
// all AttackBys should only happen when in range / unobstructed, so no range check is needed
if ( InteractionChecks . InRangeUnobstructed ( attackByEventArgs ) )
2018-02-05 13:57:26 -06:00
{
2020-05-23 02:27:31 -07:00
foreach ( var attackBy in attackBys )
2018-02-05 13:57:26 -06:00
{
2020-05-23 17:23:25 +02:00
if ( attackBy . InteractUsing ( attackByEventArgs ) )
2020-05-23 02:27:31 -07:00
{
2020-05-23 17:23:25 +02:00
// If an InteractUsing returns a status completion we finish our attack
2020-05-23 02:27:31 -07:00
return ;
}
2018-02-05 13:57:26 -06:00
}
2020-04-29 13:43:07 +02:00
}
2020-07-06 14:27:03 -07:00
var afterAtkMsg = new AfterInteractMessage ( user , weapon , attacked , clickLocation ) ;
2020-02-19 17:08:59 -08:00
RaiseLocalEvent ( afterAtkMsg ) ;
2019-04-20 16:18:16 -07:00
if ( afterAtkMsg . Handled )
2019-05-16 15:51:26 +02:00
{
2019-04-20 16:18:16 -07:00
return ;
2019-05-16 15:51:26 +02:00
}
2018-04-05 17:32:51 -05:00
2019-05-16 15:51:26 +02:00
// If we aren't directly attacking the nearby object, lets see if our item has an after attack we can do
2020-05-23 17:23:25 +02:00
var afterAttacks = weapon . GetAllComponents < IAfterInteract > ( ) . ToList ( ) ;
var afterAttackEventArgs = new AfterInteractEventArgs
2019-05-16 15:51:26 +02:00
{
2020-05-23 17:23:25 +02:00
User = user , ClickLocation = clickLocation , Target = attacked
2019-05-16 15:51:26 +02:00
} ;
2018-04-05 17:32:51 -05:00
2019-05-16 15:51:26 +02:00
foreach ( var afterAttack in afterAttacks )
2018-03-09 10:59:03 -06:00
{
2020-05-23 17:23:25 +02:00
afterAttack . AfterInteract ( afterAttackEventArgs ) ;
2018-03-09 10:59:03 -06:00
}
2018-02-05 13:57:26 -06:00
}
/// <summary>
/// Uses an empty hand on an entity
2020-05-23 17:23:25 +02:00
/// Finds components with the InteractHand interface and calls their function
2018-02-05 13:57:26 -06:00
/// </summary>
2019-04-20 16:18:16 -07:00
public void Interaction ( IEntity user , IEntity attacked )
2018-02-05 13:57:26 -06:00
{
2019-04-20 16:18:16 -07:00
var message = new AttackHandMessage ( user , attacked ) ;
2020-02-19 17:08:59 -08:00
RaiseLocalEvent ( message ) ;
2019-05-16 15:51:26 +02:00
if ( message . Handled )
{
2019-04-20 16:18:16 -07:00
return ;
2019-05-16 15:51:26 +02:00
}
2019-04-20 16:18:16 -07:00
2020-05-23 17:23:25 +02:00
var attackHands = attacked . GetAllComponents < IInteractHand > ( ) . ToList ( ) ;
var attackHandEventArgs = new InteractHandEventArgs { User = user , Target = attacked } ;
2018-02-05 13:57:26 -06:00
2020-05-23 02:27:31 -07:00
// all attackHands should only fire when in range / unbostructed
if ( InteractionChecks . InRangeUnobstructed ( attackHandEventArgs ) )
2018-02-05 13:57:26 -06:00
{
2020-05-23 02:27:31 -07:00
foreach ( var attackHand in attackHands )
2018-02-05 13:57:26 -06:00
{
2020-05-23 17:23:25 +02:00
if ( attackHand . InteractHand ( attackHandEventArgs ) )
2020-05-23 02:27:31 -07:00
{
2020-05-23 17:23:25 +02:00
// If an InteractHand returns a status completion we finish our attack
2020-05-23 02:27:31 -07:00
return ;
}
2018-02-05 13:57:26 -06:00
}
}
2019-05-16 15:51:26 +02:00
// Else we run Activate.
InteractionActivate ( user , attacked ) ;
2018-02-05 13:57:26 -06:00
}
2018-04-22 06:11:38 -05:00
/// <summary>
/// Activates the Use behavior of an object
/// Verifies that the user is capable of doing the use interaction first
/// </summary>
/// <param name="user"></param>
/// <param name="used"></param>
2019-04-20 16:18:16 -07:00
public void TryUseInteraction ( IEntity user , IEntity used )
2018-04-22 06:11:38 -05:00
{
2018-12-13 07:47:19 -06:00
if ( user ! = null & & used ! = null & & ActionBlockerSystem . CanUse ( user ) )
2018-04-22 06:11:38 -05:00
{
UseInteraction ( user , used ) ;
}
}
2018-02-05 13:57:26 -06:00
/// <summary>
/// Activates/Uses an object in control/possession of a user
2018-03-09 10:59:03 -06:00
/// If the item has the IUse interface on one of its components we use the object in our hand
2018-02-05 13:57:26 -06:00
/// </summary>
2019-04-20 16:18:16 -07:00
public void UseInteraction ( IEntity user , IEntity used )
2018-02-05 13:57:26 -06:00
{
2020-01-22 17:08:14 -05:00
if ( used . TryGetComponent < UseDelayComponent > ( out var delayComponent ) )
{
2020-01-26 03:38:51 +01:00
if ( delayComponent . ActiveDelay )
2020-01-22 19:37:07 -05:00
return ;
else
delayComponent . BeginDelay ( ) ;
2020-01-22 17:08:14 -05:00
}
2019-04-20 16:18:16 -07:00
var useMsg = new UseInHandMessage ( user , used ) ;
2020-02-19 17:08:59 -08:00
RaiseLocalEvent ( useMsg ) ;
2019-05-16 15:51:26 +02:00
if ( useMsg . Handled )
{
2019-04-20 16:18:16 -07:00
return ;
2019-05-16 15:51:26 +02:00
}
2019-04-20 16:18:16 -07:00
2019-05-16 15:51:26 +02:00
var uses = used . GetAllComponents < IUse > ( ) . ToList ( ) ;
2018-02-05 13:57:26 -06:00
2019-05-16 15:51:26 +02:00
// Try to use item on any components which have the interface
foreach ( var use in uses )
2018-02-05 13:57:26 -06:00
{
2019-05-16 15:51:26 +02:00
if ( use . UseEntity ( new UseEntityEventArgs { User = user } ) )
2018-02-05 13:57:26 -06:00
{
2019-05-16 15:51:26 +02:00
// If a Use returns a status completion we finish our attack
2018-02-05 13:57:26 -06:00
return ;
}
}
}
2018-03-09 10:59:03 -06:00
2019-07-18 23:33:02 +02:00
/// <summary>
2019-11-25 00:11:47 +01:00
/// Activates the Throw behavior of an object
/// Verifies that the user is capable of doing the throw interaction first
2019-07-18 23:33:02 +02:00
/// </summary>
public bool TryThrowInteraction ( IEntity user , IEntity item )
{
if ( user = = null | | item = = null | | ! ActionBlockerSystem . CanThrow ( user ) ) return false ;
ThrownInteraction ( user , item ) ;
return true ;
}
/// <summary>
/// Calls Thrown on all components that implement the IThrown interface
/// on an entity that has been thrown.
/// </summary>
public void ThrownInteraction ( IEntity user , IEntity thrown )
{
var throwMsg = new ThrownMessage ( user , thrown ) ;
2020-02-19 17:08:59 -08:00
RaiseLocalEvent ( throwMsg ) ;
2019-07-18 23:33:02 +02:00
if ( throwMsg . Handled )
{
return ;
}
var comps = thrown . GetAllComponents < IThrown > ( ) . ToList ( ) ;
// Call Thrown on all components that implement the interface
foreach ( var comp in comps )
{
comp . Thrown ( new ThrownEventArgs ( user ) ) ;
}
}
/// <summary>
/// Calls Land on all components that implement the ILand interface
/// on an entity that has landed after being thrown.
/// </summary>
public void LandInteraction ( IEntity user , IEntity landing , GridCoordinates landLocation )
{
var landMsg = new LandMessage ( user , landing , landLocation ) ;
2020-02-19 17:08:59 -08:00
RaiseLocalEvent ( landMsg ) ;
2019-07-18 23:33:02 +02:00
if ( landMsg . Handled )
{
return ;
}
var comps = landing . GetAllComponents < ILand > ( ) . ToList ( ) ;
// Call Land on all components that implement the interface
foreach ( var comp in comps )
{
comp . Land ( new LandEventArgs ( user , landLocation ) ) ;
}
}
2020-04-21 14:38:35 +02:00
/// <summary>
/// Calls Equipped on all components that implement the IEquipped interface
/// on an entity that has been equipped.
/// </summary>
public void EquippedInteraction ( IEntity user , IEntity equipped , EquipmentSlotDefines . Slots slot )
{
var equipMsg = new EquippedMessage ( user , equipped , slot ) ;
RaiseLocalEvent ( equipMsg ) ;
if ( equipMsg . Handled )
{
return ;
}
var comps = equipped . GetAllComponents < IEquipped > ( ) . ToList ( ) ;
// Call Thrown on all components that implement the interface
foreach ( var comp in comps )
{
comp . Equipped ( new EquippedEventArgs ( user , slot ) ) ;
}
}
/// <summary>
/// Calls Unequipped on all components that implement the IUnequipped interface
/// on an entity that has been equipped.
/// </summary>
public void UnequippedInteraction ( IEntity user , IEntity equipped , EquipmentSlotDefines . Slots slot )
{
var unequipMsg = new UnequippedMessage ( user , equipped , slot ) ;
RaiseLocalEvent ( unequipMsg ) ;
if ( unequipMsg . Handled )
{
return ;
}
var comps = equipped . GetAllComponents < IUnequipped > ( ) . ToList ( ) ;
// Call Thrown on all components that implement the interface
foreach ( var comp in comps )
{
comp . Unequipped ( new UnequippedEventArgs ( user , slot ) ) ;
}
}
2019-11-25 00:11:47 +01:00
/// <summary>
/// Activates the Dropped behavior of an object
/// Verifies that the user is capable of doing the drop interaction first
/// </summary>
public bool TryDroppedInteraction ( IEntity user , IEntity item )
{
if ( user = = null | | item = = null | | ! ActionBlockerSystem . CanDrop ( user ) ) return false ;
DroppedInteraction ( user , item ) ;
return true ;
}
/// <summary>
/// Calls Dropped on all components that implement the IDropped interface
/// on an entity that has been dropped.
/// </summary>
public void DroppedInteraction ( IEntity user , IEntity item )
{
var dropMsg = new DroppedMessage ( user , item ) ;
2020-02-19 17:08:59 -08:00
RaiseLocalEvent ( dropMsg ) ;
2019-11-25 00:11:47 +01:00
if ( dropMsg . Handled )
{
return ;
}
var comps = item . GetAllComponents < IDropped > ( ) . ToList ( ) ;
// Call Land on all components that implement the interface
foreach ( var comp in comps )
{
comp . Dropped ( new DroppedEventArgs ( user ) ) ;
}
}
/// <summary>
/// Calls HandSelected on all components that implement the IHandSelected interface
/// on an item entity on a hand that has just been selected.
/// </summary>
public void HandSelectedInteraction ( IEntity user , IEntity item )
{
2020-01-22 23:09:36 +01:00
var handSelectedMsg = new HandSelectedMessage ( user , item ) ;
2020-02-19 17:08:59 -08:00
RaiseLocalEvent ( handSelectedMsg ) ;
2020-01-22 23:09:36 +01:00
if ( handSelectedMsg . Handled )
2019-11-25 00:11:47 +01:00
{
return ;
}
var comps = item . GetAllComponents < IHandSelected > ( ) . ToList ( ) ;
// Call Land on all components that implement the interface
foreach ( var comp in comps )
{
comp . HandSelected ( new HandSelectedEventArgs ( user ) ) ;
}
}
/// <summary>
/// Calls HandDeselected on all components that implement the IHandDeselected interface
/// on an item entity on a hand that has just been deselected.
/// </summary>
public void HandDeselectedInteraction ( IEntity user , IEntity item )
{
2020-01-22 23:09:36 +01:00
var handDeselectedMsg = new HandDeselectedMessage ( user , item ) ;
2020-02-19 17:08:59 -08:00
RaiseLocalEvent ( handDeselectedMsg ) ;
2020-01-22 23:09:36 +01:00
if ( handDeselectedMsg . Handled )
2019-11-25 00:11:47 +01:00
{
return ;
}
var comps = item . GetAllComponents < IHandDeselected > ( ) . ToList ( ) ;
// Call Land on all components that implement the interface
foreach ( var comp in comps )
{
comp . HandDeselected ( new HandDeselectedEventArgs ( user ) ) ;
}
}
2018-03-09 10:59:03 -06:00
/// <summary>
/// Will have two behaviors, either "uses" the weapon at range on the entity if it is capable of accepting that action
/// Or it will use the weapon itself on the position clicked, regardless of what was there
/// </summary>
2019-04-20 16:18:16 -07:00
public void RangedInteraction ( IEntity user , IEntity weapon , IEntity attacked , GridCoordinates clickLocation )
2018-03-09 10:59:03 -06:00
{
2020-07-06 14:27:03 -07:00
var rangedMsg = new RangedInteractMessage ( user , weapon , attacked , clickLocation ) ;
2020-02-19 17:08:59 -08:00
RaiseLocalEvent ( rangedMsg ) ;
2019-05-16 15:51:26 +02:00
if ( rangedMsg . Handled )
2019-04-20 16:18:16 -07:00
return ;
2020-05-23 17:23:25 +02:00
var rangedAttackBys = attacked . GetAllComponents < IRangedInteract > ( ) . ToList ( ) ;
var rangedAttackByEventArgs = new RangedInteractEventArgs
2019-05-16 15:51:26 +02:00
{
2020-05-23 17:23:25 +02:00
User = user , Using = weapon , ClickLocation = clickLocation
2019-05-16 15:51:26 +02:00
} ;
2018-03-09 10:59:03 -06:00
2019-05-16 15:51:26 +02:00
// See if we have a ranged attack interaction
foreach ( var t in rangedAttackBys )
2018-03-09 10:59:03 -06:00
{
2020-05-23 17:23:25 +02:00
if ( t . RangedInteract ( rangedAttackByEventArgs ) )
2018-03-09 10:59:03 -06:00
{
2020-05-23 17:23:25 +02:00
// If an InteractUsing returns a status completion we finish our attack
2018-03-09 10:59:03 -06:00
return ;
}
}
2020-07-06 14:27:03 -07:00
var afterAtkMsg = new AfterInteractMessage ( user , weapon , attacked , clickLocation ) ;
2020-02-19 17:08:59 -08:00
RaiseLocalEvent ( afterAtkMsg ) ;
2019-05-16 15:51:26 +02:00
if ( afterAtkMsg . Handled )
return ;
2019-04-20 16:18:16 -07:00
2020-05-23 17:23:25 +02:00
var afterAttacks = weapon . GetAllComponents < IAfterInteract > ( ) . ToList ( ) ;
var afterAttackEventArgs = new AfterInteractEventArgs
2019-05-16 15:51:26 +02:00
{
2020-05-23 17:23:25 +02:00
User = user , ClickLocation = clickLocation , Target = attacked
2019-05-16 15:51:26 +02:00
} ;
2018-04-05 17:32:51 -05:00
2019-05-16 15:51:26 +02:00
//See if we have a ranged attack interaction
foreach ( var afterAttack in afterAttacks )
{
2020-05-23 17:23:25 +02:00
afterAttack . AfterInteract ( afterAttackEventArgs ) ;
2018-03-09 10:59:03 -06:00
}
}
2019-06-30 00:01:41 +02:00
2020-01-11 14:12:20 -08:00
private void DoAttack ( IEntity player , GridCoordinates coordinates )
2019-06-30 00:01:41 +02:00
{
// Verify player is on the same map as the entity he clicked on
2019-11-26 14:16:36 -08:00
if ( _mapManager . GetGrid ( coordinates . GridID ) . ParentMapId ! = player . Transform . MapID )
2019-06-30 00:01:41 +02:00
{
Logger . WarningS ( "system.interaction" ,
$"Player named {player.Name} clicked on a map he isn't located on" ) ;
return ;
}
2020-06-21 18:31:56 -07:00
if ( ! ActionBlockerSystem . CanAttack ( player ) )
2019-06-30 00:01:41 +02:00
{
return ;
}
2020-06-21 18:31:56 -07:00
var eventArgs = new AttackEventArgs ( player , coordinates ) ;
2019-06-30 00:01:41 +02:00
2020-06-21 18:31:56 -07:00
// Verify player has a hand, and find what object he is currently holding in his active hand
if ( player . TryGetComponent < IHandsComponent > ( out var hands ) )
2019-06-30 00:01:41 +02:00
{
2020-06-21 18:31:56 -07:00
var item = hands . GetActiveHand ? . Owner ;
if ( item ! = null )
{
var attacked = false ;
foreach ( var attackComponent in item . GetAllComponents < IAttack > ( ) )
{
attackComponent . Attack ( eventArgs ) ;
attacked = true ;
}
if ( attacked )
{
return ;
}
}
2019-06-30 00:01:41 +02:00
}
2020-06-21 18:31:56 -07:00
foreach ( var attackComponent in player . GetAllComponents < IAttack > ( ) )
2019-06-30 00:01:41 +02:00
{
attackComponent . Attack ( eventArgs ) ;
}
}
2018-02-05 13:57:26 -06:00
}
}